]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/watchdog.c
tree-wide: remove Lennart's copyright lines
[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;
e96d6be7
LP
19
20static int update_timeout(void) {
21 int r;
22
23 if (watchdog_fd < 0)
24 return 0;
25
3a43da28 26 if (watchdog_timeout == USEC_INFINITY)
e96d6be7
LP
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);
4a62c710
MS
33 if (r < 0)
34 return log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
e96d6be7
LP
35 } else {
36 int sec, flags;
37 char buf[FORMAT_TIMESPAN_MAX];
38
be6b0c21 39 sec = (int) DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC);
e96d6be7 40 r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
4a62c710
MS
41 if (r < 0)
42 return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec);
e96d6be7
LP
43
44 watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
2fa4092c 45 log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0));
e96d6be7
LP
46
47 flags = WDIOS_ENABLECARD;
48 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
14f494c7
JD
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 }
e96d6be7
LP
56
57 r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
4a62c710
MS
58 if (r < 0)
59 return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
e96d6be7
LP
60 }
61
62 return 0;
63}
64
65static int open_watchdog(void) {
66 struct watchdog_info ident;
67
68 if (watchdog_fd >= 0)
69 return 0;
70
e4c98db3
EJ
71 watchdog_fd = open(watchdog_device ?: "/dev/watchdog",
72 O_WRONLY|O_CLOEXEC);
e96d6be7
LP
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
e4c98db3
EJ
84int watchdog_set_device(char *path) {
85 return free_and_strdup(&watchdog_device, path);
86}
87
e96d6be7 88int watchdog_set_timeout(usec_t *usec) {
56bcbfa5 89 int r;
e96d6be7
LP
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 */
3a43da28 95 if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY)
e96d6be7
LP
96 return 0;
97
98 if (watchdog_fd < 0)
56bcbfa5 99 r = open_watchdog();
e96d6be7 100 else
56bcbfa5 101 r = update_timeout();
e96d6be7
LP
102
103 *usec = watchdog_timeout;
56bcbfa5
MO
104
105 return r;
e96d6be7
LP
106}
107
108int 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);
4a62c710
MS
118 if (r < 0)
119 return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
e96d6be7
LP
120
121 return 0;
122}
123
124void 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)
56f64d95 137 log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
e96d6be7
LP
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) {
56f64d95 147 log_error_errno(errno, "Failed to disarm watchdog timer: %m");
e96d6be7
LP
148 break;
149 }
150 }
151 }
152
03e334a1 153 watchdog_fd = safe_close(watchdog_fd);
e96d6be7 154}