Back to index

tor  0.2.3.18-rc
procmon.c
Go to the documentation of this file.
00001 /* Copyright (c) 2011-2012, The Tor Project, Inc. */
00002 /* See LICENSE for licensing information */
00003 
00009 #include "procmon.h"
00010 
00011 #include "util.h"
00012 
00013 #ifdef HAVE_EVENT2_EVENT_H
00014 #include <event2/event.h>
00015 #else
00016 #include <event.h>
00017 #endif
00018 
00019 #ifdef HAVE_SIGNAL_H
00020 #include <signal.h>
00021 #endif
00022 #ifdef HAVE_ERRNO_H
00023 #include <errno.h>
00024 #endif
00025 
00026 #ifdef _WIN32
00027 #include <windows.h>
00028 
00029 /* Windows does not define pid_t, but _getpid() returns an int. */
00030 typedef int pid_t;
00031 #endif
00032 
00033 /* Define to 1 if process-termination monitors on this OS and Libevent
00034    version must poll for process termination themselves. */
00035 #define PROCMON_POLLS 1
00036 /* Currently we need to poll in some way on all systems. */
00037 
00038 #ifdef PROCMON_POLLS
00039 static void tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
00040                                         void *procmon_);
00041 #endif
00042 
00043 /* This struct may contain pointers into the original process
00044  * specifier string, but it should *never* contain anything which
00045  * needs to be freed. */
00046 /* DOCDOC parsed_process_specifier_t */
00047 struct parsed_process_specifier_t {
00048   pid_t pid;
00049 };
00050 
00055 static int
00056 parse_process_specifier(const char *process_spec,
00057                         struct parsed_process_specifier_t *ppspec,
00058                         const char **msg)
00059 {
00060   long pid_l;
00061   int pid_ok = 0;
00062   char *pspec_next;
00063 
00064   /* If we're lucky, long will turn out to be large enough to hold a
00065    * PID everywhere that Tor runs. */
00066   pid_l = tor_parse_long(process_spec, 0, 1, LONG_MAX, &pid_ok, &pspec_next);
00067 
00068   /* Reserve room in the ‘process specifier’ for additional
00069    * (platform-specific) identifying information beyond the PID, to
00070    * make our process-existence checks a bit less racy in a future
00071    * version. */
00072   if ((*pspec_next != 0) && (*pspec_next != ' ') && (*pspec_next != ':')) {
00073     pid_ok = 0;
00074   }
00075 
00076   ppspec->pid = (pid_t)(pid_l);
00077   if (!pid_ok || (pid_l != (long)(ppspec->pid))) {
00078     *msg = "invalid PID";
00079     goto err;
00080   }
00081 
00082   return 0;
00083  err:
00084   return -1;
00085 }
00086 
00087 /* DOCDOC tor_process_monitor_t */
00088 struct tor_process_monitor_t {
00090   log_domain_mask_t log_domain;
00091 
00096   pid_t pid;
00097 
00098 #ifdef _WIN32
00099 
00101   int poll_hproc;
00102 
00106   HANDLE hproc;
00107   /* XXX023 We can and should have Libevent watch hproc for us,
00108    * if/when some version of Libevent 2.x can be told to do so. */
00109 #endif
00110 
00111   /* XXX023 On Linux, we can and should receive the 22nd
00112    * (space-delimited) field (‘starttime’) of /proc/$PID/stat from the
00113    * owning controller and store it, and poll once in a while to see
00114    * whether it has changed -- if so, the kernel has *definitely*
00115    * reassigned the owning controller's PID and we should exit.  On
00116    * FreeBSD, we can do the same trick using either the 8th
00117    * space-delimited field of /proc/$PID/status on the seven FBSD
00118    * systems whose admins have mounted procfs, or the start-time field
00119    * of the process-information structure returned by kvmgetprocs() on
00120    * any system.  The latter is ickier. */
00121   /* XXX023 On FreeBSD (and possibly other kqueue systems), we can and
00122    * should arrange to receive EVFILT_PROC NOTE_EXIT notifications for
00123    * pid, so we don't have to do such a heavyweight poll operation in
00124    * order to avoid the PID-reassignment race condition.  (We would
00125    * still need to poll our own kqueue periodically until some version
00126    * of Libevent 2.x learns to receive these events for us.) */
00127 
00130   struct event *e;
00131 
00133   tor_procmon_callback_t cb;
00134   void *cb_arg; 
00135 };
00136 
00141 int
00142 tor_validate_process_specifier(const char *process_spec,
00143                                const char **msg)
00144 {
00145   struct parsed_process_specifier_t ppspec;
00146 
00147   tor_assert(msg != NULL);
00148   *msg = NULL;
00149 
00150   return parse_process_specifier(process_spec, &ppspec, msg);
00151 }
00152 
00153 #ifdef HAVE_EVENT2_EVENT_H
00154 #define PERIODIC_TIMER_FLAGS EV_PERSIST
00155 #else
00156 #define PERIODIC_TIMER_FLAGS (0)
00157 #endif
00158 
00159 /* DOCDOC poll_interval_tv */
00160 static struct timeval poll_interval_tv = {15, 0};
00161 /* Note: If you port this file to plain Libevent 2, you can make
00162  * poll_interval_tv const.  It has to be non-const here because in
00163  * libevent 1.x, event_add expects a pointer to a non-const struct
00164  * timeval. */
00165 
00175 tor_process_monitor_t *
00176 tor_process_monitor_new(struct event_base *base,
00177                         const char *process_spec,
00178                         log_domain_mask_t log_domain,
00179                         tor_procmon_callback_t cb, void *cb_arg,
00180                         const char **msg)
00181 {
00182   tor_process_monitor_t *procmon = tor_malloc(sizeof(tor_process_monitor_t));
00183   struct parsed_process_specifier_t ppspec;
00184 
00185   tor_assert(msg != NULL);
00186   *msg = NULL;
00187 
00188   if (procmon == NULL) {
00189     *msg = "out of memory";
00190     goto err;
00191   }
00192 
00193   procmon->log_domain = log_domain;
00194 
00195   if (parse_process_specifier(process_spec, &ppspec, msg))
00196     goto err;
00197 
00198   procmon->pid = ppspec.pid;
00199 
00200 #ifdef _WIN32
00201   procmon->hproc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
00202                                FALSE,
00203                                procmon->pid);
00204 
00205   if (procmon->hproc != NULL) {
00206     procmon->poll_hproc = 1;
00207     log_info(procmon->log_domain, "Successfully opened handle to process %d; "
00208              "monitoring it.",
00209              (int)(procmon->pid));
00210   } else {
00211     /* If we couldn't get a handle to the process, we'll try again the
00212      * first time we poll. */
00213     log_info(procmon->log_domain, "Failed to open handle to process %d; will "
00214              "try again later.",
00215              (int)(procmon->pid));
00216   }
00217 #endif
00218 
00219   procmon->cb = cb;
00220   procmon->cb_arg = cb_arg;
00221 
00222 #ifdef PROCMON_POLLS
00223   procmon->e = tor_event_new(base, -1 /* no FD */, PERIODIC_TIMER_FLAGS,
00224                              tor_process_monitor_poll_cb, procmon);
00225   /* Note: If you port this file to plain Libevent 2, check that
00226    * procmon->e is non-NULL.  We don't need to here because
00227    * tor_evtimer_new never returns NULL. */
00228 
00229   evtimer_add(procmon->e, &poll_interval_tv);
00230 #else
00231 #error OOPS?
00232 #endif
00233 
00234   return procmon;
00235  err:
00236   tor_process_monitor_free(procmon);
00237   return NULL;
00238 }
00239 
00240 #ifdef PROCMON_POLLS
00241 
00243 static void
00244 tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
00245                             void *procmon_)
00246 {
00247   tor_process_monitor_t *procmon = (tor_process_monitor_t *)(procmon_);
00248   int its_dead_jim;
00249 
00250   (void)unused1; (void)unused2;
00251 
00252   tor_assert(procmon != NULL);
00253 
00254 #ifdef _WIN32
00255   if (procmon->poll_hproc) {
00256     DWORD exit_code;
00257     if (!GetExitCodeProcess(procmon->hproc, &exit_code)) {
00258       char *errmsg = format_win32_error(GetLastError());
00259       log_warn(procmon->log_domain, "Error \"%s\" occurred while polling "
00260                "handle for monitored process %d; assuming it's dead.",
00261                errmsg, procmon->pid);
00262       tor_free(errmsg);
00263       its_dead_jim = 1;
00264     } else {
00265       its_dead_jim = (exit_code != STILL_ACTIVE);
00266     }
00267   } else {
00268     /* All we can do is try to open the process, and look at the error
00269      * code if it fails again. */
00270     procmon->hproc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
00271                                  FALSE,
00272                                  procmon->pid);
00273 
00274     if (procmon->hproc != NULL) {
00275       log_info(procmon->log_domain, "Successfully opened handle to monitored "
00276                "process %d.",
00277                procmon->pid);
00278       its_dead_jim = 0;
00279       procmon->poll_hproc = 1;
00280     } else {
00281       DWORD err_code = GetLastError();
00282       char *errmsg = format_win32_error(err_code);
00283 
00284       /* When I tested OpenProcess's error codes on Windows 7, I
00285        * received error code 5 (ERROR_ACCESS_DENIED) for PIDs of
00286        * existing processes that I could not open and error code 87
00287        * (ERROR_INVALID_PARAMETER) for PIDs that were not in use.
00288        * Since the nonexistent-process error code is sane, I'm going
00289        * to assume that all errors other than ERROR_INVALID_PARAMETER
00290        * mean that the process we are monitoring is still alive. */
00291       its_dead_jim = (err_code == ERROR_INVALID_PARAMETER);
00292 
00293       if (!its_dead_jim)
00294         log_info(procmon->log_domain, "Failed to open handle to monitored "
00295                  "process %d, and error code %lu (%s) is not 'invalid "
00296                  "parameter' -- assuming the process is still alive.",
00297                  procmon->pid,
00298                  err_code, errmsg);
00299 
00300       tor_free(errmsg);
00301     }
00302   }
00303 #else
00304   /* Unix makes this part easy, if a bit racy. */
00305   its_dead_jim = kill(procmon->pid, 0);
00306   its_dead_jim = its_dead_jim && (errno == ESRCH);
00307 #endif
00308 
00309   log(its_dead_jim ? LOG_NOTICE : LOG_INFO,
00310       procmon->log_domain, "Monitored process %d is %s.",
00311       (int)procmon->pid,
00312       its_dead_jim ? "dead" : "still alive");
00313 
00314   if (its_dead_jim) {
00315     procmon->cb(procmon->cb_arg);
00316 #ifndef HAVE_EVENT2_EVENT_H
00317   } else {
00318     evtimer_add(procmon->e, &poll_interval_tv);
00319 #endif
00320   }
00321 }
00322 #endif
00323 
00325 void
00326 tor_process_monitor_free(tor_process_monitor_t *procmon)
00327 {
00328   if (procmon == NULL)
00329     return;
00330 
00331 #ifdef _WIN32
00332   if (procmon->hproc != NULL)
00333     CloseHandle(procmon->hproc);
00334 #endif
00335 
00336   if (procmon->e != NULL)
00337     tor_event_free(procmon->e);
00338 
00339   tor_free(procmon);
00340 }
00341