Add infrastructure to handle child processes exiting.
authorMichael Poole <mdpoole@troilus.org>
Thu, 16 Feb 2006 03:27:41 +0000 (03:27 +0000)
committerMichael Poole <mdpoole@troilus.org>
Thu, 16 Feb 2006 03:27:41 +0000 (03:27 +0000)
git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/branches/u2_10_12_branch@1618 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

ChangeLog
include/ircd_signal.h
ircd/ircd.c
ircd/ircd_signal.c

index e1094add3a3baf96500f56d0dc5321a5b80887b0..5427e8da433e4edcfec2272065a90ade6186eb0d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2006-02-15  Michael Poole <mdpoole@troilus.org>
+
+       * 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 <mdpoole@troilus.org>
 
        * include/ircd_osdep.h (os_socketpair): Declare.
index 0b426537df098b2b5e423acd7a22f1df62d1474e..c48f4ac6f8e6509d260002e7b1127833eb21cdd8 100644 (file)
@@ -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 */
 
index 3ba3af7dc5b8a0f9559fac536e0d90a95614833b..2c0046fecb9ac39193d6e568a60f2a22ab4670c3 100644 (file)
@@ -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 */
index 9c868b2ff239c0beef3b1029574dfcf454bad02f..89a05c48a668a44aac31f89e78185123de7c9bc3 100644 (file)
@@ -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"
 
 /* #include <assert.h> -- Now using assert in ircd_log.h */
 #include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/** 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);
+}