%% options copyright owner = Dirk Krause copyright year = 2019-xxxx SPDX-License-Identifier: BSD-3-Clause %% header /** @file dk4dmt.h Daemon tool functions. The functions in this module can be used to program standing daemons. Three processes are involved in a standing daemon: * master process, originally started, * intermediate process, forked by master, decouples from terminal and * daemon process, forked by intermediate process. When a standing daemon is started by a systemd service unit, the master process must not exit before the daemon process has created the PID file. If you plan to give up root privileges in the child processes (use setgid() and setuid() to change effective user and group), do not use /var/run/program.pid as PID file name, better use /var/run/program/program.pid instead and make sure to set ownership and group on /var/run/program to the unprivileged user and group used by the child process. User and group need write permission to the directory, so the unprivileged child process can remove the PID file when exiting. The general usage structure looks like this: @code #define PROGRAM_NAME "my-new-service" const char program_name[] = { PROGRAM_NAME }; const char pid_file_name[] = { "/run/" PROGRAM_NAME "/" PROGRAM_NAME ".pid" }; dk4dmt_t dmt; ... pid_t cpid; dk4dmt_init(&dmt); if (0 != read_configuration_file()) { dk4dmt_set_program(&dmt, program_name); dk4dmt_set_pidfile(&dmt, pid_file_name); dk4dmt_set_syslog_feature(&dmt, syslog_feature); if (0 != dk4dmt_parent_before_fork(&dmt)) { cpid = fork(); if ((pid_t)0 == cpid) { if (0 != dk4dmt_intermediate_before_fork(&dmt)) { cpid = fork(); if ((pid_t)0 == cpid) { if (0 != dk4dmt_daemon_start(&dmt)) { do_the_service(); } dk4dmt_daemon_end(&dmt); } else { if ((pid_t)(-1) != cpid) { dk4dmt_intermediate_after_fork(&dmt); } else { dk4dmt_intermediate_fork_failed(&dmt); } } } } else { if ((pid_t)(-1) != cpid) { dk4dmt_parent_after_fork(&dmt); } else { dk4dmt_parent_fork_failed(&dmt); } } } } else { dk4dmt_error_config(&dmt); } exval = dk4dmt_get_exit_status(&dmt); exit(exval); @endcode */ #ifndef DK4CONF_H_INCLUDED #if DK4_BUILDING_DKTOOLS4 #include "dk4conf.h" #else #include #endif #endif /** Data collection for daemon. */ typedef struct { char const *prgname; /**< Program name. */ char const *pidfile; /**< PID file name. */ int pfd[2]; /**< Pipe file descriptors. */ int logstderr; /**< Flag: Logging to stderr allowed. */ int slf; /**< Syslog feature. */ int exv; /**< Exit status code to return. */ } dk4dmt_t; #ifdef __cplusplus extern "C" { #endif /** Initialize daemon tool structure. @param pdmt Daemon tool structure to initialize. */ void dk4dmt_init(dk4dmt_t *pdmt); /** Set program name for daemon tool structure. The function does not create a copy of the program name, it keeps the original pointer in the structure. So the progname buffer must exist and contain data as long as the structure is used. @param pdmt Daemon tool structure to set up. @param progname Program name. */ void dk4dmt_set_program(dk4dmt_t *pdmt, const char *progname); /** Set PID file name for daemon tool structure. The function does not create a copy of the file name, it keeps the original pointer in the structure. So the pidfile buffer must exist and contain data as long as the structure is used. @param pdmt Daemon tool structure to set up. @param pidfile PID file name. */ void dk4dmt_set_pidfile(dk4dmt_t *pdmt, const char *pidfile); /** Enable or disable logging to stderr for daemon tool structure. @param pdmt Daemon tool structure to set up. @param flag Allow or deny logging to stderr. */ void dk4dmt_set_log_stderr(dk4dmt_t *pdmt, int flag); /** Set syslog feature for daemon tool structure. @param pdmt Daemon tool structure to set up. @param feat Syslog feature to use. */ void dk4dmt_set_syslog_feature(dk4dmt_t *pdmt, int feat); /** Process initialization before first fork attempt. Open pipe for communication from daemon to parent. @param pdmt Daemon tool structure to use. @return 1 on success, 0 on error. */ int dk4dmt_parent_before_fork(dk4dmt_t *pdmt); /** Wait for startup completion notification in parent process. Close write head, read exit status, close read head. @param pdmt Daemon tool structure to use. */ void dk4dmt_parent_after_fork(dk4dmt_t *pdmt); /** React on failed fork attempt in parent process. Close both pipe heads. @param pdmt Daemon tool structure to use. */ void dk4dmt_parent_fork_failed(dk4dmt_t *pdmt); /** Prepare for second fork attempt in intermediate process. Close read head. On error write exit status code and close write pipe head. @param pdmt Daemon tool structure to use. @return 1 on success, 0 on error. */ int dk4dmt_intermediate_before_fork(dk4dmt_t *pdmt); /** Clean up in intermediate process after forking the daemon. Close write pipe head. @param pdmt Daemon tool structure to use. */ void dk4dmt_intermediate_after_fork(dk4dmt_t *pdmt); /** React on failed fork attempt in intermediate process. Write exit status code and close write pipe head. @param pdmt Daemon tool structure to use. */ void dk4dmt_intermediate_fork_failed(dk4dmt_t *pdmt); /** Create PID file and notify main process to exit. Write exit status and close write pipe head. @param pdmt Daemon tool structure to use. */ int dk4dmt_daemon_start(dk4dmt_t *pdmt); /** Clean up daemon structure before exiting daemon process (remove PID file). @param pdmt Daemon tool structure to use. */ void dk4dmt_daemon_end(dk4dmt_t *pdmt); /** Set exit code in daemon structure for failed system function. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_sysfct(dk4dmt_t *pdmt); /** Set exit code in daemon structure for failed signal process mask. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_sigprocmask(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate missing signal functionality. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_no_signal_function(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate PID file exists. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_pid_file_exists(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate PID file not configured. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_pid_file_name_missing(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate write PID file failed. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_write_pid_file(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate pipe was not opened (bug). @param pdmt Daemon tool structure to use. */ void dk4dmt_error_pipe_not_open(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate reading from pipe failed. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_pipe_read_failed(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate fork failed. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_fork_failed(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate setsid failed. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_setsid(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate dup2 failed. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_dup2_failed(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate reading /dev/null failed. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_failed_read_dev_null(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate writing /dev/null failed. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_failed_write_dev_null(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate chdir to / failed. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_failed_chdir_root(dk4dmt_t *pdmt); /** Set exit code in daemon structure for failed signal set cleanup. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_sigemptyset(dk4dmt_t *pdmt); /** Set exit code in daemon structure for unusable configuration. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_config(dk4dmt_t *pdmt); /** Set exit code in daemon structure to indicate wrong call arguments. @param pdmt Daemon tool structure to use. */ void dk4dmt_error_usage(dk4dmt_t *pdmt); /** Indicate success. @param pdmt Daemon tool structure to use. */ void dk4dmt_success(dk4dmt_t *pdmt); /** Retrieve exit status code. @param pdmt Daemon tool structure to use. @return Exit status code to return. */ int dk4dmt_get_exit_status(dk4dmt_t *pdmt); #ifdef __cplusplus } #endif %% module #include "dk4conf.h" #include #if DK4_HAVE_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SYS_STAT_H #ifndef SYS_STAT_H_INCLUDED #include #define SYS_STAT_H_INCLUDED 1 #endif #endif #if DK4_HAVE_STDLIB_H #ifndef STDLIB_H_INCLUDED #include #define STDLIB_H_INCLUDED 1 #endif #endif #if DK4_HAVE_UNISTD_H #ifndef UNISTD_H_INCLUDED #include #define UNISTD_H_INCLUDED 1 #endif #endif #if DK4_HAVE_FCNTL_H #ifndef FCNTL_H_INCLUDED #include #define FCNTL_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SIGNAL_H #ifndef SIGNAL_H_INCLUDED #include #define SIGNAL_H_INCLUDED 1 #endif #endif #if DK4_HAVE_IO_H #ifndef IO_H_INCLUDED #include #define IO_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SYSLOG_H #ifndef SYSLOG_H_INCLUDED #include #define SYSLOG_H_INCLUDED 1 #endif #endif #if DK4_HAVE_SYSEXITS_H #ifndef SYSEXITS_H_INCLUDED #include #define SYSEXITS_H_INCLUDED #endif #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4DMT_H_INCLUDED #include "dk4dmt.h" #endif #ifndef DK4MEM_H_INCLUDED #include "dk4mem.h" #endif #ifndef DK4MAO8D_H_INCLUDED #include "dk4mao8d.h" #endif #ifndef DK4STAT8_H_INCLUDED #include "dk4stat8.h" #endif #ifndef DK4STR8_H_INCLUDED #include "dk4str8.h" #endif #ifndef DK4FOPC8_H_INCLUDED #include "dk4fopc8.h" #endif #ifndef DK4MKDH8_H_INCLUDED #include "dk4mkdh8.h" #endif $!trace-include /** Constant texts used by module. */ static char const * const dk4dmt_kw[] = { $!string-table # # 0 Newline # \n # # 1 File mode for text writing # w # # 2 File name to open /dev/null # /dev/null # # 3 The root directory # / # # 4 ERROR: No signal handling functions available! # No signal handling functions available! # # 5,6 ERROR: Failed to create empty set for signal xxx! # Failed to create empty set for signal ! # # 7,8 ERROR: Failed to reset handler for signal xxx! # Failed to reset handler for signal ! # # 9 ERROR: Failed to unblock all signals! # Failed to unblock all signals! # # 10 ERROR: PID file already exists! # PID file already exists! # # 11 ERROR: Pipe not open! # Pipe not open (bug)! # # 12 ERROR: Failed to read from pipe! # Failed to read from pipe! # # 13 ERROR: Failed to fork new process! # Failed to fork new process! # # 14 ERROR: Failed to decouple from terminal! # Failed to decouple from terminal using setsid()! # # 15 ERROR: Missing function... # Missing function to decouple from terminal (setsid/setpgrp)! # # 16 ERROR: Failed to write PID file! # Failed to write PID file! # # 17 ERROR: No PID file specified! # No PID file name specified (bug)! # # 18 ERROR: Failed to duplicate standard file handle! # Failed to duplicate standard file handle using dup2()! # # 19 ERROR: Failed to open /dev/null for read access! # Failed to open /dev/null for read access! # # 20 ERROR: Failed to open /dev/null for write access! # Failed to open /dev/null for write access! # # 21 ERROR: Failed to change to / directory! # Failed to change to / directory! # # 22,23 ERROR: Failed to remove PID file! # Failed to remove PID file " "! # # 24 Module name # dk4dmt.o $!end }; static void dk4dmt_log_1(dk4dmt_t *pdmt, size_t i) { #if DK4_HAVE_SYSLOG const char *progname; #endif if (0 != pdmt->logstderr) { fputs(dk4dmt_kw[i], stderr); fputc('\n', stderr); } #if DK4_HAVE_SYSLOG progname = pdmt->prgname; if (NULL == progname) { progname = dk4dmt_kw[24]; } openlog(progname, LOG_PID, pdmt->slf); syslog(LOG_ERR, "%s", dk4dmt_kw[i]); closelog(); #endif } static void dk4dmt_log_3(dk4dmt_t *pdmt, size_t i1, size_t i2, const char *s) { #if DK4_HAVE_SYSLOG const char *progname; #endif if (0 != pdmt->logstderr) { fputs(dk4dmt_kw[i1], stderr); fputs(s, stderr); fputs(dk4dmt_kw[i2], stderr); fputc('\n', stderr); } #if DK4_HAVE_SYSLOG progname = pdmt->prgname; if (NULL == progname) { progname = dk4dmt_kw[24]; } openlog(progname, LOG_PID, pdmt->slf); syslog(LOG_ERR, "%s%s%s", dk4dmt_kw[i1], s, dk4dmt_kw[i2]); closelog(); #endif } /** Set exit status in structure. @param pdmt Structure to modify. @param syse One of the codes defined in the sysexits header. @param sdes Systemd error code. */ static void dk4dmt_set_error_exit_status(dk4dmt_t *pdmt, int syse, int sdes) { if (NULL != pdmt) { if (EXIT_SUCCESS == pdmt->exv) { #if DK4_HAVE_SYSTEMD if (0 != sdes) { pdmt->exv = sdes; } else { pdmt->exv = syse; } #else if (0 != syse) { pdmt->exv = syse; } else { switch (sdes) { default : { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) pdmt->exv = EX_OSERR; #else pdmt->exv = EXIT_FAILURE; #endif } break; } } #endif } } } void dk4dmt_init(dk4dmt_t *pdmt) { if (NULL != pdmt) { DK4_MEMRES(pdmt,sizeof(dk4dmt_t)); pdmt->prgname = NULL; pdmt->pidfile = NULL; pdmt->pfd[0] = -1; pdmt->pfd[1] = -1; pdmt->logstderr = 0; #if (DK4_HAVE_SYSLOG_H) && defined(LOG_DAEMON) pdmt->slf = LOG_DAEMON; #else pdmt->slf = (3<<3); #endif pdmt->exv = EXIT_SUCCESS; } } void dk4dmt_set_program(dk4dmt_t *pdmt, const char *progname) { if ((NULL != pdmt) && (NULL != progname)) { pdmt->prgname = progname; } } void dk4dmt_set_pidfile(dk4dmt_t *pdmt, const char *pidfile) { if ((NULL != pdmt) && (NULL != pidfile)) { pdmt->pidfile = pidfile; } } void dk4dmt_set_log_stderr(dk4dmt_t *pdmt, int flag) { if (NULL != pdmt) { pdmt->logstderr = flag; } } void dk4dmt_set_syslog_feature(dk4dmt_t *pdmt, int feat) { if (NULL != pdmt) { pdmt->slf = feat; } } void dk4dmt_error_sigprocmask(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 207); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 207); #endif } void dk4dmt_error_sigemptyset(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 207); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 207); #endif } void dk4dmt_error_no_signal_function(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 2); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 2); #endif } void dk4dmt_error_pid_file_exists(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_SOFTWARE) dk4dmt_set_error_exit_status(pdmt, EX_SOFTWARE, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } void dk4dmt_error_pid_file_name_missing(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_SOFTWARE) dk4dmt_set_error_exit_status(pdmt, EX_SOFTWARE, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } void dk4dmt_error_pipe_not_open(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_SOFTWARE) dk4dmt_set_error_exit_status(pdmt, EX_SOFTWARE, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } void dk4dmt_error_pipe_read_failed(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } void dk4dmt_error_fork_failed(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } void dk4dmt_error_dup2_failed(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } void dk4dmt_error_failed_read_dev_null(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } void dk4dmt_error_failed_write_dev_null(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } void dk4dmt_error_failed_chdir_root(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 200); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 200); #endif } void dk4dmt_error_setsid(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 220); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 220); #endif } void dk4dmt_error_write_pid_file(dk4dmt_t *pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } void dk4dmt_error_sysfct(dk4dmt_t *pdmt) { if (NULL != pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } } void dk4dmt_error_usage(dk4dmt_t *pdmt) { if (NULL != pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_USAGE) dk4dmt_set_error_exit_status(pdmt, EX_USAGE, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } } void dk4dmt_error_config(dk4dmt_t *pdmt) { if (NULL != pdmt) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_CONFIG) dk4dmt_set_error_exit_status(pdmt, EX_CONFIG, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } } static void dk4dmt_reset_all_signals(int *pback, dk4dmt_t *pdmt) { #if DK4_HAVE_SIGACTION struct sigaction sigact; /* Information for sigaction() */ #endif char buf[16*sizeof(int)]; int maxsig; /* Maximum signal number */ int signo; /* Current signal number to process */ int go; /* Flag: Reconfigure signal */ int ec; /* Error code */ ec = 0; #ifdef _NSIG maxsig = _NSIG; #else maxsig = 16; #endif for (signo = 1; ((maxsig >= signo) && (1 == *pback)); signo++) { go = 1; #ifdef SIGKILL if (SIGKILL == signo) { go = 0; } #else if (9 == signo) { go = 0; } #endif #ifdef SIGSTOP if (SIGSTOP == signo) { go = 0; } #else if (19 == signo) { go = 0; } #endif if (0 != go) { #if DK4_HAVE_SIGACTION DK4_MEMRES(&sigact, sizeof(sigact)); sigact.sa_handler = SIG_DFL; sigact.sa_flags = 0; if (0 != sigemptyset(&(sigact.sa_mask))) { if (16 > signo) { *pback = 0; dk4dmt_error_sigemptyset(pdmt); /* ERROR: Failed to empty set */ #if DK4_HAVE_SNPRINTF snprintf(buf, sizeof(buf), "%d", signo); #else sprintf(buf, "%d", signo); #endif dk4dmt_log_3(pdmt, 5, 6, buf); } } else { if (0 != sigaction(signo, &sigact, NULL)) { if (16 > signo) { *pback = 0; dk4dmt_error_sigemptyset(pdmt); /* ERROR: sigaction failed */ #if DK4_HAVE_SNPRINTF snprintf(buf, sizeof(buf), "%d", signo); #else sprintf(buf, "%d", signo); #endif dk4dmt_log_3(pdmt, 7, 8, buf); } } } #else #if DK4_HAVE_SIGSET sigset(signo, SIG_DFL); #else #if DK4_HAVE_SIGNAL signal(signo, SIG_DFL); #else *pback = 0; dk4dmt_error_no_signal_function(pdmt); /* ERROR: No function available to modify signals */ if (0 == ec) { ec = 3; } #endif #endif #endif } } switch (ec) { case 3: { dk4dmt_log_1(pdmt, 4); } break; } } static void dk4dmt_unblock_all_signals(int *pback, dk4dmt_t *pdmt) { sigset_t set; if (0 == sigemptyset(&set)) { if (0 != sigprocmask(SIG_SETMASK, &set, NULL)) { *pback = 0; dk4dmt_error_sigprocmask(pdmt); /* ERROR: sigprocmask failed */ dk4dmt_log_1(pdmt, 9); } } else { *pback = 0; dk4dmt_error_sigemptyset(pdmt); /* ERROR: Failed to sigemptyset */ dk4dmt_log_1(pdmt, 9); } } static void dk4dmt_close_non_std_file_descriptors(void) { #if DK4_HAVE_SYS_RESOURCE_H && DK4_HAVE_GETRLIMIT && defined(RLIMIT_NOFILE) struct rlimit rl; #endif int maxfd = 1024; int fd; /* Find maximum file descriptor */ #if DK4_HAVE_SYS_RESOURCE_H && DK4_HAVE_GETRLIMIT && defined(RLIMIT_NOFILE) if (0 == getrlimit(RLIMIT_NOFILE, &rl)) { #if defined(RLIM_INFINITY) if (RLIM_INFINITY != rl.rlim_max) { back = rl.rlim_max; } #else back = rl.rlim_max; #endif } #else #endif /* Close file descriptors */ for (fd = 3; fd <= maxfd; fd++) { (void)close(fd); } } int dk4dmt_parent_before_fork(dk4dmt_t *pdmt) { dk4_stat_t stb; int back = 0; if (NULL != pdmt) { back = 1; /* Close non-standard file descriptors */ dk4dmt_close_non_std_file_descriptors(); /* Reset all signals */ dk4dmt_reset_all_signals(&back, pdmt); if (1 == back) { /* Unblock all signals */ dk4dmt_unblock_all_signals(&back, pdmt); if (1 == back) { if (NULL != pdmt->pidfile) { if (0 != dk4stat_c8(&stb, pdmt->pidfile, NULL)) { back = 0; dk4dmt_error_pid_file_exists(pdmt); /* ERROR: PID file exists */ dk4dmt_log_1(pdmt, 10); } } if (1 == back) { if (0 != pipe(&(pdmt->pfd[0]))) { back = 0; pdmt->pfd[0] = -1; pdmt->pfd[1] = -1; } } } } } return back; } void dk4dmt_parent_after_fork(dk4dmt_t *pdmt) { int v; if (NULL != pdmt) { /* Close write head */ if (-1 != pdmt->pfd[1]) { close(pdmt->pfd[1]); pdmt->pfd[1] = -1; } else { dk4dmt_error_pipe_not_open(pdmt); /* ERROR: Pipe was not open (bug) */ dk4dmt_log_1(pdmt, 11); } /* Read exit status code and close read head */ if (-1 != pdmt->pfd[0]) { if ((int)sizeof(int) == (int)read(pdmt->pfd[0], &v, sizeof(int))) { pdmt->exv = v; } else { dk4dmt_error_pipe_read_failed(pdmt); /* ERROR: Failed to read from pipe */ dk4dmt_log_1(pdmt, 12); } close(pdmt->pfd[0]); pdmt->pfd[0] = -1; } else { dk4dmt_error_pipe_not_open(pdmt); /* ERROR: Pipe was not open (bug) */ dk4dmt_log_1(pdmt, 11); } } } void dk4dmt_parent_fork_failed(dk4dmt_t *pdmt) { if (NULL != pdmt) { /* Write error exit status code */ dk4dmt_error_fork_failed(pdmt); /* ERROR: Fork failed */ dk4dmt_log_1(pdmt, 13); /* Close write head */ if (-1 != pdmt->pfd[1]) { close(pdmt->pfd[1]); pdmt->pfd[1] = -1; } /* Close read head */ if (-1 != pdmt->pfd[0]) { close(pdmt->pfd[0]); pdmt->pfd[0] = -1; } } } int dk4dmt_intermediate_before_fork(dk4dmt_t *pdmt) { int back = 0; if (NULL != pdmt) { back = 1; /* Close read head */ if (-1 != pdmt->pfd[0]) { close(pdmt->pfd[0]); pdmt->pfd[0] = -1; } #if DK4_HAVE_SETSID if ((pid_t)(-1) == setsid()) { back = 0; dk4dmt_error_setsid(pdmt); /* ERROR: setsid() failed */ dk4dmt_log_1(pdmt, 14); } #else #if DK4_HAVE_SETPGRP setpgrp(); #else back = 0; dk4dmt_error_setsid(pdmt); /* ERROR: No function to decouple from terminal */ dk4dmt_log_1(pdmt, 15); #endif #endif } return back; } void dk4dmt_intermediate_after_fork(dk4dmt_t *pdmt) { if (NULL != pdmt) { /* Close write head */ if (-1 != pdmt->pfd[1]) { close(pdmt->pfd[1]); pdmt->pfd[1] = -1; } } } void dk4dmt_intermediate_fork_failed(dk4dmt_t *pdmt) { int v; if (NULL != pdmt) { /* Set exit status code */ dk4dmt_parent_fork_failed(pdmt); /* ERROR: Fork failed */ dk4dmt_log_1(pdmt, 13); /* Close write head */ if (-1 != pdmt->pfd[1]) { v = pdmt->exv; if ((int)sizeof(v) != (int)write(pdmt->pfd[1], &v, sizeof(v))) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } close(pdmt->pfd[1]); pdmt->pfd[1] = -1; } } } int dk4dmt_daemon_start(dk4dmt_t *pdmt) { char pidbuf[16*sizeof(dk4_um_t)]; FILE *fipo; int res; int v; int fdout; int fdin; int back = 0; if (NULL != pdmt) { if (NULL != pdmt->pidfile) { res = dk4ma_write_c8_decimal_unsigned( pidbuf, sizeof(pidbuf), (dk4_um_t)getpid(), 0, NULL ); if (0 != res) { if (0 != dk4str8_cat_s(pidbuf,sizeof(pidbuf),dk4dmt_kw[0],NULL)) { (void)dk4mkdir_hierarchy_c8(pdmt->pidfile, 0, NULL); fipo = dk4fopen_c8( pdmt->pidfile,dk4dmt_kw[1],DK4_FOPEN_SC_PRIVILEGED,NULL ); if (NULL != fipo) { back = 1; if (EOF == fputs(pidbuf, fipo)) { back = 0; dk4dmt_error_write_pid_file(pdmt); /* ERROR: Failed to write PID file */ dk4dmt_log_1(pdmt, 16); } if (0 != fclose(fipo)) { back = 0; dk4dmt_error_write_pid_file(pdmt); /* ERROR: Failed to write PID file */ dk4dmt_log_1(pdmt, 16); } } else { dk4dmt_error_write_pid_file(pdmt); /* ERROR: Failed to write PID file */ dk4dmt_log_1(pdmt, 16); } } } if (0 == back) { unlink(pdmt->pidfile); } } else { dk4dmt_error_pid_file_name_missing(pdmt); /* ERROR: Missing PID file name */ dk4dmt_log_1(pdmt, 17); } /* Connect /dev/null to stdin, stdout and stderr */ if (0 != back) { fdin = open(dk4dmt_kw[2], O_RDONLY); if (-1 < fdin) { if (-1 == dup2(fdin, 0)) { back = 0; dk4dmt_error_dup2_failed(pdmt); /* ERROR: Failed to duplicate file handle */ dk4dmt_log_1(pdmt, 18); } close(fdin); } else { back = 0; dk4dmt_error_failed_read_dev_null(pdmt); /* ERROR: Failed to read /dev/null */ dk4dmt_log_1(pdmt, 19); } fdout = open(dk4dmt_kw[2], O_WRONLY); if (-1 < fdout) { if (-1 == dup2(fdout, 1)) { back = 0; dk4dmt_error_dup2_failed(pdmt); /* ERROR: Failed to duplicate file handle */ dk4dmt_log_1(pdmt, 18); } if (-1 == dup2(fdout, 2)) { back = 0; dk4dmt_error_dup2_failed(pdmt); /* ERROR: Failed to duplicate file handle */ dk4dmt_log_1(pdmt, 18); } close(fdout); } else { back = 0; dk4dmt_error_failed_write_dev_null(pdmt); /* ERROR: Failed to write /dev/null */ dk4dmt_log_1(pdmt, 20); } } /* Use exact permissions when creating new files or directories */ if (0 != back) { umask(0); } /* Change to root directory */ if (0 != back) { if (0 != chdir(dk4dmt_kw[3])) { back = 0; dk4dmt_error_failed_chdir_root(pdmt); /* ERROR: Failed to change into root directory */ dk4dmt_log_1(pdmt, 21); } } /* Send exit status code to main process */ if (-1 != pdmt->pfd[1]) { v = pdmt->exv; if ((int)sizeof(v) != (int)write(pdmt->pfd[1], &v, sizeof(v))) { #if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); #else dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); #endif } close(pdmt->pfd[1]); pdmt->pfd[1] = -1; } } return back; } void dk4dmt_daemon_end(dk4dmt_t *pdmt) { if (NULL != pdmt) { if (NULL != pdmt->pidfile) { if (0 != unlink(pdmt->pidfile)) { /* ERROR: Failed to remove PID file */ dk4dmt_log_3(pdmt, 22, 23, pdmt->pidfile); } } } } void dk4dmt_success(dk4dmt_t *pdmt) { if (NULL != pdmt) { pdmt->exv = EXIT_SUCCESS; } } int dk4dmt_get_exit_status(dk4dmt_t *pdmt) { int back = EXIT_FAILURE; if (NULL != pdmt) { back = pdmt->exv; } return back; } /* vim: set ai sw=4 ts=4 : */