]>
Commit | Line | Data |
---|---|---|
663996b3 MS |
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; | |
5eef597e | 32 | static usec_t watchdog_timeout = USEC_INFINITY; |
663996b3 MS |
33 | |
34 | static int update_timeout(void) { | |
35 | int r; | |
36 | ||
37 | if (watchdog_fd < 0) | |
38 | return 0; | |
39 | ||
5eef597e | 40 | if (watchdog_timeout == USEC_INFINITY) |
663996b3 MS |
41 | return 0; |
42 | else if (watchdog_timeout == 0) { | |
43 | int flags; | |
44 | ||
45 | flags = WDIOS_DISABLECARD; | |
46 | r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); | |
f47781d8 MP |
47 | if (r < 0) |
48 | return log_warning_errno(errno, "Failed to disable hardware watchdog: %m"); | |
663996b3 MS |
49 | } else { |
50 | int sec, flags; | |
51 | char buf[FORMAT_TIMESPAN_MAX]; | |
52 | ||
53 | sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); | |
54 | r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec); | |
f47781d8 MP |
55 | if (r < 0) |
56 | return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec); | |
663996b3 MS |
57 | |
58 | watchdog_timeout = (usec_t) sec * USEC_PER_SEC; | |
59 | log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0)); | |
60 | ||
61 | flags = WDIOS_ENABLECARD; | |
62 | r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); | |
86f210e9 MP |
63 | if (r < 0) { |
64 | /* ENOTTY means the watchdog is always enabled so we're fine */ | |
65 | log_full(errno == ENOTTY ? LOG_DEBUG : LOG_WARNING, | |
66 | "Failed to enable hardware watchdog: %m"); | |
67 | if (errno != ENOTTY) | |
68 | return -errno; | |
69 | } | |
663996b3 MS |
70 | |
71 | r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); | |
f47781d8 MP |
72 | if (r < 0) |
73 | return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); | |
663996b3 MS |
74 | } |
75 | ||
76 | return 0; | |
77 | } | |
78 | ||
79 | static int open_watchdog(void) { | |
80 | struct watchdog_info ident; | |
81 | ||
82 | if (watchdog_fd >= 0) | |
83 | return 0; | |
84 | ||
85 | watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC); | |
86 | if (watchdog_fd < 0) | |
87 | return -errno; | |
88 | ||
89 | if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0) | |
90 | log_info("Hardware watchdog '%s', version %x", | |
91 | ident.identity, | |
92 | ident.firmware_version); | |
93 | ||
94 | return update_timeout(); | |
95 | } | |
96 | ||
97 | int watchdog_set_timeout(usec_t *usec) { | |
98 | int r; | |
99 | ||
100 | 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 */ | |
5eef597e | 104 | if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY) |
663996b3 MS |
105 | return 0; |
106 | ||
107 | if (watchdog_fd < 0) | |
108 | r = open_watchdog(); | |
109 | else | |
110 | r = update_timeout(); | |
111 | ||
112 | *usec = watchdog_timeout; | |
113 | ||
114 | return r; | |
115 | } | |
116 | ||
117 | int watchdog_ping(void) { | |
118 | int r; | |
119 | ||
120 | if (watchdog_fd < 0) { | |
121 | r = open_watchdog(); | |
122 | if (r < 0) | |
123 | return r; | |
124 | } | |
125 | ||
126 | r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); | |
f47781d8 MP |
127 | if (r < 0) |
128 | return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); | |
663996b3 MS |
129 | |
130 | return 0; | |
131 | } | |
132 | ||
133 | void watchdog_close(bool disarm) { | |
134 | int r; | |
135 | ||
136 | if (watchdog_fd < 0) | |
137 | return; | |
138 | ||
139 | if (disarm) { | |
140 | int flags; | |
141 | ||
142 | /* Explicitly disarm it */ | |
143 | flags = WDIOS_DISABLECARD; | |
144 | r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); | |
145 | if (r < 0) | |
f47781d8 | 146 | log_warning_errno(errno, "Failed to disable hardware watchdog: %m"); |
663996b3 MS |
147 | |
148 | /* To be sure, use magic close logic, too */ | |
149 | for (;;) { | |
150 | static const char v = 'V'; | |
151 | ||
152 | if (write(watchdog_fd, &v, 1) > 0) | |
153 | break; | |
154 | ||
155 | if (errno != EINTR) { | |
f47781d8 | 156 | log_error_errno(errno, "Failed to disarm watchdog timer: %m"); |
663996b3 MS |
157 | break; |
158 | } | |
159 | } | |
160 | } | |
161 | ||
60f067b4 | 162 | watchdog_fd = safe_close(watchdog_fd); |
663996b3 | 163 | } |