WORD PTR

Pointed Development

Let's Write a Linux Daemon - Part I

| Comments

  1. Bug fix: Post updated on 2011-01-31 based on community feedback.
  2. Bug fix: I found a bug in the implementation of the pid lock handler on 2012-11-25. Code corrected in GitHub.
  3. Revision: I’m now creating a Linux Daemon library from this project. See also the GitHub project page, wordptr.libwpd.

This is Part I in a series. Check out the rest of the series: Part II – Configuration and Logging

Thanks!

Last week was my very first post. I received a lot of positive feedback from the good people over at the proggit subreddit… and a lot of good, constructive criticism. Thanks everyone!

First, tl;dr:

  • fork; setsid; reset file mask; cd; reopen standard files

(special thanks to tinou for the idea).

Introduction

This is the first part in a series of posts dedicated to writing a Linux-based daemon that does… something interesting. Part I is primarily dedicated to setting up the environment and establishing the daemon framework.

As with all of the source code on this blog, I’ve pushed everything up to github under the repository. You can retrieve the latest like so:

git clone git://github.com/jgshort/wordptr.git

The example project that I’ll be building through the course of this series (conveniently called the Example Service Daemon [exsvcd]) will fork itself and run perpetually, waiting for specific events and responding as necessary:

1
2
3
4
5
6
7
8
ctor@asphyxia:~/git/nm/exsvcd$ sudo src/exsvcd
ctor@asphyxia:~/git/nm/exsvcd$ ps aux | grep exsvcd
daemon   13068  0.0  0.0  12424   368 ?        S    16:55   0:00 src/exsvcd
ctor     13070  0.0  0.0   7624   920 pts/0    S    16:55   0:00 grep --color=auto exsvcd
ctor@asphyxia:~/git/nm/exsvcd$ sudo kill -TERM 13068
ctor@asphyxia:~/git/nm/exsvcd$ ps aux | grep exsvcd
ctor     13073  0.0  0.0   7624   924 pts/0    S    16:56   0:00 grep --color=auto exsvcd
ctor@asphyxia:~/git/nm/exsvcd$

Other example daemons include mysqld, memcached and httpd.

In Part I, we’ll look at the basic building blocks of a daemon in C.

Example

By the end of this article, we’ll have the essential pieces put together to kick off a daemon service. The following code demonstrates the basic object interface:

Daemonization Example (wpd.c) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <assert.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

#include <libwpd.h>

/* sed-begin-wait-loop */
static void daemon_on_start(const wp_daemonizer_t *self) {
  assert(self); /* make compiler happy */
  sigset_t mask, oldmask;
  sigemptyset(&mask);
  sigaddset(&mask, SIGUSR1);
  sigprocmask(SIG_BLOCK, &mask, &oldmask);

  while(true) {
    sigsuspend(&oldmask);
  }

  sigprocmask(SIG_UNBLOCK, &mask, NULL);
}
/* sed-end-wait-loop */

static void reconfigure_daemon(const struct wp_daemonizer *daemon, const wp_configuration_pt config) {
  daemon = daemon;
  config->set_enable_verbose_logging(config, true);
  config->set_daemon_on_start_method(config, &daemon_on_start);
}

int main(int argc, char* argv[]) {
  wp_status_t status = WP_FAILURE;
  wp_daemonizer_pt daemon = NULL;

  if((status = wp_daemonizer_initialize(&daemon, &reconfigure_daemon)) == WP_SUCCESS) {;
    atexit(&(*daemon->shutdown));

    /* Daemonize ourselves:
     * fork; setsid; reset file mask; cd; reopen standard files
     */
    if((status = daemon->daemonize(daemon)) == WP_SUCCESS) {
      /* Assuming we successfully daemonize, here we start. */
      status = daemon->start(daemon);
    }
  }

  argc = argc; argv = argv;
  return status;
}

Details

The public interface for the daemonizer is presented below:

Public Interface (wp_daemonizer.h) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
 * File:   wp_daemonizer.h
 * Author: Jason Short <ctor@wordptr.com>
 *
 * Created on November 28, 2012, 6:10 AM
 */

#ifndef WP_DAEMONIZER__H
#define WP_DAEMONIZER__H

#include <wp_common.h>
#include <wp_configuration.h>

struct wp_daemonizer;

/* Keep the private impementation... private. */
struct __wp_daemonizer_private_t;
typedef struct __wp_daemonizer_private_t *wp_daemonizer_private_t;

typedef void (*wp_reconfigure_method_fn)(const struct wp_daemonizer *, wp_configuration_pt);

/* Here's the public interface! */
typedef struct wp_daemonizer {
  /* The daemonize method which will fork, etc., our process */
  wp_status_t (*daemonize)(const struct wp_daemonizer *self);
  /* Start the "main loop." */
  wp_status_t (*start)(const struct wp_daemonizer *self);

  /* Return an instance of the daemon singleton. */
  struct wp_daemonizer* (*get_instance)();

  /* Handle signals from the OS. */
  void (*signal_handler)(int sig);
  void (*install_signal_handlers)();
  /* Shutdown the daemon. */
  void (*shutdown)();

  void (*set_reconfigure_method)(const struct wp_daemonizer *self, wp_reconfigure_method_fn fn);

  /* Our private implementation details. */
  wp_daemonizer_private_t data;
} wp_daemonizer_t, *wp_daemonizer_pt;

/* Utility method to create a new instance of the daemonizer */
/*wp_status_t wp_daemonizer_initialize(wp_daemonizer_t **self_out, wp_configuration_t *config);*/
wp_status_t wp_daemonizer_initialize(wp_daemonizer_pt *out, wp_reconfigure_method_fn fn);

#endif /* WP_DAEMONIZER__H */

We have a basic framework from which we can build up a much more complete daemon in the upcoming weeks. For now, this just gets us started. In order to encapsulate everything into a single class and keep the details as straight forward as possible, I implemented the daemonizer class as a singleton. We can either initialize or retrieve the instance with get_instance or wp_daemonizer_initialize.

In the future, we will provide initialization parameters to the initialize method. For now, we’ll simply start up with some defaults.

The real work is contained within the wp_daemonizer_daemonize method. Though I’ve done my best to keep this blog and the source on github in sync, I defer you to the actual repository for specifics. The following method, wp_daemonizer_daemonize is contained within the wp_daemonizer.c implementation file, located here: wp_daemonizer.c.

Daemonization Method (wp_daemonize.c) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
static wp_status_t wp_daemonizer_daemonize(const wp_daemonizer_t *self) {
  wp_status_t res = WP_FAILURE;
  wp_configuration_t *config = NULL;
  pid_t pid, sid;

  config = self->data->config;

  if(!config->get_enable_daemon(config)) {
    wp_log(stdout, config, LOG_INFO, "Daemon option not enabled, starting main loop: %m");
    return WP_SUCCESS;
  }

  if((res = wp_daemonizer_set_uid(self)) == WP_SUCCESS) {
    /* Forking. Opening syslog for exsvcd. */
    if((pid = fork()) < 0) {
      /* fork error */
      /* FATAL: fork: %m */
      exit(EXIT_FAILURE);
    } else if(pid != 0) {
      exit(EXIT_SUCCESS);
    }

    umask(027);

    sid = setsid(); /* get a new process group. */
    if(sid < 0) {
      /* FATAL: setsid: %m */
      exit(EXIT_FAILURE);
    }

    /* log to syslog prefixed with exsvcd */
    openlog("exsvcd", LOG_PID, LOG_USER);

    wp_log(stderr, self->data->config, LOG_INFO, "syslog file opened: %m");
    struct sigaction sa;
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if(sigaction(SIGHUP, &sa, NULL) < 0) {
      wp_log(stderr, self->data->config, LOG_ERR, "FATAL: sigaction: %m");
      exit(EXIT_FAILURE);
    }

    /* double-fork pattern to prevent zombie children */
    if((pid = fork()) < 0) {
      wp_log(stderr, self->data->config, LOG_ERR, "FATAL: fork: %m");
      exit(EXIT_FAILURE);
    } else if(pid != 0) {
      /* parent */
      exit(EXIT_SUCCESS);
    }

    char *run_path = config->get_run_folder_path(config);
    if(chdir(run_path) == 0) {
      if(wp_daemonizer_set_pid_lock(self) != WP_SUCCESS) {
        exit(EXIT_FAILURE);
      }
    } else {
      wp_log(stderr, self->data->config, LOG_ERR, "FATAL: chdir: %m");
      exit(EXIT_FAILURE);
    }

    /* redirect stdin, out, err to NULL */
    wp_daemonizer_null_file_descriptors(self);

    return WP_SUCCESS;
  } else {
    /* couldn't set UID. */
    wp_log(stderr, self->data->config, LOG_ERR, "FATAL: Couldn't set UID: %m");
  }

  exit(EXIT_FAILURE);
}

Many things are happening in this lenghty snippet of code:

  • It calls the method wp_daemonizer_set_uid which attempts to see if we’re already running as a daemon and sets the UID to user “daemon.” This assumes we were started as root
  • Assuming we’re successful, we are now running as user “daemon,” we create a new process by calling fork
  • We open up a PID file and save our PID to the file. This is one method of ensuring the daemon is running only once
  • We set default file system permissions with umask
  • We open a syslog for the daemon with the call to openlog. This will become essential in coming posts: We cannot output diagnostic information to the screen as a service, since we set all of the standard file descriptors to /dev/null. The simplest solution is syslogd
  • We fork again to prevent zombie children
  • Before we complete the deamonization process, we chdir to a “safe” folder path, in this case /tmp

By the conclusion of this method, the UID has changed, process has forked, we’ve created a PID file and chdir to a neutral location. Our daemon is running and ready to receive events.

Eventually, the deamon will sit around listening for interesting events. Right now, we don’t listen for anything… We just run in a loop forever.

Wait Loop (wp_wait_loop.c) download
1
2
3
4
5
6
7
8
9
10
11
12
13
static void daemon_on_start(const wp_daemonizer_t *self) {
  assert(self); /* make compiler happy */
  sigset_t mask, oldmask;
  sigemptyset(&mask);
  sigaddset(&mask, SIGUSR1);
  sigprocmask(SIG_BLOCK, &mask, &oldmask);

  while(true) {
    sigsuspend(&oldmask);
  }

  sigprocmask(SIG_UNBLOCK, &mask, NULL);
}

Conclusion

We now have a simple class that sets up a process as a daemon service. Many things need to be fleshed out, but the framework is present. We still need to:

  • Establish a logging framework. This is the highest priority as – once the daemon is running – we’re blind to anything going on.
  • Listen for events. Right now our daemon starts up… but then it just runs and doesn’t do anything at all. What a worthless service!

In the next several posts, we’ll look more into the implementation details of the daemon, and we’ll set up a framework from which we can listen to and handle events, whether that be on a socket or the file system or whatever.

This is a comparatively short post but I sincerely hope you find it useful. As with anything I write, I welcome criticisms, suggestions and bug fixes. I’m open to ideas for the service, too.

Thanks!

Update (2011-01-31)

Wow! Lots of great info up on the Reddit proggit entry for this post and in the comments below.

kurin asks, “Don’t you double fork, to prevent zombies?”

The answer is yes, you do. See here, here and here. The original implementation of my deamon did perform the double fork. I removed it thinking A) It was a mistake on my part, and B) I was under the impression that if the parent exists immediately after the fork, the double fork is not necessary. I’ve reimplemented the double fork pattern in the source. See the github repository for the latest.

There’s always int daemon(int nochdir, int noclose) in unistd.h.

Personally, I don’t care for this implementation of daemon. Eventually, my implementation will have a bit more configuration-driven options and overrides. I like that. Again, just my personal preference.

As kev009 states, “It’s also tradition to close stdin, stdout, stdrr and reopen them as null to prevent misuse of those FDs.”

This is correct and I failed to mention it in the post above, but it is implemented in code:

NULL File Descriptors (wp_null_file_descriptors.c) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * Redirect standard file descriptors to /dev/null.
 * @param self pointer to an instance of the daemonizer. Not utilized at this time.
 */
static void wp_daemonizer_null_file_descriptors(const wp_daemonizer_t *self) {
  assert(self); /* make compiler happy for now. */
  struct fp { FILE *file; const char *mode; };
  struct fp files[] = {
    { .file = stdin , .mode = "r" },
    { .file = stdout, .mode = "w" },
    { .file = stderr, .mode = "w" }
  };
  for(size_t i = 0; i < sizeof(files) / sizeof(struct fp); i++) {
    FILE *f = freopen("/dev/null", files[i].mode, files[i].file);
    if(f == NULL) {
      wp_log(stderr, self->data->config, LOG_ERR, "FATAL: freopen: %m");
      exit(EXIT_FAILURE);
    }
  }
}

Stefan M. (see below) comments that this is really a tutorial on the interface for my daemon code and not necessarily a POSIX daemon tutorial.

He’s absolutely right, this post does not attempt to teach the fundamentals of POSIX programming. For that, I highly recommend the seminal works of the late W. Richard Stevens. His writings helped shape me into the developer I am today and I personally feel they are invaluable to any UNIX system developer.

Thanks for all of the great comments, everyone!

References

Comments