/* * IRC - Internet Relay Chat, ircd/ircd_signal.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file * @brief Signal handlers for ircu. * @version $Id: ircd_signal.c 1633 2006-03-25 03:46:56Z entrope $ */ #include "config.h" #include "ircd.h" #include "ircd_alloc.h" #include "ircd_events.h" #include "ircd_log.h" #include "ircd_signal.h" #include "s_conf.h" #include "ssl.h" /* #include -- Now using assert in ircd_log.h */ #include #include #include #include /** Records a function to be called when a child process terminates. */ struct ChildRecord { struct ChildRecord *next; SigChldCallBack call; void *datum; pid_t cpid; }; /** Counts various types of signals that we receive. * Using volatile sig_atomic_t for signal-safe access from handlers. */ static volatile sig_atomic_t SignalCounter_alrm = 0; /**< Received SIGALRM count. */ static volatile sig_atomic_t SignalCounter_hup = 0; /**< Received SIGHUP count. */ static volatile sig_atomic_t SignalCounter_chld = 0; /**< Received SIGCHLD count. */ static volatile sig_atomic_t SignalCounter_usr1 = 0; /**< Received SIGUSR1 count. */ /** Event generator for SIGHUP. */ static struct Signal sig_hup; /** Event generator for SIGINT. */ static struct Signal sig_int; /** Event generator for SIGTERM. */ static struct Signal sig_term; /** Event generator for SIGCHLD. */ static struct Signal sig_chld; /** Event generator for SIGUSR1. */ static struct Signal sig_usr1; /** List of active child process callback requests. */ static struct ChildRecord *children; /** List of inactive (free) child records. */ static struct ChildRecord *crec_freelist; /* Make sure we have a definition for SIGCHLD. */ #if !defined(SIGCHLD) # define SIGCHLD SIGCLD #endif /** Signal handler for SIGALRM. * @param[in] sig Signal number (ignored). */ static void sigalrm_handler(int sig) { ++SignalCounter_alrm; } /** Signal callback for SIGTERM. * @param[in] ev Signal event descriptor. */ static void sigterm_callback(struct Event* ev) { assert(0 != ev_signal(ev)); assert(ET_SIGNAL == ev_type(ev)); assert(SIGTERM == sig_signal(ev_signal(ev))); assert(SIGTERM == ev_data(ev)); server_die("received signal SIGTERM"); } /** Signal callback for SIGHUP. * @param[in] ev Signal event descriptor. */ static void sighup_callback(struct Event* ev) { assert(0 != ev_signal(ev)); assert(ET_SIGNAL == ev_type(ev)); assert(SIGHUP == sig_signal(ev_signal(ev))); assert(SIGHUP == ev_data(ev)); ++SignalCounter_hup; rehash(&me, 1); } /** Signal callback for SIGUSR1. * @param[in] ev Signal event descriptor. */ static void sigusr1_callback(struct Event* ev) { assert(0 != ev_signal(ev)); assert(ET_SIGNAL == ev_type(ev)); assert(SIGUSR1 == sig_signal(ev_signal(ev))); assert(SIGUSR1 == ev_data(ev)); ++SignalCounter_usr1; #ifdef USE_SSL ssl_reinit(1); #endif } /** Signal callback for SIGINT. * @param[in] ev Signal event descriptor. */ static void sigint_callback(struct Event* ev) { assert(0 != ev_signal(ev)); assert(ET_SIGNAL == ev_type(ev)); assert(SIGINT == sig_signal(ev_signal(ev))); assert(SIGINT == ev_data(ev)); #ifdef RESTART_ON_SIGINT server_restart("caught signal: SIGINT"); #else server_die("received signal SIGINT (Ctrl-C)"); #endif } /** Allocate a child callback record. * @return Newly allocated callback record. */ static struct ChildRecord *alloc_crec(void) { struct ChildRecord *crec; if (crec_freelist) { crec = crec_freelist; crec_freelist = crec->next; } else { crec = MyCalloc(1, sizeof(*crec)); } memset(crec, 0, sizeof(*crec)); crec->next = NULL; return crec; } /** Release \a crec, which is after \a prev. * @param[in] crec Child process callback record to release. */ static void release_crec(struct ChildRecord *crec) { memset(crec, 0, sizeof(*crec)); crec->next = crec_freelist; crec_freelist = crec; } /** Register a function to be called when a child process terminates. * @param[in] child Child process ID. * @param[in] call Function to call when process \a child terminates. * @param[in] datum Additional data parameter to pass to \a call. */ void register_child(pid_t child, SigChldCallBack call, void *datum) { struct ChildRecord *crec; crec = alloc_crec(); /* Link into #children list. */ crec->next = children; children = crec; /* Fill in user fields. */ crec->call = call; crec->datum = datum; crec->cpid = child; } /** Unregister all callbacks for a child process, optionally calling * them first. * @param[in] child Child process ID to unregister. * @param[in] do_call If non-zero, make the callbacks. * @param[in] status If \a do_call is non-zero, the child's exit status. */ static void do_unregister_child(pid_t child, int do_call, int status) { struct ChildRecord *crec = children; struct ChildRecord *prev = NULL; while (crec != NULL) { if (crec->cpid == child) { if (do_call) crec->call(child, crec->datum, status); if (prev) prev->next = crec->next; else children = crec->next; release_crec(crec); } else prev = crec; crec = prev ? prev->next : children; } } /** Unregister all callbacks for a child process. * @param[in] child Child process ID to unregister. */ void unregister_child(pid_t child) { do_unregister_child(child, 0, 0); } /** Signal handler for SIGCHLD. * @param[in] ev Signal event descriptor. */ static void sigchld_callback(struct Event *ev) { pid_t cpid; int status; ++SignalCounter_chld; do { cpid = waitpid(-1, &status, WNOHANG); if (cpid > 0) do_unregister_child(cpid, 1, status); } while (cpid > 0); } /** Register all necessary signal handlers. */ void setup_signals(void) { struct sigaction act; act.sa_handler = SIG_IGN; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGPIPE); sigaddset(&act.sa_mask, SIGALRM); sigaddset(&act.sa_mask, SIGUSR2); #ifdef SIGWINCH sigaddset(&act.sa_mask, SIGWINCH); sigaction(SIGWINCH, &act, 0); #endif sigaction(SIGPIPE, &act, 0); sigaction(SIGUSR2, &act, 0); act.sa_handler = sigalrm_handler; sigaction(SIGALRM, &act, 0); signal_add(&sig_hup, sighup_callback, 0, SIGHUP); signal_add(&sig_int, sigint_callback, 0, SIGINT); signal_add(&sig_term, sigterm_callback, 0, SIGTERM); signal_add(&sig_chld, sigchld_callback, 0, SIGCHLD); signal_add(&sig_usr1, sigusr1_callback, 0, SIGUSR1); #ifdef HAVE_RESTARTABLE_SYSCALLS /* siginterrupt is deprecated on modern systems (sa_flags=0 already * prevents SA_RESTART), but kept for compatibility with ancient systems. */ #if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif siginterrupt(SIGALRM, 1); #if defined(__GNUC__) #pragma GCC diagnostic pop #endif #endif } /** Kill and clean up all child processes. */ void reap_children(void) { /* Send SIGTERM to all children in process group. Sleep for a * second to let them exit before we try to clean them up. */ kill(0, SIGTERM); sleep(1); sigchld_callback(NULL); }