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