Self-daemonizing code is awful

The classical UNIX approach to services is to implement them as “daemons,” programs that run without a terminal attached and provide some service. The key feature of a classical daemon is that, when started, it carefully detaches itself from its initial environment and terminal, then continues running in the background.

This is awful and I'm glad modern init replacements discourage it.

Process Tracking

Daemons don't exist in a vacuum. Administrators and owners need to be able to start and stop daemons reliably, and check their status. The classic self-daemonization approach makes this impossible.

Traditionally, daemons run as children of init (pid 1), even if they start out as children of some terminal or startup process. Posix only provides deterministic APIs for processes to manage their children and their immediate parents; the classic daemonisation protocol hands the newly-started daemon process off from its original parent process, which knows how to start and stop it, to an unsuspecting init, which has no idea how this specific daemon is special.

The standard workaround has daemons write their own PIDs to a file, but a file is “dead” data: it's not automatically updated if the daemon dies, and can linger long enough to contain the PID of some later, unrelated program. PID file validity checks generally suffer from subtle (or, sometimes, quite gross) race conditions.

Complexity

The actual code to correctly daemonize a process is surprisingly complex, given the individual interfaces' relative simplicity:

See this list for a longer version. It's worse than you think.

All of this gets even more complicated if the daemon has its own child processes, a pattern common to network services. Naturally, a lot of daemons in the real world get some of these steps wrong.

The Future

Supervisord, Foreman, Upstart, Launchd, systemd, and daemontools all encourage services not to self-daemonize by providing a sane system for starting the daemon with the right parent process and the right environment in the first place.

This is a great application of DRY, as the daemon management code only needs to be written once (in the daemon-managing daemon) rather than many times over (in each individual daemon). It also makes daemon execution more predictable, since daemons “in production” behave more like they do when run attached to a developer's console during debugging or development.