Avoid Desktop Bus (D-Bus) bus activation

nosh pages:

D-Bus bus activation is the idea that if a service does not exist on the Desktop Bus, the bus controller dæmon will directly auto-start the service program on demand, when a client first asks for the service. The technical details can be found in the freedesktop.org doco on system bus activation.

It's a bad idea.

Problems

Conflicts with properly managed D-Bus services

The D-Bus bus controller dæmon isn't a service manager, and it has almost no mechanisms for dæmon management, such as resource limit controls, auto-restart control, log management, privilege/account management, and so forth. But common D-Bus services — PolicyKit, PackageKit, ModemManager, NetworkManager, ConsoleKit, DeviceKit (UDisks and UPower), GNOME Terminal, MATE Notification, GNOME Editor, and so forth — are dæmon processes, and require proper dæmon management.

The obvious thing to do is for the bus controller dæmon to hand off the task of starting and managing these dæmons to a dæmon management subsystem. Unfortunately, despite several such mangement systems having been in wide use since the 1990s and the turn of the century, the writers of D-Bus didn't make it capable of talking to any of them. They didn't even include upstart. The writers of upstart patched D-Bus to know about upstart job files, but the improvements never made it into D-Bus proper. Lennart Poettering explicitly blocked them in 2011.

The System Desktop Bus has but just two modes of operation:

Had the writers of D-Bus even merely handed things off to the service command, we would be in a a far better position, as various dæmon management systems implement either a full service command or some shim that translates to their own native mechanisms. Spawning D-Bus services under proper service management would not then be locked in to just one single service management system, as is currently the case. James Hunt of Canonical, discussing this very same systemd-only problem from an upstart perspective, wrote in 2013 in Ubuntu bug #1238514:

the ideal would be a consistent and generic service activation approach which does not require hard-coding particular init daemon names anywhere.

But instead what happens is that on non-systemd systems where there are properly configured managed services (for NetworkManager, ModemManager, PolicyKit, PackageKit, ConsoleKit, UPower, UDisks, et al.) there are conflicts. The most visible instance of such a conflict is a failure of the properly managed service to start because the D-Bus bus controller has decided that it is in non-systemd mode and directly spawned a prior, conflicting, and unmanaged, instance of the service.

Under nosh service management, the typical case will be one instance of the service that is a child of process #1 and another — repeatedly failing and restarting — instance of the service that is a child of the service manager. This is because the hand-rolled service spawning by the D-Bus bus controller also involves the completely unnecessary and wholly unwise step of double-fork()ing in order to "dæmonize", even though the processes are already dæmonized, since it is a dæmon, the bus controller dæmon, that is starting them in the first place and they are already running in a dæmon execution context.

NetworkManager respawning ModemManager unnecessarily

Other problems include D-Bus services that take it upon themselves to explicitly trigger the activation of other D-Bus services, rather than letting service management configuration under the control of the system administrator determine whether a service is to be started or not; or, worse, attempt to spawn other D-Bus services directly without even going through the bus activation mechanism.

The most prominent example of this is ModemManager. Most people have absolutely no need of this service, because their systems do not have modems. Yet older versions of NetworkManager would attempt to start ModemManager directly. This would conflict with explicit ModemManager services (like the one provided with nosh, or indeed the one provided with systemd) resulting in continual vain attempts by NetworkManager to bring up a second ModemManager service; and on systems where ModemManager is inappropriate (and the managed service, appropriately, disabled) continual vain attempts by NetworkManager to bring up a first ModemManger service.

OpenSUSE's bug list has bugs about this aspect of NetworkManager from 2011. In 2014 the system was changed; NetworkManager now, at least, uses indirect means (bus activation) to spawn ModemManager. See Mageia Bug #11725, GNOME Bug #703040, GNOME Bug #701229, RedHat Bug #948404, RedHat Bug #1018017, and Debian Bug #770871 for the lengthy and muddled details.

Alas, initial fixes were to use bus activation only when systemd is the service manager; even though D-Bus system bus activation is nominally available independent of what service manager is running. But even with that fixed it just brings NetworkManager to the same stage as the other services, where bus activation generates conflicting services everywhere but with systemd.

Limits inherited from the D-Bus controller

In the non-systemd mode, where the bus controller dæmon tries to spawn dæmons itself, if no helper program is used the server dæmon is forked and executed directly by the bus controller dæmon. It thus runs in the same service execution context as the bus controller. This context may have per-process or per-service limits that are appropriate to running just the bus controller, possibly spawning the occasional helper process, but inappropriate for the bus controller and all of the dæmons running alongside it to run all in one big service.

This causes surprising and hard to diagnose dæmon failures. For examples:

Avoidance

Since the D-Bus bus controller and its helper don't use widely available APIs such as the service command to talk to service management, there are two approaches remaining on non-systemd systems.

Just turn it off

One remaining approach is to just cut out all instances of bus activated D-Bus services and avoid bus activation entirely by making it a no-operation.

One hand-edits the relevant files in /usr/local/share/dbus-1/system-services/ so that the bus controller (helper) either uses service or systemctl to start the service indirectly or simply doesn't try to do anything at all. Files to edit are:

To make it not try to do anything at all, replace the command that the bus controller runs with

Exec=/bin/true

Interestingly, simply excising D-Bus system bus activation entirely is the route that has already been taken for some D-Bus services, where the vanilla D-Bus .service configuration files in place of describing how to spawn the service program now read:

Exec=/bin/false

The Avahi D-Bus service definition file, /usr/local/share/dbus-1/system-services/org.freedesktop.Avahi.service, that the Fedora Project uses as an exemplar of good D-Bus service definition, even gives this very explanation (albeit making the error that the service management in use in non-systemd mode must be System 5 rc) in a comment:

# This service should not be bus activated if systemd isn't running,
# so that activation won't conflict with the init script startup.
Exec=/bin/false

Sadly, and somewhat ironically, this approach is incorrect for several service activators, which expect service activation to exit with a success exit code and complain if it does not. Hence /bin/true rather than /bin/false.

Change the Desktop Bus helper program

The other remaining approach is to replace the helper program that comes bundled with the Desktop Bus dæmon program with a different helper program that does understand service managers other than systemd. The nosh toolset comes with one such replacement helper program, dbus-daemon-launch-helper, that can use system-control to speak to nosh service management, initctl to speak to upstart service management, or systemctl to speak to systemd service management.

One hand-edits /usr/local/etc/dbus-1/system.conf to replace the helper. Where it reads:

<!-- This is a setuid helper that is used to launch system services -->
<servicehelper>/usr/local/libexec/dbus-daemon-launch-helper</servicehelper>

Replace that with:

<!-- This is a non-setuid helper that is used to tell service management to launch system services -->
<servicehelper>/usr/local/bin/dbus-daemon-launch-helper</servicehelper>

The dbus-daemon service bundles provided out-of-the-box in the nosh toolset come pre-configured with per-service configuration files to which this substitution has already been applied.

The user account that the Desktop Bus dæmon process runs as, and hence that the helper runs as, is usually an unprivileged account such as (for example) messagebus. This raised two important points:

The dbus-daemon-launch-helper manual page explains in detail a short-cut that becomes available when one adopts the principles that the helper only deals with starting D-Bus services via service management, and that service management service names have a definite relationship to D-Bus .service file names.


© Copyright 2015,2016 Jonathan de Boyne Pollard. "Moral" rights asserted.
Permission is hereby granted to copy and to distribute this WWW page in its original, unmodified form as long as its last modification datestamp information is preserved.