Daemon Processes
https://www.masterraghu.com/subjects/np/introduction/
unix_network_programming_v1.3/ch13.html
https://notes.shichao.io/apue/ch13/#chapter-13-daemon-processes
Introduction
Daemons are processes that are often started when the system is bootstrapped and terminate only when the system is
shut down. Because they don’t have a controlling terminal, they run in the background. UNIX systems have numerous
daemons that perform day-to-day activities.
Daemon Characteristics¶
This section describes some common system daemons with the concepts of process groups, controlling terminals, and sessions as
described in Chapter 9.
ps -axj
The -a option shows the status of processes owned by others.
The -x option shows processes that don’t have a controlling terminal.
The -j option displays the job-related information:
o Session ID
o Process group ID
o Controlling terminal
o Terminal process group ID
The output from ps on Linux 3.2.0 looks like:
UID PID PPID PGID SID TTY CMD
root 1 0 1 1 ? /sbin/init
root 2 0 0 0 ? [kthreadd]
root 3 2 0 0 ? [ksoftirqd/0]
root 6 2 0 0 ? [migration/0]
root 7 2 0 0 ? [watchdog/0]
root 21 2 0 0 ? [cpuset]
root 22 2 0 0 ? [khelper]
root 26 2 0 0 ? [sync_supers]
root 27 2 0 0 ? [bdi-default]
root 29 2 0 0 ? [kblockd]
root 35 2 0 0 ? [kswapd0]
root 49 2 0 0 ? [scsi_eh_0]
root 256 2 0 0 ? [jbd2/sda5-8]
root 257 2 0 0 ? [ext4-dio-unwrit]
Coding Rules
This section states basic rules to coding a daemon prevent unwanted interactions from happening, followed by a
function daemonize, that implements these rules.
1. Call umask to set the file mode creation mask to a known value, usually 0.
o If the daemon process creates files, it may want to set specific permissions.
o On the other hand, if the daemon calls library functions that result in files being created, then it might make
sense to set the file mode create mask to a more restrictive value (such as 007), since the library functions
might not allow the caller to specify the permissions through an explicit argument.
2. Call fork and have the parent exit. This does several things:
o If the daemon was started as a simple shell command, having the parent terminate makes the shell think that
the command is done
o The child inherits the process group ID of the parent but gets a new process ID, so we’re guaranteed that the
child is not a process group leader. This is a prerequisite for the call to setsid that is done next. (See Ensuring
the successful call of setsid in Chapter 9)
3. Call setsid to create a new session. The three steps listed in Section 9.5 occur. The process:
o becomes the leader of a new session,
o becomes the leader of a new process group,
o and is disassociated from its controlling terminal.
Under System V–based systems, some people recommend calling fork again at this point, terminating the parent, and
continuing the daemon in the child. This guarantees that the daemon is not a session leader, which prevents it from
acquiring a controlling terminal under the System V rules (Section 9.6). Alternatively, to avoid acquiring a controlling
terminal, be sure to specify O_NOCTTY whenever opening a terminal device.
4. Change the current working directory to the root directory. The current working directory inherited from the parent
could be on a mounted file system. Since daemons normally exist until the system is rebooted, if the daemon stays on a
mounted file system, that file system cannot be unmounted.
Alternatively, some daemons might change the current working directory to a specific location where they will do all their
work. For example, a line printer spooling daemon might change its working directory to its spool directory.
5. Unneeded file descriptors should be closed. This prevents the daemon from holding open any descriptors that it may
have inherited from its parent (which could be a shell or some other process). We can use our open_max function or
the getrlimit function (Section 7.11) to determine the highest descriptor and close all descriptors up to that value.
6. Some daemons open file descriptors 0, 1, and 2 to /dev/null so that any library routines that try to read from
standard input or write to standard output or standard error will have no effect. [p466-467]
Sysloged (syslog function)
A central daemon error-logging facility is required. The BSD syslog facility has been widely used since 4.2BSD. Most
daemons use this facility. The following figure illustrates its structure:
There are three ways to generate log messages:
1. Kernel routines can call the log function. These messages can be read by any user process that opens and reads
the /dev/klog device.
2. Most user processes (daemons) call the syslog(3) function to generate log messages. This causes the message to be
sent to the UNIX domain datagram socket /dev/log.
3. A user process on this host or some other host that is connected to this host by a TCP/IP network, can send log
messages to UDP port 514. Note that the syslog function never generates these UDP datagrams: they require explicit
network programming by the process generating the log message.
The syslogd daemon reads all three forms of log messages. On start-up, this daemon reads a configuration file,
usually /etc/syslog.conf, which determines where different classes of messages are to be sent. For example, urgent messages
can be sent to the system administrator (if logged in) and printed on the console, whereas warnings may be logged to a file.
#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 */
Calling openlog is optional. If it’s not called, the first time syslog is called, openlog is called automatically.
Calling closelog is also optional. It closes the descriptor being used to communicate with the syslogd daemon.
The openlog function:
o indent argument is the name of the program.
o option argument is a bitmask specifying various options. (see the table below)
o facility argument lets the configuration file specify that messages from different facilities are to be handled
differently. If we don’t call openlog, or if we call it with a facility of 0, we can still specify the facility as part of the
priority argument to syslog. (see the table below)
XS
option I Description
LOG_CONS x If the log message can’t be sent to syslogd via the UNIX domain datagram, the
message is written to the console instead.
LOG_NDELA x Open the UNIX domain datagram socket to the syslogd daemon immediately;
Y don’t wait until the first message is logged. Normally, the socket is not opened
until the first message is logged.
LOG_NOWAI x Do not wait for child processes that might have been created in the process of
T logging the message. This prevents conflicts with applications that
catch SIGCHLD, since the application might have retrieved the child’s status by
the time that syslog calls wait.
LOG_ODELA x Delay the opening of the connection to the syslogd daemon until the first
Y message is logged.
LOG_PERRO Write the log message to standard error in addition to sending it to syslogd.
R (Unavailable on Solaris.)
LOG_PID x Log the process ID with each message. This is intended for daemons that fork a
child process to handle different requests (as compared to daemons, such
as syslogd, that never call fork).
facility XSI Description
LOG_AUDIT the audit facility
LOG_AUTH authorization programs: login, su, getty, ...
LOG_AUTHPRIV same as LOG_AUTH, but logged to file with restricted permissions
LOG_CONSOLE messages written to /dev/console
LOG_CRON cron and at
LOG_DAEMON system daemons: inetd, routed, ...
LOG_FTP the FTP daemon (ftpd)
LOG_KERN messages generated by the kernel
facility XSI Description
LOG_LOCAL0 ~ LOG_LOCAL x reserved for local use
7
LOG_LPR line printer system: lpd, lpc, ...
LOG_MAIL the mail system
LOG_NEWS the Usenet network news system
LOG_NTP the network time protocol system
LOG_SECURITY the security subsystem
LOG_SYSLOG the syslogd daemon itself
LOG_USER
LOG_UUCP the UUCP system
The syslog function:
o The priority argument is a combination of the facility and a level (shown in the table below). These levels are
ordered by priority, from highest to lowest.
o The format argument and any remaining arguments are passed to the vsprintf function for formatting. Any
occurrences of the characters %m are first replaced with the error message string (strerror) corresponding to
the value of errno.
level Description
LOG_EMERG emergency (system is unusable) (highest priority)
LOG_ALERT condition that must be fixed immediately
LOG_CRIT critical condition (e.g., hard device error)
LOG_ERR error condition
LOG_WARNING warning condition
LOG_NOTICE normal, but significant condition
LOG_INFO informational message
level Description
LOG_DEBUG debug message (lowest priority)
daemon_init Function
Figure 13.4 shows a function named daemon_init that we can call (normally
from a server) to daemonize the process. This function should be suitable for
use on all variants of Unix, but some offer a C library function
called daemon that provides similar features. BSD offers the daemon function, as
does Linux.
Figure 13.4 daemon_init function: daemonizes the process.
daemon_init.c
1 #include "unp.h"
2 #include <syslog.h>
3 #define MAXFD 64
4 extern int daemon_proc; /* defined in error.c */
5 int
6 daemon_init(const char *pname, int facility)
7 {
8 int i;
9 pid_t pid;
10 if ( (pid = Fork()) < 0)
11 return (-1);
12 else if (pid)
13 _exit(0); /* parent terminates */
14 /* child 1 continues... */
15 if (setsid() < 0) /* become session leader */
16 return (-1);
17 Signal(SIGHUP, SIG_IGN);
18 if ( (pid = Fork()) < 0)
19 return (-1);
20 else if (pid)
21 _exit(0); /* child 1 terminates */
22 /* child 2 continues... */
23 daemon_proc = 1; /* for err_XXX() functions */
24 chdir("/"); /* change working directory */
25 /* close off file descriptors */
26 for (i = 0; i < MAXFD; i++)
27 close(i);
28 /* redirect stdin, stdout, and stderr to /dev/null */
29 open("/dev/null", O_RDONLY);
30 open("/dev/null", O_RDWR);
31 open("/dev/null", O_RDWR);
32 openlog(pname, LOG_PID, facility);
33 return (0); /* success */
34 }
fork
10–13 We first call fork and then the parent terminates, and the child
continues. If the process was started as a shell command in the foreground,
when the parent terminates, the shell thinks the command is done. This
automatically runs the child process in the background. Also, the child
inherits the process group ID from the parent but gets its own process ID.
This guarantees that the child is not a process group leader, which is
required for the next call to setsid.
setsid
15–16 setsid is a POSIX function that creates a new session. (Chapter 9 of
APUE talks about process relationships and sessions in detail.) The process
becomes the session leader of the new session, becomes the process group
leader of a new process group, and has no controlling terminal.
Ignore SIGHUP and Fork Again
17–21 We ignore SIGHUP and call fork again. When this function returns, the
parent is really the first child and it terminates, leaving the second child
running. The purpose of this second fork is to guarantee that the daemon
cannot automatically acquire a controlling terminal should it open a terminal
device in the future. When a session leader without a controlling terminal
opens a terminal device (that is not currently some other session's
controlling terminal), the terminal becomes the controlling terminal of the
session leader. But by calling fork a second time, we guarantee that the
second child is no longer a session leader, so it cannot acquire a controlling
terminal. We must ignore SIGHUP because when the session leader terminates
(the first child), all processes in the session (our second child) receive
the SIGHUP signal.
inetd Daemon
On a typical Unix system, there could be many servers in existence, just
waiting for a client request to arrive. Examples are FTP, Telnet, Rlogin, TFTP,
and so on. With systems before 4.3BSD, each of these services had a process
associated with it. This process was started at boot-time from the file /etc/rc,
and each process did nearly identical startup tasks: create a socket, bind the
server's well-known port to the socket, wait for a connection (if TCP) or a
datagram (if UDP), and then fork. The child process serviced the client and
the parent waited for the next client request. There are two problems with
this model:
1. All these daemons contained nearly identical startup code,
first with respect to socket creation, and also with respect to
becoming a daemon process (similar to
our daemon_init function).
2. Each daemon took a slot in the process table, but each daemon was
asleep most of the time.
The 4.3BSD release simplified this by providing an
Internet superserver: the inetd daemon. This daemon can be used by servers
that use either TCP or UDP. It does not handle other protocols, such as Unix
domain sockets. This daemon fixes the two problems just mentioned:
1. It simplifies writing daemon processes since most of the
startup details are handled by inetd. This obviates the need for
each server to call our daemon_init function.
2. It allows a single process (inetd) to be waiting for incoming client
requests for multiple services, instead of one process for each service.
This reduces the total number of processes in the system.
The inetd process establishes itself as a daemon using the techniques that
we described with our daemon_init function. It then reads and processes its
configuration file, typically /etc/inetd.conf. This file specifies the services
that the superserver is to handle, and what to do when a service request
arrives. Each line contains the fields shown in Figure 13.6.
Figure 13.6. Fields in inetd.conf file.
https://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/
ch13lev1sec5.html
Steps performed by inetd.
1. On startup, it reads the /etc/inetd.conf file and creates a socket
of the appropriate type (stream or datagram) for all the
services specified in the file. The maximum number of servers
that inetd can handle depends on the maximum number of
descriptors that inetd can create. Each new socket is added to a
descriptor set that will be used in a call to select.
2. bind is called for the socket, specifying the port for the server and the
wildcard IP address. This TCP or UDP port number is obtained by
calling getservbyname with the service-name and protocol fields from the
configuration file as arguments.
3. For TCP sockets, listen is called so that incoming connection requests
are accepted. This step is not done for datagram sockets.
4. After all the sockets are created, select is called to wait for any of the
sockets to become readable. Recall from Section 6.3 that a listening
TCP socket becomes readable when a new connection is ready to
be accepted and a UDP socket becomes readable when a datagram
arrives. inetd spends most of its time blocked in this call to select,
waiting for a socket to be readable.
5. When select returns that a socket is readable, if the socket is a TCP
socket and the nowait flag is given, accept is called to accept the new
connection.
6. The inetd daemon forks and the child process handles the service
request. This is similar to a standard concurrent server (Section 4.8).
The child closes all descriptors except the socket descriptor it is
handling: the new connected socket returned by accept for a TCP
server or the original UDP socket. The child calls dup2 three times,
duplicating the socket onto descriptors 0, 1, and 2 (standard input,
standard output, and standard error). The original socket descriptor is
then closed. By doing this, the only descriptors that are open in the
child are 0, 1, and 2. If the child reads from standard input, it is reading
from the socket and anything it writes to standard output or standard
error is written to the socket. The child calls getpwnam to get the
password file entry for the login-name specified in the configuration
file. If this field is not root, then the child becomes the specified user
by executing the setgid and setuid function calls. (Since
the inetd process is executing with a user ID of 0, the child process
inherits this user ID across the fork, and is able to become any user
that it chooses.)
The child process now does an exec to execute the appropriate server-
program to handle the request, passing the arguments specified in the
configuration file.
7. If the socket is a stream socket, the parent process must close the
connected socket (like our standard concurrent server). The parent
calls select again, waiting for the next socket to become readable.