Do you write Unix daemons? Do you feel tired of life, and wish daily for hordes of sysadmins to put you out of your misery? Fear not: just follow these tips!
Initialisation
- Insist on backgrounding, and don't provide any options to run in the foreground.
- If foreground mode is supported, make it only available as part of a debug option that will flood the logs and/or make the program run ten times slower.
- Make sure to close all open file descriptors. Just to be sure, close all the way up to the maximum file descriptor value allowed by the system, in order to stop fghack working.
- While you're at it, double-check that file descriptor 0 is closed as well, just to stop the the fghack workaround working.
- Close the above-mentioned file descriptors using direct system calls, so that they cannot be masked by overriding
close in an LD_PRELOADed library.
Logging
- Insist on using syslog, and don't provide any options to log to standard error or standard output.
- Just to be sure, explicitly close file descriptors 1 and 2, or redirect them to /dev/null.
- If logging to file is supported, make it work only on regular files. Just to be sure that the output is a regular file, seek to the end of the file before writing instead of opening in append mode.
- Be sure to close the logging file after every line. You wouldn't want to write to a rotated-out log file, would you?
- Always write local-time timestamps in log lines, and don't give an option to turn this off. You wouldn't want users to miss when something happened, would you?
- Instead of calling
syslog, connect to /dev/log (or write to UDP port 514) directly, so log messages cannot be redirected elsewhere.
- Perform the above-mentioned connections and/or writes using direct system calls.
Rationale
Here are some (serious) reasons why the above are so frustrating.
- Ideally, a daemon has an option to remain in the foreground, so that it can be monitored using supervise or the like.
- Ideally, a daemon also has an option to log to standard error or standard output, so that when run as an svscan service, log messages go to an appropriate multilog process that logs reliably (unlike syslog).
- Programs that give an option to log to file can be asked to log to /dev/stderr or /dev/stdout, but only if they leave file descriptor 2 or 1 open, and only if they don't seek on them.
- Assuming that a program doesn't seek on the file it logs to (so that it can log to a FIFO), it is wholly frustrating for it to close the file too often, since a multilog process that sees end of input will terminate and have to be restarted. (This happens if the daemon closed standard error and standard output, and thus needs to write to a FIFO by name, for instance; in such a case, svscan is not around to keep the pipe open.) Thus, if the log file is closed and reopened for each log line, that would mean forking off one process for every log line.
- Most human-readable timestamp formats are not easy for a program to parse. This is especially so if the timestamp was written in local time (which will overlap when shifting from daylight savings time to standard time). multilog can be asked to write out timestamps (in TAI64N format), which are easy to parse, and not subject to daylight savings and leap second problems.
Also, Jonathan de Boyne Pollard expands on some of my points in greater detail in Mistakes to avoid when designing Unix dæmon programs.