All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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")));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy