#include "core/or/or.h"
#include "lib/evloop/compat_libevent.h"
#include "app/config/config.h"
#include "core/mainloop/mainloop.h"
#include "core/mainloop/periodic.h"
static const int MAX_INTERVAL = 10 * 365 * 86400;
static smartlist_t *the_periodic_events = NULL;
static void
periodic_event_set_interval(periodic_event_item_t *event,
time_t next_interval)
{
tor_assert(next_interval < MAX_INTERVAL);
struct timeval tv;
tv.tv_sec = next_interval;
tv.tv_usec = 0;
mainloop_event_schedule(event->ev, &tv);
}
static void
periodic_event_dispatch(mainloop_event_t *ev, void *data)
{
periodic_event_item_t *event = data;
tor_assert(ev == event->ev);
time_t now = time(NULL);
update_current_time(now);
const or_options_t *options = get_options();
int r = event->fn(now, options);
int next_interval = 0;
if (!periodic_event_is_enabled(event)) {
return;
}
if (r==0) {
log_err(LD_BUG, "Invalid return value for periodic event from %s.",
event->name);
tor_assert(r != 0);
} else if (r > 0) {
event->last_action_time = now;
tor_assert(r < MAX_INTERVAL);
next_interval = r;
} else {
next_interval = 1;
}
struct timeval tv = { next_interval , 0 };
mainloop_event_schedule(ev, &tv);
}
void
periodic_event_reschedule(periodic_event_item_t *event)
{
if (event->ev && periodic_event_is_enabled(event)) {
periodic_event_set_interval(event, 1);
}
}
void
periodic_event_connect(periodic_event_item_t *event)
{
if (event->ev) {
log_err(LD_BUG, "Initial dispatch should only be done once.");
tor_assert(0);
}
event->ev = mainloop_event_new(periodic_event_dispatch,
event);
tor_assert(event->ev);
}
void
periodic_event_launch(periodic_event_item_t *event)
{
if (! event->ev) {
log_err(LD_BUG, "periodic_event_launch without periodic_event_connect");
tor_assert(0);
}
if (periodic_event_is_enabled(event)) {
log_err(LD_BUG, "periodic_event_launch on an already enabled event");
tor_assert(0);
}
event->enabled = 1;
periodic_event_dispatch(event->ev, event);
}
static void
periodic_event_disconnect(periodic_event_item_t *event)
{
if (!event)
return;
periodic_event_disable(event);
mainloop_event_free(event->ev);
event->last_action_time = 0;
}
void
periodic_event_enable(periodic_event_item_t *event)
{
tor_assert(event);
if (periodic_event_is_enabled(event)) {
return;
}
tor_assert(event->ev);
event->enabled = 1;
mainloop_event_activate(event->ev);
}
void
periodic_event_disable(periodic_event_item_t *event)
{
tor_assert(event);
if (!periodic_event_is_enabled(event)) {
return;
}
mainloop_event_cancel(event->ev);
event->enabled = 0;
}
void
periodic_event_schedule_and_disable(periodic_event_item_t *event)
{
tor_assert(event);
if (!periodic_event_is_enabled(event))
return;
periodic_event_disable(event);
mainloop_event_activate(event->ev);
}
void
periodic_events_register(periodic_event_item_t *item)
{
if (!the_periodic_events)
the_periodic_events = smartlist_new();
if (BUG(smartlist_contains(the_periodic_events, item)))
return;
smartlist_add(the_periodic_events, item);
}
void
periodic_events_connect_all(void)
{
if (! the_periodic_events)
return;
SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
if (item->ev)
continue;
periodic_event_connect(item);
} SMARTLIST_FOREACH_END(item);
}
void
periodic_events_reset_all(void)
{
if (! the_periodic_events)
return;
SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
if (!item->ev)
continue;
periodic_event_reschedule(item);
} SMARTLIST_FOREACH_END(item);
}
periodic_event_item_t *
periodic_events_find(const char *name)
{
if (! the_periodic_events)
return NULL;
SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
if (strcmp(name, item->name) == 0)
return item;
} SMARTLIST_FOREACH_END(item);
return NULL;
}
void
periodic_events_rescan_by_roles(int roles, bool net_disabled)
{
if (! the_periodic_events)
return;
SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
if (!item->ev)
continue;
int enable = !!(item->roles & roles);
if (net_disabled &&
(item->flags & PERIODIC_EVENT_FLAG_NEED_NET)) {
enable = 0;
}
if (enable) {
log_debug(LD_GENERAL, "Launching periodic event %s", item->name);
periodic_event_enable(item);
} else {
log_debug(LD_GENERAL, "Disabling periodic event %s", item->name);
if (item->flags & PERIODIC_EVENT_FLAG_RUN_ON_DISABLE) {
periodic_event_schedule_and_disable(item);
} else {
periodic_event_disable(item);
}
}
} SMARTLIST_FOREACH_END(item);
}
void
periodic_events_disconnect_all(void)
{
if (! the_periodic_events)
return;
SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
periodic_event_disconnect(item);
} SMARTLIST_FOREACH_END(item);
smartlist_free(the_periodic_events);
}
#define LONGEST_TIMER_PERIOD (30 * 86400)
int
safe_timer_diff(time_t now, time_t next)
{
if (next > now) {
tor_assert(next > TIME_MIN + LONGEST_TIMER_PERIOD);
if (next - LONGEST_TIMER_PERIOD > now)
return LONGEST_TIMER_PERIOD;
return (int)(next - now);
} else {
return 1;
}
}