123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- /* Copyright (c) 2003-2004, Roger Dingledine
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2017, 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_GENERATE2(process_map, waitpid_callback_t, node, process_map_entry_hash_,
- process_map_entries_eq_, 0.6, tor_reallocarray_, tor_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 /* !defined(_WIN32) */
|