Daemon conventions

    On UNIX systems, daemons follow the following general conventions.
    (1) If the daemon uses a lock file, the file is usually stored in the /var/run directory. However, the daemon may need superuser privileges to create files in this directory. The name of the lock file is usually name.pid, where name is the name of the daemon or service. For example, the name of the cron daemon lock file is /var/run/crond.pid.
    (2) If the daemon supports configuration options, the configuration file is usually stored in the /etc directory, and the name is usually name.conf, where name is the name of the daemon or service. For example, the configuration file for the syslogd daemon is usually /etc/syslog.conf.
    (3) Daemons can be started from the command line, but usually they are started by one of the system initialization scripts (/etc/rc* or /etc/init.d/*). If you want to restart the daemon automatically when it terminates, you can include a respawn entry for the daemon in /etc/inittab so that init will restart the daemon.
    (4) The configuration file of the daemon process is generally read when the daemon process starts (if any), and will not be viewed after that. If the configuration file is changed, the daemon may need to be restarted for the changes to take effect. To avoid this trouble, some daemons trap the SIGHUP signal. When they receive this signal, they re-read the configuration file. Since daemons are not tied to a terminal, they are either session leaders without a controlling terminal, or are members of the orphan process group, there is no reason to receive SIGHUP, so it is safe to reuse SIGHUP.
    Additionally, some daemons only allow one copy of the daemon to run at any one time in order to function properly. For example, the daemon might need exclusive access to a device. In this case, file and record locking mechanisms can be adopted. This approach guarantees that only one copy of a daemon is running. If each daemon creates a file with a fixed name and applies a write lock to the file, then only one such write lock is allowed, and subsequent attempts to create a write lock will fail. When the daemon terminates, the lock will be automatically removed.
    The following function demonstrates how to use file and record locking to ensure that only one copy of a daemon is allowed. The implementation of the lockfile function is shown in the fcntl record lock section below.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <syslog.h>
#include <sys/stat.h>

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

extern int lockfile(int);

int already_running(void){
	int 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){
		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);
	char buf[16];
	sprintf(buf, "%ld", (long)getpid());
	write(fd, buf, strlen(buf)+1);
	return 0;
}

    Here, if the operating file is already locked, the lockfile function will fail and errno will be set to EACCES or EAGAIN, indicating that the daemon is already running. Otherwise, truncate the file length to 0 and write the process ID (the reason for truncating the file length to 0 is that the process ID string of the previous daemon process may be longer than the current process ID string. For example, it used to be 12345, and now it is 9999 , then after writing, 99995 will be left in the file instead of 9999).
    The following program demonstrates a way for a daemon to reread its configuration file in multiple threads. The already_running function defined above is used, and the implementation of another function, daemonize, see Daemonize Writing Rules and Error Records .
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <pthread.h>

extern void daemonize(const char *);
extern int already_running(void);

sigset_t mask;

void reread(void){
	/* read configuration... */
}

void *thr_fn(void *arg){
	int sign;
	for(;;){
		if(sigwait(&mask, &signo) != 0){
			syslog(LOG_ERR, "sigwait error");
			exit(1);
		}
		switch(signo){
		case SIGHUP:
			syslog(LOG_INFO, "reread configuration");
			reread();
			break;
		case SIGTERM:
			syslog(LOG_INFO, "got SIGTEM; thread exit");
			exit(0);
		default:
			syslog(LOG_INFO, "unexpected signal %d\n", signo);
		}
	}
	return 0;
}

int main(int argc, char *argv[]){
	char *cmd = NULL;
	if((cmd = strrchr(argv[0], '/')) == NULL)
		cmd = argv[0];
	else
		cmd++;
	daemonize(cmd);			// become a daemon
	if(already_running()){	// ensure only one copy of the daemon is running.
		syslog(LOG_ERR, "daemon already running");
		exit(1);
	}
	struct sigaction act;
	act.sa_handler = SIG_DFL;	// restore SIGHUP default
	sigemptyset (& act.sa_mask);
	act.sa_flags = 0;
	if(sigaction(SIGHUP, &act, NULL) != 0){
		syslog(LOG_ERR, "can't restore SIGHUP default");
		exit(1);
	}
	sigfillset(&mask);			// and block all signals
	if(pthread_sigmask(SIG_BLOCK, &mask, NULL) != 0){
		syslog(LOG_ERR, "pthread_sigmask error");
		exit(1);
	}
	pthread_t tid;		// create a thread to handle SIGHUP and SIGTERM
	if(pthread_create(&tid, NULL, thr_fn, 0) != 0){
		syslog(LOG_ERR, "pthread_create error\n");
		exit(1);
	}
	/* preceed with the rest of the daemon */
	for(;;){
		pause();
	}
	exit(0);
}

    It should be noted here that the default processing behavior of SIGHUP is modified in the daemonize function, so the default processing action should be restored after the call, otherwise the thread calling sigwait will never see the signal. As recommended for multithreaded programs, all signals are blocked here, and a single thread is created to handle the SIGHUP and SIGTERM signals. Because the default action for SIGHUP and SIGTERM is to terminate the process, after blocking these signals, the daemon will not die when one of them is sent to the daemon.
    The result of running is as follows (the line where grep itself is located and some unimportant columns are omitted):
$ sudo ./rereadConfig.out # Requires root privileges to run
$ ps -efj | grep rereadConfig.out # View the daemon
UID      PID     PPID  PGID    SID     TTY    CMD  
root     109758  1     109757  109757  ?      ./rereadConfig.out
$
$ sudo kill -s SIGHUP 109758 # Send SIGHUP signal to reread configuration
$ ps -efj | grep rereadConfig.out # SIGHUP signal will not kill the daemon
UID      PID     PPID  PGID    SID     TTY    CMD  
root     109758  1     109757  109757  ?      ./rereadConfig.out
$
$ sudo kill -s SIGTERM 109758 # Send SIGTERM signal to reread configuration
$ ps -efj | grep rereadConfig.out # SIGTERM signal terminated the daemon
$

    Of course, if you want to use a single-threaded daemon to reread the configuration, you can configure signal handlers for SIGHUP and SIGTERM after initializing the daemon, and then put the reread logic in the signal handler, or you can set a signal handler in the signal handler. flag, and the main thread of the daemon does all the work. For example, the above program can be modified like this.
int main(){
	...
	sa.sa_handler = sigterm;
	sigemptyset (& sa.sa_mask);
	sigaddset (& sa.sa_mask, SIGHUP);
	sa.sa_flags = 0;
	sigaction (SIGTERM, & sa, NULL);
	sa.sa_handler = sighup;
	sigemptyset (& sa.sa_mask);
	sigaddset (& sa.sa_mask, SIGTERM);
	sa.sa_flags = 0;
	sigaction(SIGHUP, &sa, NULL);
	...
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326042625&siteId=291194637