summaryrefslogtreecommitdiffstats
path: root/src/master/master_sig.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/master/master_sig.c')
-rw-r--r--src/master/master_sig.c275
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);
+}