From: Michael Poole Date: Thu, 16 Feb 2006 03:27:41 +0000 (+0000) Subject: Add infrastructure to handle child processes exiting. X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=commitdiff_plain;h=09af095aabc97e3a2d3ae6469e1c3d9dd9bf493c Add infrastructure to handle child processes exiting. git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/branches/u2_10_12_branch@1618 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- diff --git a/ChangeLog b/ChangeLog index e1094ad..5427e8d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2006-02-15 Michael Poole + + * include/ircd_signal.h (SigChldCallBack): New typedef. + (register_child): Declare. + (unregister_child): Declare. + (reap_children): Declare. + + * ircd/ircd_signal.c (alloc_crec): New function. + (release_crec): New function. + (register_child): New function. + (do_unregister_child): New function. + (unregister_child): New function. + (sigchld_callback): New function. + (setup_signals): Hook SIGCHLD. + (reap_children): New function. + + * ircd/ircd.c (server_restart): Call reap_children() on exit. + 2006-02-15 Michael Poole * include/ircd_osdep.h (os_socketpair): Declare. diff --git a/include/ircd_signal.h b/include/ircd_signal.h index 0b42653..c48f4ac 100644 --- a/include/ircd_signal.h +++ b/include/ircd_signal.h @@ -5,7 +5,12 @@ #ifndef INCLUDED_ircd_signal_h #define INCLUDED_ircd_signal_h +typedef void (*SigChldCallBack)(pid_t child_pid, void *datum, int status); + extern void setup_signals(void); +extern void register_child(pid_t child, SigChldCallBack call, void *datum); +extern void unregister_child(pid_t child); +extern void reap_children(void); #endif /* INCLUDED_ircd_signal_h */ diff --git a/ircd/ircd.c b/ircd/ircd.c index 3ba3af7..2c0046f 100644 --- a/ircd/ircd.c +++ b/ircd/ircd.c @@ -180,6 +180,8 @@ void server_restart(const char *message) close_connections(!(thisServer.bootopt & (BOOT_TTY | BOOT_DEBUG | BOOT_CHKCONF))); + reap_children(); + execv(SPATH, thisServer.argv); /* Have to reopen since it has been closed above */ diff --git a/ircd/ircd_signal.c b/ircd/ircd_signal.c index 9c868b2..89a05c4 100644 --- a/ircd/ircd_signal.c +++ b/ircd/ircd_signal.c @@ -24,6 +24,7 @@ #include "config.h" #include "ircd.h" +#include "ircd_alloc.h" #include "ircd_events.h" #include "ircd_log.h" #include "ircd_signal.h" @@ -31,11 +32,23 @@ /* #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; /**< Received SIGALRM count. */ unsigned int hup; /**< Received SIGHUP count. */ + unsigned int chld; /**< Received SIGCHLD count. */ } SignalCounter; /** Event generator for SIGHUP. */ @@ -44,6 +57,17 @@ static struct Signal sig_hup; 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). @@ -93,6 +117,111 @@ static void sigint_callback(struct Event* 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)); + } + + 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) { @@ -115,6 +244,7 @@ void setup_signals(void) 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 /* @@ -126,3 +256,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); +}