[APUE] Ch 13 Daemon Process

What is Daemon

Daemons are processes that live for a long time.
Started when the system is bootstrapped and terminate only when the system is shut down.
Because they don’t have a controlling terminal, we say that they run in the background.

ps -efj > o //(結果重定向到文件裡方便處理)
//e(every)代表所有進程;f(full)代表現實全部信息;j(job)job模式,結果如下圖:

enter image description here

What for

init

enter image description here

rpcbind

enter image description here
enter image description here

rsyslogd

enter image description here

inetd

enter image description here

NFS ( nfsd, nfsiod, lockd, rpciod, rpc.idmapd, rpc.statd, and rpc.mountd)

enter image description here

cron

enter image description here

sshd

enter image description here

How to write a Daemon

Example: https://codeshare.io/3fC0z

Steps:

  1. Call umask to set the file mode creation mask to a known value, usually 0.
  2. Call fork and have the parent exit.
    • The parent terminate makes the shell think that the command is done.
    • To guarantee that the child is not a process group leader. This is a prerequisite for the call to setsid.
  3. Call setsid to create a new session.
    (a.) Becomes the leader of a new session
    (b.) Becomes the leader of a new process group
    (c.) Disassociated from its controlling terminal
  4. Change the current working directory to the root so we won’t prevent file systems from being unmounted.
  5. Unneeded file descriptors should be closed.
  6. Attach file descriptors 0, 1, and 2 to /dev/null. ( Standard input, Standard output, Standard error)
  7. Initialize the log file.
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <syslog .h>

static  void skeleton_daemon()
{
    pid_t pid;
    
    pid = fork(); // 1. fork off the parent process 
    if (pid < 0 ) {
        exit(EXIT_FAILURE); 
    } 
    if (pid > 0 ) { // 1. terminates the parent process 
        exit(EXIT_SUCCESS);
    } 
    if (setsid()< 0 ) { // 2. child process becomes session leader 
        exit(EXIT_FAILURE);
    } 
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    pid = fork(); // 3. fork off the second time 
    if (pid< 0 ) {
        exit(EXIT_FAILURE); 
    }
    if (pid> 0 ) { // terminates the parents 
        exit(EXIT_SUCCESS);
    }

    umask( 0 ); // 4. set new file permissions 

    chdir( " / " ); // 5. change the working directory

    int x; // 6. close all open file descriptors 
    for (x=sysconf(_SC_OPEN_MAX); x> 0 ; x-- )
    {
        close(x);
    }
	/*
	 * 6. Attach file descriptors 0, 1, and 2 to /dev/null.
	 */
    int fd0, fd1, fd2;
	fd0 = open("/dev/null", O_RDWR);
	fd1 = dup(0);
	fd2 = dup(0);
  
    openlog( " firstdaemon " , LOG_PID, LOG_DAEMON);
}

int main()
{
    skeleton_daemon();
    while ( 1 )
    {
        syslog(LOG_NOTICE, " First daemon started. " );
        sleep( 20 );
         break ;
    }

    syslog(LOG_NOTICE, " First daemon terminated. " );
    closelog();

    return EXIT_SUCCESS;
}

Result:

enter image description here

How Daemons deal error logging

We also don’t want each daemon writing its own error messages into a separate file. It would be a headache for anyone administering the system to keep up with which daemon writes to which log file and to check these files on a regular basis. A central daemon error-logging facility is required.

enter image description here

There are three ways to generate log messages:

  1. Kernel routines can call the log function.
  2. Most user processes (daemons) call the syslog(3) function to generate log
    messages
  3. Send log messages to UDP port 514 by a TCP/IP network.

Interface of syslog:

https://codeshare.io/UqnrT

#include <syslog.h>

void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
int setlogmask(int maskpri);

/* Returns: previous log priority mask value */

The openlog function:

	void openlog(const char *ident, int option, int facility);

The syslog function:

void syslog(int priority, const char *format, ...);

example

#include <syslog.h>

int main( int argc, char ** argv)
{
    openlog( " test error " , LOG_CONS | LOG_PID, 0 );
    syslog(LOG_INFO, " This is a syslog test message generated by program '%s'\n " , argv[ 0 ]);
    closelog();
    return  0 ;
}

Single-Instance Daemons

What if we have 2 cron instance…

enter image description here enter image description here

The file- and record-locking mechanism

If each daemon creates a file with a fixed name and places a write lock on the entire file, only one such write lock will be allowed to be created. Successive attempts to create write locks will fail, serving as an indication to successive copies of the daemon that another instance is already running.

Example of using file locking to ensure single copy of daemon

Example: https://codeshare.io/rYG2s

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>

#define LOCKFILE "/var/run/daemon.pid"
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)

extern int lockfile(int);

int
already_running(void)
{
    int     fd;
    char    buf[16];

    fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);
    if (fd < 0) {
        syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno));
        exit(1);
    }
    if (lockfile(fd) < 0) {  //lockfile is implement in CH14
        if (errno == EACCES || errno == EAGAIN) {
            close(fd);
            return(1);
        }
        syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno));
        exit(1);
    }
    ftruncate(fd, 0);
    sprintf(buf, "%ld", (long)getpid());
    write(fd, buf, strlen(buf)+1);
    return(0);
}

We need to truncate the file, because the previous instance of the daemon might
have had a process ID larger than ours, with a larger string length. For example, if the
previous instance of the daemon was process ID 12345, and the new instance is process
ID 9999, when we write the process ID to the file, we will be left with 99995 in the file.
Truncating the file prevents data from the previous daemon appearing as if it applies to
the current daemon.

Daemon Conventions

Reference