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

org.freedesktop.dbus.bin.DBusDaemon Maven / Gradle / Ivy

Go to download

Improved version of the DBus-Java library provided by freedesktop.org (https://dbus.freedesktop.org/doc/dbus-java/). This is the OSGi compliant bundle of all required libraries in one bundle.

The newest version!
package org.freedesktop.dbus.bin;

import org.freedesktop.dbus.Marshalling;
import org.freedesktop.dbus.connections.BusAddress;
import org.freedesktop.dbus.connections.transports.AbstractTransport;
import org.freedesktop.dbus.connections.transports.TransportBuilder;
import org.freedesktop.dbus.connections.transports.TransportBuilder.SaslAuthMode;
import org.freedesktop.dbus.connections.transports.TransportConnection;
import org.freedesktop.dbus.errors.AccessDenied;
import org.freedesktop.dbus.errors.MatchRuleInvalid;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.interfaces.DBus;
import org.freedesktop.dbus.interfaces.DBus.NameOwnerChanged;
import org.freedesktop.dbus.interfaces.FatalException;
import org.freedesktop.dbus.interfaces.Introspectable;
import org.freedesktop.dbus.interfaces.Peer;
import org.freedesktop.dbus.messages.DBusSignal;
import org.freedesktop.dbus.messages.Message;
import org.freedesktop.dbus.messages.MessageFactory;
import org.freedesktop.dbus.messages.MethodCall;
import org.freedesktop.dbus.types.UInt32;
import org.freedesktop.dbus.types.Variant;
import org.freedesktop.dbus.utils.AddressBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * A replacement DBusDaemon
 */
public class DBusDaemon extends Thread implements Closeable {

    public static final int                                                     QUEUE_POLL_WAIT = 500;

    private static final String                                                 DBUS_BUSPATH    = "/org/freedesktop/DBus";
    private static final String                                                 DBUS_BUSNAME    = "org.freedesktop.DBus";
    private static final String[]                                               EMPTY_STRING_ARRAY = new String[0];

    private static final Logger                                                 LOGGER          =
            LoggerFactory.getLogger(DBusDaemon.class);

    private final Map                 conns           =
            new ConcurrentHashMap<>();
    private final Map                                 names           =
            Collections.synchronizedMap(new HashMap<>()); // required because of "null" key

    private final BlockingDeque>> outqueue       =
            new LinkedBlockingDeque<>();
    private final BlockingDeque>> inqueue        =
            new LinkedBlockingDeque<>();

    private final List                                        sigrecips       = new ArrayList<>();
    private final DBusServer                                                    dbusServer      = new DBusServer();

    private final DBusDaemonSenderThread                                        sender          =
            new DBusDaemonSenderThread();
    private final AtomicBoolean                                                 run             =
            new AtomicBoolean(false);
    private final AtomicInteger                                                 nextUnique      = new AtomicInteger(0);

    private final AbstractTransport                                             transport;

    public DBusDaemon(AbstractTransport _transport) {
        setName(getClass().getSimpleName() + "-Thread");
        transport = _transport;
        names.put(DBUS_BUSNAME, null);
    }

    private void send(ConnectionStruct _connStruct, Message _msg) {
        send(_connStruct, _msg, false);
    }

    private void send(ConnectionStruct _connStruct, Message _msg, boolean _head) {

        // send to all connections
        if (_connStruct == null) {
            LOGGER.trace("Queuing message {} for all connections", _msg);
            for (ConnectionStruct d : conns.keySet()) {
                if (d.connection == null || d.connection.getChannel() == null || !d.connection.getChannel().isConnected()) {
                    LOGGER.debug("Ignoring broadcast message for disconnected connection {}: {}", d.connection, _msg);
                } else {
                    if (_head) {
                        outqueue.addFirst(new Pair<>(_msg, new WeakReference<>(d)));
                    } else {
                        outqueue.addLast(new Pair<>(_msg, new WeakReference<>(d)));
                    }
                }
            }
        } else {
            LOGGER.trace("Queuing message {} for {}", _msg, _connStruct.unique);
            if (_head) {
                outqueue.addFirst(new Pair<>(_msg, new WeakReference<>(_connStruct)));
            } else {
                outqueue.addLast(new Pair<>(_msg, new WeakReference<>(_connStruct)));
            }
        }
    }

    @Override
    public void run() {
        run.set(true);
        sender.start();

        while (isRunning()) {
            try {
                Pair> pollFirst = inqueue.take();
                ConnectionStruct connectionStruct = pollFirst.second.get();
                if (connectionStruct != null) {
                    Message m = pollFirst.first;
                    logMessage(" Got message {} from {}", m, connectionStruct.unique);
                    MessageFactory messageFactory = connectionStruct.connection.getMessageFactory();
                    // check if they have hello'd
                    if (null == connectionStruct.unique && (!(m instanceof MethodCall) || !DBUS_BUSNAME.equals(m.getDestination()) || !"Hello".equals(m.getName()))) {
                        send(connectionStruct,
                            messageFactory.createError(DBUS_BUSNAME, null, "org.freedesktop.DBus.Error.AccessDenied", m.getSerial(), "s", "You must send a Hello message"));
                    } else {
                        try {
                            if (null != connectionStruct.unique) {
                                m.setSource(connectionStruct.unique);
                                LOGGER.trace("Updated source to {}", connectionStruct.unique);
                            }
                        } catch (DBusException _ex) {
                            LOGGER.debug("Error setting source", _ex);
                            send(connectionStruct, messageFactory.createError(DBUS_BUSNAME, null, "org.freedesktop.DBus.Error.GeneralError", m.getSerial(), "s", "Sending message failed"));
                        }

                        if (DBUS_BUSNAME.equals(m.getDestination())) {
                            dbusServer.handleMessage(connectionStruct, pollFirst.first);
                        } else {
                            if (m instanceof DBusSignal) {
                                List l;
                                synchronized (sigrecips) {
                                    l = new ArrayList<>(sigrecips);
                                }
                                List list = l;
                                for (ConnectionStruct d : list) {
                                    send(d, m);
                                }
                            } else {
                                ConnectionStruct dest = names.get(m.getDestination());

                                if (null == dest) {
                                    send(connectionStruct, messageFactory.createError(DBUS_BUSNAME, null,
                                            "org.freedesktop.DBus.Error.ServiceUnknown", m.getSerial(), "s",
                                            String.format("The name `%s' does not exist", m.getDestination())));
                                } else {
                                    send(dest, m);
                                }
                            }
                        }
                    }
                }

            } catch (DBusException _ex) {
                LOGGER.debug("Error processing connection", _ex);
            } catch (InterruptedException _ex) {
                LOGGER.debug("Interrupted");
                close();
                interrupt();
            }
        }

    }

    private static void logMessage(String _logStr, Message _m, String _connUniqueId) {
        Object logMsg = _m;
        if (_m != null && Introspectable.class.getName().equals(_m.getInterface()) && !LOGGER.isTraceEnabled()) {
            logMsg = "";
        }

        if (LOGGER.isTraceEnabled()) {
            LOGGER.trace(_logStr, logMsg, _connUniqueId);
        } else {
            LOGGER.debug(_logStr, _m, _connUniqueId);
        }
    }

    public synchronized boolean isRunning() {
        return run.get();
    }

    @Override
    public void close() {
        run.set(false);
        if (!conns.isEmpty()) {
            // disconnect all remaining connections
            Set connections = new HashSet<>(conns.keySet());
            for (ConnectionStruct c : connections) {
                removeConnection(c);
            }
        }
        sender.terminate();
        if (transport != null) {
            LOGGER.debug("Terminating transport {}", transport);
            try {
                // shutdown listener
                transport.close();
            } catch (IOException _ex) {
                LOGGER.debug("Error closing transport", _ex);
            }
        }
        interrupt();
    }

    private void removeConnection(ConnectionStruct _c) {

        DBusDaemonReaderThread oldThread = conns.remove(_c);

        if (oldThread != null) {
            LOGGER.debug("Terminating reader thread for {}", _c);
            oldThread.terminate();

            try {
                if (_c.connection != null) {
                    _c.connection.close();
                    LOGGER.debug("Terminated connection {}", _c.connection);
                }
            } catch (IOException _exIo) {
                LOGGER.debug("Error while closing socketchannel", _exIo);
            }
        }

        LOGGER.debug("Removing signal destination {}", _c);
        synchronized (sigrecips) {
            if (sigrecips.removeIf(e -> e.equals(_c))) {
                LOGGER.debug("Removed one or more signal destinations for {}", _c);
            }
        }

        LOGGER.debug("Removing name registration for {}", _c);
        synchronized (names) {
            List toRemove = new ArrayList<>();

            // find connection by name
            for (Entry e : names.entrySet()) {
                if (e.getValue() == _c) {
                    toRemove.add(e.getKey());
                }
            }

            // remove registered name and send signal to remaining connections
            for (String name : toRemove) {
                names.remove(name);
                try {
                    send(null, new NameOwnerChanged(DBUS_BUSPATH, name, _c.unique, ""));
                } catch (DBusException _ex) {
                    LOGGER.debug("Unable to change owner", _ex);
                }
            }
        }

    }

    void addSock(TransportConnection _s) {
        LOGGER.debug("New Client");

        ConnectionStruct c = new ConnectionStruct(_s);
        DBusDaemonReaderThread r = new DBusDaemonReaderThread(c);
        conns.put(c, r);
        r.start();
    }

    public static void syntax() {
        System.out.println("Syntax: DBusDaemon [--version] [-v] [--help] [-h] [--listen address] "
                + "[-l address] [--print-address] [-r] [--pidfile file] [-p file] [--addressfile file] "
                + "[--auth-mode AUTH_ANONYMOUS|AUTH_COOKIE|AUTH_EXTERNAL] [-m AUTH_ANONYMOUS|AUTH_COOKIE|AUTH_EXTERNAL]"
                + "[-a file] [--unix] [-u] [--tcp] [-t] ");
        System.exit(1);
    }

    public static void version() {
        System.out.println("D-Bus Java Version: " + System.getProperty("Version"));
        System.exit(1);
    }

    public static void saveFile(String _data, String _file) throws IOException {
        try (PrintWriter w = new PrintWriter(new FileOutputStream(_file))) {
            w.println(_data);
        }
    }

    public static void main(String[] _args) throws Exception {

        String addr = null;
        String pidfile = null;
        String addrfile = null;
        String authModeStr = null;
        boolean printaddress = false;
        boolean unix = true;
        boolean tcp = false;
        // parse options
        try {
            for (int i = 0; i < _args.length; i++) {
                if ("--help".equals(_args[i]) || "-h".equals(_args[i])) {
                    syntax();
                } else if ("--version".equals(_args[i]) || "-v".equals(_args[i])) {
                    version();
                } else if ("--listen".equals(_args[i]) || "-l".equals(_args[i])) {
                    addr = _args[++i];
                } else if ("--pidfile".equals(_args[i]) || "-p".equals(_args[i])) {
                    pidfile = _args[++i];
                } else if ("--addressfile".equals(_args[i]) || "-a".equals(_args[i])) {
                    addrfile = _args[++i];
                } else if ("--print-address".equals(_args[i]) || "-r".equals(_args[i])) {
                    printaddress = true;
                } else if ("--unix".equals(_args[i]) || "-u".equals(_args[i])) {
                    unix = true;
                    tcp = false;
                } else if ("--tcp".equals(_args[i]) || "-t".equals(_args[i])) {
                    tcp = true;
                    unix = false;
                } else if ("--auth-mode".equals(_args[i]) || "-m".equals(_args[i])) {
                    authModeStr = _args[++i];
                } else {
                    syntax();
                }
            }
        } catch (ArrayIndexOutOfBoundsException _ex) {
            syntax();
        }

        // generate a random address if none specified
        if (null == addr && unix) {
            addr = TransportBuilder.createDynamicSession("UNIX", true);
        } else if (null == addr && tcp) {
            addr = TransportBuilder.createDynamicSession("TCP", true);
        }

        BusAddress address = BusAddress.of(addr);

        // print address to stdout
        if (printaddress) {
            System.out.println(addr);
        }

        SaslAuthMode saslAuthMode = null;
        if (authModeStr != null) {
            String selectedMode = authModeStr;
            saslAuthMode = Arrays.stream(SaslAuthMode.values())
                    .filter(e -> e.name().toLowerCase().matches(selectedMode.toLowerCase()))
                    .findFirst()
                    .orElseThrow(() -> new IllegalArgumentException("Auth mode '" + selectedMode + "' unsupported"));
        }

        // print address to file
        if (null != addrfile) {
            saveFile(addr, addrfile);
        }

        // print PID to file
        if (null != pidfile) {
            saveFile(System.getProperty("Pid"), pidfile);
        }

        // start the daemon
        LOGGER.info("Binding to {}", addr);
        try (EmbeddedDBusDaemon daemon = new EmbeddedDBusDaemon(address)) {
            daemon.setSaslAuthMode(saslAuthMode);
            daemon.startInForeground();
        }

    }

    public static class ConnectionStruct {
        private final TransportConnection       connection;
        private String                          unique;

        ConnectionStruct(TransportConnection _c) {
            connection = _c;
        }

        @Override
        public String toString() {
            return null == unique ? ":?-?" : unique;
        }
    }

    public class DBusServer implements DBus, Introspectable, Peer {

        private final String machineId;
        private ConnectionStruct connStruct;

        public DBusServer() {
            machineId = AddressBuilder.createMachineId();
        }

        /**
         * Create a 'NameAcquired' signal manually.
* This is required because the implementation in DBusNameAquired is for receiving of this signal only. * * @param _connection connection * @param _name name to announce * * @return signal * @throws DBusException if signal creation fails */ private DBusSignal generateNameAcquiredSignal(TransportConnection _connection, String _name) throws DBusException { return _connection.getMessageFactory().createSignal(DBUS_BUSNAME, DBUS_BUSPATH, DBUS_BUSNAME, "NameAcquired", "s", _name); } /** * Create a 'NameOwnerChanged' signal manually.
* This is required because the implementation in DBusNameAquired is for receiving of this signal only. * * @param _connection connection * @param _name name to announce * @param _oldOwner previous owner * @param _newOwner new owner * * @return signal * @throws DBusException if signal creation fails */ private DBusSignal generatedNameOwnerChangedSignal(TransportConnection _connection, String _name, String _oldOwner, String _newOwner) throws DBusException { return _connection.getMessageFactory().createSignal(DBUS_BUSNAME, DBUS_BUSPATH, DBUS_BUSNAME, "NameOwnerChanged", "sss", _name, _oldOwner, _newOwner); } @Override public boolean isRemote() { return false; } @Override public String Hello() { synchronized (connStruct) { if (null != connStruct.unique) { throw new AccessDenied("Connection has already sent a Hello message"); } connStruct.unique = ":1." + nextUnique.incrementAndGet(); } names.put(connStruct.unique, connStruct); LOGGER.info("Client {} registered", connStruct.unique); try { send(connStruct, generateNameAcquiredSignal(connStruct.connection, connStruct.unique)); send(null, generatedNameOwnerChangedSignal(connStruct.connection, connStruct.unique, "", connStruct.unique)); } catch (DBusException _ex) { LOGGER.debug("", _ex); } return connStruct.unique; } @Override public String[] ListNames() { String[] ns; Set nss = names.keySet(); ns = nss.toArray(EMPTY_STRING_ARRAY); return ns; } @Override public boolean NameHasOwner(String _name) { return names.containsKey(_name); } @Override public String GetNameOwner(String _name) { ConnectionStruct owner = names.get(_name); String o; if (null == owner) { o = ""; } else { o = owner.unique; } return o; } @Override public UInt32 GetConnectionUnixUser(String _connectionName) { return new UInt32(0); } @Override public UInt32 StartServiceByName(String _name, UInt32 _flags) { return new UInt32(0); } @Override @SuppressWarnings("checkstyle:innerassignment") public UInt32 RequestName(String _name, UInt32 _flags) { boolean exists = false; synchronized (names) { if (!(exists = names.containsKey(_name))) { names.put(_name, connStruct); } } int rv; if (exists) { rv = DBUS_REQUEST_NAME_REPLY_EXISTS; } else { LOGGER.info("Client {} acquired name {}", connStruct.unique, _name); rv = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; try { send(connStruct, generateNameAcquiredSignal(connStruct.connection, _name)); send(null, generatedNameOwnerChangedSignal(connStruct.connection, _name, "", connStruct.unique)); } catch (DBusException _ex) { LOGGER.debug("", _ex); } } return new UInt32(rv); } @Override public UInt32 ReleaseName(String _name) { boolean exists = false; synchronized (names) { if (names.containsKey(_name) && names.get(_name).equals(connStruct)) { exists = names.remove(_name) != null; } } int rv; if (!exists) { rv = DBUS_RELEASE_NAME_REPLY_NON_EXISTANT; } else { LOGGER.info("Client {} acquired name {}", connStruct.unique, _name); rv = DBUS_RELEASE_NAME_REPLY_RELEASED; try { send(connStruct, new NameLost(DBUS_BUSPATH, _name)); send(null, new NameOwnerChanged(DBUS_BUSPATH, _name, connStruct.unique, "")); } catch (DBusException _ex) { LOGGER.debug("", _ex); } } return new UInt32(rv); } @Override public void AddMatch(String _matchrule) throws MatchRuleInvalid { LOGGER.trace("Adding match rule: {}", _matchrule); synchronized (sigrecips) { if (!sigrecips.contains(connStruct)) { sigrecips.add(connStruct); } } } @Override public void RemoveMatch(String _matchrule) throws MatchRuleInvalid { LOGGER.trace("Removing match rule: {}", _matchrule); } @Override public String[] ListQueuedOwners(String _name) { return new String[0]; } @Override public UInt32 GetConnectionUnixProcessID(String _connectionName) { return new UInt32(0); } @Override public Byte[] GetConnectionSELinuxSecurityContext(String _args) { return new Byte[0]; } @SuppressWarnings("unchecked") private void handleMessage(ConnectionStruct _connStruct, Message _msg) throws DBusException { LOGGER.trace("Handling message {} from {}", _msg, _connStruct.unique); if (!(_msg instanceof MethodCall)) { return; } Object[] args = _msg.getParameters(); Class[] cs = new Class[args.length]; for (int i = 0; i < cs.length; i++) { cs[i] = args[i].getClass(); } java.lang.reflect.Method meth = null; Object rv = null; MessageFactory messageFactory = _connStruct.connection.getMessageFactory(); try { meth = DBusServer.class.getMethod(_msg.getName(), cs); try { this.connStruct = _connStruct; rv = meth.invoke(dbusServer, args); if (null == rv) { send(_connStruct, messageFactory.createMethodReturn(DBUS_BUSNAME, (MethodCall) _msg, null), true); } else { String sig = Marshalling.getDBusType(meth.getGenericReturnType())[0]; send(_connStruct, messageFactory.createMethodReturn(DBUS_BUSNAME, (MethodCall) _msg, sig, rv), true); } } catch (InvocationTargetException _exIte) { LOGGER.debug("", _exIte); send(_connStruct, messageFactory.createError(DBUS_BUSNAME, _msg, _exIte.getCause())); } catch (DBusExecutionException _exDnEe) { LOGGER.debug("", _exDnEe); send(_connStruct, messageFactory.createError(DBUS_BUSNAME, _msg, _exDnEe)); } catch (Exception _ex) { LOGGER.debug("", _ex); send(_connStruct, messageFactory.createError(DBUS_BUSNAME, _connStruct.unique, "org.freedesktop.DBus.Error.GeneralError", _msg.getSerial(), "s", "An error occurred while calling " + _msg.getName())); } } catch (NoSuchMethodException _exNsm) { send(_connStruct, messageFactory.createError(DBUS_BUSNAME, _connStruct.unique, "org.freedesktop.DBus.Error.UnknownMethod", _msg.getSerial(), "s", "This service does not support " + _msg.getName())); } } @Override public String getObjectPath() { return null; } @Override public String Introspect() { return """ """; } @Override public void Ping() { // no-op } @Override public String[] ListActivatableNames() { return null; } @Override public Map> GetConnectionCredentials(String _busName) { return null; } @Override public Byte[] GetAdtAuditSessionData(String _busName) { return null; } @Override public void UpdateActivationEnvironment(Map[] _environment) { // no-op } @Override public String GetId() { return null; } @Override public String GetMachineId() { return machineId; } } public class DBusDaemonSenderThread extends Thread { private final Logger logger = LoggerFactory.getLogger(getClass()); private final AtomicBoolean running = new AtomicBoolean(false); // switch running status when thread begins public DBusDaemonSenderThread() { setName(getClass().getSimpleName().replace('$', '-')); } @Override public void run() { logger.debug(">>>> Sender thread started <<<<"); running.set(true); while (isRunning() && running.get()) { logger.trace("Acquiring lock on outqueue and blocking for data"); // block on outqueue try { Pair> pollFirst = outqueue.take(); if (pollFirst != null) { ConnectionStruct connectionStruct = pollFirst.second.get(); if (connectionStruct != null) { if (connectionStruct.connection.getChannel().isConnected()) { logger.debug(" Got message {} for {}", pollFirst.first, connectionStruct.unique); try { connectionStruct.connection.getWriter().writeMessage(pollFirst.first); } catch (IOException _ex) { logger.debug("Disconnecting client due to previous exception", _ex); removeConnection(connectionStruct); } } else { logger.warn("Connection to {} broken", connectionStruct.connection); removeConnection(connectionStruct); } } else { logger.info("Discarding {} connection reaped", pollFirst.first); } } } catch (InterruptedException _ex) { logger.debug("Got interrupted", _ex); currentThread().interrupt(); } } logger.debug(">>>> Sender Thread terminated <<<<"); } public synchronized void terminate() { running.set(false); interrupt(); } } public class DBusDaemonReaderThread extends Thread { private final Logger logger = LoggerFactory.getLogger(getClass()); private ConnectionStruct conn; private final WeakReference weakconn; private final AtomicBoolean running = new AtomicBoolean(false); public DBusDaemonReaderThread(ConnectionStruct _conn) { this.conn = _conn; weakconn = new WeakReference<>(_conn); setName(getClass().getSimpleName()); } public void terminate() { running.set(false); } @Override public void run() { logger.debug(">>>> Reader Thread started <<<<"); running.set(true); while (isRunning() && running.get()) { Message m = null; try { m = conn.connection.getReader().readMessage(); } catch (IOException _ex) { LOGGER.debug("Error reading message", _ex); removeConnection(conn); } catch (DBusException _ex) { LOGGER.debug("", _ex); if (_ex instanceof FatalException) { removeConnection(conn); } } if (null != m) { logMessage("Read {} from {}", m, conn.unique); inqueue.add(new Pair<>(m, weakconn)); } } conn = null; logger.debug(">>>> Reader Thread terminated <<<<"); } } static class Pair { private final A first; private final B second; Pair(A _first, B _second) { first = _first; second = _second; } @Override public int hashCode() { return Objects.hash(first, second); } @Override public boolean equals(Object _obj) { if (this == _obj) { return true; } if (!(_obj instanceof Pair other)) { return false; } return Objects.equals(first, other.first) && Objects.equals(second, other.second); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy