Back to index

tor  0.2.3.19-rc
Classes | Defines | Functions | Variables
procmon.c File Reference

Process-termination monitor functions. More...

#include "procmon.h"
#include "util.h"
#include <event.h>

Go to the source code of this file.

Classes

struct  parsed_process_specifier_t
struct  tor_process_monitor_t

Defines

#define PROCMON_POLLS   1
#define PERIODIC_TIMER_FLAGS   (0)

Functions

static void tor_process_monitor_poll_cb (evutil_socket_t unused1, short unused2, void *procmon_)
 Libevent callback to poll for the existence of the process monitored by procmon_.
static int parse_process_specifier (const char *process_spec, struct parsed_process_specifier_t *ppspec, const char **msg)
 Parse the process specifier given in process_spec into *ppspec.
int tor_validate_process_specifier (const char *process_spec, const char **msg)
 Verify that the process specifier given in process_spec is syntactically valid.
tor_process_monitor_ttor_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)
 Create a process-termination monitor for the process specifier given in process_spec.
void tor_process_monitor_free (tor_process_monitor_t *procmon)
 Free the process-termination monitor procmon.

Variables

static struct timeval = {15, 0}

Detailed Description

Process-termination monitor functions.

Definition in file procmon.c.


Class Documentation

struct parsed_process_specifier_t

Definition at line 47 of file procmon.c.

Class Members
pid_t pid
struct tor_process_monitor_t

Definition at line 88 of file procmon.c.

Class Members
tor_procmon_callback_t cb A callback to be called when the process ends.
void * cb_arg A user-specified pointer to be passed to cb.
struct event * e A Libevent event structure, to either poll for the process's existence or receive a notification when the process ends.
log_domain_mask_t log_domain Log domain for warning messages.
pid_t pid All systems: The best we can do in general is poll for the process's existence by PID periodically, and hope that the kernel doesn't reassign the same PID to another process between our polls.

Define Documentation

#define PERIODIC_TIMER_FLAGS   (0)

Definition at line 156 of file procmon.c.

#define PROCMON_POLLS   1

Definition at line 35 of file procmon.c.


Function Documentation

static int parse_process_specifier ( const char *  process_spec,
struct parsed_process_specifier_t ppspec,
const char **  msg 
) [static]

Parse the process specifier given in process_spec into *ppspec.

Return 0 on success; return -1 and store an error message into *msg on failure. The caller must not free the returned error message.

Definition at line 56 of file procmon.c.

{
  long pid_l;
  int pid_ok = 0;
  char *pspec_next;

  /* If we're lucky, long will turn out to be large enough to hold a
   * PID everywhere that Tor runs. */
  pid_l = tor_parse_long(process_spec, 0, 1, LONG_MAX, &pid_ok, &pspec_next);

  /* Reserve room in the ‘process specifier’ for additional
   * (platform-specific) identifying information beyond the PID, to
   * make our process-existence checks a bit less racy in a future
   * version. */
  if ((*pspec_next != 0) && (*pspec_next != ' ') && (*pspec_next != ':')) {
    pid_ok = 0;
  }

  ppspec->pid = (pid_t)(pid_l);
  if (!pid_ok || (pid_l != (long)(ppspec->pid))) {
    *msg = "invalid PID";
    goto err;
  }

  return 0;
 err:
  return -1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Free the process-termination monitor procmon.

Definition at line 326 of file procmon.c.

{
  if (procmon == NULL)
    return;

#ifdef _WIN32
  if (procmon->hproc != NULL)
    CloseHandle(procmon->hproc);
#endif

  if (procmon->e != NULL)
    tor_event_free(procmon->e);

  tor_free(procmon);
}

Here is the call graph for this function:

Here is the caller graph for this function:

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 
)

Create a process-termination monitor for the process specifier given in process_spec.

Return a newly allocated tor_process_monitor_t on success; return NULL and store an error message into *msg on failure. The caller must not free the returned error message.

When the monitored process terminates, call cb(cb_arg).

Definition at line 176 of file procmon.c.

{
  tor_process_monitor_t *procmon = tor_malloc(sizeof(tor_process_monitor_t));
  struct parsed_process_specifier_t ppspec;

  tor_assert(msg != NULL);
  *msg = NULL;

  if (procmon == NULL) {
    *msg = "out of memory";
    goto err;
  }

  procmon->log_domain = log_domain;

  if (parse_process_specifier(process_spec, &ppspec, msg))
    goto err;

  procmon->pid = ppspec.pid;

#ifdef _WIN32
  procmon->hproc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
                               FALSE,
                               procmon->pid);

  if (procmon->hproc != NULL) {
    procmon->poll_hproc = 1;
    log_info(procmon->log_domain, "Successfully opened handle to process %d; "
             "monitoring it.",
             (int)(procmon->pid));
  } else {
    /* If we couldn't get a handle to the process, we'll try again the
     * first time we poll. */
    log_info(procmon->log_domain, "Failed to open handle to process %d; will "
             "try again later.",
             (int)(procmon->pid));
  }
#endif

  procmon->cb = cb;
  procmon->cb_arg = cb_arg;

#ifdef PROCMON_POLLS
  procmon->e = tor_event_new(base, -1 /* no FD */, PERIODIC_TIMER_FLAGS,
                             tor_process_monitor_poll_cb, procmon);
  /* Note: If you port this file to plain Libevent 2, check that
   * procmon->e is non-NULL.  We don't need to here because
   * tor_evtimer_new never returns NULL. */

  evtimer_add(procmon->e, &poll_interval_tv);
#else
#error OOPS?
#endif

  return procmon;
 err:
  tor_process_monitor_free(procmon);
  return NULL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void tor_process_monitor_poll_cb ( evutil_socket_t  unused1,
short  unused2,
void *  procmon_ 
) [static]

Libevent callback to poll for the existence of the process monitored by procmon_.

Definition at line 244 of file procmon.c.

{
  tor_process_monitor_t *procmon = (tor_process_monitor_t *)(procmon_);
  int its_dead_jim;

  (void)unused1; (void)unused2;

  tor_assert(procmon != NULL);

#ifdef _WIN32
  if (procmon->poll_hproc) {
    DWORD exit_code;
    if (!GetExitCodeProcess(procmon->hproc, &exit_code)) {
      char *errmsg = format_win32_error(GetLastError());
      log_warn(procmon->log_domain, "Error \"%s\" occurred while polling "
               "handle for monitored process %d; assuming it's dead.",
               errmsg, procmon->pid);
      tor_free(errmsg);
      its_dead_jim = 1;
    } else {
      its_dead_jim = (exit_code != STILL_ACTIVE);
    }
  } else {
    /* All we can do is try to open the process, and look at the error
     * code if it fails again. */
    procmon->hproc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
                                 FALSE,
                                 procmon->pid);

    if (procmon->hproc != NULL) {
      log_info(procmon->log_domain, "Successfully opened handle to monitored "
               "process %d.",
               procmon->pid);
      its_dead_jim = 0;
      procmon->poll_hproc = 1;
    } else {
      DWORD err_code = GetLastError();
      char *errmsg = format_win32_error(err_code);

      /* When I tested OpenProcess's error codes on Windows 7, I
       * received error code 5 (ERROR_ACCESS_DENIED) for PIDs of
       * existing processes that I could not open and error code 87
       * (ERROR_INVALID_PARAMETER) for PIDs that were not in use.
       * Since the nonexistent-process error code is sane, I'm going
       * to assume that all errors other than ERROR_INVALID_PARAMETER
       * mean that the process we are monitoring is still alive. */
      its_dead_jim = (err_code == ERROR_INVALID_PARAMETER);

      if (!its_dead_jim)
        log_info(procmon->log_domain, "Failed to open handle to monitored "
                 "process %d, and error code %lu (%s) is not 'invalid "
                 "parameter' -- assuming the process is still alive.",
                 procmon->pid,
                 err_code, errmsg);

      tor_free(errmsg);
    }
  }
#else
  /* Unix makes this part easy, if a bit racy. */
  its_dead_jim = kill(procmon->pid, 0);
  its_dead_jim = its_dead_jim && (errno == ESRCH);
#endif

  log(its_dead_jim ? LOG_NOTICE : LOG_INFO,
      procmon->log_domain, "Monitored process %d is %s.",
      (int)procmon->pid,
      its_dead_jim ? "dead" : "still alive");

  if (its_dead_jim) {
    procmon->cb(procmon->cb_arg);
#ifndef HAVE_EVENT2_EVENT_H
  } else {
    evtimer_add(procmon->e, &poll_interval_tv);
#endif
  }
}

Here is the caller graph for this function:

int tor_validate_process_specifier ( const char *  process_spec,
const char **  msg 
)

Verify that the process specifier given in process_spec is syntactically valid.

Return 0 on success; return -1 and store an error message into *msg on failure. The caller must not free the returned error message.

Definition at line 142 of file procmon.c.

{
  struct parsed_process_specifier_t ppspec;

  tor_assert(msg != NULL);
  *msg = NULL;

  return parse_process_specifier(process_spec, &ppspec, msg);
}

Here is the call graph for this function:


Variable Documentation

struct timeval = {15, 0} [static]

Definition at line 160 of file procmon.c.