summaryrefslogtreecommitdiff
path: root/ecp/server/timer.c
blob: 7a477abfad3df4fe1a84615cd561c7e2862fd42e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

#include <ecp/core.h>
#include <ecp/dir/dir.h>

#include "dir.h"

#include "server.h"
#include "timer.h"

static struct timespec timer_ts_mono;
static timer_t timer_ann_block_id;
static timer_t timer_online_switch_id;

int timer_set_next(time_t tv_now) {
    struct itimerspec its = { 0 };
    time_t tv_midnight;
    int rv = 0;

    /* next midnight */
    tv_midnight = (tv_now / ONLINE_SWITCH_PERIOD) * ONLINE_SWITCH_PERIOD + ONLINE_SWITCH_PERIOD;

    if (tv_now < (tv_midnight - ANN_BLOCK_TIME)) {
        dir_announce_allow();

        its.it_value.tv_sec = tv_midnight - ANN_BLOCK_TIME;
        rv = timer_settime(timer_ann_block_id, TIMER_ABSTIME, &its, NULL);
        if (rv) return ECP_ERR;
    } else {
        dir_announce_block();
    }

    its.it_value.tv_sec = tv_midnight;
    rv = timer_settime(timer_online_switch_id, TIMER_ABSTIME, &its, NULL);
    if (rv) return ECP_ERR;

    return ECP_OK;
}

void timer_ann_block(union sigval timer_data) {
    dir_announce_block();
}

void timer_online_switch(union sigval timer_data) {
    ECPSocket *sock = timer_data.sival_ptr;
    struct timespec ts_prev;
    int rv;

    ts_prev = timer_ts_mono;
    clock_gettime(CLOCK_MONOTONIC, &timer_ts_mono);

    /* exit if someone is messing with realtime clock */
    if ((timer_ts_mono.tv_sec - ts_prev.tv_sec) < (ONLINE_SWITCH_PERIOD / 2)) goto online_switch_fin;

    dir_online_switch(sock, 1);

online_switch_fin:
    rv = timer_set_next(time(NULL));
    if (rv) LOG(LOG_ERR, "timer_online_switch: set next timer err:%d\n", rv);
}

int timer_init(ECPSocket *sock) {
    struct sigevent timer_ann_block_evt = { 0 };
    struct sigevent timer_online_switch_evt = { 0 };
    time_t tv_now;
    int rv = 0;

    timer_ann_block_evt.sigev_notify = SIGEV_THREAD;
    timer_ann_block_evt.sigev_notify_function = timer_ann_block;
    timer_ann_block_evt.sigev_value.sival_ptr = NULL;
    rv = timer_create(CLOCK_REALTIME, &timer_ann_block_evt, &timer_ann_block_id);
    if (rv) return ECP_ERR;

    timer_online_switch_evt.sigev_notify = SIGEV_THREAD;
    timer_online_switch_evt.sigev_notify_function = timer_online_switch;
    timer_online_switch_evt.sigev_value.sival_ptr = sock;
    rv = timer_create(CLOCK_REALTIME, &timer_online_switch_evt, &timer_online_switch_id);
    if (rv) {
        timer_delete(timer_ann_block_id);
        return ECP_ERR;
    }

    /* ensure that first dir_online_switch is executed */
    clock_gettime(CLOCK_MONOTONIC, &timer_ts_mono);
    timer_ts_mono.tv_sec -= ONLINE_SWITCH_PERIOD / 2;

    tv_now = time(NULL);
    dit_init_serial(tv_now / ONLINE_SWITCH_PERIOD);
    rv = timer_set_next(tv_now);
    if (rv) {
        timer_delete(timer_ann_block_id);
        timer_delete(timer_online_switch_id);
        return rv;
    }

    return ECP_OK;
}