1 /* Copyright (c) 2011-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
6 * \brief Process-termination monitor functions
9 #include "lib/evloop/procmon.h"
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"
23 #ifdef HAVE_SYS_TIME_H
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. */
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
44 #error Unknown: SIZEOF_PID_T
45 #endif /* (0 == SIZEOF_PID_T) && defined(_WIN32) || ... */
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. */
53 static void tor_process_monitor_poll_cb(periodic_timer_t
*ev
,
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
{
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. */
70 parse_process_specifier(const char *process_spec
,
71 struct parsed_process_specifier_t
*ppspec
,
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
);
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
86 if ((*pspec_next
!= 0) && (*pspec_next
!= ' ') && (*pspec_next
!= ':')) {
90 ppspec
->pid
= (pid_t
)(pid_l
);
91 if (!pid_ok
|| (pid_l
!= (long)(ppspec
->pid
))) {
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
;
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
113 /** Windows-only: Should we poll hproc? If false, poll pid
117 /** Windows-only: Get a handle to the process (if possible) and
118 * periodically check whether the process we have a handle to has
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) */
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. */
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.) */
143 /** A Libevent event structure, to either poll for the process's
144 * existence or receive a notification when the process ends. */
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. */
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. */
157 tor_validate_process_specifier(const char *process_spec
,
160 struct parsed_process_specifier_t ppspec
;
162 tor_assert(msg
!= NULL
);
165 return parse_process_specifier(process_spec
, &ppspec
, msg
);
168 /* We check this often for presence of owning controller process. */
169 static const struct timeval poll_interval_tv
= {15, 0}; // 15 seconds.
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.
177 * When the monitored process terminates, call
178 * <b>cb</b>(<b>cb_arg</b>).
180 tor_process_monitor_t
*
181 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
,
187 tor_process_monitor_t
*procmon
= tor_malloc_zero(
188 sizeof(tor_process_monitor_t
));
189 struct parsed_process_specifier_t ppspec
;
191 tor_assert(msg
!= NULL
);
194 if (procmon
== NULL
) {
195 *msg
= "out of memory";
199 procmon
->log_domain
= log_domain
;
201 if (parse_process_specifier(process_spec
, &ppspec
, msg
))
204 procmon
->pid
= ppspec
.pid
;
207 procmon
->hproc
= OpenProcess(PROCESS_QUERY_INFORMATION
| SYNCHRONIZE
,
211 if (procmon
->hproc
!= NULL
) {
212 procmon
->poll_hproc
= 1;
213 log_info(procmon
->log_domain
, "Successfully opened handle to process "
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 "
225 #endif /* defined(_WIN32) */
228 procmon
->cb_arg
= cb_arg
;
231 procmon
->e
= periodic_timer_new(base
,
233 tor_process_monitor_poll_cb
, procmon
);
234 #else /* !(defined(PROCMON_POLLS)) */
236 #endif /* defined(PROCMON_POLLS) */
240 tor_process_monitor_free(procmon
);
245 /** Libevent callback to poll for the existence of the process
246 * monitored by <b>procmon_</b>. */
248 tor_process_monitor_poll_cb(periodic_timer_t
*event
, void *procmon_
)
251 tor_process_monitor_t
*procmon
= (tor_process_monitor_t
*)(procmon_
);
254 tor_assert(procmon
!= NULL
);
257 if (procmon
->poll_hproc
) {
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 "
264 errmsg
, procmon
->pid
);
268 its_dead_jim
= (exit_code
!= STILL_ACTIVE
);
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
,
277 if (procmon
->hproc
!= NULL
) {
278 log_info(procmon
->log_domain
, "Successfully opened handle to monitored "
279 "process "PID_T_FORMAT
".",
282 procmon
->poll_hproc
= 1;
284 DWORD err_code
= GetLastError();
285 char *errmsg
= format_win32_error(err_code
);
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
);
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.",
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) */
312 tor_log(its_dead_jim
? LOG_NOTICE
: LOG_INFO
,
313 procmon
->log_domain
, "Monitored process "PID_T_FORMAT
" is %s.",
315 its_dead_jim
? "dead" : "still alive");
318 procmon
->cb(procmon
->cb_arg
);
321 #endif /* defined(PROCMON_POLLS) */
323 /** Free the process-termination monitor <b>procmon</b>. */
325 tor_process_monitor_free_(tor_process_monitor_t
*procmon
)
331 if (procmon
->hproc
!= NULL
)
332 CloseHandle(procmon
->hproc
);
335 if (procmon
->e
!= NULL
)
336 periodic_timer_free(procmon
->e
);