summaryrefslogtreecommitdiff
path: root/src/watchdog.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2012-04-05 22:08:10 +0200
committerLennart Poettering <lennart@poettering.net>2012-04-05 22:15:29 +0200
commite96d6be763014be75d480fde503d0b77f41194a0 (patch)
tree1bc3753e708c8b588c43ccd5b5c52c0adefc01c9 /src/watchdog.c
parent348e27fedfd4cdd2238ff31a46785a70b9dc6fc0 (diff)
systemd: add hardware watchdog support
This adds minimal hardware watchdog support to PID 1. The idea is that PID 1 supervises and watchdogs system services, while the hardware watchdog is used to supervise PID 1. This adds two hardware watchdog configuration options, for the runtime watchdog and for a shutdown watchdog. The former is active during normal operation, the latter only at reboots to ensure that if a clean reboot times out we reboot nonetheless. If the runtime watchdog is enabled PID 1 will automatically wake up at half the configured interval and write to the watchdog daemon. By default we enable the shutdown watchdog, but leave the runtime watchdog disabled in order not to break independent hardware watchdog daemons people might be using. This is only the most basic hookup. If necessary we can later on hook up the watchdog ping more closely with services deemed crucial.
Diffstat (limited to 'src/watchdog.c')
-rw-r--r--src/watchdog.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/watchdog.c b/src/watchdog.c
new file mode 100644
index 000000000..9625e151a
--- /dev/null
+++ b/src/watchdog.c
@@ -0,0 +1,166 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/watchdog.h>
+
+#include "watchdog.h"
+#include "log.h"
+
+static int watchdog_fd = -1;
+static usec_t watchdog_timeout = (usec_t) -1;
+
+static int update_timeout(void) {
+ int r;
+
+ if (watchdog_fd < 0)
+ return 0;
+
+ if (watchdog_timeout == (usec_t) -1)
+ return 0;
+ else if (watchdog_timeout == 0) {
+ int flags;
+
+ flags = WDIOS_DISABLECARD;
+ r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
+ if (r < 0) {
+ log_warning("Failed to disable hardware watchdog: %m");
+ return -errno;
+ }
+ } else {
+ int sec, flags;
+ char buf[FORMAT_TIMESPAN_MAX];
+
+ sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
+ r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec);
+ if (r < 0) {
+ log_warning("Failed to set timeout to %is: %m", sec);
+ return -errno;
+ }
+
+ watchdog_timeout = (usec_t) sec * USEC_PER_SEC;
+ log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout));
+
+ flags = WDIOS_ENABLECARD;
+ r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
+ if (r < 0) {
+ log_warning("Failed to enable hardware watchdog: %m");
+ return -errno;
+ }
+
+ r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
+ if (r < 0) {
+ log_warning("Failed to ping hardware watchdog: %m");
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+static int open_watchdog(void) {
+ struct watchdog_info ident;
+
+ if (watchdog_fd >= 0)
+ return 0;
+
+ watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC);
+ if (watchdog_fd < 0)
+ return -errno;
+
+ if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0)
+ log_info("Hardware watchdog '%s', version %x",
+ ident.identity,
+ ident.firmware_version);
+
+ return update_timeout();
+}
+
+int watchdog_set_timeout(usec_t *usec) {
+
+ watchdog_timeout = *usec;
+
+ /* If we didn't open the watchdog yet and didn't get any
+ * explicit timeout value set, don't do anything */
+ if (watchdog_fd < 0 && watchdog_timeout == (usec_t) -1)
+ return 0;
+
+ if (watchdog_fd < 0)
+ return open_watchdog();
+ else
+ return update_timeout();
+
+ *usec = watchdog_timeout;
+}
+
+int watchdog_ping(void) {
+ int r;
+
+ if (watchdog_fd < 0) {
+ r = open_watchdog();
+ if (r < 0)
+ return r;
+ }
+
+ r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
+ if (r < 0) {
+ log_warning("Failed to ping hardware watchdog: %m");
+ return -errno;
+ }
+
+ return 0;
+}
+
+void watchdog_close(bool disarm) {
+ int r;
+
+ if (watchdog_fd < 0)
+ return;
+
+ if (disarm) {
+ int flags;
+
+ /* Explicitly disarm it */
+ flags = WDIOS_DISABLECARD;
+ r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags);
+ if (r < 0)
+ log_warning("Failed to disable hardware watchdog: %m");
+
+ /* To be sure, use magic close logic, too */
+ for (;;) {
+ static const char v = 'V';
+
+ if (write(watchdog_fd, &v, 1) > 0)
+ break;
+
+ if (errno != EINTR) {
+ log_error("Failed to disarm watchdog timer: %m");
+ break;
+ }
+ }
+ }
+
+ close_nointr_nofail(watchdog_fd);
+ watchdog_fd = -1;
+}