Daemonisation With a Feedback

Author / Автор: Сергей Сацкий
Publication date / Опубликовано: 10.12.2005
Version / Версия текста: 1.0

Introduction

A typical scheme of most of non trivial applications is as follows:


Figure 1. An application block - scheme

Usually an application performs resource allocation, for example a memory allocation, at the initialization stage. At the clean up stage it usually performs resource deallocation. The resource allocation stage may fail for example because of absence of the required hardware. In this case an application usually gives an error message and returns the corresponding error code to an operation system. The error message is usually put to the standard error stream (stderr) or to a log file. Sometimes an error message is put to the standard output (stdout) stream instead of the stderr.

In case of a daemon (a program which runs in a background and has neither stderr no stdout) the things are a bit more complicated. Daemonisation, that is elimination of dependency on a controlling console and a parent shell, is usually built using the system call fork(). The parent process plays a role of a launch pad for the child process which does all the background work. There is no sense to perform resource allocation in the parent process as soon as the resources are used in the child process only. So an error of the initialization stage may appear in the child process only. However the operating system will get a return code which can be analysed from the parent process only. The bad scenario may look as follows:

  • Operating system runs a daemon.
  • A parent process creates a child process.
  • The child process starts the initialization stage.
  • The parent process finishes. The operating system gets the success return code. A user is probably informed about the daemon successful start.
  • The child process cannot perform resource allocation. It finishes its work with the corresponding return code. The return code is analysed by nobody.

The user is misinformed in this scenario about the real daemon start. Surely if the user digs into application log files he or she can find the corresponding error messages but how do they know that it is necessary to do? It is even worse if the startup control is performed by some scripts basing on return codes.

In order to fix the problem it is necessary to make the parent process waiting till the moment when the child process completes the initialization and informs the parent process of successful or unsuccessful completion of the initialization. As soon as the parent process receives the notification it returns to the operating system the corresponding code.

The Skeleton

The daemonisation skeleton with a feedback uses two signals: SIGUSR1 is used to signal a successful initialization and SIGUSR2 is used to signal that an initialization failed. All the required functions and variables are located in a single header file SDaemonFramework.h. In order to daemonise an application it is only required to include the header file, call the Daemonise() function and call the corresponding function SignalOK() or SignalFailed() after the initialization completion to provide a feedback.


. . .

#include "SDaemonFramework.h"

int   main( int  argc, char **  argv )
{
    Daemonise();

        // Here: it is a daemon. Let's start the initialisation

    . . .

    if ( . . . )
    {
            // May be put a message into a log file etc.
        SignalFailed();
        return 1;
    }

        // Everything is fine. Let's continue to work
    SignalOK();

    . . .

    return 0;
}

Debugging and Security

A daemon debugging is more complicated than debugging of a usual application. It is sometimes convenient to have ability to compile the same application in different ways - as a daemon and as a usual application. The skeleton supports this feature via a preprocessor directive. The DAEMON variable controls how the skeleton behaves. A Makefile could be written similar to the following:

. . .
# Two values are allowed: 0 - a usual application, 1 - a daemon
DAEMON=1
. . .
main.o: main.cpp
    g++ -c -DDAEMON=$\{DAEMON} main.cpp
. . .

So it is required to change the variable value in a single place to change the application type. The application will be compiled as a usual one by default.

To make it safe to start a daemon from any point of the file system without a potential blocking the file system it is necessary to change a working directory of the daemon. The skeleton supports an ability to specify explicitly the working directory via a preprocessor variable HOME. If the variable is not defined the root directory will be set as the working one.

Notes

The SDaemonFramework.h header file has all the required functions and variables. All the header file content is inside an anonymous namespace. This helps to avoid linkage errors even if the header file is included into many compilation units. It is however worth keeping in mind that each unnecessary inclusion of the header file leads to overheads. In this case the final executable module will be a bit larger than it could be.

Advice

Include the SDaemonFramework.h file only once into the source code file where daemonisation is performed. Usually it is a file where the main(. . .) function is defined.

Controlling Script

A usual daemon controlling script in SystemV style supports the start, stop, restart and status commands. At the operating system start up time similar scripts run the required daemons and print at the console [OK] or [FAILED]. A script usually uses a return code of a daemon executable module. Taking into account the implemented feedback a similar script will inform the user correctly how a daemon started. Any of the already written scripts could be taken as a template.

Environment

The skeleton code is checked and debugged on Linux Red Hat 7.3 using gcc 3.4.3. Most probably the skeleton will work fine on the other pairs of OS - compiler.

A source code of a trivial example could be found at the author's web site http://satsky.spb.ru.

References

1. Andrey Robachevskiy. UNIX operating system. St. Petersburg., BHV-Petersburg, 2000


Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

Разрешается копирование и распространение этой статьи любым способом без внесения изменений, при условии, что это разрешение сохраняется.
Last Updated: December 10, 2005