272 lines
12 KiB
Markdown
272 lines
12 KiB
Markdown
# dbus
|
|
|
|
From our system administrator's point of view, dbus is a bit like systemd.
|
|
Most of the time we're unaware of it's existence but it's an essential part of most modern Linux distributions.
|
|
But what *is* it and what does it do?
|
|
|
|
The plain truth is that dbus is an inter process communication bus or [IPC](https://en.wikipedia.org/wiki/Inter-process_communication).
|
|
Each program of server we launch is an independent *process* that is managed by our operating system.
|
|
It will claim the necessary memory it needs to run, to store data and variables, but this chunk of memory is exclusive to the process.
|
|
No other processes *should* be able to access this memory and data.
|
|
But how can programs talk to each other if they need to?
|
|
This is where dbus comes on the scene.
|
|
|
|
![dbus](./assets/dbus_01.png)
|
|
|
|
Dbus allows for programs to expose variables and method, functions the program can execute, to the other program connected to dbus.
|
|
The program exposing has full control over what it will **do** when methods are called or variables accessed.
|
|
Dbus is in charge of connecting and delivering what it's client demand of each other.
|
|
While this sounds simple, it does this with grate care and precision.
|
|
The dbus [specification](https://dbus.freedesktop.org/doc/dbus-specification.html) states that:
|
|
|
|
> D-Bus is a system for low-overhead, easy to use interprocess communication (IPC). In more detail:
|
|
|
|
> * D-Bus is low-overhead because it uses a binary protocol, and does not have to convert to and from a text format such as XML. Because D-Bus is intended for potentially high-resolution same-machine IPC, not primarily for Internet IPC, this is an interesting optimization. D-Bus is also designed to avoid round trips and allow asynchronous operation, much like the X protocol.
|
|
|
|
> * D-Bus is easy to use because it works in terms of messages rather than byte streams, and automatically handles a lot of the hard IPC issues. Also, the D-Bus library is designed to be wrapped in a way that lets developers use their framework's existing object/type system, rather than learning a new one specifically for IPC.
|
|
|
|
## Two different busses
|
|
|
|
On most machines you'll encounter two different, and independent, busses.
|
|
|
|
* a system bus used by the system to communicate
|
|
* a session bus (per logged in user) for programs run by the user
|
|
|
|
This separation is needed from both practical and security stand point.
|
|
Practically speaking I want to control *my* VLC player and not some other one.
|
|
From a security point of view unprivileged users should not be allowed to circumvent permissions by hopping on the system bus and triggering all sorts of things they are not allowed to!
|
|
|
|
Let's have a look at what's running on my home computer.
|
|
We clearly see two independent busses running.
|
|
|
|
```bash
|
|
➜ ~ git:(master) ✗ ps aux | grep dbus
|
|
message+ 548 0.0 0.0 8864 5192 ? Ss Aug28 0:06 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
|
|
waldek 1780 0.0 0.0 8628 5136 ? Ss Aug28 0:01 /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
|
|
waldek 799748 0.0 0.0 6316 656 pts/2 S+ 20:42 0:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox dbus
|
|
➜ ~ git:(master) ✗
|
|
```
|
|
|
|
Now on a server.
|
|
Where we only see **one** bus which is the system bus.
|
|
This server is headless, so no graphical login is possible.
|
|
|
|
```bash
|
|
➜ ~ ps aux | grep dbus
|
|
message+ 596 0.0 0.1 8956 3744 ? Ss Mar22 29:09 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
|
|
waldek 2982 0.0 0.0 6144 824 pts/0 S+ 18:44 0:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox dbus
|
|
➜ ~
|
|
```
|
|
|
|
Both `PID`'s are in the same *ballpark* number wise and on the lower end of the spectrum.
|
|
This probably means the dbus system bus process is started at boot.
|
|
Let's find out a bit more about it.
|
|
As it's a program running at boot, it's probably started by `systemd`.
|
|
|
|
```bash
|
|
➜ ~ sudo systemctl list-dependencies dbus.service --reverse
|
|
dbus.service
|
|
● └─multi-user.target
|
|
● └─graphical.target
|
|
➜ ~
|
|
```
|
|
|
|
Here we can see that dbus is started when the computer reaches the `multi-user.target` runlevel.
|
|
If we would reboot in single user mode, or `rescue.target`, dbus would not be launched!
|
|
What about the user bus?
|
|
We don't see any specific service files for that one?
|
|
Let's have a look at the `systemctl --user` output.
|
|
|
|
```bash
|
|
➜ ~ git:(master) ✗ systemctl --user --type=service | grep dbus
|
|
dbus.service loaded active running D-Bus User Message Bus
|
|
➜ ~ git:(master) ✗
|
|
```
|
|
|
|
```bash
|
|
➜ ~ git:(master) ✗ systemctl --user list-dependencies dbus.service
|
|
dbus.service
|
|
● ├─app.slice
|
|
● ├─dbus.socket
|
|
● └─basic.target
|
|
● ├─paths.target
|
|
● ├─sockets.target
|
|
● │ ├─dbus.socket
|
|
● │ ├─dirmngr.socket
|
|
● │ ├─gpg-agent-browser.socket
|
|
● │ ├─gpg-agent-extra.socket
|
|
● │ ├─gpg-agent-ssh.socket
|
|
● │ ├─gpg-agent.socket
|
|
● │ ├─pipewire.socket
|
|
● │ ├─pk-debconf-helper.socket
|
|
● │ └─pulseaudio.socket
|
|
● └─timers.target
|
|
➜ ~ git:(master) ✗
|
|
```
|
|
|
|
```bash
|
|
➜ ~ git:(master) ✗ systemctl --user list-dependencies dbus.service --reverse
|
|
dbus.service
|
|
➜ ~ git:(master) ✗
|
|
```
|
|
|
|
## Inspecting the bus
|
|
|
|
### Graphical
|
|
|
|
When you're in a graphical environment the *easiest* tool to understand what is exposed on the bus is `d-feet`.
|
|
To see what is going on on the bus you're better off with `bustle`.
|
|
Let's install both and open up d-feet first.
|
|
We can do all of this in the command line as well, but first we'll have a look at the graphical applications.
|
|
For an easy demonstration I would like you to install `vlc` as well.
|
|
|
|
```bash
|
|
➜ ~ git:(master) ✗ sudo apt install d-feet bustle vlc
|
|
Reading package lists... Done
|
|
Building dependency tree... Done
|
|
Reading state information... Done
|
|
bustle is already the newest version (0.8.0-1).
|
|
d-feet is already the newest version (0.3.15-3).
|
|
vlc is already the newest version (3.0.16-1).
|
|
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
|
|
➜ ~ git:(master) ✗
|
|
```
|
|
|
|
Open up `d-feet` and `vlc` and search for vlc on the session bus.
|
|
You should see something very similar to the screenshot below.
|
|
Open a video or audio file in vlc and try to pause it from d-feet.
|
|
Have a look at some properties and some methods.
|
|
What would the *signals* be?
|
|
|
|
![d-feet](./assets/dbus_02.png)
|
|
|
|
Now keep both vlc and d-feet open and we'll add bustle to the mix.
|
|
Bustle can record events on either the system or your session bus.
|
|
As we're using vlc for the demonstration go ahead and record the session bus.
|
|
Try to pause and play your video and try to spit the flow of operations.
|
|
|
|
![bustle](./assets/dbus_03.png)
|
|
|
|
### From the command line
|
|
|
|
Dbus comes with it's own command line tools to interact with programs exposed on the bus.
|
|
Let's have a look at them.
|
|
First up is `dbus-monitor` and let's see it's arguments.
|
|
|
|
```bash
|
|
➜ ~ git:(master) ✗ dbus-monitor --help
|
|
Usage: dbus-monitor [--system | --session | --address ADDRESS] [--monitor | --profile | --pcap | --binary ] [watch expressions]
|
|
➜ ~ git:(master) ✗
|
|
```
|
|
|
|
The most general way to invoke it is with either `--system` or `--session`.
|
|
We've already looked at the session bus so let's now look at the system bus.
|
|
Let's one op one terminal, run `sudo dbus-monitor --system` and keep it running.
|
|
In a second terminal stop one of your running services, such as ssh.
|
|
You'll see a bunch of messages appearing in the monitor terminal.
|
|
Start the service back up.
|
|
Do you understand what is happening here?
|
|
Try and visualize both terminals at the same time as in the screenshot below.
|
|
Try some tabcomplete and see what is happening.
|
|
|
|
![tab complete](./assets/dbus_04.png)
|
|
|
|
## Sending messages
|
|
|
|
We can send things over dbus ourselves with the `dbus-send` program but first we'll use a special program that's very handy to send messages to the notification program of our desktop environment.
|
|
It's called `notify-send` and you can install it by installing the `libnotify-bin` package ins Debian.
|
|
Once it's installed try it out as follows.
|
|
|
|
```
|
|
➜ ~ git:(master) ✗ notify-send "hello"
|
|
➜ ~ git:(master) ✗
|
|
```
|
|
|
|
It immediately return but a notification should have popped up on your screen.
|
|
You can use this from within bash scripts to signal the user about updates or errors.
|
|
But let's dig a little deeper to see what's happening on the inside.
|
|
For this we need to inspect the session bus and post a message again.
|
|
Look for your message and observe the method that was called.
|
|
It should look similar to the one below.
|
|
|
|
```bash
|
|
method call time=1630874537.889652 sender=:1.544 -> destination=:1.52 serial=7 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=Notify
|
|
string "notify-send"
|
|
uint32 0
|
|
string "dialog-information"
|
|
string "Hello world!"
|
|
string "I am a notification..."
|
|
array [
|
|
]
|
|
array [
|
|
dict entry(
|
|
string "urgency"
|
|
variant byte 1
|
|
)
|
|
]
|
|
int32 -1
|
|
```
|
|
|
|
With this information we can construct a raw dbus-send command ourselves (but we'll run into a problem!).
|
|
Let's try and map the usage to what we observed above.
|
|
Luckily `dbus-send` does autocomplete!
|
|
But we can take d-feet on the side to better understand the structure.
|
|
|
|
```bash
|
|
➜ ~ git:(master) ✗ dbus-send --help
|
|
Usage: dbus-send [--help] [--system | --session | --bus=ADDRESS | --peer=ADDRESS] [--dest=NAME] [--type=TYPE] [--print-reply[=literal]] [--reply-timeout=MSEC] <destination object path> <message name> [contents ...]
|
|
➜ ~ git:(master) ✗ dbus-send --session --print-reply --dest=org.freedesktop.Notifications /org/freedesktop/Notifications org.freedesktop.Notifications.Notify
|
|
```
|
|
|
|
We need to send a message to:
|
|
|
|
* `--session` bus
|
|
* we want to see the reply, or error, if any `--print-reply`
|
|
* our destination is `--dest` the notifications
|
|
* at this destination we only have one object `/org/freedesktop/Notifications`
|
|
* on this object we can call the `org.freedesktop.Notifications.Notify` method
|
|
|
|
But we run in to an error! (that's why we want the `--print-reply` argument)
|
|
|
|
```bash
|
|
Error org.freedesktop.DBus.Error.InvalidArgs: Type of message, “()”, does not match expected type “(susssasa{sv}i)”
|
|
```
|
|
|
|
The `Notify` method needs arguments and quite a lot of them!
|
|
In `d-feet` or our `dbus-monitor` output above we can visualize what these arguments are.
|
|
The *type* in the error message looks abstract but when we put it side by side with our dbus-monitor output it starts to make sense.
|
|
|
|
```bash
|
|
string "notify-send"
|
|
uint32 0
|
|
string "dialog-information"
|
|
string "Hello world!"
|
|
string "I am a notification..."
|
|
array [
|
|
]
|
|
array [
|
|
dict entry(
|
|
string "urgency"
|
|
variant byte 1
|
|
)
|
|
]
|
|
int32 -1
|
|
```
|
|
|
|
Dbus can send only specific types over the wire and they are described in the specification online.
|
|
We could try to get get to work on the command line but sadly it won't!
|
|
`dbus-send` and not send the *variant* type in an array so we're out of luck (I might be wrong here!).
|
|
An alternative program that can complete this challenge is `gdbus`.
|
|
Try to get that one to display a notification!
|
|
|
|
Let's try to interact with vlc via dbus-send instead.
|
|
Below are some examples that you'll have to tweak to suit your needs.
|
|
|
|
```bash
|
|
dbus-send --session --print-reply=literal --dest=org.mpris.MediaPlayer2.vlc /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Pause
|
|
dbus-send --session --print-reply=literal --dest=org.mpris.MediaPlayer2.vlc /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Play
|
|
dbus-send --session --print-reply=literal --dest=org.mpris.MediaPlayer2.vlc /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.SetPosition objpath:/org/videolan/vlc/playlist/3 int64:100000000
|
|
dbus-send --session --print-reply=literal --dest=org.mpris.MediaPlayer2.vlc /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.OpenUri string:"file:///home/waldek/Coralis_MASTER+VO-V5.mp4"
|
|
```
|
|
|