Basic daemon programming in C

Basic daemon programming in C

Hello there! As a first C article, I thought I could describe a simple, yet useful, daemon programming process. Let’s quote some definitions first.

In multitasking computer operating systems, a daemon is a computer program that runs as a background process, rather than being under the direct control of an interactive user. Traditionally daemon names end with the letter d: for example, syslogd is the daemon that implements the system logging facility and sshd is a daemon that services incoming SSH connections. – Wikipedia.

For more information about daemon on UNIX-like systems, here’s an interesting link. Now, let’s get down to business.

Required libraries

Programming a UNIX daemon will require the following header files to be included :

  • stdio – daemons usually need to manipulate streams, and well, that’s a C basic. Implementing a log file could be a great idea as you can’t see errors on the standard error output stream (which we’ll close).
  • stdlib – useful for memory manipulation and constants such as EXIT_SUCCESS.
  • errno – humans are not perfect, which means you’ll need this.
  • unistd – some other constants (STDIN_FILENO, …) and process-related functions.
  • signal – I wont give too much information about this one, but it might become essential in your implementation. If you need programs to communicate with your daemon (IPC – more about that), signals are a nice way to keep it simple (have a look at UNIX sockets and message queues for more complex IPC).

Algorithm

Before getting straight into the code, let’s have a look at the mechanism itself. In order to become a daemon, a program has to follow a few steps :

  1. Get rid of the current process state – at execution time, the process is attached to your terminal (your shell) and has an appropriate environment for this. In order to destroy this dependence, we’ll create a child process (fork), and kill the father.
  2. Get a new umask – setting the umask to zero will guarantee a universal behaviour, no matter who starts the program, or the environment it’s initiated in.
  3. Open logs and files – before closing streams and making your process independent, you may wan’t to try reaching the log files. If it fails, the program will still be able to say “wait! I can’t use the logs, you won’t know if I fail!”
  4.  Create a new session with a new PID – we just killed the process’ father, that’s cruel. In order to redeem ourselves, we’ll give a brand new session and PID to the program, it’ll make it happy! That’s where your program become completely independent.
  5. Change current working directory – you never know (well actually, you could) where your daemon will be initiated. If you use relative paths to your files, you might run into some trouble if you run your daemon from an unusual directory. By setting a new CWD, you’ll be certain your program handles relative paths the same way.
  6. Close standard streams – daemons run in background, without being attached to a terminal. That means it won’t need stdinstdout or stderr, as there won’t be any user (terminal) to read/write there. Therefore, we’ll just close those streams once the process has daemonised itself. From now, errors should be reported in the logs.
  7. Start looping – the daemon is ready, now it can work. Daemon programs are usually waiting for events, or timers, therefore they run in a loop. I’ll use a pseudo-boolean variable in my code to show this.
  8. Done !

Programming

I’ll use comments from here, have a look, and make it match the algorithm above!

About logs… Writing on a disk is a heavy operation. Well, not “heavy” as you think of it, but compared to memory manipulation, it takes a while to write to a file. That’s why most UNIX systems use file buffers : contents are loaded into the buffer, and your modifications happen in it as well. Regularly, the kernel will unload the buffer into the files in order to apply changes. This operation is called syncing, and even though it happens regularly on kernel orders, you can ask for it using the sync() function. If you don’t, and your daemon crashes, changes to the log file might not be visible, as the automatic syncing may not have happened yet. By the way, buffer contents are pushed into the real file upon the fclose call, no need for a final sync().

Well, that’s all! Hope you’ll like it, and see you next time!