]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/watchdog.c
tree-wide: fix spelling errors
[thirdparty/systemd.git] / src / shared / watchdog.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e96d6be7 2
e96d6be7
LP
3#include <errno.h>
4#include <fcntl.h>
cf0fbc49 5#include <sys/ioctl.h>
a8fbdf54 6#include <syslog.h>
e96d6be7
LP
7#include <unistd.h>
8#include <linux/watchdog.h>
9
3ffd4af2 10#include "fd-util.h"
cf0fbc49 11#include "log.h"
e4c98db3 12#include "string-util.h"
a8fbdf54 13#include "time-util.h"
cf0fbc49 14#include "watchdog.h"
e96d6be7
LP
15
16static int watchdog_fd = -1;
e4c98db3 17static char *watchdog_device = NULL;
3a43da28 18static usec_t watchdog_timeout = USEC_INFINITY;
c5f8a179 19static usec_t watchdog_last_ping = USEC_INFINITY;
e96d6be7
LP
20
21static int update_timeout(void) {
22 int r;
23
24 if (watchdog_fd < 0)
25 return 0;
26
3a43da28 27 if (watchdog_timeout == USEC_INFINITY)
e96d6be7
LP
28 return 0;
29 else if (watchdog_timeout == 0) {
30 int flags;
31
32 flags = WDIOS_DISABLECARD;
33 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
4a62c710
MS
34 if (r < 0)
35 return log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
e96d6be7
LP
36 } else {
37 int sec, flags;
38 char buf[FORMAT_TIMESPAN_MAX];
39
be6b0c21 40 sec = (int) DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC);
e96d6be7 41 r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
4a62c710
MS
42 if (r < 0)
43 return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec);
e96d6be7
LP
44
45 watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
2fa4092c 46 log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0));
e96d6be7
LP
47
48 flags = WDIOS_ENABLECARD;
49 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
14f494c7
JD
50 if (r < 0) {
51 /* ENOTTY means the watchdog is always enabled so we're fine */
52 log_full(errno == ENOTTY ? LOG_DEBUG : LOG_WARNING,
53 "Failed to enable hardware watchdog: %m");
54 if (errno != ENOTTY)
55 return -errno;
56 }
e96d6be7
LP
57
58 r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
4a62c710
MS
59 if (r < 0)
60 return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
c5f8a179
AP
61
62 watchdog_last_ping = now(clock_boottime_or_monotonic());
e96d6be7
LP
63 }
64
65 return 0;
66}
67
68static int open_watchdog(void) {
69 struct watchdog_info ident;
70
71 if (watchdog_fd >= 0)
72 return 0;
73
e4c98db3
EJ
74 watchdog_fd = open(watchdog_device ?: "/dev/watchdog",
75 O_WRONLY|O_CLOEXEC);
e96d6be7
LP
76 if (watchdog_fd < 0)
77 return -errno;
78
79 if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0)
80 log_info("Hardware watchdog '%s', version %x",
81 ident.identity,
82 ident.firmware_version);
83
84 return update_timeout();
85}
86
e4c98db3 87int watchdog_set_device(char *path) {
1fedf138
ZJS
88 int r;
89
90 r = free_and_strdup(&watchdog_device, path);
91 if (r < 0)
92 return r;
93
94 if (r > 0) /* watchdog_device changed */
95 watchdog_fd = safe_close(watchdog_fd);
96
97 return r;
e4c98db3
EJ
98}
99
e96d6be7 100int watchdog_set_timeout(usec_t *usec) {
56bcbfa5 101 int r;
e96d6be7
LP
102
103 watchdog_timeout = *usec;
104
105 /* If we didn't open the watchdog yet and didn't get any
106 * explicit timeout value set, don't do anything */
3a43da28 107 if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY)
e96d6be7
LP
108 return 0;
109
110 if (watchdog_fd < 0)
56bcbfa5 111 r = open_watchdog();
e96d6be7 112 else
56bcbfa5 113 r = update_timeout();
e96d6be7
LP
114
115 *usec = watchdog_timeout;
56bcbfa5
MO
116
117 return r;
e96d6be7
LP
118}
119
c5f8a179
AP
120usec_t watchdog_runtime_wait(void) {
121 usec_t rtwait;
122 usec_t ntime;
123
124 if (!timestamp_is_set(watchdog_timeout))
125 return USEC_INFINITY;
126
86b52a39 127 /* Sleep half the watchdog timeout since the last successful ping at most */
c5f8a179
AP
128 if (timestamp_is_set(watchdog_last_ping)) {
129 ntime = now(clock_boottime_or_monotonic());
130 assert(ntime >= watchdog_last_ping);
131 rtwait = usec_sub_unsigned(watchdog_last_ping + (watchdog_timeout / 2), ntime);
132 } else
133 rtwait = watchdog_timeout / 2;
134
135 return rtwait;
136}
137
e96d6be7 138int watchdog_ping(void) {
c5f8a179 139 usec_t ntime;
e96d6be7
LP
140 int r;
141
c5f8a179
AP
142 ntime = now(clock_boottime_or_monotonic());
143
144 /* Never ping earlier than watchdog_timeout/4 and try to ping
145 * by watchdog_timeout/2 plus scheduling latencies the latest */
146 if (timestamp_is_set(watchdog_last_ping)) {
147 assert(ntime >= watchdog_last_ping);
148 if ((ntime - watchdog_last_ping) < (watchdog_timeout / 4))
149 return 0;
150 }
151
e96d6be7
LP
152 if (watchdog_fd < 0) {
153 r = open_watchdog();
154 if (r < 0)
155 return r;
156 }
157
158 r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
4a62c710
MS
159 if (r < 0)
160 return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
e96d6be7 161
c5f8a179
AP
162 watchdog_last_ping = ntime;
163
e96d6be7
LP
164 return 0;
165}
166
167void watchdog_close(bool disarm) {
168 int r;
169
170 if (watchdog_fd < 0)
171 return;
172
173 if (disarm) {
174 int flags;
175
176 /* Explicitly disarm it */
177 flags = WDIOS_DISABLECARD;
178 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
179 if (r < 0)
56f64d95 180 log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
e96d6be7
LP
181
182 /* To be sure, use magic close logic, too */
183 for (;;) {
184 static const char v = 'V';
185
186 if (write(watchdog_fd, &v, 1) > 0)
187 break;
188
189 if (errno != EINTR) {
56f64d95 190 log_error_errno(errno, "Failed to disarm watchdog timer: %m");
e96d6be7
LP
191 break;
192 }
193 }
194 }
195
03e334a1 196 watchdog_fd = safe_close(watchdog_fd);
e96d6be7 197}