Line data Source code
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 :
9 : #include "lib/evloop/procmon.h"
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"
15 : #include "lib/string/parse_int.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. */
35 : typedef 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
53 : static void tor_process_monitor_poll_cb(periodic_timer_t *ev,
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 */
61 : struct 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. */
69 : static int
70 7 : parse_process_specifier(const char *process_spec,
71 : struct parsed_process_specifier_t *ppspec,
72 : const char **msg)
73 : {
74 7 : long pid_l;
75 7 : int pid_ok = 0;
76 7 : 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 7 : 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 7 : if ((*pspec_next != 0) && (*pspec_next != ' ') && (*pspec_next != ':')) {
87 2 : pid_ok = 0;
88 : }
89 :
90 7 : ppspec->pid = (pid_t)(pid_l);
91 7 : if (!pid_ok || (pid_l != (long)(ppspec->pid))) {
92 3 : *msg = "invalid PID";
93 3 : goto err;
94 : }
95 :
96 : return 0;
97 3 : err:
98 3 : return -1;
99 : }
100 :
101 : /* DOCDOC tor_process_monitor_t */
102 : struct tor_process_monitor_t {
103 : /** Log domain for warning messages. */
104 : log_domain_mask_t log_domain;
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. */
145 : periodic_timer_t *e;
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. */
156 : int
157 2 : tor_validate_process_specifier(const char *process_spec,
158 : const char **msg)
159 : {
160 2 : struct parsed_process_specifier_t ppspec;
161 :
162 2 : tor_assert(msg != NULL);
163 2 : *msg = NULL;
164 :
165 2 : return parse_process_specifier(process_spec, &ppspec, msg);
166 : }
167 :
168 : /* We check this often for presence of owning controller process. */
169 : static 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 : */
180 : tor_process_monitor_t *
181 5 : tor_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 5 : tor_process_monitor_t *procmon = tor_malloc_zero(
188 : sizeof(tor_process_monitor_t));
189 5 : struct parsed_process_specifier_t ppspec;
190 :
191 5 : tor_assert(msg != NULL);
192 5 : *msg = NULL;
193 :
194 5 : if (procmon == NULL) {
195 0 : *msg = "out of memory";
196 0 : goto err;
197 : }
198 :
199 5 : procmon->log_domain = log_domain;
200 :
201 5 : if (parse_process_specifier(process_spec, &ppspec, msg))
202 2 : goto err;
203 :
204 3 : 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 3 : procmon->cb = cb;
228 3 : procmon->cb_arg = cb_arg;
229 :
230 : #ifdef PROCMON_POLLS
231 3 : procmon->e = periodic_timer_new(base,
232 : &poll_interval_tv,
233 : tor_process_monitor_poll_cb, procmon);
234 : #else /* !(defined(PROCMON_POLLS)) */
235 : #error OOPS?
236 : #endif /* defined(PROCMON_POLLS) */
237 :
238 3 : return procmon;
239 2 : err:
240 2 : tor_process_monitor_free(procmon);
241 2 : 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>. */
247 : static void
248 0 : tor_process_monitor_poll_cb(periodic_timer_t *event, void *procmon_)
249 : {
250 0 : (void)event;
251 0 : tor_process_monitor_t *procmon = (tor_process_monitor_t *)(procmon_);
252 0 : int its_dead_jim;
253 :
254 0 : 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 0 : its_dead_jim = kill(procmon->pid, 0);
309 0 : its_dead_jim = its_dead_jim && (errno == ESRCH);
310 : #endif /* defined(_WIN32) */
311 :
312 0 : 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 0 : if (its_dead_jim) {
318 0 : procmon->cb(procmon->cb_arg);
319 : }
320 0 : }
321 : #endif /* defined(PROCMON_POLLS) */
322 :
323 : /** Free the process-termination monitor <b>procmon</b>. */
324 : void
325 5 : tor_process_monitor_free_(tor_process_monitor_t *procmon)
326 : {
327 5 : if (procmon == NULL)
328 : return;
329 :
330 : #ifdef _WIN32
331 : if (procmon->hproc != NULL)
332 : CloseHandle(procmon->hproc);
333 : #endif
334 :
335 5 : if (procmon->e != NULL)
336 3 : periodic_timer_free(procmon->e);
337 :
338 5 : tor_free(procmon);
339 : }
|