So for various reasons I’ve got to thinking about D-Bus, the “system bus” that’s found its way into most (all?) Linux distributions and possibly some other OSes as well. I’m more and more coming to believe that D-Bus is too heavyweight a solution for the problems it was intended to solve – mostly because it requires a daemon, and because we don’t really the bus aspect (at least, not in the form in which it’s been implemented).
How did I come to this conclusion? Well, consider first that, in essence, D-Bus provides only the following functionality:
- Protocol (and associated library) for a simple RPC mechanism (you can call a method in a “remote” process, and receive a reply).
- Basic security for this RPC. Basically the policy specification allows different between different users and groups, and whether the user is “at the console”.
- Map service names to the processes that provide the service (for purposes of connecting to said service).
- “Bus activation”, i.e. starting a process which provides a service when the given service is requested but is not currently being provided.
That’s really all there is to it. The fact that these are the only functions of D-Bus is, in my view, a good thing; it fits with the mantra of “do one thing, and do it well”. However, the question is if D-Bus is actually needed at all. Consider the “traditional approach” where services each have their own unix-family socket and listen on that for client requests. How does that compare to the feature set of D-Bus? Well:
- Protocol is application specific (D-Bus wins)
- Security configuration is also application specific (D-Bus wins)
- Service is determined by socket path (about as good as D-Bus, since service names and socket paths are both pretty arbitrary)
- No activation – server needs to be running before client connects (D-Bus wins)
So, in general, D-Bus wins, right? But wait – there are a few things we haven’t considered.
Yes, protocols for communication with server programs via a socket are generally application specific, but on the other hand, there is usually a library that implements that protocol, which takes some of that pain away. Of course these libraries are language-specific, so there is a duplication of effort required in adapting library interfaces to other programming languages, but there are tools like SWIG which can assist here. Also, there’s the possibility that services could offer a standard protocol (similar to the D-Bus protocol, even) for communication over their respective sockets. It turns out there are options available which give some basic functionality; I’m sure that it wouldn’t be too hard to come up with (or find  ) something which offer basic introspection as well. Heck, you could even use the D-Bus protocol, just without the intermediary server!
How about security? Well, because sockets themselves can be assigned unix permissions, we actually get some of the D-Bus security for free. For any given service, I can easily restrict access to a single user or a single group, and if I wanted to I could use ACLs to give more fine-grained permissions. On the other hand, central configuration of security policy can be nice. But that could be implemented by a library, too. If only there was some pre-existing library specifically for this purpose… ok, so PAM isn’t really meant for fine-grained security either, since it breaks things down to the service level and not any further. But it wouldn’t be hard to come up with something better – something that allows fine-grained policy, with significantly greater flexibility than D-Bus offers.
As mentioned above, there’s pretty much no difference between choosing a service name and choosing a socket path, so I don’t need to say much about this. However, it would certainly be possible to have a more standard socket path location – /var/run/service/name or something similar.
That just leaves activation as the only issue. D-Bus can start a process to provide a requested service. But it happens that this can be done quite easily with plain unix sockets, too; it’s called “socket activation” in the SystemD world, and it’s supported by various other service managers as well (including my own still-in-alpha-infancy Dinit). You could even potentially have service handover (processes queuing to own the socket), just like D-Bus has, though I don’t know if any existing service managers can do this (and on the other hand, I’m not even convinced that it’s particularly valuable functionality).
So: replace the protocol and security with libraries (potentially even a single library for both), and use your service management/supervision system for activation. Why do we actually need the D-Bus daemon?
I have a vague idea that some library might spring into existence which implements an abstraction over the D-Bus API but using the ideas presented here (which, let’s face it, are astounding only in their simplicity) instead of communicating with a single central daemon. That is, connecting to a D-Bus service would just connect to the appropriate (some name-mangling required) socket within /var/run/service, and then speak the D-Bus protocol (or another similar protocol) to it. Likewise, requesting a service would instead attempt to listen on the corresponding socket – for unprivileged processes this might require assistance from a SUID helper program or perhaps the requisite functionality could be included in the service manager. Unfortunately I’ve got too many other projects on my plate right now, but maybe one day…