tor  0.4.2.0-alpha-dev
procmon.c
Go to the documentation of this file.
1 /* Copyright (c) 2011-2019, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 
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
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 
69 static int
70 parse_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 */
105 
110  pid_t pid;
111 
112 #ifdef _WIN32
113 
115  int poll_hproc;
116 
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 
146 
148  tor_procmon_callback_t cb;
149  void *cb_arg;
150 };
151 
156 int
157 tor_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 /* DOCDOC poll_interval_tv */
169 static const struct timeval poll_interval_tv = {15, 0};
170 
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,
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,
233  tor_process_monitor_poll_cb, procmon);
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 
247 static 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 
324 void
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 }
uint32_t log_domain_mask_t
Definition: log.h:134
Header for win32err.c.
#define LOG_INFO
Definition: log.h:42
Headers for procmon.c.
periodic_timer_t * e
Definition: procmon.c:145
void tor_log(int severity, log_domain_mask_t domain, const char *format,...)
Definition: log.c:632
#define tor_free(p)
Definition: malloc.h:52
#define LOG_NOTICE
Definition: log.h:47
Headers for util_malloc.c.
static void tor_process_monitor_poll_cb(periodic_timer_t *ev, void *procmon_)
Definition: procmon.c:248
static int parse_process_specifier(const char *process_spec, struct parsed_process_specifier_t *ppspec, const char **msg)
Definition: procmon.c:70
tor_assert(buffer)
tor_procmon_callback_t cb
Definition: procmon.c:148
periodic_timer_t * periodic_timer_new(struct event_base *base, const struct timeval *tv, void(*cb)(periodic_timer_t *timer, void *data), void *data)
int tor_validate_process_specifier(const char *process_spec, const char **msg)
Definition: procmon.c:157
log_domain_mask_t log_domain
Definition: procmon.c:104
long tor_parse_long(const char *s, int base, long min, long max, int *ok, char **next)
Definition: parse_int.c:56
void tor_process_monitor_free_(tor_process_monitor_t *procmon)
Definition: procmon.c:325
Headers for log.c.
Macros to manage assertions, fatal and non-fatal.
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
Header for parse_int.c.