Daemonization

When programs run as daemons, they frequently forget to detach themselves from the parent environment. LibUCW therefore offers a simple daemonization helper which performs the necessary actions. Namely:

  • Establishing a new session via a call to setsid().

  • Switching the user and group ID if needed. The user and group can be specified by either a name or #uid. If only the user is given, the relevant groups are set according to /etc/passwd and /etc/group.

  • Redirecting standard input and output from /dev/null. Standard error output is left open, so that error messages can be printed before you set up proper logging. If you are not sure that your log streams replaced stderr, you can call log_drop_stderr() to do that.

  • Setting the umask() to a fixed value (022).

  • Switching from the current directory to /, so that it is not kept busy.

  • Writing a PID file. While the daemon is running, the PID file is kept locked by flock(), so we can easily detect stale PID files.

Example

#include <ucw/lib.h>
#include <ucw/daemon.h>
void daemon_body(struct daemon_params *dp)
{
        // ... daemon's code ...
        // When we are done, release the PID file and exit:
        daemon_exit(dp);
}
int main(int argc, char **argv)
{
        struct daemon_params dp = { .pid_file = "/var/run/example.pid" };
        // ... parse arguments ...
        daemon_init(&dp);
        // ... initialize everything ...
        // Let us fork off the daemon:
        daemon_run(daemon_body);
        // Here continues the parent process
        return 0;
}

Daemon control

Daemons using the LibUCW helpers can be controlled by traditional mechanisms present in most UNIX-like operating systems. These are usually based on PID files, but they are inherently racy, since they do not perform any locking. For example, attempts to start the daemon, while another process is trying to stop it often lead to undefined states. Also, detection of running daemons fails when the daemon dies and its PID is recycled. Checking of process name and similar tricks do not avoid the problem, since there can be multiple instances of the daemon running simultaneously.

We therefore recommend the following daemon control protocol, which prevents all such race conditions. Its implementation is available in form of the daemon_control() library function or the ucw-daemon-control stand-alone utility.

  • There exist two files:

    • PID file (usually /var/run/daemon.pid), which contains the PID of the daemon process and when the daemon runs, it keeps the file locked by flock().

    • Guard file (usually /var/run/daemon.lock), which is locked (again by flock()) whenever we want to perform an action on the daemon.

  • When we want to start the daemon:

    • Lock the guard file, creating it if it did not exist.

    • Try to lock the PID file. If it fails, the daemon is already running, so we are done.

    • Unlock the PID file.

    • Run the daemon. The daemon locks the PID file and when it has everything initialized, it forks and the parent process writes the child’s PID and exits.

    • Unlock the guard file.

  • When we want to stop it:

    • Lock the guard file, creating it if it did not exist.

    • Try to lock the PID file. If it succeeds, the daemon is not running, so we are done.

    • Read the PID from the PID file.

    • Send a signal to the process. [This is the only place when we can race. The daemon could have exited in the meantime and its PID could have been recycled. Hopefully, the time window between checking the lock and sending the signal will be short enough. Using 32-bit PIDs is advisable anyway.]

    • Lock the PID file. This will wait until the daemon finishes and releases the lock.

    • Unlock the guard file.

  • When we want to query the daemon’s status:

    • Lock the guard file, creating it if it did not exist.

    • Try to lock the PID file. If it succeeds, the daemon was not running.

    • Unlock everything.

ucw/daemon.h


struct daemon_params {
  uint flags;                           // DAEMON_FLAG_xxx
  const char *pid_file;                 // A path to PID file (optional)
  const char *run_as_user;              // User name or "#uid" (optional)
  const char *run_as_group;             // Group name or "#gid" (optional)

  // Internal
  uid_t run_as_uid;
  uid_t run_as_gid;
  int want_setuid;
  int want_setgid;
  int pid_fd;
};

Parameters passed to the daemon helper.


enum daemon_flags {
  DAEMON_FLAG_PRESERVE_CWD = 1,         // Skip chdir("/")
  DAEMON_FLAG_SIMULATE = 2,             // Simulate daemonization (avoid fork etc.)
};

Flags passed to the daemon helper.


void daemon_init(struct daemon_params *dp);

Daemon initialization. Should be run after parsing of options. It resolves the UID and GID to run with and locks the PID file. Upon error, it calls die().


void daemon_run(struct daemon_params *dp, void (*body)(struct daemon_params *dp));

Run the daemon. Should be run when everything is initialized. It forks off a new process and does all necessary setup. Inside the new process, it calls body (and when it returns, it exits the process). In the original process, it writes the PID file and returns.

When DAEMON_FLAG_SIMULATE is set, it justs calls body. This is useful for running of daemons in a debugger.


void daemon_exit(struct daemon_params *dp);

Clean up when the daemon is about to exit. It removes the PID file.


void daemon_resolve_ugid(struct daemon_params *dp);

Parse run_as_user and run_as_group and remember the results in internal fields. This is called automatically by daemon_init(), but also provided as a separate function in case you want to use daemon_switch_ugid(). Upon parse error, it calls die().


void daemon_switch_ugid(struct daemon_params *dp);

Switch user and group as specified by the run_as_user and run_as_group. This is performed automatically by daemon_run(), but sometimes you might want to switch the user and group separately. In this case, you have to call daemon_resolve_ugid() beforehand.


struct daemon_control_params {
  const char *pid_file;         // A path to PID file
  const char *guard_file;       // A path to guard file
  int action;                   // Action to perform (DAEMON_CONTROL_xxx)
  char * const *argv;           // Daemon's arguments, NULL-terminated (for DAEMON_CONTROL_START)
  int signal;                   // Signal to send (for DAEMON_CONTROL_SIGNAL)
  char error_msg[DAEMON_ERR_LEN];       // A detailed error message returned (for DAEMON_STATUS_ERROR)
};

Parameters passed to daemon_control()


enum daemon_control_status daemon_control(struct daemon_control_params *dc);

Perform an action on a daemon:

  • DAEMON_CONTROL_START to start the daemon

  • DAEMON_CONTROL_STOP to stop the daemon (send SIGTERM or dc->signal if non-zero)

  • DAEMON_CONTROL_CHECK to check that the daemon is running

  • DAEMON_CONTROL_SIGNAL to send a signal to the daemon (send SIGHUP or dc->signal if non-zero)

The function returns a status code:

  • DAEMON_STATUS_OK if the action has been performed successfully

  • DAEMON_STATUS_ALREADY_DONE if the daemon is already in the requested state

  • DAEMON_STATUS_NOT_RUNNING if the action failed, because the daemon is not running

  • DAEMON_STATUS_ERROR if the action failed for some other reason (in this case, dc->error_msg contains a full error message)

  • DAEMON_STATUS_STALE if the daemon was in an undefined state (e.g., a stale PID file); for DAEMON_CONTROL_START, it means success