[tor-commits] [tor/master] New waitpid-handler functions to run callbacks when a child exits.

nickm at torproject.org nickm at torproject.org
Sat Jun 14 15:47:50 UTC 2014


commit 4ed03965a5a412bf58540e678f48f6c331ad30d9
Author: Nick Mathewson <nickm at torproject.org>
Date:   Wed Apr 9 13:45:27 2014 -0400

    New waitpid-handler functions to run callbacks when a child exits.
    
    Also, move 'procmon' into libor_event library, since it uses libevent.
---
 src/common/include.am     |    9 ++-
 src/common/procmon.c      |    1 +
 src/common/util_process.c |  157 +++++++++++++++++++++++++++++++++++++++++++++
 src/common/util_process.h |   25 ++++++++
 src/or/main.c             |    4 +-
 src/or/or.h               |    3 -
 6 files changed, 191 insertions(+), 8 deletions(-)

diff --git a/src/common/include.am b/src/common/include.am
index 7b2465c..c9dcedd 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -57,9 +57,9 @@ LIBOR_A_SOURCES = \
   src/common/log.c					\
   src/common/memarea.c					\
   src/common/mempool.c					\
-  src/common/procmon.c					\
   src/common/util.c					\
   src/common/util_codedigest.c				\
+  src/common/util_process.c				\
   src/common/sandbox.c					\
   src/ext/csiphash.c					\
   $(libor_extra_source)
@@ -72,7 +72,9 @@ LIBOR_CRYPTO_A_SOURCES = \
   src/common/tortls.c		\
   $(libcrypto_extra_source)
 
-LIBOR_EVENT_A_SOURCES = src/common/compat_libevent.c
+LIBOR_EVENT_A_SOURCES = \
+	src/common/compat_libevent.c \
+	src/common/procmon.c
 
 src_common_libor_a_SOURCES = $(LIBOR_A_SOURCES)
 src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
@@ -111,7 +113,8 @@ COMMONHEADERS = \
   src/common/torint.h				\
   src/common/torlog.h				\
   src/common/tortls.h				\
-  src/common/util.h
+  src/common/util.h				\
+  src/common/util_process.h
 
 noinst_HEADERS+= $(COMMONHEADERS)
 
diff --git a/src/common/procmon.c b/src/common/procmon.c
index 0a49689..7c9b7c3 100644
--- a/src/common/procmon.c
+++ b/src/common/procmon.c
@@ -162,6 +162,7 @@ tor_validate_process_specifier(const char *process_spec,
   return parse_process_specifier(process_spec, &ppspec, msg);
 }
 
+/* XXXX we should use periodic_timer_new() for this stuff */
 #ifdef HAVE_EVENT2_EVENT_H
 #define PERIODIC_TIMER_FLAGS EV_PERSIST
 #else
diff --git a/src/common/util_process.c b/src/common/util_process.c
new file mode 100644
index 0000000..8ccf7f3
--- /dev/null
+++ b/src/common/util_process.c
@@ -0,0 +1,157 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file util_process.c
+ * \brief utility functions for launching processes and checking their
+ *    status. These functions are kept separately from procmon so that they
+ *    won't require linking against libevent.
+ **/
+
+#include "orconfig.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include "compat.h"
+#include "util.h"
+#include "torlog.h"
+#include "util_process.h"
+#include "ht.h"
+
+/* ================================================== */
+/* Convenience structures for handlers for waitpid().
+ *
+ * The tor_process_monitor*() code above doesn't use them, since it is for
+ * monitoring a non-child process.
+ */
+
+#ifndef _WIN32
+
+/** Mapping from a PID to a userfn/userdata pair. */
+struct waitpid_callback_t {
+  HT_ENTRY(waitpid_callback_t) node;
+  pid_t pid;
+
+  void (*userfn)(int, void *userdata);
+  void *userdata;
+
+  unsigned running;
+};
+
+static INLINE unsigned int
+process_map_entry_hash_(const waitpid_callback_t *ent)
+{
+  return (unsigned) ent->pid;
+}
+
+static INLINE unsigned int
+process_map_entries_eq_(const waitpid_callback_t *a, const waitpid_callback_t *b)
+{
+  return a->pid == b->pid;
+}
+
+static HT_HEAD(process_map, waitpid_callback_t) process_map = HT_INITIALIZER();
+
+HT_PROTOTYPE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
+             process_map_entries_eq_);
+HT_GENERATE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
+            process_map_entries_eq_, 0.6, malloc, realloc, free);
+
+/**
+ * Begin monitoring the child pid <b>pid</b> to see if we get a SIGCHLD for
+ * it.  If we eventually do, call <b>fn</b>, passing it the exit status (as
+ * yielded by waitpid) and the pointer <b>arg</b>.
+ *
+ * To cancel this, or clean up after it has triggered, call
+ * clear_waitpid_callback().
+ */
+waitpid_callback_t *
+set_waitpid_callback(pid_t pid, void (*fn)(int, void *), void *arg)
+{
+  waitpid_callback_t *old_ent;
+  waitpid_callback_t *ent = tor_malloc_zero(sizeof(waitpid_callback_t));
+  ent->pid = pid;
+  ent->userfn = fn;
+  ent->userdata = arg;
+  ent->running = 1;
+
+  old_ent = HT_REPLACE(process_map, &process_map, ent);
+  if (old_ent) {
+    log_warn(LD_BUG, "Replaced a waitpid monitor on pid %u. That should be "
+             "impossible.", (unsigned) pid);
+    old_ent->running = 0;
+  }
+
+  return ent;
+}
+
+/**
+ * Cancel a waitpid_callback_t, or clean up after one has triggered. Releases
+ * all storage held by <b>ent</b>.
+ */
+void
+clear_waitpid_callback(waitpid_callback_t *ent)
+{
+  waitpid_callback_t *old_ent;
+  if (ent == NULL)
+    return;
+
+  if (ent->running) {
+    old_ent = HT_REMOVE(process_map, &process_map, ent);
+    if (old_ent != ent) {
+      log_warn(LD_BUG, "Couldn't remove waitpid monitor for pid %u.",
+               (unsigned) ent->pid);
+      return;
+    }
+  }
+
+  tor_free(ent);
+}
+
+/** Helper: find the callack for <b>pid</b>; if there is one, run it,
+ * reporting the exit status as <b>status</b>. */
+static void
+notify_waitpid_callback_by_pid(pid_t pid, int status)
+{
+  waitpid_callback_t search, *ent;
+
+  search.pid = pid;
+  ent = HT_REMOVE(process_map, &process_map, &search);
+  if (!ent || !ent->running) {
+    log_info(LD_GENERAL, "Child process %u has exited; no callback was "
+             "registered", (unsigned)pid);
+    return;
+  }
+
+  log_info(LD_GENERAL, "Child process %u has exited; running callback.",
+           (unsigned)pid);
+
+  ent->running = 0;
+  ent->userfn(status, ent->userdata);
+}
+
+/** Use waitpid() to wait for all children that have exited, and invoke any
+ * callbacks registered for them. */
+void
+notify_pending_waitpid_callbacks(void)
+{
+  /* I was going to call this function reap_zombie_children(), but
+   * that makes it sound way more exciting than it really is. */
+  pid_t child;
+  int status = 0;
+
+  while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
+    notify_waitpid_callback_by_pid(child, status);
+    status = 0; /* should be needless */
+  }
+}
+
+#endif
+
diff --git a/src/common/util_process.h b/src/common/util_process.h
new file mode 100644
index 0000000..877702c
--- /dev/null
+++ b/src/common/util_process.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2011-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file util_process.h
+ * \brief Headers for util_process.c
+ **/
+
+#ifndef TOR_UTIL_PROCESS_H
+#define TOR_UTIL_PROCESS_H
+
+#ifndef _WIN32
+/** A callback structure waiting for us to get a SIGCHLD informing us that a
+ * PID has been closed. Created by set_waitpid_callback. Cancelled or cleaned-
+ * up from clear_waitpid_callback().  Do not access outside of the main thread;
+ * do not access from inside a signal handler. */
+typedef struct waitpid_callback_t waitpid_callback_t;
+
+waitpid_callback_t *set_waitpid_callback(pid_t pid,
+                                         void (*fn)(int, void *), void *arg);
+void clear_waitpid_callback(waitpid_callback_t *ent);
+void notify_pending_waitpid_callbacks(void);
+#endif
+
+#endif
diff --git a/src/or/main.c b/src/or/main.c
index 6713d80..2ca7174 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -54,6 +54,7 @@
 #include "routerparse.h"
 #include "statefile.h"
 #include "status.h"
+#include "util_process.h"
 #include "ext_orport.h"
 #ifdef USE_DMALLOC
 #include <dmalloc.h>
@@ -2097,8 +2098,7 @@ process_signal(uintptr_t sig)
       break;
 #ifdef SIGCHLD
     case SIGCHLD:
-      while (waitpid(-1,NULL,WNOHANG) > 0) ; /* keep reaping until no more
-                                                zombies */
+      notify_pending_waitpid_callbacks();
       break;
 #endif
     case SIGNEWNYM: {
diff --git a/src/or/or.h b/src/or/or.h
index aeaeb8e..9586034 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -42,9 +42,6 @@
 #include <sys/param.h> /* FreeBSD needs this to know what version it is */
 #endif
 #include "torint.h"
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
 #ifdef HAVE_SYS_FCNTL_H
 #include <sys/fcntl.h>
 #endif





More information about the tor-commits mailing list