Linux Create daemon

Keywords: Session shell Programming Unix

1. Create daemon steps:

To become a daemon, a program needs to complete the following steps:

  1. Execute a fork(), then the parent process exits and the child process continues to execute. (As a result, daemon becomes a child of the init process.) The reason for this step is the following two reasons:
    • Assuming that daemon is started from the command line, the termination of the parent process will be discovered by the shell, which will display another shell prompt after discovery and allow the child process to continue running in the background.
    • The child process is guaranteed not to be called a process group head process because it inherits the process group ID from its parent process and has its own unique process ID, which is different from the inherited process group ID so that the next step can be successfully performed.
  2. The subprocess calls setsid() to open a new call and release all the relationships between it and the control terminal.
  3. If Daemon has never turned on a terminal device, there is no need to worry that Daemon will re-request a control terminal. If a terminal device may be opened later on in daemon, measures must be taken to ensure that the device does not become a control terminal. This can be achieved in two ways:
    • Specify the O_NOCTTY flag in all open() calls that may be applied to a terminal device.
    • Or more simply, the second fork() is executed after the setsid() call, and then the parent process exits again and the grandson process continues to execute. This ensures that the child process will not be called session group leader, so according to the rules of getting the terminal in System V, the process will never re-request a control terminal. (One more fork() call won't do any harm.)
  4. Clear the umash of the process to ensure that daemon has the required permissions when creating files and directories.
  5. Modify the current working directory of the process, usually to the root directory (/). This is necessary because daemon usually runs until the system shuts down. If Daemon's current working directory is a file system that does not contain / it cannot be uninstalled. Or daemon can change the working directory to the directory where the task is performed or define a directory in the configuration file, as long as the file system containing the directory will never be uninstalled.
  6. Close all open file descriptors that daemon inherits from its parent process. (Daemon may need to keep the inherited file description open, so this step is optional or changeable.) There are many reasons for this. Because daemon loses its control terminal and runs in the background, it is meaningless to keep the file descriptors 0, 1 and 2 open because they point to the control terminal. In addition, it is not possible to uninstall the file system where the long-running daemon opened the file. Therefore, the usual practice is to close all useless open file descriptors, because file descriptors are a limited resource.
  7. After closing file descriptors 0, 1, and 2, daemon usually opens / dev/null and uses dup2() (or similar functions) to point all these descriptors to the device. This is done for the following two reasons:
    • It ensures that daemon does not fail unexpectedly when it calls library functions that execute I/O on these descriptors.
    • It prevents daemon from using descriptors 1 or 2 to open a file later, because library functions will write these descriptors as standard output and standard errors to data (thereby destroying the original data).

2. Sample code:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>


bool start_daemon()
{
    int fd;

    switch (fork()) {
        case -1:
            printf("fork() failed\n");
            return false;

        case 0:
            break;

        default:
            exit(0);
    }

    /*
    pid_t setsid(void);
    The process calls setsid() to create a new session.
    If the process calling this function is not the group leader of a process group, the function creates a new session with the result that:
        1,This process becomes the session leader of the new dialogue period, which is the process that creates the dialogue period.
           This process is the only one in the new dialogue period.
        2,This process becomes the leader of a new process group. The new process group ID is the process ID of the calling process.
        3,This process has no control terminal. If there is a control terminal in the last process before setsid is invoked, the connection is also removed.
    This function returns an error if the calling process is already the group leader of a process group. To ensure that this is not the case, fork() is usually called first.
    The parent process is then terminated while the child process continues to execute. Because the child process inherits the process group ID of the parent process, the process ID of the child process is new.
    Allocated, the two can not be equal, so this ensures that the sub-process is not the leader of a process group.
    */
    if (setsid() == -1) {
        printf("setsid() failed\n");
        return false;
    }

    switch (fork()) {
        case -1:
            printf("fork() failed\n");
            return false;

        case 0:
            break;

        default:
            exit(0);
    }

    umask(0);
    chdir("/");

    long maxfd;
    if ((maxfd = sysconf(_SC_OPEN_MAX)) != -1)
    {
        for (fd = 0; fd < maxfd; fd++)
        {
            close(fd);
        }
    }

    fd = open("/dev/null", O_RDWR);
    if (fd == -1) {
        printf("open(\"/dev/null\") failed\n");
        return false;
    }

    /*
    // Standard file descriptors.
    #define STDIN_FILENO    0   // Standard input.
    #define STDOUT_FILENO   1   // Standard output.
    #define STDERR_FILENO   2   // Standard error output.
    */

    /*
    int dup2(int oldfd, int newfd);
    dup2()Used to copy the file descriptor referred to by the parameter oldfd and copy it back to the parameter newfd.
    If newfd is open, close it first.
    If oldfd is an illegal descriptor, dup2() returns an error, and newfd will not be closed.
    If oldfd is a legal descriptor and newfd is equal to oldfd, dup2() does nothing and returns newfd directly.
    */
    if (dup2(fd, STDIN_FILENO) == -1) {
        printf("dup2(STDIN) failed\n");
        return false;
    }

    if (dup2(fd, STDOUT_FILENO) == -1) {
        printf("dup2(STDOUT) failed\n");
        return false;
    }

    if (dup2(fd, STDERR_FILENO) == -1) {
        printf("dup2(STDERR) failed\n");
        return false;
    }

    if (fd > STDERR_FILENO) {
        if (close(fd) == -1) {
            printf("close() failed\n");
            return false;
        }
    }

    return true;
}


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

    while (true)
    {
        sleep(100);
    }

    return 0;
}

Reference Books:

  1. Linux_UNIX System Programming Manual
  2. UNIX Environment Advanced Programming

Posted by francisexpress on Fri, 05 Apr 2019 23:12:30 -0700