]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/watchdog.c
Update mailmap and contributor list (#7006)
[thirdparty/systemd.git] / src / shared / watchdog.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2012 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <sys/ioctl.h>
23 #include <syslog.h>
24 #include <unistd.h>
25 #include <linux/watchdog.h>
26
27 #include "fd-util.h"
28 #include "log.h"
29 #include "time-util.h"
30 #include "watchdog.h"
31
32 static int watchdog_fd = -1;
33 static usec_t watchdog_timeout = USEC_INFINITY;
34
35 static int update_timeout(void) {
36 int r;
37
38 if (watchdog_fd < 0)
39 return 0;
40
41 if (watchdog_timeout == USEC_INFINITY)
42 return 0;
43 else if (watchdog_timeout == 0) {
44 int flags;
45
46 flags = WDIOS_DISABLECARD;
47 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
48 if (r < 0)
49 return log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
50 } else {
51 int sec, flags;
52 char buf[FORMAT_TIMESPAN_MAX];
53
54 sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
55 r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
56 if (r < 0)
57 return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec);
58
59 watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
60 log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0));
61
62 flags = WDIOS_ENABLECARD;
63 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
64 if (r < 0) {
65 /* ENOTTY means the watchdog is always enabled so we're fine */
66 log_full(errno == ENOTTY ? LOG_DEBUG : LOG_WARNING,
67 "Failed to enable hardware watchdog: %m");
68 if (errno != ENOTTY)
69 return -errno;
70 }
71
72 r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
73 if (r < 0)
74 return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
75 }
76
77 return 0;
78 }
79
80 static int open_watchdog(void) {
81 struct watchdog_info ident;
82
83 if (watchdog_fd >= 0)
84 return 0;
85
86 watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC);
87 if (watchdog_fd < 0)
88 return -errno;
89
90 if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0)
91 log_info("Hardware watchdog '%s', version %x",
92 ident.identity,
93 ident.firmware_version);
94
95 return update_timeout();
96 }
97
98 int watchdog_set_timeout(usec_t *usec) {
99 int r;
100
101 watchdog_timeout = *usec;
102
103 /* If we didn't open the watchdog yet and didn't get any
104 * explicit timeout value set, don't do anything */
105 if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY)
106 return 0;
107
108 if (watchdog_fd < 0)
109 r = open_watchdog();
110 else
111 r = update_timeout();
112
113 *usec = watchdog_timeout;
114
115 return r;
116 }
117
118 int watchdog_ping(void) {
119 int r;
120
121 if (watchdog_fd < 0) {
122 r = open_watchdog();
123 if (r < 0)
124 return r;
125 }
126
127 r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
128 if (r < 0)
129 return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
130
131 return 0;
132 }
133
134 void watchdog_close(bool disarm) {
135 int r;
136
137 if (watchdog_fd < 0)
138 return;
139
140 if (disarm) {
141 int flags;
142
143 /* Explicitly disarm it */
144 flags = WDIOS_DISABLECARD;
145 r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
146 if (r < 0)
147 log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
148
149 /* To be sure, use magic close logic, too */
150 for (;;) {
151 static const char v = 'V';
152
153 if (write(watchdog_fd, &v, 1) > 0)
154 break;
155
156 if (errno != EINTR) {
157 log_error_errno(errno, "Failed to disarm watchdog timer: %m");
158 break;
159 }
160 }
161 }
162
163 watchdog_fd = safe_close(watchdog_fd);
164 }