procmon.c (11021B)
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 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 */ 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 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 /* 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 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 /** Libevent callback to poll for the existence of the process 246 * monitored by <b>procmon_</b>. */ 247 static void 248 tor_process_monitor_poll_cb(periodic_timer_t *event, void *procmon_) 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>. */ 324 void 325 tor_process_monitor_free_(tor_process_monitor_t *procmon) 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 }