Supervisor vs. Launchd
-
I didn’t know that Apple’s launchd source code had been available. But it is, and as of recently apparently under the Apache license.
I haven’t really audited the code very closely (and likely won’t), but the first example given for launchd in its sparse narrative documentation is this:
The basics: For the simplest of scenarios, launchd just keeps a process alive. A simple "hello world" example of that would be: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.example.sleep</string> <key>ProgramArguments</key> <array> <string>sleep</string> <string>100</string> </array> <key>OnDemand</key> <false/> </dict> </plist> In the above example, we have three keys to our top level dictionary. The first is the Label which is what is used to uniquely identify jobs when interacting with launchd. The second is ProgramArguments which for its value, we have an array of strings which represent the tokenized arguments and the program to run. The third and final key is OnDemand which overrides the default value of true with false thereby instructing launchd to always try and keep this job running. That's it! A Label, some ProgramArguments and OnDemand set to false is all you need to keep a daemon alive with launchd! Now if you've ever written a daemon before, you've either called the daemon() function or written one yourself. With launchd, that is not only unnecessary, but unsupported. If you try and run a daemon you didn't write under launchd, you must, at the very least, find a configuration option to keep the daemon from daemonizing itself so that launchd can monitor it.Looks pretty familiar! Under supervisor, the same configuration would
be:[program:sleep] command=sleep 100
The examples go on to show other options that supervisor already has, including starting a process under a specific uid/gid, putting stdout and stderr into a named log file, and starting a process that should not be restarted automatically.
While investigating further, I read the launchd manpage, which goes on to show things which launchd has that supervisord doesn’t, like:
- environment variables on a per-process basis
- working directory on a per-process basis
- umask on a per-process basis
- nice value on a per-process basis
- setting resource consumption limits for processes
- whether to call or not call initgroups(2) before process launch
- scheduled process kickoff
- emulating inetd services
- causing processes to be kicked off based on modification of a file or the contents of a directory as a queue
- special “service ipc” behavior which programs run under launchd can implement to talk to the launchd service they’re running under.
- separate log files for stderr and stdout of a process
- some other stuff that i can’t understand because the launchd.conf manpage seems to have been written under the influence of a substance which has negatively effected the author’s grammar and sentence structure
Things that supervisord has that launchd doesn’t seem to have:
- a shell-like or web interactive user interface
- remote control of the master process via xml-rpc
- process logfile backup and rotation
- control over how many times and at what frequency a failing program will restart
- a configurable stop signal for subprocesses
I guess the main question, given this comparison, is why would I not use/develop launchd and ditch supervisor? Good question, they do about the same thing. It could use a little loving as a general-purpose process manager. Supervisor has a far more “retail” focus; for example, it doesn’t use generic object serialization as a configuration language and it has two interactive user interfaces and can be scripted via XML-RPC. launchd seems quite nice but it’s got too much focus for my taste on being “pid 1″ as opposed to being a retail process manager in sort of the same way that djb’s daemontools has too much focus on security as opposed to being a process manager for my taste.
All that said, launchd seems very nice. But I guess the real reason to continue development of supervisor is that I’m far more competent in Python than I am in C, and as a result there’s likely not much value I could add to launchd development.
So. Given that. The first five things that launchd doesn’t have but supervisord does would be very easy to add to supervisord if it were desired, and I might go ahead and add that stuff to the next release, why not? We always call initgroups, iirc; I’ll need to check out why folks might want to set this. Scheduled processing would likely be a nice-to-have, and I’ve actually run into a case where it would be nice to just not use cron, so maybe I’ll work on that too. I suspect it’s not useful for supervisord to do the job of inetd, so it likely never will. The filesystem polling I suspect is outside the scope of supervisor entirely, given that it would likely be difficult to do with any competence across many platforms. It’s likely useful to be able to log stderr and stdout into independent files, so this might go into supervisord.
As an aside, here’s an interesting chunk from the launchd manpage which is likely useful information to give to people running processes under supervisord:
EXPECTATIONS Daemons or agents managed by launchd are expected to behave certain ways. A daemon or agent launched by launchd MUST NOT do the following in the process directly launched by launchd: o fork(2) and have the parent process exit(3) or _exit(2). o Call daemon(3) A daemon or agent launched by launchd SHOULD NOT do the following as a part of their startup initialization: o Setup the user ID or group ID. o Setup the working directory. o chroot(2) o setsid(2) o Close "stray" file descriptors. o Change stdio(3) to /dev/null. o Setup resource limits with setrusage(2). o Setup priority with setpriority(2). o Ignore the SIGTERM signal.

Post a comment