What is D-Bus practically useful for?
dbus
does exactly what you said: it allows two-way communication between applications.
For your specific example you mentioned terminator
. From terminator's man page, we see:
--new-tab If this is specified and Terminator is already running, DBus will be used to spawn a new tab in the first Terminator window.
So if you do this from another terminal (konsole, xterm, gnome-terminal):
$ terminator &
$ terminator --new-tab &
You'll see that the first command opens a new window. The second command opens a new tab in the first window. That's done by the second process using dbus to find the first process, asking it to open a new tab, then quitting.
If you do this from another terminal:
$ terminator --no-dbus &
$ terminator --new-tab &
You'll see that the first command opens a new window. The second command fails to find the first window's dbus, so it launches a new window. I installed terminator to test this, and it's true.
In addition, I suspect polkit would be affected. Polkit uses dbus to elevate privileges for GUI applications. It's like the sudo
of the GUI world. If you are in gnome, and see the whole screen get covered while you are asked for the administrator's password, that's polkit in action. I suspect you won't get that prompt in any GUI application you start from terminator
if you have --no-dbus
. It'll either fail to authenticate, or fallback to some terminal authentication. From terminator
try pkexec ls
. That will run ls
with elevated privileges. See if it's different with and without the --no-dbus
option. I don't have a polkit agent in my window manager (i3) so I can't test this one out.
I mostly know about dbus in terms of systemd, so that's where the rest of my answer will come from.
Are there applications that don't work without
dbus
?
Yes. Take systemctl
. systemctl status
will issue a query to "org.freedesktop.systemd1"
, and will present that to you. systemctl start
will call a dbus method and pass the unit as an argument to that method. systemd
recieves the call and performs the action.
If you want to take action in response to a systemd unit (i.e. foo.service) changing states, you can get a file descriptor for interface org.freedesktop.DBus.Properties
with path /org/freedesktop/systemd1/unit/foo_2eservice
and member PropertiesChanged
. Setup an inotify
on that FD and you suddenly have a way to react to a service starting, stopping, failing, etc.
If you want to take a look at what's available on the systemd dbus for a specific unit (i.e. ssh.service
) try this command:
busctl introspect \
org.freedesktop.systemd1 \
/org/freedesktop/systemd1/unit/ssh_2eservice
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.DBus.Peer interface - - -
.GetMachineId method - s -
.Ping method - - -
org.freedesktop.DBus.Properties interface - - -
.Get method ss v -
.GetAll method s a{sv} -
.Set method ssv - -
.PropertiesChanged signal sa{sv}as - -
org.freedesktop.systemd1.Service interface - - -
.AttachProcesses method sau - -
.GetProcesses method - a(sus) -
.AllowedCPUs property ay 0 -
.AllowedMemoryNodes property ay 0 -
.AmbientCapabilities property t 0 const
.AppArmorProfile property (bs) false "" const
.BindPaths property a(ssbt) 0 const
.BindReadOnlyPaths property a(ssbt) 0 const
.BlockIOAccounting property b false -
.BlockIODeviceWeight property a(st) 0 -
.BlockIOReadBandwidth property a(st) 0 -
.BlockIOWeight property t 18446744073709551615 -
.BlockIOWriteBandwidth property a(st) 0 -
.BusName property s "" const
.CPUAccounting property b false -
.CPUAffinity property ay 0 const
.CPUAffinityFromNUMA property b false const
.CPUQuotaPerSecUSec property t 18446744073709551615 -
.CPUQuotaPeriodUSec property t 18446744073709551615 -
.CPUSchedulingPolicy property i 0 const
.CPUSchedulingPriority property i 0 const
.CPUSchedulingResetOnFork property b false const
.CPUShares property t 18446744073709551615 -
.CPUUsageNSec property t 18446744073709551615 -
.CPUWeight property t 18446744073709551615 -
.CacheDirectory property as 0 const
.CacheDirectoryMode property u 493 const
.CapabilityBoundingSet property t 18446744073709551615 const
.CleanResult property s "success" emits-change
.ConfigurationDirectory property as 0 const
.ConfigurationDirectoryMode property u 493 const
.ControlGroup property s "/system.slice/ssh.service" -
.ControlPID property u 0 emits-change
.CoredumpFilter property t 51 const
.DefaultMemoryLow property t 0 -
.DefaultMemoryMin property t 0 -
.Delegate property b false -
.DelegateControllers property as 0 -
.DeviceAllow property a(ss) 0 -
.DevicePolicy property s "auto" -
.DisableControllers property as 0 -
.DynamicUser property b false const
.EffectiveCPUs property ay 0 -
.EffectiveMemoryNodes property ay 0 -
.Environment property as 0 const
.EnvironmentFiles property a(sb) 1 "/etc/default/ssh" true const
.ExecCondition property a(sasbttttuii) 0 emits-invalidation
.ExecConditionEx property a(sasasttttuii) 0 emits-invalidation
.ExecMainCode property i 0 emits-change
.ExecMainExitTimestamp property t 0 emits-change
.ExecMainExitTimestampMonotonic property t 0 emits-change
.ExecMainPID property u 835 emits-change
.ExecMainStartTimestamp property t 1597235861087584 emits-change
.ExecMainStartTimestampMonotonic property t 5386565 emits-change
.ExecMainStatus property i 0 emits-change
.ExecReload property a(sasbttttuii) 2 "/usr/sbin/sshd" 2 "/usr/sbin/sshd" "… emits-invalidation
.ExecReloadEx property a(sasasttttuii) 2 "/usr/sbin/sshd" 2 "/usr/sbin/sshd" "… emits-invalidation
.ExecStart property a(sasbttttuii) 1 "/usr/sbin/sshd" 3 "/usr/sbin/sshd" "… emits-invalidation
.ExecStartEx property a(sasasttttuii) 1 "/usr/sbin/sshd" 3 "/usr/sbin/sshd" "… emits-invalidation
.ExecStartPost property a(sasbttttuii) 0 emits-invalidation
.ExecStartPostEx property a(sasasttttuii) 0 emits-invalidation
.ExecStartPre property a(sasbttttuii) 1 "/usr/sbin/sshd" 2 "/usr/sbin/sshd" "… emits-invalidation
.ExecStartPreEx property a(sasasttttuii) 1 "/usr/sbin/sshd" 2 "/usr/sbin/sshd" "… emits-invalidation
.ExecStop property a(sasbttttuii) 0 emits-invalidation
.ExecStopEx property a(sasasttttuii) 0 emits-invalidation
.ExecStopPost property a(sasbttttuii) 0 emits-invalidation
.ExecStopPostEx property a(sasasttttuii) 0 emits-invalidation
.FileDescriptorStoreMax property u 0 const
.FinalKillSignal property i 9 const
.GID property u 4294967295 emits-change
.Group property s "" const
.GuessMainPID property b true const
.IOAccounting property b false -
.IODeviceLatencyTargetUSec property a(st) 0 -
.IODeviceWeight property a(st) 0 -
.IOReadBandwidthMax property a(st) 0 -
.IOReadBytes property t 18446744073709551615 -
.IOReadIOPSMax property a(st) 0 -
.IOReadOperations property t 18446744073709551615 -
.IOSchedulingClass property i 0 const
.IOSchedulingPriority property i 0 const
.IOWeight property t 18446744073709551615 -
.IOWriteBandwidthMax property a(st) 0 -
.IOWriteBytes property t 18446744073709551615 -
.IOWriteIOPSMax property a(st) 0 -
.IOWriteOperations property t 18446744073709551615 -
.IPAccounting property b false -
.IPAddressAllow property a(iayu) 0 -
.IPAddressDeny property a(iayu) 0 -
.IPEgressBytes property t 18446744073709551615 -
.IPEgressFilterPath property as 0 -
.IPEgressPackets property t 18446744073709551615 -
.IPIngressBytes property t 18446744073709551615 -
.IPIngressFilterPath property as 0 -
.IPIngressPackets property t 18446744073709551615 -
.IgnoreSIGPIPE property b true const
.InaccessiblePaths property as 0 const
...skipping...
.CollectMode property s "inactive" const
.ConditionResult property b true emits-change
.ConditionTimestamp property t 1597235861034899 emits-change
.ConditionTimestampMonotonic property t 5333881 emits-change
.Conditions property a(sbbsi) 1 "ConditionPathExists" false true "/et… emits-invalidation
.ConflictedBy property as 0 const
.Conflicts property as 1 "shutdown.target" const
.ConsistsOf property as 0 const
.DefaultDependencies property b true const
.Description property s "OpenBSD Secure Shell server" const
.Documentation property as 2 "man:sshd(8)" "man:sshd_config(5)" const
.DropInPaths property as 0 const
.FailureAction property s "none" const
.FailureActionExitStatus property i -1 const
.Following property s "" -
.FragmentPath property s "/lib/systemd/system/ssh.service" const
.FreezerState property s "running" emits-change
.Id property s "ssh.service" const
.IgnoreOnIsolate property b false const
.InactiveEnterTimestamp property t 0 emits-change
.InactiveEnterTimestampMonotonic property t 0 emits-change
.InactiveExitTimestamp property t 1597235861039525 emits-change
.InactiveExitTimestampMonotonic property t 5338505 emits-change
.InvocationID property ay 16 90 215 118 165 228 162 72 57 179 144… emits-change
.Job property (uo) 0 "/" emits-change
.JobRunningTimeoutUSec property t 18446744073709551615 const
.JobTimeoutAction property s "none" const
.JobTimeoutRebootArgument property s "" const
.JobTimeoutUSec property t 18446744073709551615 const
.JoinsNamespaceOf property as 0 const
.LoadError property (ss) "" "" const
.LoadState property s "loaded" const
.Names property as 2 "ssh.service" "sshd.service" const
.NeedDaemonReload property b false const
.OnFailure property as 0 const
.OnFailureJobMode property s "replace" const
.PartOf property as 0 const
.Perpetual property b false const
.PropagatesReloadTo property as 0 const
.RebootArgument property s "" const
.Refs property as 0 -
.RefuseManualStart property b false const
.RefuseManualStop property b false const
.ReloadPropagatedFrom property as 0 const
.RequiredBy property as 0 const
.Requires property as 3 "system.slice" "-.mount" "sysinit.tar… const
.RequiresMountsFor property as 1 "/run/sshd" const
.Requisite property as 0 const
.RequisiteOf property as 0 const
.SourcePath property s "" const
.StartLimitAction property s "none" const
.StartLimitBurst property u 5 const
.StartLimitIntervalUSec property t 10000000 const
.StateChangeTimestamp property t 1597235861208937 emits-change
.StateChangeTimestampMonotonic property t 5507917 emits-change
.StopWhenUnneeded property b false const
.SubState property s "running" emits-change
.SuccessAction property s "none" const
.SuccessActionExitStatus property i -1 const
.Transient property b false const
.TriggeredBy property as 0 const
.Triggers property as 0 const
.UnitFilePreset property s "enabled" -
.UnitFileState property s "enabled" -
.WantedBy property as 1 "multi-user.target" const
.Wants property as 0 const
You can see from this that the dbus interface is pretty powerful.
You might ask: Why don't these applications just communicate via sockets or files?
DBus provides a common interface. You don't need different logic to call methods or check properties based on the application you are talking to. You just need to know the name of the path.
I've used systemd
as an example because that's what I understand best, but there are tons of uses of dbus on most desktops. Everything from authentication, to display settings are available on dbus.
There's already a great in-depth answer of D-Bus by @Stewart but I want to amend it with a high-level idea about the design of D-Bus.
The "traditional" way of Inter Process Communication (IPC) on UNIX and Linux systems directly uses sockets, e.g. process A opens /var/run/a.socket
and process B reads/writes to it. This works rather fine for tightly-knit programs that were designed to communicate together.
However, you may want to have communication between two programs' processes where program B didn't even exist when program A was written. D-Bus attempts to solve this issue by providing protocols for communication and service discovery. That way, only the interface b needed to exist when A was written and process B implements the interface b.
You could thus describe D-Bus as an IPC "manager".
Historically, command-line interface (CLI) tools usually also use an IPC manager, the shell, to communicate with arbitrary programs using pipes. The problem with this approach is that it doesn't provide data validation, standardised protocols etc. It's thus only usable by power users. GUI tools in general should do this "invisibly". However, more and more CLI tools start using D-Bus, pretty much as an alternative to sudo(1)
. So you can do, as an unprivileged user, execute systemctl poweroff
and it will prompt you for authentication (this can be compared to the UAC on Windows). Depending on your polkit provider this may be even a GUI prompt. At least in theory, this approach is more flexible and allows for more fine-grained privileges and works without setuid binaries (like sudo
). This can be seen as a security feature.
Of course, as an abstraction, it introduces some kind of complexity (at least in the dependencies of the programs). However, the more programs make (sane) use of it, the less of a burden this is. Whether you like D-Bus and the current development I cannot tell you. But modern operating systems tend to provide many crucial services outside of the kernel (and now move historical kernel stuff out again, microkernel-ish) due to the more complex requirements of today's computing. So, if you "just" want a "simple" terminal all this might considered be "bloat", but the industry demands it for a good reason and more and more developers note the upsides of using D-Bus. It's today's replacement for the good old pipe in your shell (not the system interface, actually D-Bus makes use of pipe()
etc.).
In traditional Unix, there is usually little communication between running programs. Every program runs in a fully separate address space and only interacts with the kernel. This model is simple and robust, but access permissions are mostly too coarse for desktop environments, and implementing fine grained access control is too complex in many cases.
One example where the kernel refines access control is the file system -- unprivileged programs can send off a write request to the kernel, which is translated to a write request sent to the harddisk without violating the separation between programs. This is a fairly complex layer though, and control still isn't fine enough for modern desktops where users might want to connect a USB stick.
For other functions, sound card access for example, the kernel only implements a simple access model: once a program has access to the soundcard, it can play and record sound, and manipulate mixer controls, and there is no mechanism for revoking that access except terminating the program.
In a desktop environment, we'd want a finer model: the browser should be able to use the microphone and webcam only after asking the user, and when a user logs out, any running program should lose microphone access, but if the user wants to run a computation over night, that should still be possible.
The X window system is a good precedent for performing access control in a user program -- rendering to a window is done by sending a request to another program that calculates the final screen contents that are displayed. Whether the request is translated to a visible change in screen contents is decided based on current access control settings: pulling a window to the front gives write access to an area of screen space to this program, sending it to the back revokes that access.
Desktop environments now provide lots of these mediator programs, so for each function there needs to be a communications protocol and a method for opening a handle to that program.
dbus provides a generic protocol, and a broker service where programs can ask to be connected to a program providing access control to specific functionality. If the targeted program is already running, the broker will just forward requests, if it isn't running already, the broker will start the program on demand if it knows how to do so.
The equivalent functionality on Windows is COM/DCOM in combination with the registry.