1 package org
.asamk
.signal
.dbus
;
3 import org
.asamk
.signal
.DbusConfig
;
4 import org
.asamk
.signal
.Shutdown
;
5 import org
.asamk
.signal
.commands
.exceptions
.CommandException
;
6 import org
.asamk
.signal
.commands
.exceptions
.IOErrorException
;
7 import org
.asamk
.signal
.commands
.exceptions
.UnexpectedErrorException
;
8 import org
.asamk
.signal
.commands
.exceptions
.UserErrorException
;
9 import org
.asamk
.signal
.manager
.Manager
;
10 import org
.asamk
.signal
.manager
.MultiAccountManager
;
11 import org
.freedesktop
.dbus
.connections
.IDisconnectCallback
;
12 import org
.freedesktop
.dbus
.connections
.impl
.DBusConnection
;
13 import org
.freedesktop
.dbus
.connections
.impl
.DBusConnectionBuilder
;
14 import org
.freedesktop
.dbus
.exceptions
.DBusException
;
15 import org
.slf4j
.Logger
;
16 import org
.slf4j
.LoggerFactory
;
18 import java
.io
.IOException
;
19 import java
.util
.ArrayList
;
20 import java
.util
.List
;
22 public class DbusHandler
implements AutoCloseable
{
24 private static final Logger logger
= LoggerFactory
.getLogger(DbusHandler
.class);
26 private final boolean isDbusSystem
;
27 private DBusConnection dBusConnection
;
28 private final String busname
;
30 private final List
<AutoCloseable
> closeables
= new ArrayList
<>();
31 private final DbusRunner dbusRunner
;
32 private final boolean noReceiveOnStart
;
35 final boolean isDbusSystem
,
38 final boolean noReceiveOnStart
40 this.isDbusSystem
= isDbusSystem
;
41 this.dbusRunner
= (connection
) -> {
43 exportDbusObject(connection
, DbusConfig
.getObjectPath(), m
).join();
44 } catch (InterruptedException ignored
) {
47 this.noReceiveOnStart
= noReceiveOnStart
;
48 this.busname
= busname
;
52 final boolean isDbusSystem
,
54 final MultiAccountManager c
,
55 final boolean noReceiveOnStart
57 this.isDbusSystem
= isDbusSystem
;
58 this.dbusRunner
= (connection
) -> {
59 final var signalControl
= new DbusSignalControlImpl(c
, DbusConfig
.getObjectPath());
60 connection
.exportObject(signalControl
);
62 c
.addOnManagerAddedHandler(m
-> {
63 final var thread
= exportManager(connection
, m
);
66 } catch (InterruptedException ignored
) {
69 c
.addOnManagerRemovedHandler(m
-> {
70 final var path
= DbusConfig
.getObjectPath(m
.getSelfNumber());
72 final var object
= connection
.getExportedObject(null, path
);
73 if (object
instanceof DbusSignalImpl dbusSignal
) {
75 closeables
.remove(dbusSignal
);
77 } catch (DBusException ignored
) {
81 final var initThreads
= c
.getManagers().stream().map(m
-> exportManager(connection
, m
)).toList();
83 for (var t
: initThreads
) {
86 } catch (InterruptedException ignored
) {
90 this.noReceiveOnStart
= noReceiveOnStart
;
91 this.busname
= busname
;
94 public void init() throws CommandException
{
95 if (dBusConnection
!= null) {
96 throw new AssertionError("DbusHandler already initialized");
98 final var busType
= isDbusSystem ? DBusConnection
.DBusBusType
.SYSTEM
: DBusConnection
.DBusBusType
.SESSION
;
99 logger
.debug("Starting DBus server on {} bus: {}", busType
, busname
);
101 dBusConnection
= DBusConnectionBuilder
.forType(busType
)
102 .withDisconnectCallback(new DisconnectCallback())
104 dbusRunner
.run(dBusConnection
);
105 } catch (DBusException e
) {
106 throw new UnexpectedErrorException("Dbus command failed: " + e
.getMessage(), e
);
107 } catch (UnsupportedOperationException e
) {
108 throw new UserErrorException("Failed to connect to Dbus: " + e
.getMessage(), e
);
112 dBusConnection
.requestBusName(busname
);
113 } catch (DBusException e
) {
114 throw new UnexpectedErrorException("Dbus command failed, maybe signal-cli dbus daemon is already running: "
115 + e
.getMessage(), e
);
118 logger
.info("Started DBus server on {} bus: {}", busType
, busname
);
122 public void close() throws Exception
{
123 if (dBusConnection
== null) {
126 dBusConnection
.close();
127 for (final var c
: new ArrayList
<>(closeables
)) {
131 dBusConnection
= null;
134 private Thread
exportDbusObject(final DBusConnection conn
, final String objectPath
, final Manager m
) {
135 final var signal
= new DbusSignalImpl(m
, conn
, objectPath
, noReceiveOnStart
);
136 closeables
.add(signal
);
138 return Thread
.ofPlatform().name("dbus-init-" + m
.getSelfNumber()).start(signal
::initObjects
);
141 private Thread
exportManager(final DBusConnection conn
, final Manager m
) {
142 final var objectPath
= DbusConfig
.getObjectPath(m
.getSelfNumber());
143 return exportDbusObject(conn
, objectPath
, m
);
146 private interface DbusRunner
{
148 void run(DBusConnection connection
) throws DBusException
;
151 private static final class DisconnectCallback
implements IDisconnectCallback
{
154 public void disconnectOnError(IOException ex
) {
155 logger
.debug("DBus daemon disconnected unexpectedly, shutting down");
156 Shutdown
.triggerShutdown(new IOErrorException("Unexpected dbus daemon disconnect", ex
));