Does anybody know why dbus exists? I’ve been wracking my brain trying to come up with a usecase for dbus that isn’t already covered by Unix sockets.
You want to remotely control a daemon? Use sockets. You want the daemon to respond to the client? Sockets. Want to exchange information in json? plaintext? binary data? Sockets can do it. Want to restrict access to a socket? Go ahead, change the socket’s permissions. Want to prevent unauthorized programs from pretending to be someone they’re not? Change the permissions of the directory containing the socket. Want network transparency? That’s why we have abstract sockets.
Plenty of well-established software uses sockets. Music player daemon uses sockets. BSPWM uses sockets. Tmux uses sockets. Pipewire uses sockets. Dhcpcd uses sockets. Heck, dbus itself relies on sockets!
For developers, using sockets is easy. I once wrote a program that interfaced with BSPWM, and it was a breeze. Dbus, on the other hand, not so much. I tried writing a Python script that would contact Network Manager and check the WiFi signal strength. Right off the bat I’m using some obscure undocumented package for interfacing with dbus. What is an introspection? What is a proxy object? What is an interface? Why do I need 60 lines of (Python!) code for a seemingly trivial operation?
So why do some developers decide to use dbus when they could just use unix sockets and save a lot of hassle for themselves and others?
With pipes/sockets, each program has to coordinate the establishment of the connection with the other program. This is especially problematic if you want to have modular daemons, e.g. to support drop-in replacements with alternative implementations, or if you have multiple programs that you need to communicate with (each with a potentially different protocol).
To solve this problem, you want to standardize the connection establishment and message delivery, which is what dbus does.
With dbus, you just write your message to the bus. Dbus will handle delivering the message to the right program. It can even start the receiving daemon if it is not yet running.
It’s a bit similar to the role of an intermediate representation in compilers.
This reminds me of QT’s signal/slot system. I.e. instead of calling functions directly, you just emit a signal and then any number of functions may have the receiving slot enabled.
Lot’s of similar systems in other frameworks too I’m sure.
It occurs to me that sendmsg() is already kind of a standard, and the problem of drop in replacements could be solved by just making the replacement bind to the same file path and emulate the same protocol, and the problem of automatically starting the daemon could be handled by a systemd socket (or even inetd if you wanna go old school). The only advantage that I can see dbus really having over Unix sockets is allowing multiple programs to respond to the same message, which is a definite advantage but AFAIK not many things take advantage of that.
A message bus won’t magically remove the need for developers to sit down together and agree on how some API would work. And not having a message bus also doesn’t magically prevent you from allowing for alternative implementations. Pipewire is an alternative implementation of pulseaudio, and neither of those rely on dbus (pulse can optionally use dbus, but not for its core features). When using dbus, developers have to agree on which path the service owns and which methods it exposes. When using unix sockets, they have to agree where the socket lives and what data format it uses. It’s all the same.
We have a tool for that, it’s called an init system. Init systems offer a large degree of control over daemons (centralized logging? making sure things are started in the correct order? letting the user disable and enable different daemons?). Dbus’ autostart mechanism is a poor substitute. Want to run daemons per-user instead of as root? Many init systems let you do that too (I know systemd and runit do).
The init system is for trusted system services that can talk directly to hardware. Unless you are working on a single-user system with no security concerns of any kind, you might consider using init to launch persistent user land or GUI processes.
DBus is for establishing a standard publish/subscribe communication protocol between user applications, and in particular, GUI applications. And because it is standard, app developers using different GUI frameworks (Gtk, Qt, WxWidgets, FLTK, SDL2) can all publish/subscribe to each other using a common protocol.
It would be certainly be possible to establish a standard place in the /tmp directory and a standard naming scheme for sockets and temporary files so that applications can obtain a view of other running applications and request to receive message from other applications using ordinary filesystem APIs, but DBus does this without needing the /tmp directory. A few simple C APIs replace the need for naming and creating your temporary files and sockets.
…systemd very much does use the init system to launch userland and GUI processes. That’s how GNOME works.
Dbus is for interprocess communication. The fact that its primary use case is communication between desktop applications is hardly relevant to its design. I don’t see how GUI frameworks are at all relevant, or how it would be possible to create an interprocess communication mechanism that only worked with one GUI framework without some heroic levels of abstraction violation (which I would not put past Qt, but that’s another story).
I don’t see why having an entire dbus daemon running in the background is better than having a cluttered /tmp or /run directory.
Let’s say you want to write a GUI for connecting to networks.
In the backend, you have NetworkManager, systemd-networkd, ConnMann, netctl, dhcpcd, …
Dbus could be a good way to expose a common API surface for clients.