Tor  0.4.3.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-2020, 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 
45 static inline unsigned int
46 process_map_entry_hash_(const waitpid_callback_t *ent)
47 {
48  return (unsigned) ent->pid;
49 }
50 
51 static inline unsigned int
52 process_map_entries_eq_(const waitpid_callback_t *a,
53  const waitpid_callback_t *b)
54 {
55  return a->pid == b->pid;
56 }
57 
58 static HT_HEAD(process_map, waitpid_callback_t) process_map = HT_INITIALIZER();
59 
60 HT_PROTOTYPE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
61  process_map_entries_eq_)
62 HT_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  */
74 set_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  */
97 void
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 callack for <b>pid</b>; if there is one, run it,
117  * reporting the exit status as <b>status</b>. */
118 static void
119 notify_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. */
140 void
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) */
void notify_pending_waitpid_callbacks(void)
Definition: waitpid.c:141
Headers for waitpid.c.
#define LD_GENERAL
Definition: log.h:62
void clear_waitpid_callback(waitpid_callback_t *ent)
Definition: waitpid.c:98
#define tor_free(p)
Definition: malloc.h:52
Headers for util_malloc.c.
void * tor_reallocarray_(void *ptr, size_t sz1, size_t sz2)
Definition: malloc.c:146
HT_GENERATE2(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq, 0.6, tor_reallocarray, tor_free_) static void cdm_diff_free_(cdm_diff_t *diff)
Definition: consdiffmgr.c:222
void tor_free_(void *mem)
Definition: malloc.c:227
HT_PROTOTYPE(HT_GENERATE2(channel_gid_map, HT_GENERATE2(channel_t, HT_GENERATE2(gidmap_node, HT_GENERATE2(channel_id_hash, HT_GENERATE2(channel_id_eq)
Definition: channel.c:121
typedef HT_HEAD(hs_service_ht, hs_service_t) hs_service_ht
static void notify_waitpid_callback_by_pid(pid_t pid, int status)
Definition: waitpid.c:119
Headers for log.c.
Macros to manage assertions, fatal and non-fatal.
#define LD_BUG
Definition: log.h:86