LCOV - code coverage report
Current view: top level - lib/evloop - procmon.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 40 53 75.5 %
Date: 2021-11-24 03:28:48 Functions: 4 5 80.0 %

          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             : }

Generated by: LCOV version 1.14