It seems I’ve been writing little daemons a lot lately–small things that don’t want to run as root but still need to be launched in the background as services. I’ve been noticing because it’s such a pain to integrate them into the system once they are written (or installed). I have to mess around as root creating /etc/init.d shell scripts (probably by copy and pasting–who out there can actually make those from scratch?) or maybe tweaking some upstart or systemd config file.
And then when I’m done it annoys me that I have to be root to start or stop a daemon that ends up running as my login user anyway.
Enter Daemon Manager
So last October (2010) I sat down and wrote daemon-manager. It started from a few core ideas:
- It must be possible for a user to set up and configure their daemons—no root access must be required for a user to create a new daemon or restart one of their daemons.
- It must be secure—users should not be allowed to control other users’ daemons (unless they are given explicit permission).
- It should allow for good security practices—users should be allowed to launch a daemon as a user other than themselves if root has explicitly allowed it. This is so you can run your daemons as a “nobody” style user.1
- It should restart the daemons if they crash (I’m looking at you, php-cgi).
- It should be easy to use—1 config file per daemon and a simple command line interface to interact with the running daemons.
There are programs out there that do parts of that list, but none that do everything:
- daemon tools: I’ve used it before and I really like its philosophy of being small and simple. But it seems to really want to run as root which means you have to be root to control it. Also, setting up new daemons is kind of a pain.
- Upstart: It’s very similar and it makes setting up a new daemon pretty easy but since it’s an “init” replacement it doesn’t seems very adept at running programs meant for non-root users. I’ve done it before but there was a lot of “sudo” configuration and it wasn’t easy. Also the config files are stored in /etc/init and only root can write new ones.
- Systemd: I really love systemd. Or the idea of it, really—some day it will make it into Debian and I’ll start actually using it. But its philosophy is great. But again, being an “init” replacement gives it most of the same downsides as upstart.
A Quick Tutorial Through Examples
Master config: /etc/daemon-manager.conf
[runs_as] david: www-data michaelc: www-data amy: www-data joann: www-data jim: greenfelt: greenfelt-daemon [manages] david: michaelc,amy,joann,greenfelt michaelc: amy, joann bill: joann jim: greenfelt
The main section is the “runs_as” section. This section tells Daemon Manager which users are allowed to start daemons and which users the daemons can be run as. In the above example, “david”, “michaelc”, “amy”, and “joann” can launch daemons as themselves and also “www-data”. “greenfelt” can launch daemons as itself and the “greenfelt-daemon” user. “jim” is only allowed to launch daemons as himself. No other users on the system are allowed to launch daemons at all because they weren’t explicitly listed.
The “manages” section is a little experimental at this point, but the idea is that “david” is allowed to manage (start, stop, or restart) the daemons of “michaelc”, “amy”, “joann”, and “greenfelt” in addition to his own daemons. This is so you can have help desk type users who can stop or restart other users’ daemons even though they may not have read or write access to the users’ home directories. As you might expect, “root” is always allowed to start and stop anyone’s daemons.
dir=/home/david start=exec deluged -d
This is a simple Daemon Manager config file that launches the deluge bittorrent daemon. “dir” and “start” are the only required entries in the config file. “dir” is the working directory and “start” is a one line shell script to run. Because it is a shell script it needs to “exec”, otherwise Daemon Manager can’t manage it properly.2
dir=/home/amy/wordpress user=www-data start=exec php-cgi -q -b wordpress.socket
This starts a PHP FastCGI daemon in a WordPress directory and starts it running with as the “www-data” user (“amy” was given explicit permission to start daemons as the www-data user in “/etc/daemon-manager.conf”).
dir=/var/www/greenfelt.net user=greenfelt-daemon start=exec ./script/greenfelt_fastcgi.pl -l greenfelt.socket -n 1 output=log
This is a paraphrase of the config file that runs greenfelt.net which is a Catalyst app that talks to nginx via FastCGI through the “greenfelt.socket” unix domain socket. It runs as the user “greenfelt-daemon” so it can have less privileges than the main “greenfelt” user. The “output” parameter tells Daemon Manager to collect the stdout and stderr of the daemon and save it to a log file in the “~/.daemon-manager/logs/” directory (the default setting is to throw away stdout and stderr).
Controlling Daemon Manager
Daemon Manager is controlled using the “dmctl” command. It is relatively simple at this point, allowing you to start, stop and restart daemons. It also lets you scan for new config files and query for daemon statistics. Here’s an example output the “status” command:
$ dmctl status daemon-id state pid respawns cooldown uptime total david/deluge-web running 2948 0 0s 3w3d 3w3d david/deluged running 2950 0 0s 3w3d 3w3d david/greenfelt stopped 0 0 0s 0s 0s david/minecraft stopped 0 0 0s 0s 0s david/moviefile running 2951 0 0s 3w3d 3w3d david/pytivo running 22905 0 0s 4d7h 4d7h david/streamium running 2958 0 0s 3w3d 3w3d david/wordpress running 27012 33 0s 12h57m 3w3d
Notice that the wordpress server (good old php-cgi) has crashed 33 times (and has been automatically respawned by Daemon Manager).
Download and Use
Daemon Manager is licensed under the GPL and can be downloaded here (the source is also available on github). If you use Debian there’s a Debian branch available for building a package. The version as of this writing is 0.9 which means that there are some obvious things that need to be fixed3 and that I’m not sure it’s 100% secure or bug free yet, though I have been using it for months now and I absolutely love it–it filled a void I wasn’t even sure was really there when I started.
For a more detailed version of the history of Daemon Manager, see this blog post.
- That way, if there is a security hole in your code the attacker doesn’t get access to your main login account. ↩
- I would like to change that but I’m having trouble getting dash (/bin/sh on my Debian system) to pass signals onto its child processes. Bash seems to work correctly and so the exec is not needed if /bin/sh is bash on your system. ↩
- The dmctl commands and arguments should be reversed so it’s more like system v init scripts or the “service” command: “dmctl wordpress stop” instead of the current “dmctl stop wordpress”. ↩