You need several things to make this work.
- Enable user services to run at boot time without user login (systemd linger).
- A systemd socket file to specify the D-Bus socket for systemd to allocate.
- A systemd service to launch the D-Bus session bus that launches, then sets the DBUS_SESSION_BUS_ADDRESS env var for other systemd services.
- Ensure your systemd
my-dbus-client.service files are of
Type=dbus or depend on the
dbus.socket unit to make sure they allocate the dbus session bus socket and start the dbus session service if it hasn't already been started.
First, to make Systemd services for a given user start at boot-time without login, you need to enable systemd user lingering - this only needs to be done once as root when configuring to enable it for a user:
# loginctl enable-linger otheruser
Next, if you're on a Debian-based system, for the next two steps, you can simply install the package dbus-user-session package:
# apt-get install dbus-user-session
If you're using some other distribution, want to do this manually, or just want to understand how it works continue on. Otherwise skip over the creation of
Create the file
/usr/lib/systemd/user/dbus.socket (note, on some distributions the user directory may be under
/lib instead of
/usr/lib) with the following content:
Description=D-Bus User Message Bus Socket
ExecStartPost=-/bin/systemctl --user set-environment DBUS_SESSION_BUS_ADDRESS=unix:path=%t/bus
DBUS_SESSION_BUS_ADDRESS to all services, which was your main concern, is addressed by the
ExecPostStart line below - all following services will have that set.
%t gets replaced with the
XDG_RUNTIME_DIR - a transient directory under
/run created by systemd specific to the user session that you can stuff files. If you wish to create this socket somewhere else, there's no reason you can't. Just make sure it's somewhere transient, or it gets cleaned up on reboot/session teardown.
I did have some issues trying to make the dbus unix socket an abstract one - systemd didn't seem to like the form
@ prefix for some reason.
Now create the file
/usr/lib/systemd/user/dbus.service with the following content:
Description=D-Bus User Message Bus
ExecStart=/usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation
ExecReload=/usr/bin/dbus-send --print-reply --session --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig
There is a little bit of magic that goes on here behind the scenes by systemd to pass in the already created unix socket to the dbus-daemon. Systemd uses the information from
dbus.socket to create the socket, and it's file descriptor gets set in the environment variable
LISTEN_FDS, which is passed into the
dbus-daemon. Special options listed above make dbus-daemon use the file descriptor passed in instead of creating a new one. This allows dbus clients to start parallel to the dbus-daemon starting without worries of the socket not existing.
Finally, create your own systemd user services, making sure that you either set the type to
BusName= to the name of one of the dbus service names that will be registered by this service, or by making sure
Requires=dbus.socket is specified in the Unit section.
Here is an example:
Description=Config Server Startup
You can place them in one of several places:
Enable your services with
systemctl --user enable <service name> and reboot, and everything should work.
man loginctl for linger
man pam_systemd for XDG_RUNTIME_DIR info
man systemd.service for Type=dbus, BusName=, and implicit dependency on dbus.socket
man sd_listen_fds for info about LISTEN_FDS environment variable
- https://wiki.archlinux.org/index.php/Systemd/User - general information on systemd user sessions