diff --git a/changes/exit-policy-default-is-not-a-prefix b/changes/exit-policy-default-is-not-a-prefix new file mode 100644 index 0000000000..6eb1e8df99 --- /dev/null +++ b/changes/exit-policy-default-is-not-a-prefix @@ -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. + diff --git a/changes/feature3049 b/changes/feature3049 new file mode 100644 index 0000000000..7960a1f475 --- /dev/null +++ b/changes/feature3049 @@ -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. + diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 2d009bd4fa..6952591d67 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -12,11 +12,11 @@ libor_extra_source= endif 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_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) if test "@SHA1SUM@" != none; then \ diff --git a/src/common/compat.h b/src/common/compat.h index eff51ab30c..e4c5f9ed23 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -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_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 * have a working in6_addr there anyway, so we can use it to parse IPv6 * addresses. */ diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 3ad9be145d..6d89be804b 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -48,7 +48,7 @@ typedef uint32_t le_version_t; * it is. */ #define LE_OLD V(0,0,0) /** 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) static le_version_t tor_get_libevent_version(const char **v_out); diff --git a/src/common/log.c b/src/common/log.c index d14563c885..ac98f13539 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -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 * care about messages with severity in domain. The content - * is formatted printf style basedc on format and extra arguments. + * is formatted printf-style based on format and extra arguments. * */ void tor_log(int severity, log_domain_mask_t domain, const char *format, ...) diff --git a/src/common/procmon.c b/src/common/procmon.c new file mode 100644 index 0000000000..bd25bc10e3 --- /dev/null +++ b/src/common/procmon.c @@ -0,0 +1,332 @@ + +/** + * \file procmon.c + * \brief Process-termination monitor functions + **/ + +#include "procmon.h" + +#include "util.h" + +#ifdef HAVE_EVENT2_EVENT_H +#include +#else +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#ifdef MS_WINDOWS +#include + +/* 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 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. */ +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 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. */ +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 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). + */ +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 procmon_. */ +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 procmon. */ +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); +} + diff --git a/src/common/procmon.h b/src/common/procmon.h new file mode 100644 index 0000000000..02eb2da61c --- /dev/null +++ b/src/common/procmon.h @@ -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 + diff --git a/src/common/torlog.h b/src/common/torlog.h index 000e32ddab..541a0d1738 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -146,7 +146,6 @@ void change_callback_log_severity(int loglevelMin, int loglevelMax, void flush_pending_log_callbacks(void); 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, ...) CHECK_PRINTF(3,4); #define log tor_log /* hack it so we don't conflict with log() as much */ diff --git a/src/common/util.c b/src/common/util.c index 7ffe0a98cb..0f871c4aae 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -775,13 +775,17 @@ tor_digest256_is_zero(const char *digest) if (next) *next = endptr; \ return 0 -/** Extract a long from the start of s, in the given numeric base. If - * there is unconverted data and next is provided, set *next to the - * first unconverted character. An error has occurred if no characters - * are converted; or if there are unconverted characters and next is NULL; or - * if the parsed value is not between min and max. When no error occurs, - * return the parsed value and set *ok (if provided) to 1. When an error - * occurs, return 0 and set *ok (if provided) to 0. +/** Extract a long from the start of s, in the given numeric + * base. If base is 0, s is parsed as a decimal, + * octal, or hex number in the syntax of a C integer literal. If + * there is unconverted data and next is provided, set + * *next to the first unconverted character. An error has + * occurred if no characters are converted; or if there are + * unconverted characters and next is NULL; or if the parsed + * value is not between min and max. When no error + * occurs, return the parsed value and set *ok (if provided) to + * 1. When an error occurs, return 0 and set *ok (if provided) + * to 0. */ long tor_parse_long(const char *s, int base, long min, long max, diff --git a/src/or/config.c b/src/or/config.c index 36fb991e33..46b0cef413 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -38,6 +38,8 @@ #include #endif +#include "procmon.h" + /** Enumeration of types which option values can take */ typedef enum config_type_t { CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ @@ -398,6 +400,7 @@ static config_var_t _option_vars[] = { VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"), VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, NULL), + VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), V(MinUptimeHidServDirectoryV2, INTERVAL, "24 hours"), V(_UsingTestNetworkDefaults, BOOL, "0"), @@ -1241,6 +1244,8 @@ options_act(or_options_t *old_options) return -1; } + monitor_owning_controller_process(options->OwningControllerProcess); + /* reload keys as needed for rendezvous services. */ if (rend_service_load_keys()<0) { 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) { int all_are_local = 1; config_line_t *ln; diff --git a/src/or/connection.c b/src/or/connection.c index 72414589c8..4d3e897185 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -479,8 +479,7 @@ connection_free(connection_t *conn) } } if (conn->type == CONN_TYPE_CONTROL) { - TO_CONTROL_CONN(conn)->event_mask = 0; - control_update_global_event_mask(); + connection_control_closed(TO_CONTROL_CONN(conn)); } connection_unregister_events(conn); _connection_free(conn); diff --git a/src/or/control.c b/src/or/control.c index 384e579f93..0dad1b9dfa 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -32,6 +32,8 @@ #include "routerlist.h" #include "routerparse.h" +#include "procmon.h" + /** Yield true iff s is the state of a control_connection_t that has * finished authentication and is accepting commands. */ #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; } +/** 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, * and report success or failure. */ static int @@ -2010,8 +2032,8 @@ static const getinfo_item_t getinfo_items[] = { "v2 networkstatus docs as retrieved from a DirPort."), ITEM("dir/status-vote/current/consensus", dir, "v3 Networkstatus consensus as retrieved from a DirPort."), - PREFIX("exit-policy/default", policies, - "The default value appended to the configured exit policy."), + ITEM("exit-policy/default", policies, + "The default value appended to the configured exit policy."), PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"), { NULL, NULL, NULL, 0 } }; @@ -2842,6 +2864,43 @@ connection_control_reached_eof(control_connection_t *conn) 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 conn 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 cmd is allowable (or at least forgivable) at this * stage of the protocol. */ static int @@ -2997,6 +3056,9 @@ connection_control_process_inbuf(control_connection_t *conn) 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; if (!strcasecmp(conn->incoming_cmd, "SETCONF")) { 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")) { if (handle_control_signal(conn, cmd_data_len, args)) 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")) { if (handle_control_mapaddress(conn, cmd_data_len, args)) return -1; @@ -3884,6 +3949,75 @@ init_cookie_authentication(int enabled) 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 process_spec 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 s into strings * tag and summary suitable for display by the controller. */ static int diff --git a/src/or/control.h b/src/or/control.h index a73ed5d3c1..ddea4cd548 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -27,6 +27,8 @@ void control_ports_write_to_file(void); int connection_control_finished_flushing(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); #define EVENT_AUTHDIR_NEWDESCS 0x000D @@ -72,6 +74,8 @@ smartlist_t *decode_hashed_passwords(config_line_t *passwords); void disable_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_problem(const char *warn, int reason); diff --git a/src/or/or.h b/src/or/or.h index b9d8319ba5..9cac5f3548 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1009,7 +1009,7 @@ typedef struct connection_t { /* XXXX023 move this field, and all the listener-only fields (just socket_family, I think), into a new listener_connection_t subtype. */ /** 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; /** 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. */ 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. */ uint32_t incoming_cmd_len; @@ -2674,6 +2677,11 @@ typedef struct { int DisablePredictedCircuits; /**< Boolean: does Tor preemptively * make circuits in the background (0), * 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 * long do we wait before exiting? */ char *SafeLogging; /**< Contains "relay", "1", "0" (meaning no scrubbing). */