chiark / gitweb /
build-sys: bump systemd version to 'udev version 182'
[elogind.git] / src / watchdog.c
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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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_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;
63                 log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout));
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) {
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 */
106         if (watchdog_fd < 0 && watchdog_timeout == (usec_t) -1)
107                 return 0;
108
109         if (watchdog_fd < 0)
110                 return open_watchdog();
111         else
112                 return update_timeout();
113
114         *usec = watchdog_timeout;
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);
127         if (r < 0) {
128                 log_warning("Failed to ping hardware watchdog: %m");
129                 return -errno;
130         }
131
132         return 0;
133 }
134
135 void 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)
148                         log_warning("Failed to disable hardware watchdog: %m");
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) {
158                                 log_error("Failed to disarm watchdog timer: %m");
159                                 break;
160                         }
161                 }
162         }
163
164         close_nointr_nofail(watchdog_fd);
165         watchdog_fd = -1;
166 }