tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

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 }