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