diff options
Diffstat (limited to 'src/master/master_sig.c')
-rw-r--r-- | src/master/master_sig.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/src/master/master_sig.c b/src/master/master_sig.c new file mode 100644 index 0000000..db5e39d --- /dev/null +++ b/src/master/master_sig.c @@ -0,0 +1,275 @@ +/*++ +/* NAME +/* master_sig 3 +/* SUMMARY +/* Postfix master - signal processing +/* SYNOPSIS +/* #include "master.h" +/* +/* int master_gotsighup; +/* int master_gotsigchld; +/* +/* int master_sigsetup() +/* DESCRIPTION +/* This module implements the master process signal handling interface. +/* +/* master_gotsighup (master_gotsigchld) is set to SIGHUP (SIGCHLD) +/* when the process receives a hangup (child death) signal. +/* +/* master_sigsetup() enables processing of hangup and child death signals. +/* Receipt of SIGINT, SIGQUIT, SIGSEGV, SIGILL, or SIGTERM +/* is interpreted as a request for termination. Child processes are +/* notified of the master\'s demise by sending them a SIGTERM signal. +/* DIAGNOSTICS +/* BUGS +/* Need a way to register cleanup actions. +/* SEE ALSO +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System libraries. */ + +#include <sys_defs.h> +#include <signal.h> +#include <unistd.h> + +/* Utility library. */ + +#include <msg.h> +#include <posix_signals.h> +#include <killme_after.h> + +/* Application-specific. */ + +#include "master.h" + +#ifdef USE_SIG_RETURN +#include <sys/syscall.h> +#undef USE_SIG_PIPE +#else +#define USE_SIG_PIPE +#endif + +/* Local stuff. */ + +#ifdef USE_SIG_PIPE +#include <errno.h> +#include <fcntl.h> +#include <iostuff.h> +#include <events.h> + +int master_sig_pipe[2]; + +#define SIG_PIPE_WRITE_FD master_sig_pipe[1] +#define SIG_PIPE_READ_FD master_sig_pipe[0] +#endif + +int master_gotsigchld; +int master_gotsighup; + +#ifdef USE_SIG_RETURN + +/* master_sighup - register arrival of hangup signal */ + +static void master_sighup(int sig) +{ + + /* + * WARNING WARNING WARNING. + * + * This code runs at unpredictable moments, as a signal handler. Don't put + * any code here other than for setting a global flag. + */ + master_gotsighup = sig; +} + +/* master_sigchld - register arrival of child death signal */ + +static void master_sigchld(int sig, int code, struct sigcontext * scp) +{ + + /* + * WARNING WARNING WARNING. + * + * This code runs at unpredictable moments, as a signal handler. Don't put + * any code here other than for setting a global flag, or code that is + * intended to be run within a signal handler. + */ + master_gotsigchld = sig; + if (scp != NULL && scp->sc_syscall == SYS_select) { + scp->sc_syscall_action = SIG_RETURN; +#ifndef SA_RESTART + } else if (scp != NULL) { + scp->sc_syscall_action = SIG_RESTART; +#endif + } +} + +#else + +/* master_sighup - register arrival of hangup signal */ + +static void master_sighup(int sig) +{ + int saved_errno = errno; + + /* + * WARNING WARNING WARNING. + * + * This code runs at unpredictable moments, as a signal handler. Don't put + * any code here other than for setting a global flag, or code that is + * intended to be run within a signal handler. Restore errno in case we + * are interrupting the epilog of a failed system call. + */ + master_gotsighup = sig; + if (write(SIG_PIPE_WRITE_FD, "", 1) != 1) + msg_warn("write to SIG_PIPE_WRITE_FD failed: %m"); + errno = saved_errno; +} + +/* master_sigchld - force wakeup from select() */ + +static void master_sigchld(int unused_sig) +{ + int saved_errno = errno; + + /* + * WARNING WARNING WARNING. + * + * This code runs at unpredictable moments, as a signal handler. Don't put + * any code here other than for setting a global flag, or code that is + * intended to be run within a signal handler. Restore errno in case we + * are interrupting the epilog of a failed system call. + */ + master_gotsigchld = 1; + if (write(SIG_PIPE_WRITE_FD, "", 1) != 1) + msg_warn("write to SIG_PIPE_WRITE_FD failed: %m"); + errno = saved_errno; +} + +/* master_sig_event - called upon return from select() */ + +static void master_sig_event(int unused_event, void *unused_context) +{ + char c[1]; + + while (read(SIG_PIPE_READ_FD, c, 1) > 0) + /* void */ ; +} + +#endif + +/* master_sigdeath - die, women and children first */ + +static void master_sigdeath(int sig) +{ + const char *myname = "master_sigdeath"; + struct sigaction action; + pid_t pid = getpid(); + + /* + * Set alarm clock here for suicide after 5s. + */ + killme_after(5); + + /* + * Terminate all processes in our process group, except ourselves. + */ + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + action.sa_handler = SIG_IGN; + if (sigaction(SIGTERM, &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction: %m", myname); + if (kill(-pid, SIGTERM) < 0) + msg_fatal("%s: kill process group: %m", myname); + + /* + * XXX We're running from a signal handler, and should not call complex + * routines at all, but it would be even worse to silently terminate + * without informing the sysadmin. For this reason, msg(3) was made safe + * for usage by signal handlers that terminate the process. + */ + msg_info("terminating on signal %d", sig); + + /* + * Undocumented: when a process runs with PID 1, Linux won't deliver a + * signal unless the process specifies a handler (i.e. SIG_DFL is treated + * as SIG_IGN). + */ + if (init_mode) + /* Don't call exit() from a signal handler. */ + _exit(0); + + /* + * Deliver the signal to ourselves and clean up. XXX We're running as a + * signal handler and really should not be doing complicated things... + */ + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + action.sa_handler = SIG_DFL; + if (sigaction(sig, &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction: %m", myname); + if (kill(pid, sig) < 0) + msg_fatal("%s: kill myself: %m", myname); +} + +/* master_sigsetup - set up signal handlers */ + +void master_sigsetup(void) +{ + const char *myname = "master_sigsetup"; + struct sigaction action; + static int sigs[] = { + SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGSEGV, SIGTERM, + }; + unsigned i; + + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + + /* + * Prepare to kill our children when we receive any of the above signals. + */ + action.sa_handler = master_sigdeath; + for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) + if (sigaction(sigs[i], &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction(%d): %m", myname, sigs[i]); + +#ifdef USE_SIG_PIPE + if (pipe(master_sig_pipe)) + msg_fatal("pipe: %m"); + non_blocking(SIG_PIPE_WRITE_FD, NON_BLOCKING); + non_blocking(SIG_PIPE_READ_FD, NON_BLOCKING); + close_on_exec(SIG_PIPE_WRITE_FD, CLOSE_ON_EXEC); + close_on_exec(SIG_PIPE_READ_FD, CLOSE_ON_EXEC); + event_enable_read(SIG_PIPE_READ_FD, master_sig_event, (void *) 0); +#endif + + /* + * Intercept SIGHUP (re-read config file) and SIGCHLD (child exit). + */ +#ifdef SA_RESTART + action.sa_flags |= SA_RESTART; +#endif + action.sa_handler = master_sighup; + if (sigaction(SIGHUP, &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction(%d): %m", myname, SIGHUP); + + action.sa_flags |= SA_NOCLDSTOP; + action.sa_handler = master_sigchld; + if (sigaction(SIGCHLD, &action, (struct sigaction *) 0) < 0) + msg_fatal("%s: sigaction(%d): %m", myname, SIGCHLD); +} |