1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
/* 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 (5.7 kb)"
//config: default y
//config: depends on 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.
//config:
//config:config FEATURE_WATCHDOG_OPEN_TWICE
//config: bool "Open watchdog device twice, closing it gracefully in between"
//config: depends on WATCHDOG
//config: default n # this behavior was essentially a hack for a broken driver
//config: help
//config: When enabled, the watchdog device is opened and then immediately
//config: magic-closed, before being opened a second time. This may be necessary
//config: for some watchdog devices, but can cause spurious warnings in the
//config: kernel log if the nowayout feature is enabled. If this workaround
//config: is really needed for you machine to work properly, consider whether
//config: it should be fixed in the kernel driver instead. Even when disabled,
//config: the behaviour is easily emulated with a "printf 'V' > /dev/watchdog"
//config: immediately before starting the busybox watchdog daemon. Say n unless
//config: you know that you absolutely need this.
//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>
#ifndef WDIOC_SETOPTIONS
# define WDIOC_SETOPTIONS 0x5704
#endif
#ifndef WDIOC_SETTIMEOUT
# define WDIOC_SETTIMEOUT 0x5706
#endif
#ifndef WDIOC_GETTIMEOUT
# define WDIOC_GETTIMEOUT 0x5707
#endif
#ifndef WDIOS_ENABLECARD
# define WDIOS_ENABLECARD 2
#endif
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_std_path_and_ext("watchdog");
shutdown_watchdog();
_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 ENABLE_FEATURE_WATCHDOG_OPEN_TWICE
/* 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);
#endif
}
int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int watchdog_main(int argc UNUSED_PARAM, char **argv)
{
static const int enable = WDIOS_ENABLECARD;
static const struct suffix_mult suffixes[] ALIGN_SUFFIX = {
{ "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;
#define OPT_FOREGROUND (1 << 0)
#define OPT_STIMER (1 << 1)
#define OPT_HTIMER (1 << 2)
opts = getopt32(argv, "^" "Ft:T:" "\0" "=1"/*must have exactly 1 arg*/,
&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);
/* maybe bb_logenv_override(); here for LOGGING=syslog to work? */
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[optind]);
/* WDIOC_SETTIMEOUT takes seconds, not milliseconds */
htimer_duration = htimer_duration / 1000;
ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable);
ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration);
#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);
#endif
write_pidfile_std_path_and_ext("watchdog");
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 */
msleep(stimer_duration);
}
return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */
}
|