]>
Commit | Line | Data |
---|---|---|
e96d6be7 LP |
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 | |
5430f7f2 LP |
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 | |
e96d6be7 LP |
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 | |
5430f7f2 | 16 | Lesser General Public License for more details. |
e96d6be7 | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
e96d6be7 LP |
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_t) -1; | |
33 | ||
34 | static int update_timeout(void) { | |
35 | int r; | |
36 | ||
37 | if (watchdog_fd < 0) | |
38 | return 0; | |
39 | ||
40 | if (watchdog_timeout == (usec_t) -1) | |
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); | |
47 | if (r < 0) { | |
48 | log_warning("Failed to disable hardware watchdog: %m"); | |
49 | return -errno; | |
50 | } | |
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); | |
57 | if (r < 0) { | |
58 | log_warning("Failed to set timeout to %is: %m", sec); | |
59 | return -errno; | |
60 | } | |
61 | ||
62 | watchdog_timeout = (usec_t) sec * USEC_PER_SEC; | |
2fa4092c | 63 | log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0)); |
e96d6be7 LP |
64 | |
65 | flags = WDIOS_ENABLECARD; | |
66 | r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); | |
67 | if (r < 0) { | |
68 | log_warning("Failed to enable hardware watchdog: %m"); | |
69 | return -errno; | |
70 | } | |
71 | ||
72 | r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); | |
73 | if (r < 0) { | |
74 | log_warning("Failed to ping hardware watchdog: %m"); | |
75 | return -errno; | |
76 | } | |
77 | } | |
78 | ||
79 | return 0; | |
80 | } | |
81 | ||
82 | static int open_watchdog(void) { | |
83 | struct watchdog_info ident; | |
84 | ||
85 | if (watchdog_fd >= 0) | |
86 | return 0; | |
87 | ||
88 | watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC); | |
89 | if (watchdog_fd < 0) | |
90 | return -errno; | |
91 | ||
92 | if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0) | |
93 | log_info("Hardware watchdog '%s', version %x", | |
94 | ident.identity, | |
95 | ident.firmware_version); | |
96 | ||
97 | return update_timeout(); | |
98 | } | |
99 | ||
100 | int watchdog_set_timeout(usec_t *usec) { | |
56bcbfa5 | 101 | int r; |
e96d6be7 LP |
102 | |
103 | watchdog_timeout = *usec; | |
104 | ||
105 | /* If we didn't open the watchdog yet and didn't get any | |
106 | * explicit timeout value set, don't do anything */ | |
107 | if (watchdog_fd < 0 && watchdog_timeout == (usec_t) -1) | |
108 | return 0; | |
109 | ||
110 | if (watchdog_fd < 0) | |
56bcbfa5 | 111 | r = open_watchdog(); |
e96d6be7 | 112 | else |
56bcbfa5 | 113 | r = update_timeout(); |
e96d6be7 LP |
114 | |
115 | *usec = watchdog_timeout; | |
56bcbfa5 MO |
116 | |
117 | return r; | |
e96d6be7 LP |
118 | } |
119 | ||
120 | int watchdog_ping(void) { | |
121 | int r; | |
122 | ||
123 | if (watchdog_fd < 0) { | |
124 | r = open_watchdog(); | |
125 | if (r < 0) | |
126 | return r; | |
127 | } | |
128 | ||
129 | r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); | |
130 | if (r < 0) { | |
131 | log_warning("Failed to ping hardware watchdog: %m"); | |
132 | return -errno; | |
133 | } | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
138 | void watchdog_close(bool disarm) { | |
139 | int r; | |
140 | ||
141 | if (watchdog_fd < 0) | |
142 | return; | |
143 | ||
144 | if (disarm) { | |
145 | int flags; | |
146 | ||
147 | /* Explicitly disarm it */ | |
148 | flags = WDIOS_DISABLECARD; | |
149 | r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); | |
150 | if (r < 0) | |
151 | log_warning("Failed to disable hardware watchdog: %m"); | |
152 | ||
153 | /* To be sure, use magic close logic, too */ | |
154 | for (;;) { | |
155 | static const char v = 'V'; | |
156 | ||
157 | if (write(watchdog_fd, &v, 1) > 0) | |
158 | break; | |
159 | ||
160 | if (errno != EINTR) { | |
161 | log_error("Failed to disarm watchdog timer: %m"); | |
162 | break; | |
163 | } | |
164 | } | |
165 | } | |
166 | ||
167 | close_nointr_nofail(watchdog_fd); | |
168 | watchdog_fd = -1; | |
169 | } |