Daemon Example in C

2020-03-05 c unix

Daemons are long running processes that run in the background with no controlling terminal - tty. Use cases for daemons are when the program needs to be available at all times and managed by the scheduler. Popular examples are httpd, sshd, cron, inetd. All of these end in “d”, which is a convention for the name of a daemon. Daemons typically do not have the ability to write to stdout, or stderr, and have no means to connect to stdin because they have no controlling terminal. Having no controlling terminal is a big deal in daemons. If they could connect to a controlling terminal they could be used in nefarious ways. We guarantee the daemon cannot take on a controlling terminal by doing the so-called “double fork”. Usually, output from the daemon is done by writing to log files. These logs are typically written in the in /var/log directory. Input to a daemon, when necessary, is typically through sockets and signals. Let’s write an example daemon in C that runs on Unix.

Using the Daemon Example

The following code main.c will call our function become_daemon() and if the return code is zero - success - the program will be running as a daemon. It’s as simple as that. The program will continue to open up syslog and will write to the logs every 60 seconds.

C Code to use the Daemon Example

// file main.c
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include "become_daemon.h"

main(int argc, char *argv[])
  int ret;
  const char *LOGNAME = "DAEMON_EXAMPLE";

  // turn this process into a daemon
  ret = become_daemon(0);
    syslog(LOG_USER | LOG_ERR, "error starting");
    return EXIT_FAILURE;

  // we are now a daemon!
  // printf now will go to /dev/null

  // open up the system log
  syslog(LOG_USER | LOG_INFO, "starting");

  // run forever in the background
    syslog(LOG_USER | LOG_INFO, "running");

  return EXIT_SUCCESS;

Header file for Daemon Example

We will define a single function become_daemon() that, when run, will turn the process into a daemon. We need the mechanism to become a daemon simple because during development you don’t want to debug a daemon process unless absolutely necessary. Typically, programs will add a flag that will run the code as a daemon, or, have a flag to turn the daemon off.

Header file for Daemon Example

We will allow our become_daemon() function to take some flags for some various options. Those options are defined above. In the example we’ll run with no flags by passing in a 0.

// file become_daemon.h

#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 /* Max file descriptors to close if
                                   sysconf(_SC_OPEN_MAX) is indeterminate */

// returns 0 on success -1 on error
int become_daemon(int flags);


C Code for Daemon Example

The daemon C code example isn’t trivial and there are number of concepts needed to be understood. The first concept used by the example is known as the “double fork”. The double fork is the safest way to run a daemon since the resultant daemon has no way to controlling terminal - tty. Also, by doing a second fork we prevent zombie process and the daemon will run as a true orphan process. The end result is the daemon’s parent process will be the init process and there is no way for the process to open up a controlling terminal.

We’ll leave for another time the theory of sessions, progress groups, parent processes, child processes, orphans and zombies. However, to truly understand a daemon knowing this theory is essential.

Onto the C example for the daemon!

// file become_daemon.c
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include "become_daemon.h"

int // returns 0 on success -1 on error
become_daemon(int flags)
  int maxfd, fd;

  /* the first fork will change our pid
   * but the sid and pgid will be the
   * calling process
  switch(fork())                    // become background process
    case -1: return -1;
    case 0: break;                  // child falls through
    default: _exit(EXIT_SUCCESS);   // parent terminates

  /* we'll become a session leader which
   * allows us to take controler of a tty.
   * Only a session leader can take control
   * of a tty.
  if(setsid() == -1)                // become leader of new session
    return -1;

  /* the magical double fork. We're the session
   * leader from the code above. Since only the
   * session leader can take control of a tty
   * we will fork and exit the session leader.
   * Once the fork is done below and we use
   * the child process we will ensure we're
   * not the session leader, thus, we cannot
   * take control of a tty.
    case -1: return -1;
    case 0: break;                  // child falls through
    default: _exit(EXIT_SUCCESS);   // parent process will exit

  if(!(flags & BD_NO_UMASK0))
    umask(0);                       // clear file creation mode 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)
      maxfd = BD_MAX_CLOSE;         // if we don't know then guess
    for(fd = 0; fd < maxfd; fd++)

  if(!(flags & BD_NO_REOPEN_STD_FDS))
    /* now time to go "dark"!
     * we'll close stdin
     * then we'll point stdout and stderr
     * to /dev/null

    fd = open("/dev/null", O_RDWR);
    if(fd != STDIN_FILENO)
      return -1;
      return -2;
      return -3;

  return 0;

Running the example

If you want the source download become_daemon.

When you run the daemon. It’s 100% uneventful! Nothing happens. You get the prompt back and the daemon goes on living it’s happy life as a daemon process in Unix. Let’s run the example and see what happens.

$ wget "https://lloydrochester.com/code/become_daemon-1.0.tar.gz"
$ tar zxf become_daemon-1.0.tar.gz
$ cd become_daemon-1.0
$ ./configure
$ make
$ ./src/become_daemon
$ # yep, that's it!

Analyzing the Daemon Process

Now, with the daemon running let’s look at what’s going on. We can analyze the parent process, if there is a tty, and the parent group and session id.

Daemon Process IDs

The following ps command, with arguments, lists out the process id, parent process id, parent group id, and session id of our daemon.

$ ps xao pid,ppid,pgid,sid,comm
 587961       1  587959  587959 become_daemon

We can also see the parent of our daemon is indeed the /sbin/init process.

$ ps 1
      1 ?        Ss     1:16 /sbin/init

Let’s verify our daemon has no tty.

$ ps 587961
 587961 ?        S      0:00 ./become_daemon

Known Issues

This code will compile and run fine on OS X, however, you will not be able to see logs. The logs should go in /var/log/system.log but nothing is there. The OS X Operating system now uses os_log as it would appear and something seems to be stopping logs from happening on OS X. Please comment if you know why?

The Linux Programming Interface Book

This example is heavily influenced by the incredible work of Michael Kerrisk’s groundbreaking book - The Linux Programming Interface. Also, known as TLPI. I highly recommend this book to anyone who is doing system programming, or wants to understand Unix better. This book is truly the Bible of Unix.

Where to go from here?

Now that we have an Example Daemon running the next steps would be to install a signal handler so you can communicate with the daemon and that it properly cleans up when signals such as SIGKILL are sent to it. Once the signal handler is installed then you can send it messages with the kill command. Alternatively, you could open a socket and take commands this way.

The second step would be create logging and write to a log file in /var/log, or, if the system logger works use it, however, see the issues mentioned in OS X.

When developing code that runs as a daemon, it’s highly recommended to be able to test everything first while the program is not running as a daemon. It’s quite difficult to debug and see what’s going on. However, there are ways.

comments powered by Disqus