|
@@ -0,0 +1,157 @@
|
|
|
+
|
|
|
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
|
|
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * \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"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ *
|
|
|
+ * The tor_process_monitor*() code above doesn't use them, since it is for
|
|
|
+ * monitoring a non-child process.
|
|
|
+ */
|
|
|
+
|
|
|
+#ifndef _WIN32
|
|
|
+
|
|
|
+
|
|
|
+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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * 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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * callbacks registered for them. */
|
|
|
+void
|
|
|
+notify_pending_waitpid_callbacks(void)
|
|
|
+{
|
|
|
+
|
|
|
+ * 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;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|
|
|
+
|