busybox/miscutils/watchdog.c
Matt Spinler 31c765081d watchdog: stop watchdog first on startup
Some watchdog implementations may do things other than issue
a reboot on a watchdog timeout.  In this case, there's the
possibility of restarting this program from the state of
the watchdog device not being properly stopped (done by writing
a 'V' and closing the device).  Since it wasn't stopped, the
driver may not be able to restart the watchdog when this program
reopens it and starts pinging it.

To fix this, the code will always first issue the stop when it
starts up.

function                                             old     new   delta
shutdown_on_signal                                     -      32     +32
watchdog_main                                        268     298     +30
shutdown_watchdog                                      -      25     +25
watchdog_shutdown                                     41       -     -41
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 1/0 up/down: 87/-41)             Total: 46 bytes

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-07-08 18:35:25 +02:00

144 lines
4.6 KiB
C

/* vi: set sw=4 ts=4: */
/*
* Mini watchdog implementation for busybox
*
* Copyright (C) 2003 Paul Mundt <lethal@linux-sh.org>
* Copyright (C) 2006 Bernhard Reutner-Fischer <busybox@busybox.net>
* Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com>
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
//config:config WATCHDOG
//config: bool "watchdog"
//config: default y
//config: select PLATFORM_LINUX
//config: help
//config: The watchdog utility is used with hardware or software watchdog
//config: device drivers. It opens the specified watchdog device special file
//config: and periodically writes a magic character to the device. If the
//config: watchdog applet ever fails to write the magic character within a
//config: certain amount of time, the watchdog device assumes the system has
//config: hung, and will cause the hardware to reboot.
//applet:IF_WATCHDOG(APPLET(watchdog, BB_DIR_SBIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_WATCHDOG) += watchdog.o
//usage:#define watchdog_trivial_usage
//usage: "[-t N[ms]] [-T N[ms]] [-F] DEV"
//usage:#define watchdog_full_usage "\n\n"
//usage: "Periodically write to watchdog device DEV\n"
//usage: "\n -T N Reboot after N seconds if not reset (default 60)"
//usage: "\n -t N Reset every N seconds (default 30)"
//usage: "\n -F Run in foreground"
//usage: "\n"
//usage: "\nUse 500ms to specify period in milliseconds"
#include "libbb.h"
#include "linux/types.h" /* for __u32 */
#include "linux/watchdog.h"
#define OPT_FOREGROUND (1 << 0)
#define OPT_STIMER (1 << 1)
#define OPT_HTIMER (1 << 2)
static void shutdown_watchdog(void)
{
static const char V = 'V';
write(3, &V, 1); /* Magic, see watchdog-api.txt in kernel */
close(3);
}
static void shutdown_on_signal(int sig UNUSED_PARAM)
{
remove_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid");
shutdown_watchdog();
_exit(EXIT_SUCCESS);
}
static void watchdog_open(const char* device)
{
/* Use known fd # - avoid needing global 'int fd' */
xmove_fd(xopen(device, O_WRONLY), 3);
/* If the watchdog driver can do something other than cause a reboot
* on a timeout, then it's possible this program may be starting from
* a state when the watchdog hadn't been previously stopped with
* the magic write followed by a close. In this case the driver may
* not start properly, so always do the proper stop first just in case.
*/
shutdown_watchdog();
xmove_fd(xopen(device, O_WRONLY), 3);
}
int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int watchdog_main(int argc, char **argv)
{
static const struct suffix_mult suffixes[] = {
{ "ms", 1 },
{ "", 1000 },
{ "", 0 }
};
unsigned opts;
unsigned stimer_duration; /* how often to restart */
unsigned htimer_duration = 60000; /* reboots after N ms if not restarted */
char *st_arg;
char *ht_arg;
opt_complementary = "=1"; /* must have exactly 1 argument */
opts = getopt32(argv, "Ft:T:", &st_arg, &ht_arg);
/* We need to daemonize *before* opening the watchdog as many drivers
* will only allow one process at a time to do so. Since daemonizing
* is not perfect (child may run before parent finishes exiting), we
* can't rely on parent exiting before us (let alone *cleanly* releasing
* the watchdog fd -- something else that may not even be allowed).
*/
if (!(opts & OPT_FOREGROUND))
bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
if (opts & OPT_HTIMER)
htimer_duration = xatou_sfx(ht_arg, suffixes);
stimer_duration = htimer_duration / 2;
if (opts & OPT_STIMER)
stimer_duration = xatou_sfx(st_arg, suffixes);
bb_signals(BB_FATAL_SIGS, shutdown_on_signal);
watchdog_open(argv[argc - 1]);
/* WDIOC_SETTIMEOUT takes seconds, not milliseconds */
htimer_duration = htimer_duration / 1000;
#ifndef WDIOC_SETTIMEOUT
# error WDIOC_SETTIMEOUT is not defined, cannot compile watchdog applet
#else
# if defined WDIOC_SETOPTIONS && defined WDIOS_ENABLECARD
{
static const int enable = WDIOS_ENABLECARD;
ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable);
}
# endif
ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration);
#endif
#if 0
ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration);
printf("watchdog: SW timer is %dms, HW timer is %ds\n",
stimer_duration, htimer_duration * 1000);
#endif
write_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid");
while (1) {
/*
* Make sure we clear the counter before sleeping,
* as the counter value is undefined at this point -- PFM
*/
write(3, "", 1); /* write zero byte */
usleep(stimer_duration * 1000L);
}
return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */
}