Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright 2012 Lennart Poettering
7 :
8 : systemd is free software; you can redistribute it and/or modify it
9 : under the terms of the GNU Lesser General Public License as published by
10 : the Free Software Foundation; either version 2.1 of the License, or
11 : (at your option) any later version.
12 :
13 : systemd is distributed in the hope that it will be useful, but
14 : WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : Lesser General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 : ***/
21 :
22 : #include <sys/ioctl.h>
23 : #include <errno.h>
24 : #include <fcntl.h>
25 : #include <unistd.h>
26 : #include <linux/watchdog.h>
27 :
28 : #include "watchdog.h"
29 : #include "log.h"
30 :
31 : static int watchdog_fd = -1;
32 : static usec_t watchdog_timeout = USEC_INFINITY;
33 :
34 0 : static int update_timeout(void) {
35 : int r;
36 :
37 0 : if (watchdog_fd < 0)
38 0 : return 0;
39 :
40 0 : if (watchdog_timeout == USEC_INFINITY)
41 0 : return 0;
42 0 : else if (watchdog_timeout == 0) {
43 : int flags;
44 :
45 0 : flags = WDIOS_DISABLECARD;
46 0 : r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
47 0 : if (r < 0)
48 0 : return log_warning_errno(errno, "Failed to disable hardware watchdog: %m");
49 : } else {
50 : int sec, flags;
51 : char buf[FORMAT_TIMESPAN_MAX];
52 :
53 0 : sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
54 0 : r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
55 0 : if (r < 0)
56 0 : return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec);
57 :
58 0 : watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
59 0 : log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0));
60 :
61 0 : flags = WDIOS_ENABLECARD;
62 0 : r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
63 0 : if (r < 0) {
64 : /* ENOTTY means the watchdog is always enabled so we're fine */
65 0 : log_full(errno == ENOTTY ? LOG_DEBUG : LOG_WARNING,
66 : "Failed to enable hardware watchdog: %m");
67 0 : if (errno != ENOTTY)
68 0 : return -errno;
69 : }
70 :
71 0 : r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
72 0 : if (r < 0)
73 0 : return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
74 : }
75 :
76 0 : return 0;
77 : }
78 :
79 0 : static int open_watchdog(void) {
80 : struct watchdog_info ident;
81 :
82 0 : if (watchdog_fd >= 0)
83 0 : return 0;
84 :
85 0 : watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC);
86 0 : if (watchdog_fd < 0)
87 0 : return -errno;
88 :
89 0 : if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0)
90 0 : log_info("Hardware watchdog '%s', version %x",
91 : ident.identity,
92 : ident.firmware_version);
93 :
94 0 : return update_timeout();
95 : }
96 :
97 0 : int watchdog_set_timeout(usec_t *usec) {
98 : int r;
99 :
100 0 : 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 0 : if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY)
105 0 : return 0;
106 :
107 0 : if (watchdog_fd < 0)
108 0 : r = open_watchdog();
109 : else
110 0 : r = update_timeout();
111 :
112 0 : *usec = watchdog_timeout;
113 :
114 0 : return r;
115 : }
116 :
117 0 : int watchdog_ping(void) {
118 : int r;
119 :
120 0 : if (watchdog_fd < 0) {
121 0 : r = open_watchdog();
122 0 : if (r < 0)
123 0 : 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 0 : void watchdog_close(bool disarm) {
134 : int r;
135 :
136 0 : if (watchdog_fd < 0)
137 0 : 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 : 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 0 : }
160 : }
161 :
162 0 : watchdog_fd = safe_close(watchdog_fd);
163 : }
|