Implementing a D-Bus Daemon in C
One of the advantages of developing applications on Linux (and sharply contrasted with the current trend of smartphone app development) is its robust inter-process communication, which allows things like GNOME Shell extensions that allow you to interact with any media player that supports a specific protocol. D-Bus is currently being used by many Linux applications to enable talking to one another, and since documentation on it is rather sparse, this post will teach you how to write a long-running D-Bus daemon using C and GLib.
This type of design isn’t going to be globally useful for all applications using D-Bus. What it is intended to do is enable small, potentially single-threaded, applications to start up a local “server” unique to them that they can talk to and share. For example, it could be used with Vim to maintain some data in memory that would otherwise cause it to pause or hang, especially if there is some intense calculation or processing involved.
I won’t go into the code itself in great detail, but instead I’m going to cover what it does and why. I recommend keeping it open in another tab or window to provide some context for the rest of this post.
At a high level, what it’s supposed to do is this:
Daemonize. This involves forking the process and telling the parent to quit, then configuring the child to be more daemon-like. I also open up a unix pipe to enable direct communication between the child and parent.
Set up a signal handler. In particular we want to listen for the
SIGINTsignal to know when to clean up and quit.Connect to the D-Bus. Rather than creating our own D-Bus server, we just open up a connection to the existing session bus. This will give us a unique name that other applications can use to talk to the daemon. It’s more common for D-Bus applications to create a connection using an owned name like “com.example.MusicPlayer1”, but it’s unnecessary and could cause complications should you want to start two separate instances (if you had two Vim sessions open, for example).
Register an object. D-Bus is built on a model that slightly resembles OOP; messages sent to the daemon will be in the form of method calls or signals on an object. This also requires setting up an interface so that D-Bus knows how to interact with it.
Start listening for requests.
Now, let’s go into more detail.
Daemonize
There are two parts here to daemonizing. First, open up a pipe so that the child and parent can talk to one another after being forked. The reason this is necessary is that we need the parent to report back to the caller what the unique name is for our D-Bus connection, but we won’t know that until the child opens it up. Creating a pipe on Linux is simple:
1 2 3 4 | |
The pipe() function fills the pipefd array with two file descriptors, one for input and one for output. If it returns -1, then there’s nothing we can do, so we exit right away. The only way pipe() will error out is if the system is out of memory or there are too many open files.
Second, fork the process. For the child, this also involves setting up some more daemon-like behavior, including fully detaching from the parent, resetting the file mask, changing the current directory, closing the standard streams, and opening syslog (with a call to atexit() to ensure it gets closed when the process terminates). For the parent, we first check to make sure the fork worked, and exit if it didn’t. Assuming it worked, then we want to print two things to standard output, one on each line: the PID of the new child process so that we can kill it later, and the unique name from the child’s D-Bus connection. fork() gave us the value of the child’s PID, so we can print that right away, but we need to wait for the child to send the name of the connection across the pipe. Once that’s done, then we can tell the parent to quit.
Everything in main right after the call to daemonize() is running as the child process in daemon mode.
Set up a signal handler
The way to kill a process in Linux is to send it a signal. Pressing Ctrl-C in a terminal or running the command kill -s SIGINT <pid> will send that process the SIGINT signal, which is the operating system politely asking the process to quit. We want to set up a signal handler so that we can exit gracefully.
Connect to the D-Bus
Since we’re using GLib’s bindings to D-Bus, we first need to initialize the type system, hence the call to g_type_init(). After that we create the main loop, but it won’t be started until everything is registered, and open up the connection. For the connection we simply request to join the existing session bus. If it failed, log the error, close the pipe (which will cause the parent to report an empty string as the unique name), and quit. Otherwise, get the unique name of our connection and write it to the pipe, then close it because we don’t need it anymore.
Register an object
Now that we’re connected, we need to register an “object” that will receive the signals and method calls. The actual implementation is defined via XML embedded at the top of the file, so we just tell GLib to parse it and register it with the connection, passing in a vtable that points to the methods handle_method_call(), handle_get_property(), and handle_set_property() for handling various types of requests. Only method calls are fully implemented, but it’s pretty easy to add code to the other handlers to take care of other types of requests.
Start listening for requests
Last but certainly not least, we call g_main_loop_run(), which starts up the main loop that will be listening on our D-Bus connection. Now you can compile it (gio-2.0 is the only dependency), run it, and send it a message:
$ dbus-send --print-reply --type=method_call --dest=<address> /dradtke/DBusDaemon dradtke.DBusDaemon.SendMessage string:'hello, daemons'
Happy daemonizing.
Comments