/*++ /* 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 #include #include /* Utility library. */ #include #include #include /* Application-specific. */ #include "master.h" #ifdef USE_SIG_RETURN #include #undef USE_SIG_PIPE #else #define USE_SIG_PIPE #endif /* Local stuff. */ #ifdef USE_SIG_PIPE #include #include #include #include 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); }