mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-25 04:43:31 +01:00
Merge branch 'feature3049-v2' into maint-0.2.2
Conflicts: src/common/Makefile.am
This commit is contained in:
commit
b80a8bba19
5
changes/exit-policy-default-is-not-a-prefix
Normal file
5
changes/exit-policy-default-is-not-a-prefix
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
o Minor bugfixes:
|
||||||
|
- Remove a trailing asterisk from "exit-policy/default" in the
|
||||||
|
output of the control port command "GETINFO info/names". Bugfix
|
||||||
|
on 0.1.2.5-alpha.
|
||||||
|
|
6
changes/feature3049
Normal file
6
changes/feature3049
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
o Major features:
|
||||||
|
- Add an __OwningControllerProcess configuration option and a
|
||||||
|
TAKEOWNERSHIP control-port command, so that a Tor controller can
|
||||||
|
ensure that when it exits, Tor will shut down. Implements
|
||||||
|
feature 3049.
|
||||||
|
|
@ -12,11 +12,11 @@ libor_extra_source=
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
libor_a_SOURCES = address.c log.c util.c compat.c container.c mempool.c \
|
libor_a_SOURCES = address.c log.c util.c compat.c container.c mempool.c \
|
||||||
memarea.c di_ops.c util_codedigest.c $(libor_extra_source)
|
memarea.c di_ops.c procmon.c util_codedigest.c $(libor_extra_source)
|
||||||
libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
|
libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
|
||||||
libor_event_a_SOURCES = compat_libevent.c
|
libor_event_a_SOURCES = compat_libevent.c
|
||||||
|
|
||||||
noinst_HEADERS = address.h torlog.h crypto.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h ciphers.inc compat_libevent.h tortls_states.h di_ops.h
|
noinst_HEADERS = address.h torlog.h crypto.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h ciphers.inc compat_libevent.h tortls_states.h di_ops.h procmon.h
|
||||||
|
|
||||||
common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)
|
common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)
|
||||||
if test "@SHA1SUM@" != none; then \
|
if test "@SHA1SUM@" != none; then \
|
||||||
|
@ -398,7 +398,7 @@ int get_n_open_sockets(void);
|
|||||||
#define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags)
|
#define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags)
|
||||||
#define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags)
|
#define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags)
|
||||||
|
|
||||||
/** Implementatino of struct in6_addr for platforms that do not have it.
|
/** Implementation of struct in6_addr for platforms that do not have it.
|
||||||
* Generally, these platforms are ones without IPv6 support, but we want to
|
* Generally, these platforms are ones without IPv6 support, but we want to
|
||||||
* have a working in6_addr there anyway, so we can use it to parse IPv6
|
* have a working in6_addr there anyway, so we can use it to parse IPv6
|
||||||
* addresses. */
|
* addresses. */
|
||||||
|
@ -48,7 +48,7 @@ typedef uint32_t le_version_t;
|
|||||||
* it is. */
|
* it is. */
|
||||||
#define LE_OLD V(0,0,0)
|
#define LE_OLD V(0,0,0)
|
||||||
/** Represents a version of libevent so weird we can't figure out what version
|
/** Represents a version of libevent so weird we can't figure out what version
|
||||||
* it it. */
|
* it is. */
|
||||||
#define LE_OTHER V(0,0,99)
|
#define LE_OTHER V(0,0,99)
|
||||||
|
|
||||||
static le_version_t tor_get_libevent_version(const char **v_out);
|
static le_version_t tor_get_libevent_version(const char **v_out);
|
||||||
|
@ -390,7 +390,7 @@ logv(int severity, log_domain_mask_t domain, const char *funcname,
|
|||||||
|
|
||||||
/** Output a message to the log. It gets logged to all logfiles that
|
/** Output a message to the log. It gets logged to all logfiles that
|
||||||
* care about messages with <b>severity</b> in <b>domain</b>. The content
|
* care about messages with <b>severity</b> in <b>domain</b>. The content
|
||||||
* is formatted printf style basedc on <b>format</b> and extra arguments.
|
* is formatted printf-style based on <b>format</b> and extra arguments.
|
||||||
* */
|
* */
|
||||||
void
|
void
|
||||||
tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
|
tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
|
||||||
|
332
src/common/procmon.c
Normal file
332
src/common/procmon.c
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* \file procmon.c
|
||||||
|
* \brief Process-termination monitor functions
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "procmon.h"
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_EVENT2_EVENT_H
|
||||||
|
#include <event2/event.h>
|
||||||
|
#else
|
||||||
|
#include <event.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SIGNAL_H
|
||||||
|
#include <signal.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
/* Windows does not define pid_t, but _getpid() returns an int. */
|
||||||
|
typedef int pid_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define to 1 if process-termination monitors on this OS and Libevent
|
||||||
|
version must poll for process termination themselves. */
|
||||||
|
#define PROCMON_POLLS 1
|
||||||
|
/* Currently we need to poll in some way on all systems. */
|
||||||
|
|
||||||
|
#ifdef PROCMON_POLLS
|
||||||
|
static void tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
|
||||||
|
void *procmon_);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This struct may contain pointers into the original process
|
||||||
|
* specifier string, but it should *never* contain anything which
|
||||||
|
* needs to be freed. */
|
||||||
|
struct parsed_process_specifier_t {
|
||||||
|
pid_t pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Parse the process specifier given in <b>process_spec</b> into
|
||||||
|
* *<b>ppspec</b>. Return 0 on success; return -1 and store an error
|
||||||
|
* message into *<b>msg</b> on failure. The caller must not free the
|
||||||
|
* returned error message. */
|
||||||
|
static int
|
||||||
|
parse_process_specifier(const char *process_spec,
|
||||||
|
struct parsed_process_specifier_t *ppspec,
|
||||||
|
const char **msg)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tor_process_monitor_t {
|
||||||
|
/** Log domain for warning messages. */
|
||||||
|
log_domain_mask_t log_domain;
|
||||||
|
|
||||||
|
/** 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. */
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
/** Windows-only: Should we poll hproc? If false, poll pid
|
||||||
|
* instead. */
|
||||||
|
int poll_hproc;
|
||||||
|
|
||||||
|
/** Windows-only: Get a handle to the process (if possible) and
|
||||||
|
* periodically check whether the process we have a handle to has
|
||||||
|
* ended. */
|
||||||
|
HANDLE hproc;
|
||||||
|
/* XXX023 We can and should have Libevent watch hproc for us,
|
||||||
|
* if/when some version of Libevent 2.x can be told to do so. */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* XXX023 On Linux, we can and should receive the 22nd
|
||||||
|
* (space-delimited) field (‘starttime’) of /proc/$PID/stat from the
|
||||||
|
* owning controller and store it, and poll once in a while to see
|
||||||
|
* whether it has changed -- if so, the kernel has *definitely*
|
||||||
|
* reassigned the owning controller's PID and we should exit. On
|
||||||
|
* FreeBSD, we can do the same trick using either the 8th
|
||||||
|
* space-delimited field of /proc/$PID/status on the seven FBSD
|
||||||
|
* systems whose admins have mounted procfs, or the start-time field
|
||||||
|
* of the process-information structure returned by kvmgetprocs() on
|
||||||
|
* any system. The latter is ickier. */
|
||||||
|
/* XXX023 On FreeBSD (and possibly other kqueue systems), we can and
|
||||||
|
* should arrange to receive EVFILT_PROC NOTE_EXIT notifications for
|
||||||
|
* pid, so we don't have to do such a heavyweight poll operation in
|
||||||
|
* order to avoid the PID-reassignment race condition. (We would
|
||||||
|
* still need to poll our own kqueue periodically until some version
|
||||||
|
* of Libevent 2.x learns to receive these events for us.) */
|
||||||
|
|
||||||
|
/** A Libevent event structure, to either poll for the process's
|
||||||
|
* existence or receive a notification when the process ends. */
|
||||||
|
struct event *e;
|
||||||
|
|
||||||
|
/** A callback to be called when the process ends. */
|
||||||
|
tor_procmon_callback_t cb;
|
||||||
|
void *cb_arg; /**< A user-specified pointer to be passed to cb. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Verify that the process specifier given in <b>process_spec</b> is
|
||||||
|
* syntactically valid. Return 0 on success; return -1 and store an
|
||||||
|
* error message into *<b>msg</b> on failure. The caller must not
|
||||||
|
* free the returned error message. */
|
||||||
|
int
|
||||||
|
tor_validate_process_specifier(const char *process_spec,
|
||||||
|
const char **msg)
|
||||||
|
{
|
||||||
|
struct parsed_process_specifier_t ppspec;
|
||||||
|
|
||||||
|
tor_assert(msg != NULL);
|
||||||
|
*msg = NULL;
|
||||||
|
|
||||||
|
return parse_process_specifier(process_spec, &ppspec, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_EVENT2_EVENT_H
|
||||||
|
#define PERIODIC_TIMER_FLAGS EV_PERSIST
|
||||||
|
#else
|
||||||
|
#define PERIODIC_TIMER_FLAGS (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct timeval poll_interval_tv = {15, 0};
|
||||||
|
|
||||||
|
/** Create a process-termination monitor for the process specifier
|
||||||
|
* given in <b>process_spec</b>. Return a newly allocated
|
||||||
|
* tor_process_monitor_t on success; return NULL and store an error
|
||||||
|
* message into *<b>msg</b> on failure. The caller must not free
|
||||||
|
* the returned error message.
|
||||||
|
*
|
||||||
|
* When the monitored process terminates, call
|
||||||
|
* <b>cb</b>(<b>cb_arg</b>).
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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 MS_WINDOWS
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PROCMON_POLLS
|
||||||
|
/** Libevent callback to poll for the existence of the process
|
||||||
|
* monitored by <b>procmon_</b>. */
|
||||||
|
static void
|
||||||
|
tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
|
||||||
|
void *procmon_)
|
||||||
|
{
|
||||||
|
tor_process_monitor_t *procmon = (tor_process_monitor_t *)(procmon_);
|
||||||
|
int its_dead_jim;
|
||||||
|
|
||||||
|
(void)unused1; (void)unused2;
|
||||||
|
|
||||||
|
tor_assert(procmon != NULL);
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
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 %d (%s) is not 'invalid "
|
||||||
|
"parameter' -- assuming the process is still alive.",
|
||||||
|
procmon->pid,
|
||||||
|
err_code, err_msg);
|
||||||
|
|
||||||
|
tor_free(err_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Free the process-termination monitor <b>procmon</b>. */
|
||||||
|
void
|
||||||
|
tor_process_monitor_free(tor_process_monitor_t *procmon)
|
||||||
|
{
|
||||||
|
if (procmon == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
if (procmon->hproc != NULL)
|
||||||
|
CloseHandle(procmon->hproc);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (procmon->e != NULL)
|
||||||
|
tor_event_free(procmon->e);
|
||||||
|
|
||||||
|
tor_free(procmon);
|
||||||
|
}
|
||||||
|
|
30
src/common/procmon.h
Normal file
30
src/common/procmon.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* \file procmon.h
|
||||||
|
* \brief Headers for procmon.c
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef TOR_PROCMON_H
|
||||||
|
#define TOR_PROCMON_H
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
#include "compat_libevent.h"
|
||||||
|
|
||||||
|
#include "torlog.h"
|
||||||
|
|
||||||
|
typedef struct tor_process_monitor_t tor_process_monitor_t;
|
||||||
|
|
||||||
|
typedef void (*tor_procmon_callback_t)(void *);
|
||||||
|
|
||||||
|
int tor_validate_process_specifier(const char *process_spec,
|
||||||
|
const char **msg);
|
||||||
|
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);
|
||||||
|
void tor_process_monitor_free(tor_process_monitor_t *procmon);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -146,7 +146,6 @@ void change_callback_log_severity(int loglevelMin, int loglevelMax,
|
|||||||
void flush_pending_log_callbacks(void);
|
void flush_pending_log_callbacks(void);
|
||||||
void log_set_application_name(const char *name);
|
void log_set_application_name(const char *name);
|
||||||
|
|
||||||
/* Outputs a message to stdout */
|
|
||||||
void tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
|
void tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
|
||||||
CHECK_PRINTF(3,4);
|
CHECK_PRINTF(3,4);
|
||||||
#define log tor_log /* hack it so we don't conflict with log() as much */
|
#define log tor_log /* hack it so we don't conflict with log() as much */
|
||||||
|
@ -775,13 +775,17 @@ tor_digest256_is_zero(const char *digest)
|
|||||||
if (next) *next = endptr; \
|
if (next) *next = endptr; \
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
/** Extract a long from the start of s, in the given numeric base. If
|
/** Extract a long from the start of <b>s</b>, in the given numeric
|
||||||
* there is unconverted data and next is provided, set *next to the
|
* <b>base</b>. If <b>base</b> is 0, <b>s</b> is parsed as a decimal,
|
||||||
* first unconverted character. An error has occurred if no characters
|
* octal, or hex number in the syntax of a C integer literal. If
|
||||||
* are converted; or if there are unconverted characters and next is NULL; or
|
* there is unconverted data and <b>next</b> is provided, set
|
||||||
* if the parsed value is not between min and max. When no error occurs,
|
* *<b>next</b> to the first unconverted character. An error has
|
||||||
* return the parsed value and set *ok (if provided) to 1. When an error
|
* occurred if no characters are converted; or if there are
|
||||||
* occurs, return 0 and set *ok (if provided) to 0.
|
* unconverted characters and <b>next</b> is NULL; or if the parsed
|
||||||
|
* value is not between <b>min</b> and <b>max</b>. When no error
|
||||||
|
* occurs, return the parsed value and set *<b>ok</b> (if provided) to
|
||||||
|
* 1. When an error occurs, return 0 and set *<b>ok</b> (if provided)
|
||||||
|
* to 0.
|
||||||
*/
|
*/
|
||||||
long
|
long
|
||||||
tor_parse_long(const char *s, int base, long min, long max,
|
tor_parse_long(const char *s, int base, long min, long max,
|
||||||
|
@ -38,6 +38,8 @@
|
|||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "procmon.h"
|
||||||
|
|
||||||
/** Enumeration of types which option values can take */
|
/** Enumeration of types which option values can take */
|
||||||
typedef enum config_type_t {
|
typedef enum config_type_t {
|
||||||
CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */
|
CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */
|
||||||
@ -398,6 +400,7 @@ static config_var_t _option_vars[] = {
|
|||||||
VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"),
|
VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"),
|
||||||
VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword,
|
VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword,
|
||||||
NULL),
|
NULL),
|
||||||
|
VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL),
|
||||||
V(MinUptimeHidServDirectoryV2, INTERVAL, "24 hours"),
|
V(MinUptimeHidServDirectoryV2, INTERVAL, "24 hours"),
|
||||||
V(_UsingTestNetworkDefaults, BOOL, "0"),
|
V(_UsingTestNetworkDefaults, BOOL, "0"),
|
||||||
|
|
||||||
@ -1241,6 +1244,8 @@ options_act(or_options_t *old_options)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monitor_owning_controller_process(options->OwningControllerProcess);
|
||||||
|
|
||||||
/* reload keys as needed for rendezvous services. */
|
/* reload keys as needed for rendezvous services. */
|
||||||
if (rend_service_load_keys()<0) {
|
if (rend_service_load_keys()<0) {
|
||||||
log_warn(LD_GENERAL,"Error loading rendezvous service keys");
|
log_warn(LD_GENERAL,"Error loading rendezvous service keys");
|
||||||
@ -3480,6 +3485,16 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options->OwningControllerProcess) {
|
||||||
|
const char *validate_pspec_msg = NULL;
|
||||||
|
if (tor_validate_process_specifier(options->OwningControllerProcess,
|
||||||
|
&validate_pspec_msg)) {
|
||||||
|
tor_asprintf(msg, "Bad OwningControllerProcess: %s",
|
||||||
|
validate_pspec_msg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options->ControlListenAddress) {
|
if (options->ControlListenAddress) {
|
||||||
int all_are_local = 1;
|
int all_are_local = 1;
|
||||||
config_line_t *ln;
|
config_line_t *ln;
|
||||||
|
@ -479,8 +479,7 @@ connection_free(connection_t *conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (conn->type == CONN_TYPE_CONTROL) {
|
if (conn->type == CONN_TYPE_CONTROL) {
|
||||||
TO_CONTROL_CONN(conn)->event_mask = 0;
|
connection_control_closed(TO_CONTROL_CONN(conn));
|
||||||
control_update_global_event_mask();
|
|
||||||
}
|
}
|
||||||
connection_unregister_events(conn);
|
connection_unregister_events(conn);
|
||||||
_connection_free(conn);
|
_connection_free(conn);
|
||||||
|
138
src/or/control.c
138
src/or/control.c
@ -32,6 +32,8 @@
|
|||||||
#include "routerlist.h"
|
#include "routerlist.h"
|
||||||
#include "routerparse.h"
|
#include "routerparse.h"
|
||||||
|
|
||||||
|
#include "procmon.h"
|
||||||
|
|
||||||
/** Yield true iff <b>s</b> is the state of a control_connection_t that has
|
/** Yield true iff <b>s</b> is the state of a control_connection_t that has
|
||||||
* finished authentication and is accepting commands. */
|
* finished authentication and is accepting commands. */
|
||||||
#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN)
|
#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN)
|
||||||
@ -1275,6 +1277,26 @@ handle_control_signal(control_connection_t *conn, uint32_t len,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Called when we get a TAKEOWNERSHIP command. Mark this connection
|
||||||
|
* as an owning connection, so that we will exit if the connection
|
||||||
|
* closes. */
|
||||||
|
static int
|
||||||
|
handle_control_takeownership(control_connection_t *conn, uint32_t len,
|
||||||
|
const char *body)
|
||||||
|
{
|
||||||
|
(void)len;
|
||||||
|
(void)body;
|
||||||
|
|
||||||
|
conn->is_owning_control_connection = 1;
|
||||||
|
|
||||||
|
log_info(LD_CONTROL, "Control connection %d has taken ownership of this "
|
||||||
|
"Tor instance.",
|
||||||
|
(int)(conn->_base.s));
|
||||||
|
|
||||||
|
send_control_done(conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** Called when we get a MAPADDRESS command; try to bind all listed addresses,
|
/** Called when we get a MAPADDRESS command; try to bind all listed addresses,
|
||||||
* and report success or failure. */
|
* and report success or failure. */
|
||||||
static int
|
static int
|
||||||
@ -2010,8 +2032,8 @@ static const getinfo_item_t getinfo_items[] = {
|
|||||||
"v2 networkstatus docs as retrieved from a DirPort."),
|
"v2 networkstatus docs as retrieved from a DirPort."),
|
||||||
ITEM("dir/status-vote/current/consensus", dir,
|
ITEM("dir/status-vote/current/consensus", dir,
|
||||||
"v3 Networkstatus consensus as retrieved from a DirPort."),
|
"v3 Networkstatus consensus as retrieved from a DirPort."),
|
||||||
PREFIX("exit-policy/default", policies,
|
ITEM("exit-policy/default", policies,
|
||||||
"The default value appended to the configured exit policy."),
|
"The default value appended to the configured exit policy."),
|
||||||
PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"),
|
PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"),
|
||||||
{ NULL, NULL, NULL, 0 }
|
{ NULL, NULL, NULL, 0 }
|
||||||
};
|
};
|
||||||
@ -2842,6 +2864,43 @@ connection_control_reached_eof(control_connection_t *conn)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Shut down this Tor instance in the same way that SIGINT would, but
|
||||||
|
* with a log message appropriate for the loss of an owning controller. */
|
||||||
|
static void
|
||||||
|
lost_owning_controller(const char *owner_type, const char *loss_manner)
|
||||||
|
{
|
||||||
|
int shutdown_slowly = server_mode(get_options());
|
||||||
|
|
||||||
|
log_notice(LD_CONTROL, "Owning controller %s has %s -- %s.",
|
||||||
|
owner_type, loss_manner,
|
||||||
|
shutdown_slowly ? "shutting down" : "exiting now");
|
||||||
|
|
||||||
|
/* XXXX Perhaps this chunk of code should be a separate function,
|
||||||
|
* called here and by process_signal(SIGINT). */
|
||||||
|
|
||||||
|
if (!shutdown_slowly) {
|
||||||
|
tor_cleanup();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
/* XXXX This will close all listening sockets except control-port
|
||||||
|
* listeners. Perhaps we should close those too. */
|
||||||
|
hibernate_begin_shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called when <b>conn</b> is being freed. */
|
||||||
|
void
|
||||||
|
connection_control_closed(control_connection_t *conn)
|
||||||
|
{
|
||||||
|
tor_assert(conn);
|
||||||
|
|
||||||
|
conn->event_mask = 0;
|
||||||
|
control_update_global_event_mask();
|
||||||
|
|
||||||
|
if (conn->is_owning_control_connection) {
|
||||||
|
lost_owning_controller("connection", "closed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Return true iff <b>cmd</b> is allowable (or at least forgivable) at this
|
/** Return true iff <b>cmd</b> is allowable (or at least forgivable) at this
|
||||||
* stage of the protocol. */
|
* stage of the protocol. */
|
||||||
static int
|
static int
|
||||||
@ -2997,6 +3056,9 @@ connection_control_process_inbuf(control_connection_t *conn)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* XXXX Why is this not implemented as a table like the GETINFO
|
||||||
|
* items are? Even handling the plus signs at the beginnings of
|
||||||
|
* commands wouldn't be very hard with proper macros. */
|
||||||
cmd_data_len = (uint32_t)data_len;
|
cmd_data_len = (uint32_t)data_len;
|
||||||
if (!strcasecmp(conn->incoming_cmd, "SETCONF")) {
|
if (!strcasecmp(conn->incoming_cmd, "SETCONF")) {
|
||||||
if (handle_control_setconf(conn, cmd_data_len, args))
|
if (handle_control_setconf(conn, cmd_data_len, args))
|
||||||
@ -3022,6 +3084,9 @@ connection_control_process_inbuf(control_connection_t *conn)
|
|||||||
} else if (!strcasecmp(conn->incoming_cmd, "SIGNAL")) {
|
} else if (!strcasecmp(conn->incoming_cmd, "SIGNAL")) {
|
||||||
if (handle_control_signal(conn, cmd_data_len, args))
|
if (handle_control_signal(conn, cmd_data_len, args))
|
||||||
return -1;
|
return -1;
|
||||||
|
} else if (!strcasecmp(conn->incoming_cmd, "TAKEOWNERSHIP")) {
|
||||||
|
if (handle_control_takeownership(conn, cmd_data_len, args))
|
||||||
|
return -1;
|
||||||
} else if (!strcasecmp(conn->incoming_cmd, "MAPADDRESS")) {
|
} else if (!strcasecmp(conn->incoming_cmd, "MAPADDRESS")) {
|
||||||
if (handle_control_mapaddress(conn, cmd_data_len, args))
|
if (handle_control_mapaddress(conn, cmd_data_len, args))
|
||||||
return -1;
|
return -1;
|
||||||
@ -3884,6 +3949,75 @@ init_cookie_authentication(int enabled)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A copy of the process specifier of Tor's owning controller, or
|
||||||
|
* NULL if this Tor instance is not currently owned by a process. */
|
||||||
|
static char *owning_controller_process_spec = NULL;
|
||||||
|
|
||||||
|
/** A process-termination monitor for Tor's owning controller, or NULL
|
||||||
|
* if this Tor instance is not currently owned by a process. */
|
||||||
|
static tor_process_monitor_t *owning_controller_process_monitor = NULL;
|
||||||
|
|
||||||
|
/** Process-termination monitor callback for Tor's owning controller
|
||||||
|
* process. */
|
||||||
|
static void
|
||||||
|
owning_controller_procmon_cb(void *unused)
|
||||||
|
{
|
||||||
|
(void)unused;
|
||||||
|
|
||||||
|
lost_owning_controller("process", "vanished");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set <b>process_spec</b> as Tor's owning controller process.
|
||||||
|
* Exit on failure. */
|
||||||
|
void
|
||||||
|
monitor_owning_controller_process(const char *process_spec)
|
||||||
|
{
|
||||||
|
const char *msg;
|
||||||
|
|
||||||
|
tor_assert((owning_controller_process_spec == NULL) ==
|
||||||
|
(owning_controller_process_monitor == NULL));
|
||||||
|
|
||||||
|
if (owning_controller_process_spec != NULL) {
|
||||||
|
if ((process_spec != NULL) && !strcmp(process_spec,
|
||||||
|
owning_controller_process_spec)) {
|
||||||
|
/* Same process -- return now, instead of disposing of and
|
||||||
|
* recreating the process-termination monitor. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We are currently owned by a process, and we should no longer be
|
||||||
|
* owned by it. Free the process-termination monitor. */
|
||||||
|
tor_process_monitor_free(owning_controller_process_monitor);
|
||||||
|
owning_controller_process_monitor = NULL;
|
||||||
|
|
||||||
|
tor_free(owning_controller_process_spec);
|
||||||
|
owning_controller_process_spec = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tor_assert((owning_controller_process_spec == NULL) &&
|
||||||
|
(owning_controller_process_monitor == NULL));
|
||||||
|
|
||||||
|
if (process_spec == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
owning_controller_process_spec = tor_strdup(process_spec);
|
||||||
|
owning_controller_process_monitor =
|
||||||
|
tor_process_monitor_new(tor_libevent_get_base(),
|
||||||
|
owning_controller_process_spec,
|
||||||
|
LD_CONTROL,
|
||||||
|
owning_controller_procmon_cb, NULL,
|
||||||
|
&msg);
|
||||||
|
|
||||||
|
if (owning_controller_process_monitor == NULL) {
|
||||||
|
log_err(LD_BUG, "Couldn't create process-termination monitor for "
|
||||||
|
"owning controller: %s. Exiting.",
|
||||||
|
msg);
|
||||||
|
owning_controller_process_spec = NULL;
|
||||||
|
tor_cleanup();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Convert the name of a bootstrapping phase <b>s</b> into strings
|
/** Convert the name of a bootstrapping phase <b>s</b> into strings
|
||||||
* <b>tag</b> and <b>summary</b> suitable for display by the controller. */
|
* <b>tag</b> and <b>summary</b> suitable for display by the controller. */
|
||||||
static int
|
static int
|
||||||
|
@ -27,6 +27,8 @@ void control_ports_write_to_file(void);
|
|||||||
|
|
||||||
int connection_control_finished_flushing(control_connection_t *conn);
|
int connection_control_finished_flushing(control_connection_t *conn);
|
||||||
int connection_control_reached_eof(control_connection_t *conn);
|
int connection_control_reached_eof(control_connection_t *conn);
|
||||||
|
void connection_control_closed(control_connection_t *conn);
|
||||||
|
|
||||||
int connection_control_process_inbuf(control_connection_t *conn);
|
int connection_control_process_inbuf(control_connection_t *conn);
|
||||||
|
|
||||||
#define EVENT_AUTHDIR_NEWDESCS 0x000D
|
#define EVENT_AUTHDIR_NEWDESCS 0x000D
|
||||||
@ -72,6 +74,8 @@ smartlist_t *decode_hashed_passwords(config_line_t *passwords);
|
|||||||
void disable_control_logging(void);
|
void disable_control_logging(void);
|
||||||
void enable_control_logging(void);
|
void enable_control_logging(void);
|
||||||
|
|
||||||
|
void monitor_owning_controller_process(const char *process_spec);
|
||||||
|
|
||||||
void control_event_bootstrap(bootstrap_status_t status, int progress);
|
void control_event_bootstrap(bootstrap_status_t status, int progress);
|
||||||
void control_event_bootstrap_problem(const char *warn, int reason);
|
void control_event_bootstrap_problem(const char *warn, int reason);
|
||||||
|
|
||||||
|
10
src/or/or.h
10
src/or/or.h
@ -1009,7 +1009,7 @@ typedef struct connection_t {
|
|||||||
/* XXXX023 move this field, and all the listener-only fields (just
|
/* XXXX023 move this field, and all the listener-only fields (just
|
||||||
socket_family, I think), into a new listener_connection_t subtype. */
|
socket_family, I think), into a new listener_connection_t subtype. */
|
||||||
/** If the connection is a CONN_TYPE_AP_DNS_LISTENER, this field points
|
/** If the connection is a CONN_TYPE_AP_DNS_LISTENER, this field points
|
||||||
* to the evdns_server_port is uses to listen to and answer connections. */
|
* to the evdns_server_port it uses to listen to and answer connections. */
|
||||||
struct evdns_server_port *dns_server_port;
|
struct evdns_server_port *dns_server_port;
|
||||||
|
|
||||||
/** Unique ID for measuring tunneled network status requests. */
|
/** Unique ID for measuring tunneled network status requests. */
|
||||||
@ -1242,6 +1242,9 @@ typedef struct control_connection_t {
|
|||||||
|
|
||||||
/** True if we have sent a protocolinfo reply on this connection. */
|
/** True if we have sent a protocolinfo reply on this connection. */
|
||||||
unsigned int have_sent_protocolinfo:1;
|
unsigned int have_sent_protocolinfo:1;
|
||||||
|
/** True if we have received a takeownership command on this
|
||||||
|
* connection. */
|
||||||
|
unsigned int is_owning_control_connection:1;
|
||||||
|
|
||||||
/** Amount of space allocated in incoming_cmd. */
|
/** Amount of space allocated in incoming_cmd. */
|
||||||
uint32_t incoming_cmd_len;
|
uint32_t incoming_cmd_len;
|
||||||
@ -2674,6 +2677,11 @@ typedef struct {
|
|||||||
int DisablePredictedCircuits; /**< Boolean: does Tor preemptively
|
int DisablePredictedCircuits; /**< Boolean: does Tor preemptively
|
||||||
* make circuits in the background (0),
|
* make circuits in the background (0),
|
||||||
* or not (1)? */
|
* or not (1)? */
|
||||||
|
|
||||||
|
/** Process specifier for a controller that ‘owns’ this Tor
|
||||||
|
* instance. Tor will terminate if its owning controller does. */
|
||||||
|
char *OwningControllerProcess;
|
||||||
|
|
||||||
int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how
|
int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how
|
||||||
* long do we wait before exiting? */
|
* long do we wait before exiting? */
|
||||||
char *SafeLogging; /**< Contains "relay", "1", "0" (meaning no scrubbing). */
|
char *SafeLogging; /**< Contains "relay", "1", "0" (meaning no scrubbing). */
|
||||||
|
Loading…
Reference in New Issue
Block a user