%% 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 : */