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