Skip to content
Benjamin Loison edited this page Sep 21, 2023 · 50 revisions

See also: DBus man page.

signal-cli can run in daemon mode and provides an experimental dbus interface.

  • Run in daemon mode (dbus session bus)

    signal-cli [-a ACCOUNT] daemon --dbus
    
  • Send a message via dbus

    signal-cli --dbus send -m "Message" [RECIPIENT [RECIPIENT ...]] [-a [ATTACHMENT [ATTACHMENT ...]]]
    

MacOS

MacOS and signal-cli

MacOS users can run the following commands if needed:

brew install dbus
brew services start dbus

The environment variable DBUS_LAUNCHD_SESSION_BUS_SOCKET needs to be set correctly. Normally, this is handled automatically upon login by the MacOS launchd system working with Homebrew. If the variable has not been set correctly, you will see error messages similar to those in the Troubleshooting section below. If that happens, issue this command:

export DBUS_LAUNCHD_SESSION_BUS_SOCKET=$(launchctl getenv DBUS_LAUNCHD_SESSION_BUS_SOCKET)

If the DBus service is not running at all, the environment variable will remain unset after issued this command. To test it:

printenv DBUS_LAUNCHD_SESSION_BUS_SOCKET

You should see a response like: /private/tmp/com.apple.launchd.DoArnyRzbQ/unix_domain_listener

If the response is empty, you must manually restart the DBus service and set the environment variable:

brew services restart dbus
export DBUS_LAUNCHD_SESSION_BUS_SOCKET=$(launchctl getenv DBUS_LAUNCHD_SESSION_BUS_SOCKET)

Note that this will work only from a normal Macintosh desktop session.

To use the system bus:

export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/usr/local/var/run/dbus/system_bus_socket

This works either from a desktop session or an SSH session.

MacOS and SSH

As mentioned, the session bus command lines above work only from a normal Macintosh desktop session, because launchctl is not available via SSH.

To work around this problem, there are two cases:

Case 1: dbus is already running

You can determine if dbus is already running with the command:

ps auxw|grep dbus|grep $(whoami)|grep session

If it is running, you can rely on the notion that the most recent listener in /private/tmp is assigned to this version of dbus. (This might not be 100% reliable in the future, so proceed at your own risk.) Use the command:

export DBUS_LAUNCHD_SESSION_BUS_SOCKET=$(find /private/tmp -user $(whoami) -type s -name unix_domain_listener -print0 | xargs -0 ls -rt|tail -n 1)

Case 2: dbus is not running

If you have determined that dbus is not running, you can choose your own address and set DBUS_LAUNCHD_SESSION_BUS_SOCKET to match it. For example, to use $HOME/Library/Caches/dbus/bus (make sure the directory exists and has permissions 0700):

mkdir -p $HOME/Library/Caches/dbus
chmod 0700 $HOME/Library/Caches/dbus
DBUS_LAUNCHD_SESSION_BUS_SOCKET=$HOME/Library/Caches/dbus/bus
/usr/local/Cellar/dbus/1.12.20/bin/dbus-daemon --nofork --session --address=unix:path=$DBUS_LAUNCHD_SESSION_BUS_SOCKET

MacOS and dbus-send

In order for dbus-send to work correctly, it needs to either be started from a terminal within a normal Mac desktop session, or it needs to have the environment variable DBUS_SESSION_BUS_ADDRESS set correctly.

The command to set the environment variable is:

export DBUS_SESSION_BUS_ADDRESS=unix:path=$DBUS_LAUNCHD_SESSION_BUS_SOCKET

System bus

To run on the system bus you need to take some additional steps. It’s advisable to run signal-cli as a separate unix user, the following steps assume you created a user named signal-cli. To run a service on the system bus, a config file is needed to allow the signal-cli user to take a name on the system bus. An example config file can be found in data/org.asamk.Signal.conf. This file also configures that any user can talk with the signal-cli daemon. The data/org.asamk.Signal.service and data/signal-cli.service files configure a dbus activated systemd service for signal-cli, so the service is automatically started when the dbus service is requested.

These steps, executed as root, should work on all distributions using systemd.

cp data/org.asamk.Signal.conf /etc/dbus-1/system.d/
cp data/org.asamk.Signal.service /usr/share/dbus-1/system-services/
cp data/signal-cli.service /etc/systemd/system/
sed -i -e "s|%dir%|<INSERT_INSTALL_PATH>|" /etc/systemd/system/signal-cli.service
systemctl daemon-reload
systemctl enable signal-cli.service
systemctl reload dbus.service

Mind the fact that signal-cli.service executes the signal-cli with "--config /var/lib/signal-cli". If you registered with user signal-cli, remove the config option.

Make sure to use "--dbus-system" with the send command, the service will be autostarted by dbus the first time it is requested.

On MacOSX (which uses launchd, not systemd), the corresponding command is

cp data/org.asamk.Signal.conf /usr/local/etc/dbus-1/system.d/

Send using dbus-send

To avoid the startup time of the JVM for the dbus client, you can use any dbus capable program to send messages via the signal-cli dbus daemon. Example with dbus-send:

dbus-send --session --type=method_call --print-reply --dest="org.asamk.Signal" /org/asamk/Signal org.asamk.Signal.sendMessage string:MessageText array:string: string:RECIPIENT

Example if you started the daemon without the optional USERNAME (exports all local users):

dbus-send --session --type=method_call --print-reply --dest="org.asamk.Signal" /org/asamk/Signal/_33123456789 org.asamk.Signal.sendMessage string:MessageText array:string: string:RECIPIENT

Keep in mind that if you use DBus as a system service and want to send a message using the dbus daemon, you must use --system instead of --session in the dbus send command.

The groupId is handled as an array of bytes rather than a Base64 string. (See #272.)

The byte array can be obtained from a base64-encoded string (which looks like ixZI93QgqmjNpM8V+E0= and can be found in signal-cli's config file, by default in ~/.local/share/signal-cli/data/<PHONE_NUMBER>.d/group-cache/). Note that any underscore must be converted into a slash (/) character to provide a valid base64-encoded string. To decode it to byte array, you can use python:

python3 -c 'import base64; print(",".join(str(i) for i in base64.b64decode("ixZI93QgqmjNpM8V+E0=".replace("_","/"))))'

or, using Bash shell syntax:

echo "ixZI93QgqmjNpM8V+E0="|sed 's/_/\//g' | base64 -d |xxd -p -c1|while read i; do printf "%d " $((16#${i})); done|sed 's/ /,/g'|sed 's/,$/\n/g'

To reverse the process:

python3 -c 'import base64; print(base64.b64encode(bytearray([139,22,72,247,116,32,170,104,205,164,207,21,248,77])))'

... or using Bash ...

echo "139,22,72,247,116,32,170,104,205,164,207,21,248,77"|sed 's/,/ /g'|while read i; do printf "%02x" ${i}; done|sed 's/^/00000000: /g'|xxd -r -c 256|base64

In the examples above, attachments are identified by the first array:string: argument (dbus-send arguments must be in the correct order). The last argument can either be a single string:RECIPIENT (as in the examples above) or an array:string:RECIPIENT1,RECIPIENT2,... (for dbus-send, an array argument consists of a comma-separated list).

Receive messages from signal-cli daemon

The signal-cli daemon publishes new messages to dbus.

Here's an example using python:

def msgRcv (timestamp, source, groupID, message, attachments):
   print ("Message", message, "received in group", signal.getGroupName (groupID))
   return

from pydbus import SystemBus
from gi.repository import GLib

bus = SystemBus()
loop = GLib.MainLoop()

signal = bus.get('org.asamk.Signal') 
# NOTE: when daemon was started without explicit `-u USERNAME`, replace the line above with
# signal = bus.get("org.asamk.Signal", "/org/asamk/Signal/_YOURPHONENUMBER")

signal.onMessageReceived = msgRcv
loop.run()

Here's an example using Perl:

#!/bin/perl

use Modern::Perl;

use Net::DBus;
use Net::DBus::Reactor;

sub msgRcv {
    my ($timestamp, $sender, $groupID, $message, $attachments) = @_;
    print "Message: $message\nSender: $sender\nTimestamp: $timestamp\nAttachments: $attachments\n";
    return;
}

my $bus = Net::DBus->system();
my $sig = $bus->get_service("org.asamk.Signal");
my $obj = $sig->get_object("/org/asamk/Signal","org.asamk.Signal");
my $sigid = $obj->connect_to_signal('MessageReceived', \&msgRcv);

my $reactor=Net::DBus::Reactor->main();
$reactor->run();

exit 0;

Troubleshooting

DBus exception when starting daemon

$ signal-cli daemon
org.freedesktop.dbus.exceptions.DBusException: Cannot Resolve Session Bus Address
	...

	--- OR --- 

org.freedesktop.dbus.exceptions.DBusException: Failed to connect to bus: No such file or directory
	...

Environment variable DBUS_SESSION_BUS_ADDRESS needs to be set. See #394 and links to other issues in it.

Under Mac OSX, the environment variable DBUS_LAUNCHD_SESSION_BUS_SOCKET needs to be set (see above). This can be done by issuing the command

export DBUS_LAUNCHD_SESSION_BUS_SOCKET=$(launchctl getenv DBUS_LAUNCHD_SESSION_BUS_SOCKET)

DBus exception when calling dbus-send via Cron under Ubuntu

  Failed to open connection to "session" message bus: Unable to autolaunch a dbus-daemon without a $DISPLAY for X11

Script started via Cron must export environment variable DBUS_SESSION_BUS_ADDRESS before calling dbus-send (Credits to Thomas)

export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus