From ed89bb32535fbf354b406a36f3176380a4e226bf Mon Sep 17 00:00:00 2001 From: David Goulet Date: Mon, 16 Apr 2018 15:50:50 -0400 Subject: main: Specialize the periodic events on a per-role basis In tor, we have a series of possible "roles" that the tor daemon can be enabled for. They are: Client, Bridge, Relay, Authority (directory or bridge) and Onion service. They can be combined sometimes. For instance, a Directory Authority is also a Relay. This adds a "roles" field to a periodic event item object which is used to know for which roles the event is for. The next step is to enable the event only if the roles apply. No behavior change at this commit. Pars of #25762 Signed-off-by: David Goulet --- src/or/periodic.h | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'src/or/periodic.h') diff --git a/src/or/periodic.h b/src/or/periodic.h index 285400b8bb..4544ae2763 100644 --- a/src/or/periodic.h +++ b/src/or/periodic.h @@ -6,6 +6,29 @@ #define PERIODIC_EVENT_NO_UPDATE (-1) +/* Tor roles for which a periodic event item is for. An event can be for + * multiple roles, they can be combined. */ +#define PERIODIC_EVENT_ROLE_CLIENT (1U << 0) +#define PERIODIC_EVENT_ROLE_RELAY (1U << 1) +#define PERIODIC_EVENT_ROLE_BRIDGE (1U << 2) +#define PERIODIC_EVENT_ROLE_DIRAUTH (1U << 3) +#define PERIODIC_EVENT_ROLE_BRIDGEAUTH (1U << 4) +#define PERIODIC_EVENT_ROLE_HS_SERVICE (1U << 5) + +/* Helper macro to make it a bit less annoying to defined groups of roles that + * are often used. */ + +/* Router that is a Bridge or Relay. */ +#define PERIODIC_EVENT_ROLE_ROUTER \ + (PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_RELAY) +/* Authorities that is both bridge and directory. */ +#define PERIODIC_EVENT_ROLE_AUTHORITIES \ + (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_DIRAUTH) +/* All roles. */ +#define PERIODIC_EVENT_ROLE_ALL \ + (PERIODIC_EVENT_ROLE_AUTHORITIES | PERIODIC_EVENT_ROLE_CLIENT | \ + PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_ROUTER) + /** Callback function for a periodic event to take action. The return value * influences the next time the function will get called. Return * PERIODIC_EVENT_NO_UPDATE to not update last_action_time and be polled @@ -23,11 +46,14 @@ typedef struct periodic_event_item_t { struct mainloop_event_t *ev; /**< Libevent callback we're using to implement * this */ const char *name; /**< Name of the function -- for debug */ + + /* Bitmask of roles define above for which this event applies. */ + uint32_t roles; } periodic_event_item_t; /** events will get their interval from first execution */ -#define PERIODIC_EVENT(fn) { fn##_callback, 0, NULL, #fn } -#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL } +#define PERIODIC_EVENT(fn, r) { fn##_callback, 0, NULL, #fn, r } +#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL, 0 } void periodic_event_launch(periodic_event_item_t *event); void periodic_event_setup(periodic_event_item_t *event); -- cgit v1.2.3-54-g00ecf From a4fcdc5decfe60bbd95aee2e5586e90c40b73225 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 17 Apr 2018 09:31:50 -0400 Subject: main: Launch periodic events by roles Signed-off-by: David Goulet --- src/or/main.c | 40 +++++++++++++++++++++++++++++++++++++--- src/or/networkstatus.c | 2 +- src/or/networkstatus.h | 2 ++ src/or/periodic.c | 12 +++++++++++- src/or/periodic.h | 14 ++++++++++++-- 5 files changed, 63 insertions(+), 7 deletions(-) (limited to 'src/or/periodic.h') diff --git a/src/or/main.c b/src/or/main.c index 5ca6277c15..52e83822ef 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1437,6 +1437,32 @@ find_periodic_event(const char *name) return NULL; } +/** Return a bitmask of the roles this tor instance is configured for using + * the given options. */ +static int +get_my_roles(const or_options_t *options) +{ + tor_assert(options); + + int roles = 0; + int is_bridge = options->BridgeRelay; + int is_client = 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); + int is_hidden_service = !!hs_service_get_num_services() || + !!rend_num_services(); + + if (is_bridge) roles |= PERIODIC_EVENT_ROLE_BRIDGE; + if (is_client) roles |= PERIODIC_EVENT_ROLE_CLIENT; + if (is_relay) roles |= PERIODIC_EVENT_ROLE_RELAY; + if (is_dirauth) roles |= PERIODIC_EVENT_ROLE_DIRAUTH; + if (is_bridgeauth) roles |= PERIODIC_EVENT_ROLE_BRIDGEAUTH; + if (is_hidden_service) roles |= PERIODIC_EVENT_ROLE_HS_SERVICE; + + return roles; +} + /** Event to run initialize_periodic_events_cb */ static struct event *initialize_periodic_events_event = NULL; @@ -1451,10 +1477,17 @@ initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data) (void) fd; (void) events; (void) data; + + int roles = get_my_roles(get_options()); + tor_event_free(initialize_periodic_events_event); - int i; - for (i = 0; periodic_events[i].name; ++i) { - periodic_event_launch(&periodic_events[i]); + + for (int i = 0; periodic_events[i].name; ++i) { + periodic_event_item_t *item = &periodic_events[i]; + if (item->roles & roles && !periodic_event_is_enabled(item)) { + periodic_event_launch(item); + log_debug(LD_GENERAL, "Launching periodic event %s", item->name); + } } } @@ -1466,6 +1499,7 @@ initialize_periodic_events(void) tor_assert(periodic_events_initialized == 0); periodic_events_initialized = 1; + /* Set up all periodic events. We'll launch them by roles. */ int i; for (i = 0; periodic_events[i].name; ++i) { periodic_event_setup(&periodic_events[i]); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 5f19792c7d..b0db0cecbc 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1680,7 +1680,7 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c, * XXXX If we need this elsewhere at any point, we should make it nonstatic * XXXX and move it into another file. */ -static int +int any_client_port_set(const or_options_t *options) { return (options->SocksPort_set || diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 1851a55e82..04cf277d2f 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -140,6 +140,8 @@ 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 fa40965de1..42bea3ae65 100644 --- a/src/or/periodic.c +++ b/src/or/periodic.c @@ -78,7 +78,10 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data) void periodic_event_reschedule(periodic_event_item_t *event) { - periodic_event_set_interval(event, 1); + /* Don't reschedule a disabled event. */ + if (periodic_event_is_enabled(event)) { + periodic_event_set_interval(event, 1); + } } /** Initializes the libevent backend for a periodic event. */ @@ -104,9 +107,15 @@ periodic_event_launch(periodic_event_item_t *event) log_err(LD_BUG, "periodic_event_launch without periodic_event_setup"); tor_assert(0); } + /* Event already enabled? This is a bug */ + if (periodic_event_is_enabled(event)) { + log_err(LD_BUG, "periodic_event_launch on an already enabled event"); + tor_assert(0); + } // Initial dispatch periodic_event_dispatch(event->ev, event); + event->enabled = 1; } /** Release all storage associated with event */ @@ -117,5 +126,6 @@ periodic_event_destroy(periodic_event_item_t *event) return; mainloop_event_free(event->ev); event->last_action_time = 0; + event->enabled = 0; } diff --git a/src/or/periodic.h b/src/or/periodic.h index 4544ae2763..1b346a1cc8 100644 --- a/src/or/periodic.h +++ b/src/or/periodic.h @@ -49,11 +49,21 @@ typedef struct periodic_event_item_t { /* Bitmask of roles define above for which this event applies. */ uint32_t roles; + /* Indicate that this event has been enabled that is scheduled. */ + unsigned int enabled : 1; } periodic_event_item_t; /** events will get their interval from first execution */ -#define PERIODIC_EVENT(fn, r) { fn##_callback, 0, NULL, #fn, r } -#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL, 0 } +#define PERIODIC_EVENT(fn, r) { fn##_callback, 0, NULL, #fn, r, 0 } +#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL, 0, 0 } + +/* Return true iff the given event was setup before thus is enabled to be + * scheduled. */ +static inline int +periodic_event_is_enabled(const periodic_event_item_t *item) +{ + return item->enabled; +} void periodic_event_launch(periodic_event_item_t *event); void periodic_event_setup(periodic_event_item_t *event); -- cgit v1.2.3-54-g00ecf From 4e85f17eec06fc963c33cc3f637876e7f1627a57 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 17 Apr 2018 09:57:09 -0400 Subject: periodic: Add an enable and disable function Two helper functions to enable an event and disable an event which wraps the launch and destroy of an event but takes care of the enabled flag. They are also idempotent that is can be called multiple time on the same event without effect if the event was already enabled or disabled. Signed-off-by: David Goulet --- src/or/main.c | 22 +++++++++------------- src/or/periodic.c | 41 ++++++++++++++++++++++++++++++++++++++++- src/or/periodic.h | 2 ++ 3 files changed, 51 insertions(+), 14 deletions(-) (limited to 'src/or/periodic.h') diff --git a/src/or/main.c b/src/or/main.c index eb8835198a..1c5ac198b0 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1484,8 +1484,9 @@ initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data) for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; - if (item->roles & roles && !periodic_event_is_enabled(item)) { - periodic_event_launch(item); + if (item->roles & roles) { + /* This is safe to be called on an already enabled event. */ + periodic_event_enable(item); log_debug(LD_GENERAL, "Launching periodic event %s", item->name); } } @@ -1541,18 +1542,13 @@ rescan_periodic_events(const or_options_t *options) for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; - int is_enabled = periodic_event_is_enabled(item); - int need_item = (item->roles & roles); - /* We need this event but it is *not* enabled. */ - if (need_item && !is_enabled) { - periodic_event_launch(item); - continue; - } - /* We do *not* need this event but it is enabled. */ - if (!need_item && is_enabled) { - periodic_event_destroy(item); - continue; + /* Enable the event if needed. It is safe to enable an event that was + * already enabled. Same goes for disabling it. */ + if (item->roles & roles) { + periodic_event_enable(item); + } else { + periodic_event_disable(item); } } } diff --git a/src/or/periodic.c b/src/or/periodic.c index 42bea3ae65..76aa418b35 100644 --- a/src/or/periodic.c +++ b/src/or/periodic.c @@ -43,12 +43,22 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data) periodic_event_item_t *event = data; tor_assert(ev == event->ev); + if (BUG(!periodic_event_is_enabled(event))) { + return; + } + time_t now = time(NULL); const or_options_t *options = get_options(); // log_debug(LD_GENERAL, "Dispatching %s", event->name); int r = event->fn(now, options); int next_interval = 0; + if (!periodic_event_is_enabled(event)) { + /* The event got disabled from inside its callback; no need to + * reschedule. */ + return; + } + /* update the last run time if action was taken */ if (r==0) { log_err(LD_BUG, "Invalid return value for periodic event from %s.", @@ -114,8 +124,8 @@ periodic_event_launch(periodic_event_item_t *event) } // Initial dispatch - periodic_event_dispatch(event->ev, event); event->enabled = 1; + periodic_event_dispatch(event->ev, event); } /** Release all storage associated with event */ @@ -126,6 +136,35 @@ periodic_event_destroy(periodic_event_item_t *event) return; mainloop_event_free(event->ev); event->last_action_time = 0; +} + +/** Enable the given event which means the event is launched and then the + * event's enabled flag is set. This can be called for an event that is + * already enabled. */ +void +periodic_event_enable(periodic_event_item_t *event) +{ + tor_assert(event); + /* Safely and silently ignore if this event is already enabled. */ + if (periodic_event_is_enabled(event)) { + return; + } + + periodic_event_launch(event); +} + +/** Disable the given event which means the event is destroyed and then the + * event's enabled flag is unset. This can be called for an event that is + * already disabled. */ +void +periodic_event_disable(periodic_event_item_t *event) +{ + tor_assert(event); + /* Safely and silently ignore if this event is already disabled. */ + if (!periodic_event_is_enabled(event)) { + return; + } + mainloop_event_cancel(event->ev); event->enabled = 0; } diff --git a/src/or/periodic.h b/src/or/periodic.h index 1b346a1cc8..dde27db5af 100644 --- a/src/or/periodic.h +++ b/src/or/periodic.h @@ -69,6 +69,8 @@ void periodic_event_launch(periodic_event_item_t *event); void periodic_event_setup(periodic_event_item_t *event); void periodic_event_destroy(periodic_event_item_t *event); void periodic_event_reschedule(periodic_event_item_t *event); +void periodic_event_enable(periodic_event_item_t *event); +void periodic_event_disable(periodic_event_item_t *event); #endif /* !defined(TOR_PERIODIC_H) */ -- cgit v1.2.3-54-g00ecf