X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fircd_signal.c;h=650102ed1f9bf32433c4c02843f64b0c1648353a;hb=1570a04e15bec6b2945e4351b1e05211aecdcacc;hp=00e09f500522defe43f60b0d2968e3373ab8af15;hpb=1b4e637606464f9af948a6e87879565f9fc1b726;p=ircu2.10.12-pk.git diff --git a/ircd/ircd_signal.c b/ircd/ircd_signal.c index 00e09f5..650102e 100644 --- a/ircd/ircd_signal.c +++ b/ircd/ircd_signal.c @@ -16,42 +16,214 @@ * 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. - * - * $Id$ + */ +/** @file + * @brief Signal handlers for ircu. + * @version $Id$ */ #include "config.h" -#include "ircd_signal.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 -- 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. */ static struct tag_SignalCounter { - unsigned int alrm; - unsigned int hup; + unsigned int alrm; /**< Received SIGALRM count. */ + unsigned int hup; /**< Received SIGHUP count. */ + unsigned int chld; /**< Received SIGCHLD count. */ } SignalCounter; -void sigalrm_handler(int sig) +/** 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; +/** 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; } -void sigterm_handler(int sig) +/** 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"); } -static void sighup_handler(int sig) +/** 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; - GlobalRehashFlag = 1; + rehash(&me, 1); } -static void sigint_handler(int sig) +/** Signal callback for SIGINT. + * @param[in] ev Signal event descriptor. + */ +static void sigint_callback(struct Event* ev) { - GlobalRestartFlag = 1; + assert(0 != ev_signal(ev)); + assert(ET_SIGNAL == ev_type(ev)); + assert(SIGINT == sig_signal(ev_signal(ev))); + assert(SIGINT == ev_data(ev)); + + server_restart("caught signal: SIGINT"); +} + +/** 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; @@ -70,16 +242,10 @@ void setup_signals(void) act.sa_handler = sigalrm_handler; sigaction(SIGALRM, &act, 0); - sigemptyset(&act.sa_mask); - - act.sa_handler = sighup_handler; - sigaction(SIGHUP, &act, 0); - - act.sa_handler = sigint_handler; - sigaction(SIGINT, &act, 0); - - act.sa_handler = sigterm_handler; - sigaction(SIGTERM, &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); #ifdef HAVE_RESTARTABLE_SYSCALLS /* @@ -91,3 +257,13 @@ void setup_signals(void) #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); +}