at.yawk.dbus.protocol.DbusConnector Maven / Gradle / Ivy
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package at.yawk.dbus.protocol;
import at.yawk.dbus.protocol.auth.AuthClient;
import at.yawk.dbus.protocol.auth.mechanism.AuthMechanism;
import at.yawk.dbus.protocol.auth.mechanism.ExternalAuthMechanism;
import at.yawk.dbus.protocol.codec.DbusMainProtocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.epoll.EpollDomainSocketChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.unix.DomainSocketAddress;
import java.io.StringReader;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.concurrent.CompletionStage;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
/**
* @author yawkat
*/
@Slf4j
public class DbusConnector {
private final Bootstrap bootstrap;
/**
* The consumer to use for initial messages.
*/
@Setter private MessageConsumer initialConsumer = MessageConsumer.DISCARD;
@Setter private AuthMechanism authMechanism;
public DbusConnector() {
bootstrap = new Bootstrap();
bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.config().setAutoRead(false);
}
});
}
/**
* Connect to the dbus server at the given {@link SocketAddress}.
*/
public DbusChannel connect(SocketAddress address) throws Exception {
Bootstrap localBootstrap = bootstrap.clone();
if (address instanceof DomainSocketAddress) {
localBootstrap.group(new EpollEventLoopGroup());
localBootstrap.channel(EpollDomainSocketChannel.class);
} else {
localBootstrap.group(new NioEventLoopGroup());
localBootstrap.channel(NioSocketChannel.class);
}
Channel channel = localBootstrap.connect(address).sync().channel();
AuthClient authClient = new AuthClient();
if (LoggingInboundAdapter.isEnabled()) {
channel.pipeline().addLast(new LoggingInboundAdapter());
}
channel.pipeline().addLast("auth", authClient);
channel.config().setAutoRead(true);
log.trace("Pipeline is now {}", channel.pipeline());
// I really don't get why dbus does this
channel.write(Unpooled.wrappedBuffer(new byte[]{ 0 }));
if (authMechanism == null) {
authMechanism = new ExternalAuthMechanism();
}
CompletionStage> completionPromise = authClient.startAuth(channel, authMechanism);
SwappableMessageConsumer swappableConsumer = new SwappableMessageConsumer(initialConsumer);
completionPromise.toCompletableFuture().thenRun(() -> {
channel.pipeline().replace("auth", "main", new DbusMainProtocol(swappableConsumer));
log.trace("Pipeline is now {}", channel.pipeline());
}).get();
DbusChannelImpl dbusChannel = new DbusChannelImpl(channel, swappableConsumer);
dbusChannel.write(MessageFactory.methodCall(
"/",
"org.freedesktop.DBus",
"org.freedesktop.DBus",
"Hello"
));
return dbusChannel;
}
public DbusChannel connect(DbusAddress address) throws Exception {
log.info("Connecting to dbus server at {}", address);
switch (address.getProtocol()) {
case "unix":
if (address.hasProperty("path")) {
return connect(new DomainSocketAddress(address.getProperty("path")));
} else if (address.hasProperty("abstract")) {
return connect(new DomainSocketAddress('\0' + address.getProperty("abstract")));
} else {
throw new IllegalArgumentException("Neither path nor abstract given in dbus url");
}
case "tcp":
String host = address.getProperty("host");
int port = Integer.parseInt(address.getProperty("port"));
return connect(new InetSocketAddress(host, port));
default:
throw new UnsupportedOperationException("Unsupported protocol " + address.getProtocol());
}
}
public DbusChannel connectSession() throws Exception {
String address = System.getenv("DBUS_SESSION_BUS_ADDRESS");
if (address == null) {
String machineId = new String(Files.readAllBytes(Paths.get("/etc/machine-id"))).trim();
String response = DbusUtil.callCommand("dbus-launch", "--autolaunch", machineId);
Properties properties = new Properties();
properties.load(new StringReader(response));
address = properties.getProperty("DBUS_SESSION_BUS_ADDRESS");
}
return connect(DbusAddress.parse(address));
}
public DbusChannel connectSystem() throws Exception {
// this is the default system socket location defined in dbus
return connect(DbusAddress.fromUnixSocket(Paths.get("/run/dbus/system_bus_socket")));
}
}