]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/watchdog.c
c423af6917a534e164d303d6e442237d394b3330
[thirdparty/systemd.git] / src / shared / watchdog.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <sys/ioctl.h>
6 #include <syslog.h>
7 #include <unistd.h>
8 #include <linux/watchdog.h>
9
10 #include "fd-util.h"
11 #include "log.h"
12 #include "string-util.h"
13 #include "time-util.h"
14 #include "watchdog.h"
15
16 static int watchdog_fd = -1;
17 static char *watchdog_device = NULL;
18 static usec_t watchdog_timeout = USEC_INFINITY;
19
20 static int update_timeout(void) {
21 int r;
22
23 if (watchdog_fd < 0)
24 return 0;
25
26 if (watchdog_timeout == USEC_INFINITY)
27 return 0;
28 else if (watchdog_timeout == 0) {
29 int flags;
30
31 flags = WDIOS_DISABLECARD;
32 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
33 if (r < 0)
34 return log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
35 } else {
36 int sec, flags;
37 char buf[FORMAT_TIMESPAN_MAX];
38
39 sec = (int) DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC);
40 r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
41 if (r < 0)
42 return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec);
43
44 watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
45 log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0));
46
47 flags = WDIOS_ENABLECARD;
48 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
49 if (r < 0) {
50 /* ENOTTY means the watchdog is always enabled so we're fine */
51 log_full(errno == ENOTTY ? LOG_DEBUG : LOG_WARNING,
52 "Failed to enable hardware watchdog: %m");
53 if (errno != ENOTTY)
54 return -errno;
55 }
56
57 r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
58 if (r < 0)
59 return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
60 }
61
62 return 0;
63 }
64
65 static int open_watchdog(void) {
66 struct watchdog_info ident;
67
68 if (watchdog_fd >= 0)
69 return 0;
70
71 watchdog_fd = open(watchdog_device ?: "/dev/watchdog",
72 O_WRONLY|O_CLOEXEC);
73 if (watchdog_fd < 0)
74 return -errno;
75
76 if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0)
77 log_info("Hardware watchdog '%s', version %x",
78 ident.identity,
79 ident.firmware_version);
80
81 return update_timeout();
82 }
83
84 int watchdog_set_device(char *path) {
85 return free_and_strdup(&watchdog_device, path);
86 }
87
88 int watchdog_set_timeout(usec_t *usec) {
89 int r;
90
91 watchdog_timeout = *usec;
92
93 /* If we didn't open the watchdog yet and didn't get any
94 * explicit timeout value set, don't do anything */
95 if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY)
96 return 0;
97
98 if (watchdog_fd < 0)
99 r = open_watchdog();
100 else
101 r = update_timeout();
102
103 *usec = watchdog_timeout;
104
105 return r;
106 }
107
108 int watchdog_ping(void) {
109 int r;
110
111 if (watchdog_fd < 0) {
112 r = open_watchdog();
113 if (r < 0)
114 return r;
115 }
116
117 r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
118 if (r < 0)
119 return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
120
121 return 0;
122 }
123
124 void watchdog_close(bool disarm) {
125 int r;
126
127 if (watchdog_fd < 0)
128 return;
129
130 if (disarm) {
131 int flags;
132
133 /* Explicitly disarm it */
134 flags = WDIOS_DISABLECARD;
135 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
136 if (r < 0)
137 log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
138
139 /* To be sure, use magic close logic, too */
140 for (;;) {
141 static const char v = 'V';
142
143 if (write(watchdog_fd, &v, 1) > 0)
144 break;
145
146 if (errno != EINTR) {
147 log_error_errno(errno, "Failed to disarm watchdog timer: %m");
148 break;
149 }
150 }
151 }
152
153 watchdog_fd = safe_close(watchdog_fd);
154 }