GNU-Linux Rapid Embedded Programming
上QQ阅读APP看书,第一时间看更新

Writing a custom daemon

In this last section, we'll learn how to write our own daemon in several programming languages using a skeleton that can be used to quickly develop really complex daemons. Due to the lack of space, we cannot add all the possible features a daemon has, but the presented skeletons will have whatever you need to know about daemon creation.

All example code will implement a daemon with the following command line usage:

usage: mydaemon [-h] [-d] [-f] [-l]
-h - show this message
-d - enable debugging messages
-f - do not daemonize
-l - log on stderr

The -h option argument will show the help message, while -d will enable the debugging messages. The -f option argument will prevent the daemon from running in the background, and the -l option will print the logging messages to the standard error channel. Apart from the -h option argument, the other arguments are very useful during the debugging stages if used together in the form:

# ./mydaemon -d -f -l

The developer can run the daemon in the foreground with the debugging messages enabled and printed on the current terminal.

A daemon in C

In C language, a daemon skeleton can be written as in the chapter_04/mydaemon/my_daemon.c file in the book's example code repository. The most important steps here are from the openlog() function call and the daemon_body() one. In fact, the two signal() system calls are used to set up the signal handlers, while the whole job is done by the daemon() function call (refer to the beginning of this chapter). Here is the relevant code:

/* Open the communication with syslogd */
loglevel = LOG_PID;
if (logstderr)
    loglevel |= LOG_PERROR;
openlog(NAME, loglevel, LOG_USER);

/* Install the signals traps */
sig_h = signal(SIGTERM, sig_handler);
if (sig_h == SIG_ERR) {
    fprintf(stderr, "unable to catch SIGTERM");
    exit(-1);
}
sig_h = signal(SIGINT, sig_handler);
if (sig_h == SIG_ERR) {
    fprintf(stderr, "unable to catch SIGINT");
    exit(-1);
}
dbg("signals traps installed");

/* Should run as a daemon? */
if (daemonize) {
    ret = daemon(!daemonize, 1); 
    if (ret) {
        fprintf(stderr, "unable to daemonize the process");
        exit(-1);
    }   
}

daemon_body();

Now we can compile the code using make and then we can execute it using the following command line:

root@bbb:~# make
cc -Wall -O2 -D_GNU_SOURCE mydaemon.c -o mydaemon
root@bbb:~# ./mydaemon
root@bbb:~# 

We notice that it seems that nothing happens since the prompt is returned! However, after looking at the system log files, we can see the daemon's activity:

root@bbb:~/mydaemon# tail -f /var/log/syslog
Apr 2 22:35:01 bbb mydaemon[3359]: I'm working hard!
Apr 2 22:35:02 bbb mydaemon[3359]: I'm working hard!
Apr 2 22:35:03 bbb mydaemon[3359]: I'm working hard!

The daemon can now be stopped using the killall command, as follows:

root@bbb:~# killall mydaemon

A daemon in PHP

In PHP, creating a daemon is a bit more complex due the fact that there is no dedicated function to daemonize a running process; however, the task is still quite simple, as shown in the chapter_04/mydaemon/my_daemon.php file in the book's example code repository. As for the C example, the important steps are all after the openlog() function call; the pcntl_signal() functions are used to install the signal handlers, while the daemon is created using the pcntl_fork(), exit(), chdir() and fclose()functions, as already explained at the beginning of this chapter. Here is the code snippet:

openlog(NAME, $loglevel, LOG_USER); 
 
# Install the signals traps 
pcntl_signal(SIGTERM, "sig_handler"); 
pcntl_signal(SIGINT,  "sig_handler"); 
dbg("signals traps installed"); 
 
# Start the daemon 
if ($daemonize) { 
    dbg("going in background..."); 
    $pid = pcntl_fork(); 
    if ($pid < 0) { 
        die("unable to daemonize!"); 
    } 
    if ($pid) { 
        # The parent can exit... 
        exit(0); 
    } 
    # ... while the children goes on! 
 
    # Set the working directory to / 
    chdir("/"); 
 
    # Close all of the standard file descriptors as we are running 
    # as a daemon 
    fclose(STDIN); 
    fclose(STDOUT); 
    fclose(STDERR); 
} 
 
daemon_body(); 
Note

The documentation of the pcntl_fork() function is online at:  http://php.net/manual/en/function.pcntl-fork.php .

In this case, the daemon can be executed using the following command line, and we get the same output as the earlier one:

root@bbb:~# ./mydaemon.php

We can check it by using again the tail command:

root@bbb:~# tail -f /var/log/syslog
Apr 2 22:36:59 bbb mydaemon.php[3365]: I'm working hard!
Apr 2 22:37:00 bbb mydaemon.php[3365]: I'm working hard!
Apr 2 22:37:01 bbb mydaemon.php[3365]: I'm working hard!

Then, to stop it, we use the killall utility again:

root@bbb:~# killall mydaemon.php

A daemon in Python

In Python, the task is easier than in C due to the fact that we have a dedicated library to daemonize the running process.

The code is in the chapter_04/mydaemon/my_daemon.py file in the book's example code repository. As earlier, the relevant part is after the syslog.openlog() method call; we simply create a dedicated context with the daemon.DaemonContext()method, and then within that context, we execute our daemon_body() function. The relevant code is reported as follows:

# Open the communication with syslogd 
loglevel = syslog.LOG_PID 
if logstderr: 
    loglevel |= syslog.LOG_PERROR 
syslog.openlog(NAME, loglevel, syslog.LOG_USER) 
 
# Define the daemon context and install the signals traps 
context = daemon.DaemonContext( 
    detach_process = daemonize, 
) 
context.signal_map = { 
    signal.SIGTERM: sig_handler, 
    signal.SIGINT: sig_handler, 
} 
dbg("signals traps installed") 
 
# Start the daemon 
with context: 
    daemon_body() 
Note

The documentation of the Python standard daemon process library is at:  https://www.python.org/dev/peps/pep-3143/ .

The daemon is launched as earlier with the following command line:

root@bbb:~# ./mydaemon.py 

Again, we can check the functioning with tail:

root@bbb:~# tail -f /var/log/syslog
Apr 2 22:47:59 bbb mydaemon.py[4339]: I'm working hard!
Apr 2 22:48:00 bbb mydaemon.py[4339]: I'm working hard!
Apr 2 22:48:01 bbb mydaemon.py[4339]: I'm working hard!

Then, we use killall to stop the daemon:

root@bbb:~# killall mydaemon.py

A daemon in Bash

As a last example, we present the daemon implementation of a Bash script. This example is not as relevant as the previous ones since it is very rare to implement a daemon as a Bash script; however, it's interesting in order to show you how Bash scripting can be powerful.

The Bash demon code is reported in the chapter_04/mydaemon/my_daemon.sh file in the book's example code repository. In this case, the relevant code is after the trap command, which is used to install the signals handler, and it's all concentrated in the line with the eval command. The daemon_body() function is called in such a way that the stdin and stdout channels are redirected to the /dev/null file, while the stderr is redirected if no option is supplied, while the background or foreground execution mode is selected by the respective command-line option argument. The relevant code is as follows:

# Install the signals traps 
trap sig_handler SIGTERM SIGINT 
dbg "signals traps installed" 
 
# Start the daemon 
if [ -n "$daemonize" ] ; then 
    dbg "going in background..." 
 
    # Set the working directory to / 
    cd / 
fi 
[ -z "$logstderr" ] && tmp="2>&1" 
eval daemon_body </dev/null >/dev/null $tmp $daemonize 
Note

To get more information on the background process execution, standard input/output redirection and other Bash-related stuff used in this example, you can take a look at Bash's man pages using the usual command:

$ man bash

In this case, we can run the daemon in the debugging mode and then look at its output directly on the terminal:

root@bbb:~# ./mydaemon.sh -d -f -l
mydaemon.sh: signals traps installed
mydaemon.sh: start main loop
mydaemon.sh: I'm working hard!
mydaemon.sh: I'm working hard!
mydaemon.sh: I'm working hard!

This time, we can stop the daemon by simply pressing the CRTL + C keys sequence, and this is the output:

^Cmydaemon.sh: signal trapped!
root@bbb:~#