Tor 0.4.9.0-alpha-dev
procmon.c
Go to the documentation of this file.
1/* Copyright (c) 2011-2021, The Tor Project, Inc. */
2/* See LICENSE for licensing information */
3
4/**
5 * \file procmon.c
6 * \brief Process-termination monitor functions
7 **/
8
10
11#include "lib/log/log.h"
12#include "lib/log/util_bug.h"
13#include "lib/log/win32err.h"
14#include "lib/malloc/malloc.h"
16
17#ifdef HAVE_SIGNAL_H
18#include <signal.h>
19#endif
20#ifdef HAVE_ERRNO_H
21#include <errno.h>
22#endif
23#ifdef HAVE_SYS_TIME_H
24#include <sys/time.h>
25#endif
26
27#ifdef _WIN32
28#include <winsock2.h>
29#include <windows.h>
30#endif
31
32#if (0 == SIZEOF_PID_T) && defined(_WIN32)
33/* Windows does not define pid_t sometimes, but _getpid() returns an int.
34 * Everybody else needs to have a pid_t. */
35typedef int pid_t;
36#define PID_T_FORMAT "%d"
37#elif (SIZEOF_PID_T == SIZEOF_INT) || (SIZEOF_PID_T == SIZEOF_SHORT)
38#define PID_T_FORMAT "%d"
39#elif (SIZEOF_PID_T == SIZEOF_LONG)
40#define PID_T_FORMAT "%ld"
41#elif (SIZEOF_PID_T == 8)
42#define PID_T_FORMAT "%"PRId64
43#else
44#error Unknown: SIZEOF_PID_T
45#endif /* (0 == SIZEOF_PID_T) && defined(_WIN32) || ... */
46
47/* Define to 1 if process-termination monitors on this OS and Libevent
48 version must poll for process termination themselves. */
49#define PROCMON_POLLS 1
50/* Currently we need to poll in some way on all systems. */
51
52#ifdef PROCMON_POLLS
54 void *procmon_);
55#endif
56
57/* This struct may contain pointers into the original process
58 * specifier string, but it should *never* contain anything which
59 * needs to be freed. */
60/* DOCDOC parsed_process_specifier_t */
62 pid_t pid;
63};
64
65/** Parse the process specifier given in <b>process_spec</b> into
66 * *<b>ppspec</b>. Return 0 on success; return -1 and store an error
67 * message into *<b>msg</b> on failure. The caller must not free the
68 * returned error message. */
69static int
70parse_process_specifier(const char *process_spec,
71 struct parsed_process_specifier_t *ppspec,
72 const char **msg)
73{
74 long pid_l;
75 int pid_ok = 0;
76 char *pspec_next;
77
78 /* If we're lucky, long will turn out to be large enough to hold a
79 * PID everywhere that Tor runs. */
80 pid_l = tor_parse_long(process_spec, 10, 1, LONG_MAX, &pid_ok, &pspec_next);
81
82 /* Reserve room in the ‘process specifier’ for additional
83 * (platform-specific) identifying information beyond the PID, to
84 * make our process-existence checks a bit less racy in a future
85 * version. */
86 if ((*pspec_next != 0) && (*pspec_next != ' ') && (*pspec_next != ':')) {
87 pid_ok = 0;
88 }
89
90 ppspec->pid = (pid_t)(pid_l);
91 if (!pid_ok || (pid_l != (long)(ppspec->pid))) {
92 *msg = "invalid PID";
93 goto err;
94 }
95
96 return 0;
97 err:
98 return -1;
99}
100
101/* DOCDOC tor_process_monitor_t */
103 /** Log domain for warning messages. */
105
106 /** All systems: The best we can do in general is poll for the
107 * process's existence by PID periodically, and hope that the kernel
108 * doesn't reassign the same PID to another process between our
109 * polls. */
110 pid_t pid;
111
112#ifdef _WIN32
113 /** Windows-only: Should we poll hproc? If false, poll pid
114 * instead. */
115 int poll_hproc;
116
117 /** Windows-only: Get a handle to the process (if possible) and
118 * periodically check whether the process we have a handle to has
119 * ended. */
120 HANDLE hproc;
121 /* XXXX We should have Libevent watch hproc for us,
122 * if/when some version of Libevent can be told to do so. */
123#endif /* defined(_WIN32) */
124
125 /* XXXX On Linux, we can and should receive the 22nd
126 * (space-delimited) field (‘starttime’) of /proc/$PID/stat from the
127 * owning controller and store it, and poll once in a while to see
128 * whether it has changed -- if so, the kernel has *definitely*
129 * reassigned the owning controller's PID and we should exit. On
130 * FreeBSD, we can do the same trick using either the 8th
131 * space-delimited field of /proc/$PID/status on the seven FBSD
132 * systems whose admins have mounted procfs, or the start-time field
133 * of the process-information structure returned by kvmgetprocs() on
134 * any system. The latter is ickier. */
135
136 /* XXXX On FreeBSD (and possibly other kqueue systems), we can and
137 * should arrange to receive EVFILT_PROC NOTE_EXIT notifications for
138 * pid, so we don't have to do such a heavyweight poll operation in
139 * order to avoid the PID-reassignment race condition. (We would
140 * still need to poll our own kqueue periodically until some version
141 * of Libevent 2.x learns to receive these events for us.) */
142
143 /** A Libevent event structure, to either poll for the process's
144 * existence or receive a notification when the process ends. */
146
147 /** A callback to be called when the process ends. */
148 tor_procmon_callback_t cb;
149 void *cb_arg; /**< A user-specified pointer to be passed to cb. */
150};
151
152/** Verify that the process specifier given in <b>process_spec</b> is
153 * syntactically valid. Return 0 on success; return -1 and store an
154 * error message into *<b>msg</b> on failure. The caller must not
155 * free the returned error message. */
156int
157tor_validate_process_specifier(const char *process_spec,
158 const char **msg)
159{
160 struct parsed_process_specifier_t ppspec;
161
162 tor_assert(msg != NULL);
163 *msg = NULL;
164
165 return parse_process_specifier(process_spec, &ppspec, msg);
166}
167
168/* We check this often for presence of owning controller process. */
169static const struct timeval poll_interval_tv = {15, 0}; // 15 seconds.
170
171/** Create a process-termination monitor for the process specifier
172 * given in <b>process_spec</b>. Return a newly allocated
173 * tor_process_monitor_t on success; return NULL and store an error
174 * message into *<b>msg</b> on failure. The caller must not free
175 * the returned error message.
176 *
177 * When the monitored process terminates, call
178 * <b>cb</b>(<b>cb_arg</b>).
179 */
181tor_process_monitor_new(struct event_base *base,
182 const char *process_spec,
183 log_domain_mask_t log_domain,
184 tor_procmon_callback_t cb, void *cb_arg,
185 const char **msg)
186{
187 tor_process_monitor_t *procmon = tor_malloc_zero(
188 sizeof(tor_process_monitor_t));
189 struct parsed_process_specifier_t ppspec;
190
191 tor_assert(msg != NULL);
192 *msg = NULL;
193
194 if (procmon == NULL) {
195 *msg = "out of memory";
196 goto err;
197 }
198
199 procmon->log_domain = log_domain;
200
201 if (parse_process_specifier(process_spec, &ppspec, msg))
202 goto err;
203
204 procmon->pid = ppspec.pid;
205
206#ifdef _WIN32
207 procmon->hproc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
208 FALSE,
209 procmon->pid);
210
211 if (procmon->hproc != NULL) {
212 procmon->poll_hproc = 1;
213 log_info(procmon->log_domain, "Successfully opened handle to process "
214 PID_T_FORMAT"; "
215 "monitoring it.",
216 procmon->pid);
217 } else {
218 /* If we couldn't get a handle to the process, we'll try again the
219 * first time we poll. */
220 log_info(procmon->log_domain, "Failed to open handle to process "
221 PID_T_FORMAT"; will "
222 "try again later.",
223 procmon->pid);
224 }
225#endif /* defined(_WIN32) */
226
227 procmon->cb = cb;
228 procmon->cb_arg = cb_arg;
229
230#ifdef PROCMON_POLLS
231 procmon->e = periodic_timer_new(base,
232 &poll_interval_tv,
234#else /* !(defined(PROCMON_POLLS)) */
235#error OOPS?
236#endif /* defined(PROCMON_POLLS) */
237
238 return procmon;
239 err:
240 tor_process_monitor_free(procmon);
241 return NULL;
242}
243
244#ifdef PROCMON_POLLS
245/** Libevent callback to poll for the existence of the process
246 * monitored by <b>procmon_</b>. */
247static void
249{
250 (void)event;
251 tor_process_monitor_t *procmon = (tor_process_monitor_t *)(procmon_);
252 int its_dead_jim;
253
254 tor_assert(procmon != NULL);
255
256#ifdef _WIN32
257 if (procmon->poll_hproc) {
258 DWORD exit_code;
259 if (!GetExitCodeProcess(procmon->hproc, &exit_code)) {
260 char *errmsg = format_win32_error(GetLastError());
261 log_warn(procmon->log_domain, "Error \"%s\" occurred while polling "
262 "handle for monitored process "PID_T_FORMAT"; assuming "
263 "it's dead.",
264 errmsg, procmon->pid);
265 tor_free(errmsg);
266 its_dead_jim = 1;
267 } else {
268 its_dead_jim = (exit_code != STILL_ACTIVE);
269 }
270 } else {
271 /* All we can do is try to open the process, and look at the error
272 * code if it fails again. */
273 procmon->hproc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
274 FALSE,
275 procmon->pid);
276
277 if (procmon->hproc != NULL) {
278 log_info(procmon->log_domain, "Successfully opened handle to monitored "
279 "process "PID_T_FORMAT".",
280 procmon->pid);
281 its_dead_jim = 0;
282 procmon->poll_hproc = 1;
283 } else {
284 DWORD err_code = GetLastError();
285 char *errmsg = format_win32_error(err_code);
286
287 /* When I tested OpenProcess's error codes on Windows 7, I
288 * received error code 5 (ERROR_ACCESS_DENIED) for PIDs of
289 * existing processes that I could not open and error code 87
290 * (ERROR_INVALID_PARAMETER) for PIDs that were not in use.
291 * Since the nonexistent-process error code is sane, I'm going
292 * to assume that all errors other than ERROR_INVALID_PARAMETER
293 * mean that the process we are monitoring is still alive. */
294 its_dead_jim = (err_code == ERROR_INVALID_PARAMETER);
295
296 if (!its_dead_jim)
297 log_info(procmon->log_domain, "Failed to open handle to monitored "
298 "process "PID_T_FORMAT", and error code %lu (%s) is not "
299 "'invalid parameter' -- assuming the process is still alive.",
300 procmon->pid,
301 err_code, errmsg);
302
303 tor_free(errmsg);
304 }
305 }
306#else /* !defined(_WIN32) */
307 /* Unix makes this part easy, if a bit racy. */
308 its_dead_jim = kill(procmon->pid, 0);
309 its_dead_jim = its_dead_jim && (errno == ESRCH);
310#endif /* defined(_WIN32) */
311
312 tor_log(its_dead_jim ? LOG_NOTICE : LOG_INFO,
313 procmon->log_domain, "Monitored process "PID_T_FORMAT" is %s.",
314 procmon->pid,
315 its_dead_jim ? "dead" : "still alive");
316
317 if (its_dead_jim) {
318 procmon->cb(procmon->cb_arg);
319 }
320}
321#endif /* defined(PROCMON_POLLS) */
322
323/** Free the process-termination monitor <b>procmon</b>. */
324void
326{
327 if (procmon == NULL)
328 return;
329
330#ifdef _WIN32
331 if (procmon->hproc != NULL)
332 CloseHandle(procmon->hproc);
333#endif
334
335 if (procmon->e != NULL)
336 periodic_timer_free(procmon->e);
337
338 tor_free(procmon);
339}
periodic_timer_t * periodic_timer_new(struct event_base *base, const struct timeval *tv, void(*cb)(periodic_timer_t *timer, void *data), void *data)
void tor_log(int severity, log_domain_mask_t domain, const char *format,...)
Definition: log.c:591
Headers for log.c.
#define LOG_NOTICE
Definition: log.h:50
#define LOG_INFO
Definition: log.h:45
uint64_t log_domain_mask_t
Definition: logging_types.h:21
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:56
long tor_parse_long(const char *s, int base, long min, long max, int *ok, char **next)
Definition: parse_int.c:59
Header for parse_int.c.
int tor_validate_process_specifier(const char *process_spec, const char **msg)
Definition: procmon.c:157
tor_process_monitor_t * tor_process_monitor_new(struct event_base *base, const char *process_spec, log_domain_mask_t log_domain, tor_procmon_callback_t cb, void *cb_arg, const char **msg)
Definition: procmon.c:181
static int parse_process_specifier(const char *process_spec, struct parsed_process_specifier_t *ppspec, const char **msg)
Definition: procmon.c:70
void tor_process_monitor_free_(tor_process_monitor_t *procmon)
Definition: procmon.c:325
static void tor_process_monitor_poll_cb(periodic_timer_t *ev, void *procmon_)
Definition: procmon.c:248
Headers for procmon.c.
periodic_timer_t * e
Definition: procmon.c:145
tor_procmon_callback_t cb
Definition: procmon.c:148
log_domain_mask_t log_domain
Definition: procmon.c:104
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:103
Header for win32err.c.