Branch data Line data Source code
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 : 0 : static int update_timeout(void) {
21 : : int r;
22 : :
23 [ # # ]: 0 : if (watchdog_fd < 0)
24 : 0 : return 0;
25 : :
26 [ # # ]: 0 : if (watchdog_timeout == USEC_INFINITY)
27 : 0 : return 0;
28 [ # # ]: 0 : else if (watchdog_timeout == 0) {
29 : : int flags;
30 : :
31 : 0 : flags = WDIOS_DISABLECARD;
32 : 0 : r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
33 [ # # ]: 0 : if (r < 0)
34 [ # # ]: 0 : 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 : 0 : sec = (int) DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC);
40 : 0 : r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
41 [ # # ]: 0 : if (r < 0)
42 [ # # ]: 0 : return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec);
43 : :
44 : 0 : watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
45 [ # # ]: 0 : log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0));
46 : :
47 : 0 : flags = WDIOS_ENABLECARD;
48 : 0 : r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
49 [ # # ]: 0 : if (r < 0) {
50 : : /* ENOTTY means the watchdog is always enabled so we're fine */
51 [ # # # # ]: 0 : log_full(errno == ENOTTY ? LOG_DEBUG : LOG_WARNING,
52 : : "Failed to enable hardware watchdog: %m");
53 [ # # ]: 0 : if (errno != ENOTTY)
54 : 0 : return -errno;
55 : : }
56 : :
57 : 0 : r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
58 [ # # ]: 0 : if (r < 0)
59 [ # # ]: 0 : return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
60 : : }
61 : :
62 : 0 : return 0;
63 : : }
64 : :
65 : 24 : static int open_watchdog(void) {
66 : : struct watchdog_info ident;
67 : :
68 [ - + ]: 24 : if (watchdog_fd >= 0)
69 : 0 : return 0;
70 : :
71 [ - + ]: 24 : watchdog_fd = open(watchdog_device ?: "/dev/watchdog",
72 : : O_WRONLY|O_CLOEXEC);
73 [ + - ]: 24 : if (watchdog_fd < 0)
74 : 24 : return -errno;
75 : :
76 [ # # ]: 0 : if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0)
77 [ # # ]: 0 : log_info("Hardware watchdog '%s', version %x",
78 : : ident.identity,
79 : : ident.firmware_version);
80 : :
81 : 0 : return update_timeout();
82 : : }
83 : :
84 : 0 : int watchdog_set_device(char *path) {
85 : : int r;
86 : :
87 : 0 : r = free_and_strdup(&watchdog_device, path);
88 [ # # ]: 0 : if (r < 0)
89 : 0 : return r;
90 : :
91 [ # # ]: 0 : if (r > 0) /* watchdog_device changed */
92 : 0 : watchdog_fd = safe_close(watchdog_fd);
93 : :
94 : 0 : return r;
95 : : }
96 : :
97 : 4 : int watchdog_set_timeout(usec_t *usec) {
98 : : int r;
99 : :
100 : 4 : watchdog_timeout = *usec;
101 : :
102 : : /* If we didn't open the watchdog yet and didn't get any
103 : : * explicit timeout value set, don't do anything */
104 [ + - - + ]: 4 : if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY)
105 : 0 : return 0;
106 : :
107 [ + - ]: 4 : if (watchdog_fd < 0)
108 : 4 : r = open_watchdog();
109 : : else
110 : 0 : r = update_timeout();
111 : :
112 : 4 : *usec = watchdog_timeout;
113 : :
114 : 4 : return r;
115 : : }
116 : :
117 : 20 : int watchdog_ping(void) {
118 : : int r;
119 : :
120 [ + - ]: 20 : if (watchdog_fd < 0) {
121 : 20 : r = open_watchdog();
122 [ + - ]: 20 : if (r < 0)
123 : 20 : return r;
124 : : }
125 : :
126 : 0 : r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
127 [ # # ]: 0 : if (r < 0)
128 [ # # ]: 0 : return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
129 : :
130 : 0 : return 0;
131 : : }
132 : :
133 : 4 : void watchdog_close(bool disarm) {
134 : : int r;
135 : :
136 [ + - ]: 4 : if (watchdog_fd < 0)
137 : 4 : return;
138 : :
139 [ # # ]: 0 : if (disarm) {
140 : : int flags;
141 : :
142 : : /* Explicitly disarm it */
143 : 0 : flags = WDIOS_DISABLECARD;
144 : 0 : r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
145 [ # # ]: 0 : if (r < 0)
146 [ # # ]: 0 : log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
147 : :
148 : : /* To be sure, use magic close logic, too */
149 : 0 : for (;;) {
150 : : static const char v = 'V';
151 : :
152 [ # # ]: 0 : if (write(watchdog_fd, &v, 1) > 0)
153 : 0 : break;
154 : :
155 [ # # ]: 0 : if (errno != EINTR) {
156 [ # # ]: 0 : log_error_errno(errno, "Failed to disarm watchdog timer: %m");
157 : 0 : break;
158 : : }
159 : : }
160 : : }
161 : :
162 : 0 : watchdog_fd = safe_close(watchdog_fd);
163 : : }
|