To become a daemon, a program performs the following steps:
Perform a fork(), after which the parent exits and the child continues. (As a consequence, the daemon becomes a child of the init process.) This step is done for two reasons:
Assuming the daemon was started from the command line, the parent’s termination is noticed by the shell, which then displays another shell prompt and leaves the child to continue in the background.
The child process is guaranteed not to be a process group leader, since it inherited its process group ID from its parent and obtained its own unique process ID, which differs from the inherited process group ID. This is required in order to be able to successfully perform the next step.
The child process calls setsid() (Sessions) to start a new session and free itself of any association with a controlling terminal.
If the daemon never opens any terminal devices thereafter, then we don’t need to worry about the daemon reacquiring a controlling terminal. If the daemon might later open a terminal device, then we must take steps to ensure that the device does not become the controlling terminal. We can do this in two ways:
Specify the O_NOCTTY
flag on any open() that may apply to a terminal device.
Alternatively, and more simply, perform a second fork() after the setsid() call, and again have the parent exit and the (grand)child continue. This ensures that the child is not the session leader, and thus, according to the System V conventions for the acquisition of a controlling terminal (which Linux follows), the process can never reacquire a controlling terminal (Controlling Terminals and Controlling Processes).
On implementations following the BSD conventions, a process can obtain a controlling terminal only through an explicit ioctl() TIOCSCTTY
operation, and so this second fork() has no effect with regard to the acquisition of a controlling terminal, but the superfluous fork() does no harm.
Clear the process umask (The Process File Mode Creation Mask: umask()), to ensure that, when the daemon creates files and directories, they have the requested permissions.
Change the process’s current working directory, typically to the root directory (/
). This is necessary because a daemon usually runs until system shutdown; if the daemon’s current working directory is on a file system other than the one containing /
, then that file system can’t be unmounted (Unmounting a File System: umount() and umount2()). Alternatively, the daemon can change its working directory to a location where it does its job or a location defined in its configuration file, as long as we know that the file system containing this directory never needs to be unmounted. For example, cron places itself in /var/spool/cron
.
Close all open file descriptors that the daemon has inherited from its parent. (A daemon may need to keep certain inherited file descriptors open, so this step is optional, or open to variation.) This is done for a variety of reasons. Since the daemon has lost its controlling terminal and is running in the background, it makes no sense for the daemon to keep file descriptors 0, 1, and 2 open if these refer to the terminal. Furthermore, we can’t unmount any file systems on which the long-lived daemon holds files open. And, as usual, we should close unused open file descriptors because file descriptors are a finite resource.
Some UNIX implementations (e.g., Solaris 9 and some of the recent BSD releases) provide a function named closefrom(n) (or similar), which closes all file descriptors greater than or equal to n. This function isn’t available on Linux.
After having closed file descriptors 0, 1, and 2, a daemon normally opens /dev/null
and uses dup2() (or similar) to make all those descriptors refer to this device. This is done for two reasons:
It ensures that if the daemon calls library functions that perform I/O on these descriptors, those functions won’t unexpectedly fail.
It prevents the possibility that the daemon later opens a file using descriptor 1 or 2, which is then written to—and thus corrupted—by a library function that expects to treat these descriptors as standard output and standard error.
/dev/null
is a virtual device that always discards the data written to it. When we want to eliminate the standard output or error of a shell command, we can redirect it to this file. Reads from this device always return end-of-file.
We now show the implementation of a function, becomeDaemon(), that performs the steps described above in order to turn the caller into a daemon.
#include <syslog.h>
int becomeDaemon
(int flags);
Returns 0 on success, or -1 on error
The becomeDaemon() function takes a bit-mask argument, flags, that allows the caller to selectively inhibit some of the steps, as described in the comments in the header file in Example 37-1.
Example 37-1. Header file for become_daemon.c
daemons/become_daemon.h
#ifndef BECOME_DAEMON_H /* Prevent double inclusion */ #define BECOME_DAEMON_H /* Bit-mask values for 'flags' argument of becomeDaemon() */ #define BD_NO_CHDIR 01 /* Don't chdir("/") */ #define BD_NO_CLOSE_FILES 02 /* Don't close all open files */ #define BD_NO_REOPEN_STD_FDS 04 /* Don't reopen stdin, stdout, and stderr to /dev/null */ #define BD_NO_UMASK0 010 /* Don't do a umask(0) */ #define BD_MAX_CLOSE 8192 /* Maximum file descriptors to close if sysconf(_SC_OPEN_MAX) is indeterminate */ int becomeDaemon(int flags); #endifdaemons/become_daemon.h
The implementation of the becomeDaemon() function is shown in Example 37-2.
The GNU C library provides a nonstandard function, daemon(), that turns the caller into a daemon. The glibc daemon() function doesn’t have an equivalent of the flags argument of our becomeDaemon() function.
Example 37-2. Creating a daemon process
daemons/become_daemon.c
#include <sys/stat.h> #include <fcntl.h> #include "become_daemon.h" #include "tlpi_hdr.h" int /* Returns 0 on success, -1 on error */ becomeDaemon(int flags) { int maxfd, fd; switch (fork()) { /* Become background process */ case -1: return -1; case 0: break; /* Child falls through... */ default: _exit(EXIT_SUCCESS); /* while parent terminates */ } if (setsid() == -1) /* Become leader of new session */ return -1; switch (fork()) { /* Ensure we are not session leader */ case -1: return -1; case 0: break; default: _exit(EXIT_SUCCESS); } if (!(flags & BD_NO_UMASK0)) umask(0); /* Clear file mode creation mask */ if (!(flags & BD_NO_CHDIR)) chdir("/"); /* Change to root directory */ if (!(flags & BD_NO_CLOSE_FILES)) { /* Close all open files */ maxfd = sysconf(_SC_OPEN_MAX); if (maxfd == -1) /* Limit is indeterminate... */ maxfd = BD_MAX_CLOSE; /* so take a guess */ for (fd = 0; fd < maxfd; fd++) close(fd); } if (!(flags & BD_NO_REOPEN_STD_FDS)) { close(STDIN_FILENO); /* Reopen standard fd's to /dev/null */ fd = open("/dev/null", O_RDWR); if (fd != STDIN_FILENO) /* 'fd' should be 0 */ return -1; if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO) return -1; if (dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) return -1; } return 0; }daemons/become_daemon.c
If we write a program that makes the call becomeDaemon(0) and then sleeps for a while, we can use ps(1) to look at some of the attributes of the resulting process:
$./test_become_daemon
$ps -C test_become_daemon -o "pid ppid pgid sid tty command"
PID PPID PGID SID TT COMMAND 24731 1 24730 24730 ? ./test_become_daemon
We don’t show the source code for daemons/test_become_daemon.c
, since it is trivial, but the program is provided in the source code distribution for this book.
In the output of ps, the ?
under the TT heading indicates that the process has no controlling terminal. From the fact that the process ID is not the same as the session ID (SID), we can also see that the process is not the leader of its session, and so won’t reacquire a controlling terminal if it opens a terminal device. This is as things should be for a daemon.