How to Write a Daemon Process in C

I thought I’d do another C tutorial, since I haven’t done anything on C programming in a while. In this tutorial I’d like to show you how you can write your own daemon processes, also sometimes known as servers. I’m not going to show you how to write a web server or anything like that, because that’s outside the scope of this tutorial. However, I will show you the necessary steps towards creating a process that runs on its own in the background, without any controlling terminal, since this is the necessary first step towards creating a server.

First let’s go through an overview of some of the preliminary concepts. Every process in the Unix process environment has several IDs associated with it. These include the process ID (PID), parent process ID (PPID), process group ID (PGID), and session ID (SID). The PID is a unique identifier that distinguishes a process from other processes. The PPID is the PID of the parent process, that is, the process that created the current process (new processes are created with the fork function). The PGID designates a process group, which is usually a pipeline of processes linked together in a single command through pipes. The SID denotes the session, usually corresponding to the controlling terminal. Every process group has a process group leader, typically the first process in the pipeline. Similarly, every session has a specific process designated as the session leader.

There are a few additional bits of process information that we’ll need to understand before continuing. First is the three standard file descriptors that are attached to each process. They are standard input (stdin), standard output (stdout), and standard error (stderr). These are attached to terminals by default and tell the process where to get its input data from, where to send its output data to, and where to send any error messages to, respectively. The final bit of information is the file mode creation mask, or umask. This is a three-digit octal number that specifies the default permissions a file will have when created by this process. The umask is actually the inverse of the default permissions, so a umask of 111 for example means a file will have default permissions of 666.

Now that we’ve established some of the preliminaries, we’re ready to take a look at what exactly makes a process a daemon process. A daemon process has the following basic characteristics:

  1. No controlling terminal.
  2. No open files or other information inherited from the parent process.
  3. Not attached to any specific part of the filesystem, especially a mounted filesystem that may be unmounted during the lifetime of the daemon.
  4. Standard input, standard output, and standard error file descriptors are set to system logs rather than terminals.

Setting up a daemon, therefore, involves the following steps:

  1. Use the umask function to set the file mode creation mask to 0 so that the daemon process doesn’t deny any file access permissions to any processes that might need them.
  2. Call fork and then have the parent exit. This has the effect of telling the controlling terminal that the command is done so it doesn’t get stuck waiting for it to exit, and it also makes sure the daemon process is not the session leader, which is a necessary prerequisite for detaching it from the session.
  3. Call setsid with no argument to detach from the current terminal session and create a new session that is not associated with a controlling terminal.
  4. Change the current working directory to the root directory.
  5. Close any unneeded open file descriptors.
  6. Reopen file descriptors 0, 1, and 2 (the file descriptors corresponding to stdin, stdout, and stderr respectively) and associate them with something other than a terminal. The easiest thing to do is just associate them with /dev/null, which is what we will do here. Sometimes file descriptor 2 is associated with a log file (an error log). Other daemons, like cron for instance, might relay their standard output or standard error streams to the mail daemon, causing output messages to show up in your mailbox.

We are now ready to write a function that will turn the current running program into a daemon. We call this function daemonize.

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <fcntl.h>
 5 #include <errno.h>
 6 #include <syslog.h>
 8 void daemonize( char *cmd ){
 9         pid_t pid;
11         /* Clear file creation mask */
12         umask0 );
14         /* Spawn a new process and exit */
15         if( (pid = fork()) < 0 ){ /* Fork error */
16                 fprintfstderr"%s: Fork error\n", cmd );
17                 exit( errno );
18         }
19         else if( pid > 0 ){ /* Parent process */
20                 exit0 );
21         }
22         /* Child process */
23         setsid();
25         /* Change working directory to root directory */
26         ifchdir"/" ) < 0 ){
27                 fprintfstderr"%s: Error changing directory\n", cmd );
28                 exit( errno );
29         }
31         /* Close all open file descriptors */
32         forint i = 0; i < 1024; i++ ){
33                 close( i );
34         }
36         /* Reassociate file descriptors 0, 1, and 2 with /dev/null */
37         int fd0 = open"/dev/null"O_RDWR );
38         int fd1 = dup0 );
39         int fd2 = dup0 );
41         /* Open the log file */
42         openlog( cmd, LOG_CONSLOG_DAEMON );
43         if( fd0 != 0 || fd1 != 1 || fd2 != 2 ){
44                 syslogLOG_ERR"Unexpected file descriptors %d%d%d\n",
45                         fd0, fd1, fd2 );
46                 exit( errno );
47         }
48 }

Most of this is fairly self-explanatory or was explained in the paragraphs leading up to the code. For everything else I will provide an explanation here. I did add a few extra lines of code for error handling. Basically if a function returns a negative value, then that indicates an error. We handle this by printing an error message to standard error and then exiting with an exit status equal to the global variable errno. If you want more information on this variable, stay tuned, because I’m about to do another tutorial that is all about error handling in C with the errno variable and the errno.h header file fairly soon.

Lines 37-39 attempt to reopen the three standard file descriptors to /dev/null. The dup function simply duplicates a file descriptor that’s already open. If everything goes well, then because all file descriptors are closed, we should start at 0, and the numbers selected should be 0, 1, and 2. If for whatever reason we get different values (line 43) we log an error message to the error log.

This section of the code (lines 42-47) warrants some special attention, because it introduces a new concept not touched upon yet, and that’s the concept of the syslog daemon. Every running Unix/Linux system has a daemon process called syslogd, which handles error messages and alerts generated by daemons and sometimes other processes on the system. These messages are sent to a special file called /dev/log, which is a socket-type special file (specifically it is a Unix-domain socket). Sockets are files typically used as endpoints for processes communicating over TCP/IP networks, though they can also be used for local communication between processes on the same system.

We could just open the standard error stream on a special log file and write all error messages to standard error, but syslogd gives us a uniform interface through which all processes can send their error messages and have the syslog daemon decide on the appropriate response. This system exists mostly for convenience and uniformity.

To take advantage of the system log we must first call openlog. This function has three arguments. The first argument is a command string which will be prepended to all log messages. The second argument is for options (here we use LOG_CONS which means that if the system log is not available we will send the messages to the console). The third argument is the facility code, which is used to determine the priority of the message. To be precise, the priority is derived by combining this argument with the first argument to the syslog function, in this case LOG_ERR. The rest of the arguments to syslog are just a format string and some optional variables to format. You can think of this function as being kinda like fprintf, except instead of specifying a file handle as the first argument it specifies an error level.

All information in this tutorial comes from Chapter 13 of Advanced Programming in the Unix Environment (Second Edition) from the Addison-Wesley Professional Computing Series. If you want more in-depth information about daemons and other aspects of the POSIX API you can purchase the latest edition of this book here. Thanks for reading.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s