Tor 0.4.9.0-alpha-dev
waitpid.c
Go to the documentation of this file.
1/* Copyright (c) 2003-2004, Roger Dingledine
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4/* See LICENSE for licensing information */
5
6/**
7 * \file waitpid.c
8 * \brief Convenience structures for handlers for handling waitpid().
9 **/
10
11#include "orconfig.h"
12
13#ifndef _WIN32
14
15#include "lib/process/waitpid.h"
16#include "lib/log/log.h"
17#include "lib/log/util_bug.h"
18#include "lib/malloc/malloc.h"
19#include "ext/ht.h"
20
21#ifdef HAVE_SYS_WAIT_H
22#include <sys/wait.h>
23#endif
24
25#include <string.h>
26
27/* ================================================== */
28/* Convenience structures for handlers for waitpid().
29 *
30 * The tor_process_monitor*() code above doesn't use them, since it is for
31 * monitoring a non-child process.
32 */
33
34/** Mapping from a PID to a userfn/userdata pair. */
36 HT_ENTRY(waitpid_callback_t) node;
37 pid_t pid;
38
39 void (*userfn)(int, void *userdata);
40 void *userdata;
41
42 unsigned running;
43};
44
45static inline unsigned int
46process_map_entry_hash_(const waitpid_callback_t *ent)
47{
48 return (unsigned) ent->pid;
49}
50
51static inline unsigned int
52process_map_entries_eq_(const waitpid_callback_t *a,
53 const waitpid_callback_t *b)
54{
55 return a->pid == b->pid;
56}
57
58static HT_HEAD(process_map, waitpid_callback_t) process_map = HT_INITIALIZER();
59
60HT_PROTOTYPE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
61 process_map_entries_eq_);
62HT_GENERATE2(process_map, waitpid_callback_t, node, process_map_entry_hash_,
63 process_map_entries_eq_, 0.6, tor_reallocarray_, tor_free_);
64
65/**
66 * Begin monitoring the child pid <b>pid</b> to see if we get a SIGCHLD for
67 * it. If we eventually do, call <b>fn</b>, passing it the exit status (as
68 * yielded by waitpid) and the pointer <b>arg</b>.
69 *
70 * To cancel this, or clean up after it has triggered, call
71 * clear_waitpid_callback().
72 */
74set_waitpid_callback(pid_t pid, void (*fn)(int, void *), void *arg)
75{
76 waitpid_callback_t *old_ent;
77 waitpid_callback_t *ent = tor_malloc_zero(sizeof(waitpid_callback_t));
78 ent->pid = pid;
79 ent->userfn = fn;
80 ent->userdata = arg;
81 ent->running = 1;
82
83 old_ent = HT_REPLACE(process_map, &process_map, ent);
84 if (old_ent) {
85 log_warn(LD_BUG, "Replaced a waitpid monitor on pid %u. That should be "
86 "impossible.", (unsigned) pid);
87 old_ent->running = 0;
88 }
89
90 return ent;
91}
92
93/**
94 * Cancel a waitpid_callback_t, or clean up after one has triggered. Releases
95 * all storage held by <b>ent</b>.
96 */
97void
99{
100 waitpid_callback_t *old_ent;
101 if (ent == NULL)
102 return;
103
104 if (ent->running) {
105 old_ent = HT_REMOVE(process_map, &process_map, ent);
106 if (old_ent != ent) {
107 log_warn(LD_BUG, "Couldn't remove waitpid monitor for pid %u.",
108 (unsigned) ent->pid);
109 return;
110 }
111 }
112
113 tor_free(ent);
114}
115
116/** Helper: find the callback for <b>pid</b>; if there is one, run it,
117 * reporting the exit status as <b>status</b>. */
118static void
119notify_waitpid_callback_by_pid(pid_t pid, int status)
120{
121 waitpid_callback_t search, *ent;
122
123 search.pid = pid;
124 ent = HT_REMOVE(process_map, &process_map, &search);
125 if (!ent || !ent->running) {
126 log_info(LD_GENERAL, "Child process %u has exited; no callback was "
127 "registered", (unsigned)pid);
128 return;
129 }
130
131 log_info(LD_GENERAL, "Child process %u has exited; running callback.",
132 (unsigned)pid);
133
134 ent->running = 0;
135 ent->userfn(status, ent->userdata);
136}
137
138/** Use waitpid() to wait for all children that have exited, and invoke any
139 * callbacks registered for them. */
140void
142{
143 /* I was going to call this function reap_zombie_children(), but
144 * that makes it sound way more exciting than it really is. */
145 pid_t child;
146 int status = 0;
147
148 while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
149 notify_waitpid_callback_by_pid(child, status);
150 status = 0; /* should be needless */
151 }
152}
153
154#endif /* !defined(_WIN32) */
HT_PROTOTYPE(hs_circuitmap_ht, circuit_t, hs_circuitmap_node, hs_circuit_hash_token, hs_circuits_have_same_token)
typedef HT_HEAD(hs_service_ht, hs_service_t) hs_service_ht
Headers for log.c.
#define LD_BUG
Definition: log.h:86
#define LD_GENERAL
Definition: log.h:62
void * tor_reallocarray_(void *ptr, size_t sz1, size_t sz2)
Definition: malloc.c:146
void tor_free_(void *mem)
Definition: malloc.c:227
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:56
Macros to manage assertions, fatal and non-fatal.
static void notify_waitpid_callback_by_pid(pid_t pid, int status)
Definition: waitpid.c:119
void notify_pending_waitpid_callbacks(void)
Definition: waitpid.c:141
void clear_waitpid_callback(waitpid_callback_t *ent)
Definition: waitpid.c:98
Headers for waitpid.c.