From b674aad8020552f0d58ae3fffaa03a287244a9d4 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Sun, 23 Oct 2016 23:58:50 +0200 Subject: [PATCH] watchdog: Add simple built-in watchdog daemon This patch adds a simple but effective watchdog daemon with support for handover to an external daemon, should one register using the API. Disabled by default, enable by: configure --enable-watchdog The watchdog forks to the background, opening /dev/watchdog setting a three second timeout, and kicking every second. At shutdown/reboot the daemon will receive SIGPWR from Finit to exit and cause a WDT reboot. Signed-off-by: Joachim Nilsson --- Makefile.am | 3 ++ configure.ac | 11 ++++- finit.c | 6 +++ watchdog.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++ watchdog.h | 40 +++++++++++++++ 5 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 watchdog.c create mode 100644 watchdog.h diff --git a/Makefile.am b/Makefile.am index eefa966f..c200375b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,6 +19,9 @@ pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h plugin.h svc.h if INETD finit_SOURCES += inetd.c inetd.h endif +if WATCHDOGD +finit_SOURCES += watchdog.c watchdog.h +endif finit_CFLAGS = -W -Wall -Wextra -std=gnu99 $(lite_CFLAGS) $(uev_CFLAGS) finit_LDADD = $(lite_LIBS) $(uev_LIBS) diff --git a/configure.ac b/configure.ac index c48b95ab..1675cd44 100644 --- a/configure.ac +++ b/configure.ac @@ -50,6 +50,9 @@ AC_ARG_ENABLE(rw-rootfs, AC_ARG_ENABLE(silent, AS_HELP_STRING([--enable-silent], [Completely silent operation, not even boot messages])) +AC_ARG_ENABLE(watchdog, + AS_HELP_STRING([--enable-watchdog], [Enable built-in watchdog, uses /dev/watchdog])) + AC_ARG_ENABLE(inetd, AS_HELP_STRING([--disable-inetd], [Disable built-in inetd super server, default enabled])) @@ -134,6 +137,9 @@ AS_IF([test "x$enable_silent" = "xyes"], [ ], [ AC_DEFINE(SILENT_MODE, 0, [Normal output, default])]) +AS_IF([test "x$enable_watchdog" = "xyes"], [ + AC_DEFINE(BUILTIN_WATCHDOG, 1, [Enable built-in watchdog, kicks on /dev/watchdog])]) + ### Disable features ########################################################################### AS_IF([test "x$enable_inetd" != "xno"], enable_inetd="yes", [ AC_DEFINE(INETD_DISABLED, 1, [Disable built-in inetd])]) @@ -200,8 +206,9 @@ AS_IF([test "x$with_random_seed" != "xno"], [ AC_DEFINE_UNQUOTED(RANDOMSEED, "$lurvig", [Improve random at boot by seeding it with sth from before.])]) # Control build with automake flags -AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"]) -AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"]) +AM_CONDITIONAL(STATIC, [test "x$enable_static" = "xyes"]) +AM_CONDITIONAL(INETD, [test "x$enable_inetd" = "xyes"]) +AM_CONDITIONAL(WATCHDOGD, [test "x$enable_watchdog" = "xyes"]) # Generate all files AC_OUTPUT diff --git a/finit.c b/finit.c index 1facf826..2bf1dd99 100644 --- a/finit.c +++ b/finit.c @@ -46,6 +46,7 @@ #include "sm.h" #include "tty.h" #include "utmp-api.h" +#include "watchdog.h" int debug = 0; int quiet = QUIET_MODE; /* Delayed disable of silent mode. */ @@ -343,6 +344,11 @@ int main(int argc, char* argv[]) */ conf_parse_config(); + /* + * Start built-in watchdog as soon as possible, if enabled + */ + watchdog(argv[0]); + /* Set hostname as soon as possible, for syslog et al. */ set_hostname(&hostname); diff --git a/watchdog.c b/watchdog.c new file mode 100644 index 00000000..fed9f4f9 --- /dev/null +++ b/watchdog.c @@ -0,0 +1,135 @@ +/* Built-in watchdog daemon + * + * Copyright (c) 2016 Joachim Nilsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "finit.h" +#include "helpers.h" +#include "watchdog.h" + +int running = 1; +int handover = 0; + +static int notify(void) +{ + int sd, result = 255; + ssize_t len; + struct sockaddr_un sun = { + .sun_family = AF_UNIX, + .sun_path = INIT_SOCKET, + }; + struct init_request rq; + + sd = socket(AF_UNIX, SOCK_STREAM, 0); + if (-1 == sd) + return -1; + + if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) + goto exit; + + rq.cmd = INIT_CMD_WDOG_HELLO; + rq.magic = INIT_MAGIC; + rq.runlevel = getpid(); + len = sizeof(rq); + if (write(sd, &rq, len) != len) + goto exit; + + if (read(sd, &rq, len) != len) + goto exit; + + result = 0; +exit: + close(sd); + return result; +} + +static void sighandler(int signo) +{ + if (signo == SIGTERM) + handover = 1; + running = 0; +} + +static int init(char *progname, char *devnode, int timeout) +{ + int fd; + + sprintf(progname, "@finit-watchdog"); + signal(SIGTERM, sighandler); + signal(SIGPWR, sighandler); + + fd = open(devnode, O_WRONLY); + if (fd == -1) + return -1; + + ioctl(fd, WDIOC_SETTIMEOUT, &timeout); + + return fd; +} + +static int loop(int fd) +{ + int done = 0; + + while (running) { + do_sleep(1); + ioctl(fd, WDIOC_KEEPALIVE, &done); + + if (!done && !notify()) + done = 1; + } + + if (handover) + return !write(fd, "V", 1); + + return 0; +} + +void watchdog(char *progname) +{ + if (fork() == 0) { + int fd, ret; + + fd = init(progname, WDT_DEVNODE, WDT_TIMEOUT); + if (fd == -1) { + _pe("Failed connecting to watchdog %s", WDT_DEVNODE); + _exit(1); + } + + ret = loop(fd); + close(fd); + + _exit(ret); + } +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/watchdog.h b/watchdog.h new file mode 100644 index 00000000..e7610a24 --- /dev/null +++ b/watchdog.h @@ -0,0 +1,40 @@ +/* Built-in watchdog daemon + * + * Copyright (c) 2016 Joachim Nilsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" /* Generated by configure script */ + +#define WDT_DEVNODE "/dev/watchdog" +#define WDT_TIMEOUT 3 + +#ifdef BUILTIN_WATCHDOG +void watchdog(char *progname); +#else +#define watchdog(progname) +#endif /* BUILTIN_WATCHDOG */ + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */