summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/addressmap.c4
-rw-r--r--src/or/circuitbuild.c30
-rw-r--r--src/or/circuitbuild.h2
-rw-r--r--src/or/config.c23
-rw-r--r--src/or/config.h3
-rw-r--r--src/or/connection.c15
-rw-r--r--src/or/connection_edge.c1
-rw-r--r--src/or/control.c103
-rw-r--r--src/or/control.h3
-rw-r--r--src/or/dirserv.c2
-rw-r--r--src/or/hibernate.c90
-rw-r--r--src/or/hibernate.h2
-rw-r--r--src/or/hs_descriptor.c2
-rw-r--r--src/or/hs_service.c5
-rw-r--r--src/or/main.c195
-rw-r--r--src/or/main.h3
-rw-r--r--src/or/networkstatus.c20
-rw-r--r--src/or/networkstatus.h2
-rw-r--r--src/or/periodic.c2
-rw-r--r--src/or/rendservice.c5
-rw-r--r--src/or/router.c18
-rw-r--r--src/or/router.h1
22 files changed, 395 insertions, 136 deletions
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index 3cd153307d..7f861e4d24 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -960,9 +960,11 @@ addressmap_get_virtual_address(int type)
char tmp[TOR_ADDR_BUF_LEN];
tor_addr_to_str(tmp, &addr, sizeof(tmp), 0);
if (strmap_get(addressmap, tmp)) {
+ // LCOV_EXCL_START
log_warn(LD_BUG, "%s wasn't in the addressmap, but %s was.",
buf, tmp);
continue;
+ // LCOV_EXCL_STOP
}
return tor_strdup(buf);
@@ -971,8 +973,10 @@ addressmap_get_virtual_address(int type)
log_warn(LD_CONFIG, "Ran out of virtual addresses!");
return NULL;
} else {
+ // LCOV_EXCL_START
log_warn(LD_BUG, "Called with unsupported address type (%d)", type);
return NULL;
+ // LCOV_EXCL_STOP
}
}
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 6881e0ebb8..09102d0c4c 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1130,19 +1130,29 @@ circuit_send_intermediate_onion_skin(origin_circuit_t *circ,
return 0;
}
-/** Our clock just jumped by <b>seconds_elapsed</b>. Assume
- * something has also gone wrong with our network: notify the user,
- * and abandon all not-yet-used circuits. */
+/** Our clock just jumped by <b>seconds_elapsed</b>. If <b>was_idle</b> is
+ * true, then the monotonic time matches; otherwise it doesn't. Assume
+ * something has also gone wrong with our network: notify the user, and
+ * abandon all not-yet-used circuits. */
void
-circuit_note_clock_jumped(int seconds_elapsed)
+circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle)
{
int severity = server_mode(get_options()) ? LOG_WARN : LOG_NOTICE;
- tor_log(severity, LD_GENERAL, "Your system clock just jumped %d seconds %s; "
- "assuming established circuits no longer work.",
- seconds_elapsed >=0 ? seconds_elapsed : -seconds_elapsed,
- seconds_elapsed >=0 ? "forward" : "backward");
- control_event_general_status(LOG_WARN, "CLOCK_JUMPED TIME=%d",
- seconds_elapsed);
+ if (was_idle) {
+ tor_log(severity, LD_GENERAL, "Tor has been idle for "I64_FORMAT
+ " seconds; assuming established circuits no longer work.",
+ I64_PRINTF_ARG(seconds_elapsed));
+ } else {
+ tor_log(severity, LD_GENERAL,
+ "Your system clock just jumped "I64_FORMAT" seconds %s; "
+ "assuming established circuits no longer work.",
+ I64_PRINTF_ARG(
+ seconds_elapsed >=0 ? seconds_elapsed : -seconds_elapsed),
+ seconds_elapsed >=0 ? "forward" : "backward");
+ }
+ control_event_general_status(LOG_WARN, "CLOCK_JUMPED TIME="I64_FORMAT
+ " IDLE=%d",
+ I64_PRINTF_ARG(seconds_elapsed), was_idle?1:0);
/* so we log when it works again */
note_that_we_maybe_cant_complete_circuits();
control_event_client_status(severity, "CIRCUIT_NOT_ESTABLISHED REASON=%s",
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index ae4aef768a..0184898e29 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -29,7 +29,7 @@ void circuit_n_chan_done(channel_t *chan, int status,
int inform_testing_reachability(void);
int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ);
int circuit_send_next_onion_skin(origin_circuit_t *circ);
-void circuit_note_clock_jumped(int seconds_elapsed);
+void circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle);
int circuit_extend(cell_t *cell, circuit_t *circ);
int circuit_init_cpath_crypto(crypt_path_t *cpath,
const char *key_data, size_t key_data_len,
diff --git a/src/or/config.c b/src/or/config.c
index 9af613e931..94a58f3488 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -1449,9 +1449,9 @@ options_act_reversible(const or_options_t *old_options, char **msg)
consider_hibernation(time(NULL));
/* Launch the listeners. (We do this before we setuid, so we can bind to
- * ports under 1024.) We don't want to rebind if we're hibernating. If
- * networking is disabled, this will close all but the control listeners,
- * but disable those. */
+ * ports under 1024.) We don't want to rebind if we're hibernating or
+ * shutting down. If networking is disabled, this will close all but the
+ * control listeners, but disable those. */
if (!we_are_hibernating()) {
if (retry_all_listeners(replaced_listeners, new_listeners,
options->DisableNetwork) < 0) {
@@ -2001,6 +2001,9 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
+ /* See whether we need to enable/disable our once-a-second timer. */
+ reschedule_per_second_timer();
+
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||
@@ -8433,3 +8436,17 @@ init_cookie_authentication(const char *fname, const char *header,
return retval;
}
+/**
+ * Return true if any option is set in <b>options</b> to make us behave
+ * as a client.
+ */
+int
+options_any_client_port_set(const or_options_t *options)
+{
+ return (options->SocksPort_set ||
+ options->TransPort_set ||
+ options->NATDPort_set ||
+ options->DNSPort_set ||
+ options->HTTPTunnelPort_set);
+}
+
diff --git a/src/or/config.h b/src/or/config.h
index 1d3c27217e..4b41274434 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -214,6 +214,9 @@ smartlist_t *get_options_from_transport_options_line(const char *line,
const char *transport);
smartlist_t *get_options_for_server_transport(const char *transport);
+/* Port helper functions. */
+int options_any_client_port_set(const or_options_t *options);
+
#ifdef CONFIG_PRIVATE
#define CL_PORT_NO_STREAM_OPTIONS (1u<<0)
diff --git a/src/or/connection.c b/src/or/connection.c
index fa9881cb8b..5185b45b14 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -1762,13 +1762,13 @@ connection_connect_sockaddr,(connection_t *conn,
tor_assert(sa);
tor_assert(socket_error);
- if (get_options()->DisableNetwork) {
- /* We should never even try to connect anyplace if DisableNetwork is set.
- * Warn if we do, and refuse to make the connection.
+ if (net_is_completely_disabled()) {
+ /* We should never even try to connect anyplace if the network is
+ * completely shut off.
*
- * We only check DisableNetwork here, not we_are_hibernating(), since
- * we'll still try to fulfill client requests sometimes in the latter case
- * (if it is soft hibernation) */
+ * (We don't check net_is_disabled() here, since we still sometimes
+ * want to open connections when we're in soft hibernation.)
+ */
static ratelim_t disablenet_violated = RATELIM_INIT(30*60);
*socket_error = SOCK_ERRNO(ENETUNREACH);
log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG,
@@ -3440,7 +3440,7 @@ int
connection_handle_read(connection_t *conn)
{
int res;
-
+ update_current_time(time(NULL));
res = connection_handle_read_impl(conn);
return res;
}
@@ -3983,6 +3983,7 @@ int
connection_handle_write(connection_t *conn, int force)
{
int res;
+ update_current_time(time(NULL));
conn->in_connection_handle_write = 1;
res = connection_handle_write_impl(conn, force);
conn->in_connection_handle_write = 0;
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 5ae1538bfe..28e18aa853 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -3537,6 +3537,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
n_stream->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
/* default to failed, change in dns_resolve if it turns out not to fail */
+ /* If we're hibernating or shutting down, we refuse to open new streams. */
if (we_are_hibernating()) {
relay_send_end_cell_from_edge(rh.stream_id, circ,
END_STREAM_REASON_HIBERNATING, NULL);
diff --git a/src/or/control.c b/src/or/control.c
index 93b204f9a3..9323173f5d 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1,3 +1,4 @@
+
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
@@ -112,6 +113,10 @@ static int disable_log_messages = 0;
#define EVENT_IS_INTERESTING(e) \
(!! (global_event_mask & EVENT_MASK_(e)))
+/** Macro: true if any event from the bitfield 'e' is interesting. */
+#define ANY_EVENT_IS_INTERESTING(e) \
+ (!! (global_event_mask & (e)))
+
/** If we're using cookie-type authentication, how long should our cookies be?
*/
#define AUTHENTICATION_COOKIE_LEN 32
@@ -219,6 +224,7 @@ static void set_cached_network_liveness(int liveness);
static void flush_queued_events_cb(mainloop_event_t *event, void *arg);
static char * download_status_to_string(const download_status_t *dl);
+static void control_get_bytes_rw_last_sec(uint64_t *r, uint64_t *w);
/** Given a control event code for a message event, return the corresponding
* log severity. */
@@ -271,6 +277,7 @@ control_update_global_event_mask(void)
smartlist_t *conns = get_connection_array();
event_mask_t old_mask, new_mask;
old_mask = global_event_mask;
+ int any_old_per_sec_events = control_any_per_second_event_enabled();
global_event_mask = 0;
SMARTLIST_FOREACH(conns, connection_t *, _conn,
@@ -288,10 +295,13 @@ control_update_global_event_mask(void)
* we want to hear...*/
control_adjust_event_log_severity();
+ /* Macro: true if ev was false before and is true now. */
+#define NEWLY_ENABLED(ev) \
+ (! (old_mask & (ev)) && (new_mask & (ev)))
+
/* ...then, if we've started logging stream or circ bw, clear the
* appropriate fields. */
- if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) &&
- (new_mask & EVENT_STREAM_BANDWIDTH_USED)) {
+ if (NEWLY_ENABLED(EVENT_STREAM_BANDWIDTH_USED)) {
SMARTLIST_FOREACH(conns, connection_t *, conn,
{
if (conn->type == CONN_TYPE_AP) {
@@ -300,10 +310,18 @@ control_update_global_event_mask(void)
}
});
}
- if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) &&
- (new_mask & EVENT_CIRC_BANDWIDTH_USED)) {
+ if (NEWLY_ENABLED(EVENT_CIRC_BANDWIDTH_USED)) {
clear_circ_bw_fields();
}
+ if (NEWLY_ENABLED(EVENT_BANDWIDTH_USED)) {
+ uint64_t r, w;
+ control_get_bytes_rw_last_sec(&r, &w);
+ }
+ if (any_old_per_sec_events != control_any_per_second_event_enabled()) {
+ reschedule_per_second_timer();
+ }
+
+#undef NEWLY_ENABLED
}
/** Adjust the log severities that result in control_event_logmsg being called
@@ -352,6 +370,65 @@ control_event_is_interesting(int event)
return EVENT_IS_INTERESTING(event);
}
+/** Return true if any event that needs to fire once a second is enabled. */
+int
+control_any_per_second_event_enabled(void)
+{
+ return ANY_EVENT_IS_INTERESTING(
+ EVENT_MASK_(EVENT_BANDWIDTH_USED) |
+ EVENT_MASK_(EVENT_CELL_STATS) |
+ EVENT_MASK_(EVENT_CIRC_BANDWIDTH_USED) |
+ EVENT_MASK_(EVENT_CONN_BW) |
+ EVENT_MASK_(EVENT_STREAM_BANDWIDTH_USED)
+ );
+}
+
+/* The value of 'get_bytes_read()' the previous time that
+ * control_get_bytes_rw_last_sec() as called. */
+static uint64_t stats_prev_n_read = 0;
+/* The value of 'get_bytes_written()' the previous time that
+ * control_get_bytes_rw_last_sec() as called. */
+static uint64_t stats_prev_n_written = 0;
+
+/**
+ * Set <b>n_read</b> and <b>n_written</b> to the total number of bytes read
+ * and written by Tor since the last call to this function.
+ *
+ * Call this only from the main thread.
+ */
+static void
+control_get_bytes_rw_last_sec(uint64_t *n_read,
+ uint64_t *n_written)
+{
+ const uint64_t stats_n_bytes_read = get_bytes_read();
+ const uint64_t stats_n_bytes_written = get_bytes_written();
+
+ *n_read = stats_n_bytes_read - stats_prev_n_read;
+ *n_written = stats_n_bytes_written - stats_prev_n_written;
+ stats_prev_n_read = stats_n_bytes_read;
+ stats_prev_n_written = stats_n_bytes_written;
+}
+
+/**
+ * Run all the controller events (if any) that are scheduled to trigger once
+ * per second.
+ */
+void
+control_per_second_events(void)
+{
+ if (!control_any_per_second_event_enabled())
+ return;
+
+ uint64_t bytes_read, bytes_written;
+ control_get_bytes_rw_last_sec(&bytes_read, &bytes_written);
+ control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
+
+ control_event_stream_bandwidth_used();
+ control_event_conn_bandwidth_used();
+ control_event_circ_bandwidth_used();
+ control_event_circuit_cell_stats();
+}
+
/** Append a NUL-terminated string <b>s</b> to the end of
* <b>conn</b>-\>outbuf.
*/
@@ -1784,24 +1861,24 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
} else if (!strcmp(question, "process/pid")) {
int myPid = -1;
- #ifdef _WIN32
+#ifdef _WIN32
myPid = _getpid();
- #else
+#else
myPid = getpid();
- #endif
+#endif
tor_asprintf(answer, "%d", myPid);
} else if (!strcmp(question, "process/uid")) {
- #ifdef _WIN32
+#ifdef _WIN32
*answer = tor_strdup("-1");
- #else
+#else
int myUid = geteuid();
tor_asprintf(answer, "%d", myUid);
#endif /* defined(_WIN32) */
} else if (!strcmp(question, "process/user")) {
- #ifdef _WIN32
+#ifdef _WIN32
*answer = tor_strdup("");
- #else
+#else
int myUid = geteuid();
const struct passwd *myPwEntry = tor_getpwuid(myUid);
@@ -7035,6 +7112,8 @@ control_event_bootstrap_problem(const char *warn, const char *reason,
if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
dowarn = 1;
+ /* Don't warn about our bootstrapping status if we are hibernating or
+ * shutting down. */
if (we_are_hibernating())
dowarn = 0;
@@ -7606,6 +7685,8 @@ control_free_all(void)
{
smartlist_t *queued_events = NULL;
+ stats_prev_n_read = stats_prev_n_written = 0;
+
if (authentication_cookie) /* Free the auth cookie */
tor_free(authentication_cookie);
if (detached_onion_services) { /* Free the detached onion services */
diff --git a/src/or/control.h b/src/or/control.h
index 7f8a0bdb5f..92cbf866dd 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -40,6 +40,9 @@ int connection_control_process_inbuf(control_connection_t *conn);
#define EVENT_NS 0x000F
int control_event_is_interesting(int event);
+void control_per_second_events(void);
+int control_any_per_second_event_enabled(void);
+
int control_event_circuit_status(origin_circuit_t *circ,
circuit_status_event_t e, int reason);
int control_event_circuit_purpose_changed(origin_circuit_t *circ,
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index bfcec6e105..c01234e0b9 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -955,7 +955,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
tor_assert(node);
if (router_is_me(router)) {
- /* We always know if we are down ourselves. */
+ /* We always know if we are shutting down or hibernating ourselves. */
answer = ! we_are_hibernating();
} else if (router->is_hibernating &&
(router->cache_info.published_on +
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 98f32adb1c..d7d259470f 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -52,6 +52,10 @@ static time_t hibernate_end_time = 0;
* we aren't shutting down. */
static time_t shutdown_time = 0;
+/** A timed event that we'll use when it's time to wake up from
+ * hibernation. */
+static mainloop_event_t *wakeup_event = NULL;
+
/** Possible accounting periods. */
typedef enum {
UNIT_MONTH=1, UNIT_WEEK=2, UNIT_DAY=3,
@@ -131,6 +135,8 @@ static time_t start_of_accounting_period_after(time_t now);
static time_t start_of_accounting_period_containing(time_t now);
static void accounting_set_wakeup_time(void);
static void on_hibernate_state_change(hibernate_state_t prev_state);
+static void hibernate_schedule_wakeup_event(time_t now, time_t end_time);
+static void wakeup_event_callback(mainloop_event_t *ev, void *data);
/**
* Return the human-readable name for the hibernation state <b>state</b>
@@ -877,13 +883,26 @@ hibernate_begin_shutdown(void)
hibernate_begin(HIBERNATE_STATE_EXITING, time(NULL));
}
-/** Return true iff we are currently hibernating. */
+/**
+ * Return true iff we are currently hibernating -- that is, if we are in
+ * any non-live state.
+ */
MOCK_IMPL(int,
we_are_hibernating,(void))
{
return hibernate_state != HIBERNATE_STATE_LIVE;
}
+/**
+ * Return true iff we are currently _fully_ hibernating -- that is, if we are
+ * in a state where we expect to handle no network activity at all.
+ */
+MOCK_IMPL(int,
+we_are_fully_hibernating,(void))
+{
+ return hibernate_state == HIBERNATE_STATE_DORMANT;
+}
+
/** If we aren't currently dormant, close all connections and become
* dormant. */
static void
@@ -936,6 +955,63 @@ hibernate_go_dormant(time_t now)
or_state_mark_dirty(get_or_state(),
get_options()->AvoidDiskWrites ? now+600 : 0);
+
+ hibernate_schedule_wakeup_event(now, hibernate_end_time);
+}
+
+/**
+ * Schedule a mainloop event at <b>end_time</b> to wake up from a dormant
+ * state. We can't rely on this happening from second_elapsed_callback,
+ * since second_elapsed_callback will be shut down when we're dormant.
+ *
+ * (Note that We might immediately go back to sleep after we set the next
+ * wakeup time.)
+ */
+static void
+hibernate_schedule_wakeup_event(time_t now, time_t end_time)
+{
+ struct timeval delay = { 0, 0 };
+
+ if (now >= end_time) {
+ // In these cases we always wait at least a second, to avoid running
+ // the callback in a tight loop.
+ delay.tv_sec = 1;
+ } else {
+ delay.tv_sec = (end_time - now);
+ }
+
+ if (!wakeup_event) {
+ wakeup_event = mainloop_event_postloop_new(wakeup_event_callback, NULL);
+ }
+
+ mainloop_event_schedule(wakeup_event, &delay);
+}
+
+/**
+ * Called at the end of the interval, or at the wakeup time of the current
+ * interval, to exit the dormant state.
+ **/
+static void
+wakeup_event_callback(mainloop_event_t *ev, void *data)
+{
+ (void) ev;
+ (void) data;
+
+ const time_t now = time(NULL);
+ accounting_run_housekeeping(now);
+ consider_hibernation(now);
+ if (hibernate_state != HIBERNATE_STATE_DORMANT) {
+ /* We woke up, so everything's great here */
+ return;
+ }
+
+ /* We're still dormant. */
+ if (now < interval_wakeup_time)
+ hibernate_end_time = interval_wakeup_time;
+ else
+ hibernate_end_time = interval_end_time;
+
+ hibernate_schedule_wakeup_event(now, hibernate_end_time);
}
/** Called when hibernate_end_time has arrived. */
@@ -1124,6 +1200,18 @@ on_hibernate_state_change(hibernate_state_t prev_state)
if (prev_state != HIBERNATE_STATE_INITIAL) {
rescan_periodic_events(get_options());
}
+
+ reschedule_per_second_timer();
+}
+
+/** Free all resources held by the accounting module */
+void
+accounting_free_all(void)
+{
+ mainloop_event_free(wakeup_event);
+ hibernate_state = HIBERNATE_STATE_INITIAL;
+ hibernate_end_time = 0;
+ shutdown_time = 0;
}
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/hibernate.h b/src/or/hibernate.h
index 85fb42864b..453969d052 100644
--- a/src/or/hibernate.h
+++ b/src/or/hibernate.h
@@ -25,11 +25,13 @@ void accounting_add_bytes(size_t n_read, size_t n_written, int seconds);
int accounting_record_bandwidth_usage(time_t now, or_state_t *state);
void hibernate_begin_shutdown(void);
MOCK_DECL(int, we_are_hibernating, (void));
+MOCK_DECL(int, we_are_fully_hibernating,(void));
void consider_hibernation(time_t now);
int getinfo_helper_accounting(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
uint64_t get_accounting_max_total(void);
+void accounting_free_all(void);
#ifdef HIBERNATE_PRIVATE
/** Possible values of hibernate_state */
diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c
index eb48cb0601..096122392d 100644
--- a/src/or/hs_descriptor.c
+++ b/src/or/hs_descriptor.c
@@ -1897,7 +1897,7 @@ desc_sig_is_valid(const char *b64_sig,
}
/* Find the start of signature. */
- sig_start = tor_memstr(encoded_desc, encoded_len, "\n" str_signature);
+ sig_start = tor_memstr(encoded_desc, encoded_len, "\n" str_signature " ");
/* Getting here means the token parsing worked for the signature so if we
* can't find the start of the signature, we have a code flow issue. */
if (!sig_start) {
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index b7296ddcf9..9001a521ab 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -878,11 +878,6 @@ register_all_services(void)
tor_assert(hs_service_staging_list);
- /* We'll save us some allocation and computing time. */
- if (smartlist_len(hs_service_staging_list) == 0) {
- return;
- }
-
/* Allocate a new map that will replace the current one. */
new_service_map = tor_malloc_zero(sizeof(*new_service_map));
HT_INIT(hs_service_ht, new_service_map);
diff --git a/src/or/main.c b/src/or/main.c
index c3505a2d91..9dce158b33 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -163,11 +163,6 @@ token_bucket_rw_t global_bucket;
/* Token bucket for relayed traffic. */
token_bucket_rw_t global_relayed_bucket;
-/* DOCDOC stats_prev_n_read */
-static uint64_t stats_prev_n_read = 0;
-/* DOCDOC stats_prev_n_written */
-static uint64_t stats_prev_n_written = 0;
-
/* XXX we might want to keep stats about global_relayed_*_bucket too. Or not.*/
/** How many bytes have we read since we started the process? */
static uint64_t stats_n_bytes_read = 0;
@@ -1258,7 +1253,8 @@ run_connection_housekeeping(int i, time_t now)
} else if (we_are_hibernating() &&
! have_any_circuits &&
!connection_get_outbuf_len(conn)) {
- /* We're hibernating, there's no circuits, and nothing to flush.*/
+ /* We're hibernating or shutting down, there's no circuits, and nothing to
+ * flush.*/
log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) "
"[Hibernating or exiting].",
(int)conn->s,conn->address, conn->port);
@@ -1497,7 +1493,7 @@ get_my_roles(const or_options_t *options)
int roles = 0;
int is_bridge = options->BridgeRelay;
- int is_client = any_client_port_set(options);
+ int is_client = options_any_client_port_set(options);
int is_relay = server_mode(options);
int is_dirauth = authdir_mode_v3(options);
int is_bridgeauth = authdir_mode_bridge(options);
@@ -1676,6 +1672,11 @@ static mainloop_event_t *postloop_cleanup_ev=NULL;
void
mainloop_schedule_postloop_cleanup(void)
{
+ if (PREDICT_UNLIKELY(postloop_cleanup_ev == NULL)) {
+ // (It's possible that we can get here if we decide to close a connection
+ // in the earliest stages of our configuration, before we create events.)
+ return;
+ }
mainloop_event_activate(postloop_cleanup_ev);
}
@@ -2495,10 +2496,100 @@ hs_service_callback(time_t now, const or_options_t *options)
/** Timer: used to invoke second_elapsed_callback() once per second. */
static periodic_timer_t *second_timer = NULL;
-/** Number of libevent errors in the last second: we die if we get too many. */
-static int n_libevent_errors = 0;
-/** Last time that second_elapsed_callback was called. */
+
+/**
+ * Enable or disable the per-second timer as appropriate, creating it if
+ * necessary.
+ */
+void
+reschedule_per_second_timer(void)
+{
+ struct timeval one_second;
+ one_second.tv_sec = 1;
+ one_second.tv_usec = 0;
+
+ if (! second_timer) {
+ second_timer = periodic_timer_new(tor_libevent_get_base(),
+ &one_second,
+ second_elapsed_callback,
+ NULL);
+ tor_assert(second_timer);
+ }
+
+ const bool run_per_second_events =
+ control_any_per_second_event_enabled() || ! net_is_completely_disabled();
+
+ if (run_per_second_events) {
+ periodic_timer_launch(second_timer, &one_second);
+ } else {
+ periodic_timer_disable(second_timer);
+ }
+}
+
+/** Last time that update_current_time was called. */
static time_t current_second = 0;
+/** Last time that update_current_time updated current_second. */
+static monotime_coarse_t current_second_last_changed;
+
+/**
+ * Set the current time to "now", which should be the value returned by
+ * time(). Check for clock jumps and track the total number of seconds we
+ * have been running.
+ */
+void
+update_current_time(time_t now)
+{
+ if (PREDICT_LIKELY(now == current_second)) {
+ /* We call this function a lot. Most frequently, the current second
+ * will not have changed, so we just return. */
+ return;
+ }
+
+ const time_t seconds_elapsed = current_second ? (now - current_second) : 0;
+
+ /* Check the wall clock against the monotonic clock, so we can
+ * better tell idleness from clock jumps and/or other shenanigans. */
+ monotime_coarse_t last_updated;
+ memcpy(&last_updated, &current_second_last_changed, sizeof(last_updated));
+ monotime_coarse_get(&current_second_last_changed);
+
+ /** How much clock jumping do we tolerate? */
+#define NUM_JUMPED_SECONDS_BEFORE_WARN 100
+
+ /** How much idleness do we tolerate? */
+#define NUM_IDLE_SECONDS_BEFORE_WARN 3600
+
+ if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN) {
+ // moving back in time is always a bad sign.
+ circuit_note_clock_jumped(seconds_elapsed, false);
+ } else if (seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
+ /* Compare the monotonic clock to the result of time(). */
+ const int32_t monotime_msec_passed =
+ monotime_coarse_diff_msec32(&last_updated,
+ &current_second_last_changed);
+ const int monotime_sec_passed = monotime_msec_passed / 1000;
+ const int discrepancy = monotime_sec_passed - (int)seconds_elapsed;
+ /* If the monotonic clock deviates from time(NULL), we have a couple of
+ * possibilities. On some systems, this means we have been suspended or
+ * sleeping. Everywhere, it can mean that the wall-clock time has
+ * been changed -- for example, with settimeofday().
+ *
+ * On the other hand, if the monotonic time matches with the wall-clock
+ * time, we've probably just been idle for a while, with no events firing.
+ * we tolerate much more of that.
+ */
+ const bool clock_jumped = abs(discrepancy) > 2;
+
+ if (clock_jumped || seconds_elapsed >= NUM_IDLE_SECONDS_BEFORE_WARN) {
+ circuit_note_clock_jumped(seconds_elapsed, ! clock_jumped);
+ }
+ } else if (seconds_elapsed > 0) {
+ stats_n_seconds_working += seconds_elapsed;
+ }
+
+ update_approx_time(now);
+ current_second = now;
+}
/** Libevent callback: invoked once every second. */
static void
@@ -2508,43 +2599,21 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
* could use Libevent's timers for this rather than checking the current
* time against a bunch of timeouts every second. */
time_t now;
- size_t bytes_written;
- size_t bytes_read;
- int seconds_elapsed;
(void)timer;
(void)arg;
- n_libevent_errors = 0;
-
- /* log_notice(LD_GENERAL, "Tick."); */
now = time(NULL);
- update_approx_time(now);
- /* the second has rolled over. check more stuff. */
- seconds_elapsed = current_second ? (int)(now - current_second) : 0;
- bytes_read = (size_t)(stats_n_bytes_read - stats_prev_n_read);
- bytes_written = (size_t)(stats_n_bytes_written - stats_prev_n_written);
- stats_prev_n_read = stats_n_bytes_read;
- stats_prev_n_written = stats_n_bytes_written;
-
- control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
- control_event_stream_bandwidth_used();
- control_event_conn_bandwidth_used();
- control_event_circ_bandwidth_used();
- control_event_circuit_cell_stats();
-
-/** If more than this many seconds have elapsed, probably the clock
- * jumped: doesn't count. */
-#define NUM_JUMPED_SECONDS_BEFORE_WARN 100
- if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN ||
- seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
- circuit_note_clock_jumped(seconds_elapsed);
- } else if (seconds_elapsed > 0)
- stats_n_seconds_working += seconds_elapsed;
+ /* We don't need to do this once-per-second any more: time-updating is
+ * only in this callback _because it is a callback_. It should be fine
+ * to disable this callback, and the time will still get updated.
+ */
+ update_current_time(now);
- run_scheduled_events(now);
+ /* Maybe some controller events are ready to fire */
+ control_per_second_events();
- current_second = now; /* remember which second it is, for next time */
+ run_scheduled_events(now);
}
#ifdef HAVE_SYSTEMD_209
@@ -2560,21 +2629,6 @@ systemd_watchdog_callback(periodic_timer_t *timer, void *arg)
}
#endif /* defined(HAVE_SYSTEMD_209) */
-#ifndef _WIN32
-/** Called when a possibly ignorable libevent error occurs; ensures that we
- * don't get into an infinite loop by ignoring too many errors from
- * libevent. */
-static int
-got_libevent_error(void)
-{
- if (++n_libevent_errors > 8) {
- log_err(LD_NET, "Too many libevent errors in one second; dying");
- return -1;
- }
- return 0;
-}
-#endif /* !defined(_WIN32) */
-
#define UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST (6*60*60)
/** Called when our IP address seems to have changed. <b>at_interface</b>
@@ -2837,17 +2891,7 @@ do_main_loop(void)
}
/* set up once-a-second callback. */
- if (! second_timer) {
- struct timeval one_second;
- one_second.tv_sec = 1;
- one_second.tv_usec = 0;
-
- second_timer = periodic_timer_new(tor_libevent_get_base(),
- &one_second,
- second_elapsed_callback,
- NULL);
- tor_assert(second_timer);
- }
+ reschedule_per_second_timer();
#ifdef HAVE_SYSTEMD_209
uint64_t watchdog_delay;
@@ -2916,6 +2960,11 @@ do_main_loop(void)
return run_main_loop_until_done();
}
+#ifndef _WIN32
+/** Rate-limiter for EINVAL-type libevent warnings. */
+static ratelim_t libevent_error_ratelim = RATELIM_INIT(10);
+#endif
+
/**
* Run the main loop a single time. Return 0 for "exit"; -1 for "exit with
* error", and 1 for "run this again."
@@ -2981,9 +3030,12 @@ run_main_loop_once(void)
return -1;
#ifndef _WIN32
} else if (e == EINVAL) {
- log_warn(LD_NET, "EINVAL from libevent: should you upgrade libevent?");
- if (got_libevent_error())
+ log_fn_ratelim(&libevent_error_ratelim, LOG_WARN, LD_NET,
+ "EINVAL from libevent: should you upgrade libevent?");
+ if (libevent_error_ratelim.n_calls_since_last_time > 8) {
+ log_err(LD_NET, "Too many libevent errors, too fast: dying");
return -1;
+ }
#endif /* !defined(_WIN32) */
} else {
tor_assert_nonfatal_once(! ERRNO_IS_EINPROGRESS(e));
@@ -3029,6 +3081,7 @@ signal_callback(evutil_socket_t fd, short events, void *arg)
(void)fd;
(void)events;
+ update_current_time(time(NULL));
process_signal(sig);
}
@@ -3618,6 +3671,8 @@ tor_free_all(int postfork)
hs_free_all();
dos_free_all();
circuitmux_ewma_free_all();
+ accounting_free_all();
+
if (!postfork) {
config_free_all();
or_state_free_all();
@@ -3651,7 +3706,6 @@ tor_free_all(int postfork)
memset(&global_bucket, 0, sizeof(global_bucket));
memset(&global_relayed_bucket, 0, sizeof(global_relayed_bucket));
- stats_prev_n_read = stats_prev_n_written = 0;
stats_n_bytes_read = stats_n_bytes_written = 0;
time_of_process_start = 0;
time_of_last_signewnym = 0;
@@ -3665,8 +3719,9 @@ tor_free_all(int postfork)
should_init_bridge_stats = 1;
dns_honesty_first_time = 1;
heartbeat_callback_first_time = 1;
- n_libevent_errors = 0;
current_second = 0;
+ memset(&current_second_last_changed, 0,
+ sizeof(current_second_last_changed));
if (!postfork) {
release_lockfile();
diff --git a/src/or/main.h b/src/or/main.h
index a312b51e05..9dbbc6e5ee 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -66,6 +66,8 @@ void reschedule_dirvote(const or_options_t *options);
void mainloop_schedule_postloop_cleanup(void);
void rescan_periodic_events(const or_options_t *options);
+void update_current_time(time_t now);
+
MOCK_DECL(long,get_uptime,(void));
MOCK_DECL(void,reset_uptime,(void));
@@ -92,6 +94,7 @@ uint64_t get_main_loop_error_count(void);
uint64_t get_main_loop_idle_count(void);
void periodic_events_on_new_options(const or_options_t *options);
+void reschedule_per_second_timer(void);
extern time_t time_of_process_start;
extern int quiet_level;
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 51b2f4af15..998eaf74e6 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -1691,24 +1691,6 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
#endif /* defined(TOR_UNIT_TESTS) */
/**
- * Return true if any option is set in <b>options</b> to make us behave
- * as a client.
- *
- * XXXX If we need this elsewhere at any point, we should make it nonstatic
- * XXXX and move it into another file.
- */
-int
-any_client_port_set(const or_options_t *options)
-{
- return (options->SocksPort_set ||
- options->TransPort_set ||
- options->NATDPort_set ||
- options->ControlPort_set ||
- options->DNSPort_set ||
- options->HTTPTunnelPort_set);
-}
-
-/**
* Helper for handle_missing_protocol_warning: handles either the
* client case (if <b>is_client</b> is set) or the server case otherwise.
*/
@@ -1743,7 +1725,7 @@ handle_missing_protocol_warning(const networkstatus_t *c,
const or_options_t *options)
{
const int is_server = server_mode(options);
- const int is_client = any_client_port_set(options) || !is_server;
+ const int is_client = options_any_client_port_set(options) || !is_server;
if (is_server)
handle_missing_protocol_warning_impl(c, 0);
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index b133f43dc1..94f85c3c29 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -147,8 +147,6 @@ void vote_routerstatus_free_(vote_routerstatus_t *rs);
#define vote_routerstatus_free(rs) \
FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs))
-int any_client_port_set(const or_options_t *options);
-
#ifdef NETWORKSTATUS_PRIVATE
#ifdef TOR_UNIT_TESTS
STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
diff --git a/src/or/periodic.c b/src/or/periodic.c
index 76aa418b35..92fa677f8f 100644
--- a/src/or/periodic.c
+++ b/src/or/periodic.c
@@ -14,6 +14,7 @@
#include "or.h"
#include "compat_libevent.h"
#include "config.h"
+#include "main.h"
#include "periodic.h"
/** We disable any interval greater than this number of seconds, on the
@@ -48,6 +49,7 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data)
}
time_t now = time(NULL);
+ update_current_time(now);
const or_options_t *options = get_options();
// log_debug(LD_GENERAL, "Dispatching %s", event->name);
int r = event->fn(now, options);
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 43600cd913..92c323b10d 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -629,10 +629,11 @@ void
rend_service_prune_list(void)
{
smartlist_t *old_service_list = rend_service_list;
- /* Don't try to prune anything if we have no staging list. */
+
if (!rend_service_staging_list) {
- return;
+ rend_service_staging_list = smartlist_new();
}
+
rend_service_prune_list_impl_();
if (old_service_list) {
/* Every remaining service in the old list have been removed from the
diff --git a/src/or/router.c b/src/or/router.c
index 996a28a91f..07abf1f8d5 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -1599,15 +1599,24 @@ router_perform_bandwidth_test(int num_circs, time_t now)
}
}
-/** Return true iff our network is in some sense disabled: either we're
- * hibernating, entering hibernation, or the network is turned off with
- * DisableNetwork. */
+/** Return true iff our network is in some sense disabled or shutting down:
+ * either we're hibernating, entering hibernation, or the network is turned
+ * off with DisableNetwork. */
int
net_is_disabled(void)
{
return get_options()->DisableNetwork || we_are_hibernating();
}
+/** Return true iff our network is in some sense "completely disabled" either
+ * we're fully hibernating or the network is turned off with
+ * DisableNetwork. */
+int
+net_is_completely_disabled(void)
+{
+ return get_options()->DisableNetwork || we_are_fully_hibernating();
+}
+
/** Return true iff we believe ourselves to be an authoritative
* directory server.
*/
@@ -2268,6 +2277,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
/* and compute ri->bandwidthburst similarly */
ri->bandwidthburst = get_effective_bwburst(options);
+ /* Report bandwidth, unless we're hibernating or shutting down */
ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess();
if (dns_seems_to_be_broken() || has_dns_init_failed()) {
@@ -2538,6 +2548,8 @@ check_descriptor_bandwidth_changed(time_t now)
return;
prev = router_get_my_routerinfo()->bandwidthcapacity;
+ /* Consider ourselves to have zero bandwidth if we're hibernating or
+ * shutting down. */
cur = we_are_hibernating() ? 0 : rep_hist_bandwidth_assess();
if ((prev != cur && (!prev || !cur)) ||
cur > prev*2 ||
diff --git a/src/or/router.h b/src/or/router.h
index 03eca9c65d..0db2c1cfb2 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -53,6 +53,7 @@ void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now);
int net_is_disabled(void);
+int net_is_completely_disabled(void);
int authdir_mode(const or_options_t *options);
int authdir_mode_handles_descs(const or_options_t *options, int purpose);