chiark / gitweb /
split selinux label operations out of cgroup-util, socket-util
[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         int r;
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)
111                 r = open_watchdog();
112         else
113                 r = update_timeout();
114
115         *usec = watchdog_timeout;
116
117         return r;
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 }