/* * Copyright (c) 2005 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: stthreadssignal.c,v 1.4 2005/01/18 00:08:31 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/types.h" #include "sm/signal.h" #include "statethreads/st.h" /* ** Generic signal handler for statethreads application. ** This module requires a signal pipe (see below) over which it will ** send single characters that indicate which signal has been received ** (see signal.h). ** The application can read from the pipe and act accordingly. ** The advantage of this approach is that the signal handler code is ** small and "async signal safe", while the code that acts on the ** data read from the pipe doesn't have to be and hence can use logging etc. */ /* ** Signal pipe, provided by application. ** Fixme: should this be local/static and the installation function returns ** a pointer to the read part?? */ extern st_netfd_t Sig_pipe[2]; /* ** Callback function if write(2) fails and context for it. ** The context doesn't seem to be necessary, it can be a global in the ** application itself (as there is only one "global" callback function and ** context). */ static sm_sigwrfail_F sigwrfailcb = NULL; static void *sigwrfailctx = NULL; /* ** ST_SIGNAL -- install a single signal handler ** ** Parameters: ** sig -- signal number ** handler -- handler for signal ** ** Returns: ** nothing. */ static void st_signal(int sig, sm_sighandler_F handler) { struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(sig, &sa, NULL); } /* ** ST_SIGHANDLER -- Signal handler; send signal number via pipe ** ** Parameters: ** signo -- signal number ** ** Returns: ** nothing */ static void st_sighandler(int signo) { int err, fd; char sig; err = errno; fd = st_netfd_fileno(Sig_pipe[SM_WR_SIG_PIPE]); sig = '\0'; switch (signo) { case SIGHUP: sig = SM_SIG_HUP; break; case SIGINT: sig = SM_SIG_INT; break; case SIGALRM: sig = SM_SIG_ALRM; break; case SIGCHLD: sig = SM_SIG_CHLD; break; case SIGTERM: sig = SM_SIG_TERM; break; case SIGUSR1: sig = SM_SIG_USR1; break; case SIGUSR2: sig = SM_SIG_USR2; break; case SIGPIPE: sig = SM_SIG_PIPE; break; default: sig = SM_SIG_UNKNOWN; break; } /* write() is async-safe */ if (write(fd, &sig, sizeof(char)) != sizeof(char)) { if (sigwrfailcb != NULL) sigwrfailcb(errno, sigwrfailctx); } errno = err; } /* ** ST_INSTALL_SIGHANDLERS -- install signal handlers ** ** Parameters: ** signalset -- set of signals to handle ** sigwrfail -- function to invoke when write() in signal handler ** fails ** ctx -- context for sigwrfail ** ** Returns: ** usual error code */ sm_ret_T st_install_sighandlers(uint signalset, sm_sigwrfail_F sigwrfail, void *ctx) { sigset_t mask; int p[2]; /* Create signal pipe */ if (pipe(p) < 0) return sm_error_perm(SM_EM_STR, errno); /* set close-on-exec flag? fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0 fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0) */ if ((Sig_pipe[SM_RD_SIG_PIPE] = st_netfd_open(p[SM_RD_SIG_PIPE])) == NULL) return sm_error_perm(SM_EM_STTHR, errno); if ((Sig_pipe[SM_WR_SIG_PIPE] = st_netfd_open(p[SM_WR_SIG_PIPE])) == NULL) return sm_error_perm(SM_EM_STTHR, errno); sigemptyset(&mask); /* Install signal handlers */ if (SM_IS_FLAG(signalset, SM_HDL_SIG_HUP)) { st_signal(SIGHUP, st_sighandler); sigaddset(&mask, SIGHUP); } if (SM_IS_FLAG(signalset, SM_HDL_SIG_INT)) { st_signal(SIGINT, st_sighandler); sigaddset(&mask, SIGINT); } if (SM_IS_FLAG(signalset, SM_HDL_SIG_ALRM)) { st_signal(SIGALRM, st_sighandler); sigaddset(&mask, SIGALRM); } if (SM_IS_FLAG(signalset, SM_HDL_SIG_CHLD)) { st_signal(SIGCHLD, st_sighandler); sigaddset(&mask, SIGCHLD); } if (SM_IS_FLAG(signalset, SM_HDL_SIG_TERM)) { st_signal(SIGTERM, st_sighandler); sigaddset(&mask, SIGTERM); } if (SM_IS_FLAG(signalset, SM_HDL_SIG_USR1)) { st_signal(SIGUSR1, st_sighandler); sigaddset(&mask, SIGUSR1); } if (SM_IS_FLAG(signalset, SM_HDL_SIG_USR2)) { st_signal(SIGUSR2, st_sighandler); sigaddset(&mask, SIGUSR2); } if (SM_IS_FLAG(signalset, SM_HDL_SIG_PIPE)) { st_signal(SIGPIPE, st_sighandler); sigaddset(&mask, SIGPIPE); } sigprocmask(SIG_UNBLOCK, &mask, NULL); sigwrfailcb = sigwrfail; sigwrfailctx = ctx; return SM_SUCCESS; }