/* * Copyright (c) 2002-2006 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: sighdl.c,v 1.16 2006/01/19 19:29:35 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/memops.h" #include "sm/heap.h" #include "sm/signal.h" #include "sm/evthr.h" #include "evthr-int.h" #include "log.h" #define MAX_FAILS_T 10 /* ** EVTHR_GOTSIGNAL -- write signal type to internal pipe to notify main loop ** ** Parameters: ** evthr_ctx -- evthr context ** why -- which signal (EVTHR_signal) ** ** Returns: ** usual sm_error code ** ** Called by signal handler. BE CAREFUL! */ static sm_ret_T evthr_gotsignal(sm_evthr_ctx_P evthr_ctx, int why) { ssize_t r; char c; SM_IS_EVTHR_CTX(evthr_ctx); c = (char) why; r = write(wrpipe(evthr_ctx), (void *) &c, 1); if (r != 1) return sm_error_perm(SM_EM_EVTHR, errno); return SM_SUCCESS; } /* ** EVTHR_SIGNAL -- thread to deal with signals ** ** Parameters: ** ctx -- evthr context ** ** Returns: ** NULL */ static void * evthr_signal(void *ctx) { #if WIN32 hShutdownEvt = CreateEvent(NULL, true, false, EVTHR_SHUTDOWN_EVENT); if (hShutdownEvt == NULL) return NULL; if (SetConsoleCtrlHandler(et_console_handler, true) == 0) { CloseHandle(hShutdownEvt); hShutdownEvt = NULL; return NULL; } for( ; WAIT_OBJECT_0 != WaitForSingleObject(hShutdownEvt, INFINITE); ) continue; CloseHandle(hShutdownEvt); hShutdownEvt = NULL; evthr_term(ctx, EVTHR_STOP); #else /* WIN32 */ int sig, errs; sigset_t set; sm_evthr_ctx_P evthr_ctx; (void) pthread_detach(pthread_self()); sigemptyset(&set); sigaddset(&set, SIGHUP); sigaddset(&set, SIGTERM); /* Handle Ctrl-C gracefully for debugging */ sigaddset(&set, SIGINT); sigaddset(&set, SIGUSR1); sigaddset(&set, SIGUSR2); errs = 0; evthr_ctx = (sm_evthr_ctx_P) ctx; while (true) { sig = 0; if (sigwait(&set, &sig) != 0) { evthr_ctx->evthr_sige_where = EVTHR_SHE_SIGWAIT; evthr_ctx->evthr_sige_what = errno; if (++errs > MAX_FAILS_T) { evthr_gotsignal(evthr_ctx, EVTHR_ABRT); return NULL; } evthr_gotsignal(evthr_ctx, EVTHR_ERROR); continue; } errs = 0; switch (sig) { case SIGHUP: case SIGTERM: evthr_gotsignal(evthr_ctx, EVTHR_STOP); return NULL; case SIGINT: evthr_gotsignal(evthr_ctx, EVTHR_ABRT); return NULL; case SIGUSR1: evthr_gotsignal(evthr_ctx, EVTHR_USR1); break; case SIGUSR2: evthr_gotsignal(evthr_ctx, EVTHR_USR2); break; default: evthr_ctx->evthr_sige_where = EVTHR_SHE_UNKSIG; evthr_ctx->evthr_sige_what = sig; break; } } #endif /* WIN32 */ return NULL; } /* ** EVTHR_SPAWN_SIGNAL_THREAD -- spawn thread to handle signals ** ** Parameters: ** evthr_ctx -- evthr context ** ** Returns: */ static sm_ret_T evthr_spawn_signal_thread(sm_evthr_ctx_P evthr_ctx) { pthread_t tid; #if !WIN32 int status; sigset_t set; struct sigaction sigact; /* Ignore SIGPIPE */ sigact.sa_handler = SIG_IGN; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; if (sigaction(SIGPIPE, &sigact, NULL) < 0) { status = errno; sm_log_write(evthr_ctx->evthr_c_lctx, EVTHR_LCAT_SIGNAL, EVTHR_LMOD_SIGNAL, SM_LOG_ERROR, 1, "sev=ERROR, func=evthr_spawn_signal_thread, " "status=could_not_ignore_PIPE_signal, stat=%d", status); return sm_error_perm(SM_EM_EVTHR, status); } /* Mask HUP and KILL signals */ sigemptyset(&set); sigaddset(&set, SIGHUP); sigaddset(&set, SIGTERM); sigaddset(&set, SIGINT); sigaddset(&set, SIGUSR1); sigaddset(&set, SIGUSR2); status = pthread_sigmask(SIG_BLOCK, &set, NULL); if (status != 0) { sm_log_write(evthr_ctx->evthr_c_lctx, EVTHR_LCAT_SIGNAL, EVTHR_LMOD_SIGNAL, SM_LOG_ERROR, 1, "sev=ERROR, func=evthr_spawn_signal_thread, " "status=could_not_mask_HUP_and_KILL_signals, stat=%d", status); return sm_error_perm(SM_EM_EVTHR, status); } #endif /* WIN32 */ #if WIN32 #pragma warning ( disable : 4024 4047 ) #endif /* WIN32 */ status = pthread_create(&tid, NULL, evthr_signal, (void *)evthr_ctx); if (status != 0) #if WIN32 #pragma warning ( default : 4024 4047 ) #endif /* WIN32 */ { sm_log_write(evthr_ctx->evthr_c_lctx, EVTHR_LCAT_SIGNAL, EVTHR_LMOD_SIGNAL, SM_LOG_ERROR, 1, "sev=ERROR, func=evthr_spawn_signal_thread, " "status=could_not_start_signal_thread, stat=%d", status); return sm_error_perm(SM_EM_EVTHR, status); } #if WIN32 /* release the thread handle */ _unused_proc_handle((int) thread_id); #endif /* WIN32 */ return SM_SUCCESS; } /* ** EVTHR_SIGNAL_INIT -- startup for thread to handle signals ** ** Parameters: ** evthr_ctx -- context ** ** Returns: ** usual sm_error code */ sm_ret_T evthr_signal_init(sm_evthr_ctx_P evthr_ctx) { sm_ret_T ret; /* ** spawn_signal_thread must happen before other threads are spawned ** off so that it can mask the right signals and other threads ** will inherit that mask. */ ret = evthr_spawn_signal_thread(evthr_ctx); if (sm_is_err(ret)) { sm_log_write(evthr_ctx->evthr_c_lctx, EVTHR_LCAT_SIGNAL, EVTHR_LMOD_SIGNAL, SM_LOG_ERROR, 1, "sev=ERROR, func=evthr_signal_init, status=" "could_not_spawn_signal_thread, stat=%d", ret); } return ret; }