Let’s Write a Linux Daemon – Part I
- Revision: I’m now creating a Linux Daemon library from this project. See also the GitHub project page, wordptr.libwpd.
- Bug fix: I found a bug in the implementation of the pid lock handler on 2012-11-25. Code corrected in GitHub.
- Bug fix: Post updated on 2011-01-31 based on community feedback.
This is Part I in a series. Check out the rest of the series:
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 *NIX-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:
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:
#include <stdlib.h>#include <stdio.h>#include <exsvcd.h>#include <nmc_daemonizer.h>int main(int argc, char* argv[]) {
/* TODO: Utilize arguments. */nmc_status_t status = NMC_FAILURE;
nmc_daemonizer_t *daemon = NULL;
if((status = nmc_daemonizer_initialize(&daemon)) == NMC_SUCCESS) {;
/* Daemonize ourselves:* fork; setsid; reset file mask; cd; reopen standard files*/if((status = daemon->daemonize(daemon)) == NMC_SUCCESS) {
/* Assuming we successfully daemonize, here we start. */status = daemon->start(daemon);
}}return status;
}
Details
The public interface for the daemonizer is presented below:
#ifndef NMC_DAEMONIZER__H#define NMC_DAEMONIZER__H#include <nmc_common.h>/* Keep the private impementation... private. */struct __nmc_daemonizer_private_t;
typedef struct __nmc_daemonizer_private_t *nmc_daemonizer_private_t;
/* Here's the public interface! */typedef struct nmc_daemonizer {
/* The daemonize method which will fork, etc., our process */nmc_status_t (*daemonize)(const struct nmc_daemonizer *self);
/* Start the "main loop." */nmc_status_t (*start)(const struct nmc_daemonizer *self);
/* Return an instance of the daemon singleton. */struct nmc_daemonizer* (*getInstance)();
/* Handle signals from the OS. */void (*signal_handler)(int sig);
void (*install_signal_handlers)();
/* Shutdown the daemon. */void (*shutdown)();
/* Our private implementation details. */nmc_daemonizer_private_t data;} nmc_daemonizer_t;
/* Utility method to create a new instance of the daemonizer */nmc_status_t nmc_daemonizer_initialize(nmc_daemonizer_t **self);
#endif
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 getInstance or nmc_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 nmc_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, nmc_daemonizer_daemonize is contained within the nmc_daemonizer.c implementation file, located here: nmc_daemonizer.c.
static nmc_status_t nmc_daemonizer_daemonize(const nmc_daemonizer_t *self) {
nmc_status_t res = NMC_FAILURE;
pid_t pid, sid;
struct sigaction sa;
if((res = nmc_set_uid(self)) == NMC_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);
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP, &sa, NULL) < 0) {
/* FATAL: sigaction: %m */exit(EXIT_FAILURE);
}if(chdir(self->data->run_path) == 0) {
set_pid_lock(self);
null_file_descriptors(self);
return NMC_SUCCESS;
} else { /* FATAL: chdir. */ }
} else {
/* couldn't set UID. */}exit(EXIT_FAILURE);
}
Many things are happening in this very short snippet of code:
- Line 6 calls the method
nmc_set_uidwhich 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 on line 8.
- 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.
- Line 16 sets default file system permissions.
- We open a syslog for the daemon on line 25. 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.
- 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.
static nmc_status_t nmc_daemonizer_start(const nmc_daemonizer_t *self) {
sigset_t mask, oldmask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
while(true) {
sigsuspend(&oldmask);
}sigprocmask(SIG_UNBLOCK, &mask, NULL);
return NMC_SUCCESS;
}
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!
References
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:static void null_file_descriptors(const nmc_daemonizer_t *self) {
FILE *f;
f = freopen("/dev/null", "r", stdin);
if(f == NULL) {
/* FATAL: freopen: %m */exit(EXIT_FAILURE);
}f = freopen("/dev/null", "w", stdout);
if(f == NULL) {
/* FATAL: freopen: %m */exit(EXIT_FAILURE);
}f = freopen("/dev/null", "w", stderr);
if(f == NULL) {
/* 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!
Latest Posts
- GeForce GTX 660Ti, NVidia, Nouveau and Updating from CentOS 6.3 to 6.4 (2013/03/10)
- One time, at work… (2013/01/17)
- My Family Doesn’t Care About Security (2013/01/09)
- Programmer Competency Matrix – Education and Experience (2012/12/21)
- Your Code Is The Only Meaningful Project Documentation (2012/12/19)
- I’m Not Arguing With You Over Your Terms of Service (2012/12/18)
- Embracing Change (2012/12/18)
- Finding Quality Developers is No Easy Task (2012/12/14)
- From Avid Gamer to Architect (2012/12/10)
- It’s Bugs All The Way Down (2012/12/07)
- wordptr.libwpd – Expanding Configuration, Changing the Interface (2012/12/07)
- On Tightening Focus (2012/12/06)
- 20 Days With the Google Nexus 10 (2012/12/05)
- wordptr.libwpd – Hooking the Main Loop (2012/12/04)
- I Didn’t Read The Docs – MDADM and Hostname (2012/12/02)
- My Developer Toolbox (2012/12/01)
- wordptr.libwpd – Making it More Library-Like (2012/11/30)
- wordptr.libwpd – Now a Static Library (2012/11/29)
- A Linux Daemon Library – Introducing wordptr.libwpd (2012/11/28)
- CentOS, VMware Workstation, Development and Piece of Mind (2012/11/27)


Pingback: Tweets that mention Let’s Write a Linux Daemon – Part I -- Topsy.com
Pingback: === popurls.com === popular today
Pingback: Let’s Write a Linux Daemon – Part II
Pingback: Eric Blue’s Blog » Weekly Lifestream for February 6th
Pingback: Let’s Write a Linux Daemon – Part III
Pingback: C编写daemon程序 | Min的技术分享 – 54min.com
Pingback: Let’s Write a Linux Daemon – Part III | WORD PTR - Happy Fun Development
Pingback: Let’s Write a Linux Daemon – Part II | WORD PTR - Happy Fun Development
Pingback: Linux Daemon Lock File – A Bug Retrospective | WORD PTR - Pointed Development
Pingback: A Linux Daemon Library – Introducing wordptr.libwpd | WORD PTR - Pointed Development
Pingback: wordptr.libwpd – Expanding Configuration, Changing the Interface | WORD PTR - Pointed Development
Pingback: Writing Linux Daemon Processes | Arun's blog