Add optional systemd integration
[tinc] / src / linux / watchdog.c
1 #include "../system.h"
2
3 #include <systemd/sd-daemon.h>
4
5 #include "../event.h"
6 #include "../logger.h"
7 #include "../watchdog.h"
8
9 static timeout_t timer;
10 static struct timeval interval;
11
12 static uint64_t second_to_microsecond(time_t second) {
13         return second * 1000000;
14 }
15
16 static time_t microsecond_to_second(uint64_t micros) {
17         return (time_t)(micros / 1000000);
18 }
19
20 // Ignore errors from sd_notify() since there's nothing we can do if it breaks anyway.
21 // Also, there's this passage in `man sd_notify.3`:
22 //     In order to support both service managers that implement this scheme and those
23 //     which do not, it is generally recommended to ignore the return value of this call.
24 void watchdog_ping(void) {
25         sd_notify(false, "WATCHDOG=1");
26 }
27
28 static void watchdog_handler(void *data) {
29         (void)data;
30         watchdog_ping();
31         timeout_set(&timer, &interval);
32 }
33
34 static bool watchdog_register(void) {
35         uint64_t timeout = 0;
36
37         if(sd_watchdog_enabled(false, &timeout) <= 0 || !timeout) {
38                 return false;
39         }
40
41         if(timeout < second_to_microsecond(2)) {
42                 logger(DEBUG_ALWAYS, LOG_WARNING, "Consider using a higher watchdog timeout. Spurious failures may occur.");
43         }
44
45         // Send notifications twice per timeout period
46         timeout /= 2;
47
48         interval.tv_sec = microsecond_to_second(timeout);
49
50         if(interval.tv_sec) {
51                 timeout -= second_to_microsecond(interval.tv_sec);
52         }
53
54         interval.tv_usec = (suseconds_t)timeout;
55
56         timeout_add(&timer, watchdog_handler, &timer, &interval);
57         watchdog_ping();
58
59         return true;
60 }
61
62 void watchdog_start(void) {
63         sd_notify(false, "READY=1");
64         bool enabled = watchdog_register();
65         logger(DEBUG_ALWAYS, LOG_INFO, "Watchdog %s", enabled ? "started" : "is disabled");
66 }
67
68 void watchdog_stop(void) {
69         sd_notify(false, "STOPPING=1");
70         timeout_del(&timer);
71 }