busybox/miscutils/watchdog.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini watchdog implementation for busybox
   4 *
   5 * Copyright (C) 2003  Paul Mundt <lethal@linux-sh.org>
   6 * Copyright (C) 2006  Bernhard Reutner-Fischer <busybox@busybox.net>
   7 * Copyright (C) 2008  Darius Augulis <augulis.darius@gmail.com>
   8 *
   9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  10 */
  11//config:config WATCHDOG
  12//config:       bool "watchdog (5.3 kb)"
  13//config:       default y
  14//config:       help
  15//config:       The watchdog utility is used with hardware or software watchdog
  16//config:       device drivers. It opens the specified watchdog device special file
  17//config:       and periodically writes a magic character to the device. If the
  18//config:       watchdog applet ever fails to write the magic character within a
  19//config:       certain amount of time, the watchdog device assumes the system has
  20//config:       hung, and will cause the hardware to reboot.
  21//config:
  22//config:config FEATURE_WATCHDOG_OPEN_TWICE
  23//config:       bool "Open watchdog device twice, closing it gracefully in between"
  24//config:       depends on WATCHDOG
  25//config:       default n   # this behavior was essentially a hack for a broken driver
  26//config:       help
  27//config:       When enabled, the watchdog device is opened and then immediately
  28//config:       magic-closed, before being opened a second time. This may be necessary
  29//config:       for some watchdog devices, but can cause spurious warnings in the
  30//config:       kernel log if the nowayout feature is enabled. If this workaround
  31//config:       is really needed for you machine to work properly, consider whether
  32//config:       it should be fixed in the kernel driver instead. Even when disabled,
  33//config:       the behaviour is easily emulated with a "printf 'V' > /dev/watchdog"
  34//config:       immediately before starting the busybox watchdog daemon. Say n unless
  35//config:       you know that you absolutely need this.
  36
  37//applet:IF_WATCHDOG(APPLET(watchdog, BB_DIR_SBIN, BB_SUID_DROP))
  38
  39//kbuild:lib-$(CONFIG_WATCHDOG) += watchdog.o
  40
  41//usage:#define watchdog_trivial_usage
  42//usage:       "[-t N[ms]] [-T N[ms]] [-F] DEV"
  43//usage:#define watchdog_full_usage "\n\n"
  44//usage:       "Periodically write to watchdog device DEV\n"
  45//usage:     "\n        -T N    Reboot after N seconds if not reset (default 60)"
  46//usage:     "\n        -t N    Reset every N seconds (default 30)"
  47//usage:     "\n        -F      Run in foreground"
  48//usage:     "\n"
  49//usage:     "\nUse 500ms to specify period in milliseconds"
  50
  51#include "libbb.h"
  52#include <linux/types.h> /* for __u32 */
  53#include <linux/watchdog.h>
  54
  55#ifndef WDIOC_SETOPTIONS
  56# define WDIOC_SETOPTIONS 0x5704
  57#endif
  58#ifndef WDIOC_SETTIMEOUT
  59# define WDIOC_SETTIMEOUT 0x5706
  60#endif
  61#ifndef WDIOC_GETTIMEOUT
  62# define WDIOC_GETTIMEOUT 0x5707
  63#endif
  64#ifndef WDIOS_ENABLECARD
  65# define WDIOS_ENABLECARD 2
  66#endif
  67
  68static void shutdown_watchdog(void)
  69{
  70        static const char V = 'V';
  71        write(3, &V, 1);  /* Magic, see watchdog-api.txt in kernel */
  72        close(3);
  73}
  74
  75static void shutdown_on_signal(int sig UNUSED_PARAM)
  76{
  77        remove_pidfile_std_path_and_ext("watchdog");
  78        shutdown_watchdog();
  79        _exit(EXIT_SUCCESS);
  80}
  81
  82static void watchdog_open(const char* device)
  83{
  84        /* Use known fd # - avoid needing global 'int fd' */
  85        xmove_fd(xopen(device, O_WRONLY), 3);
  86
  87#if ENABLE_FEATURE_WATCHDOG_OPEN_TWICE
  88        /* If the watchdog driver can do something other than cause a reboot
  89         * on a timeout, then it's possible this program may be starting from
  90         * a state when the watchdog hadn't been previously stopped with
  91         * the magic write followed by a close.  In this case the driver may
  92         * not start properly, so always do the proper stop first just in case.
  93         */
  94        shutdown_watchdog();
  95
  96        xmove_fd(xopen(device, O_WRONLY), 3);
  97#endif
  98}
  99
 100int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 101int watchdog_main(int argc UNUSED_PARAM, char **argv)
 102{
 103        static const int enable = WDIOS_ENABLECARD;
 104        static const struct suffix_mult suffixes[] ALIGN_SUFFIX = {
 105                { "ms", 1 },
 106                { "", 1000 },
 107                { "", 0 }
 108        };
 109
 110        unsigned opts;
 111        unsigned stimer_duration; /* how often to restart */
 112        unsigned htimer_duration = 60000; /* reboots after N ms if not restarted */
 113        char *st_arg;
 114        char *ht_arg;
 115
 116#define OPT_FOREGROUND  (1 << 0)
 117#define OPT_STIMER      (1 << 1)
 118#define OPT_HTIMER      (1 << 2)
 119        opts = getopt32(argv, "^" "Ft:T:" "\0" "=1"/*must have exactly 1 arg*/,
 120                                &st_arg, &ht_arg
 121        );
 122
 123        /* We need to daemonize *before* opening the watchdog as many drivers
 124         * will only allow one process at a time to do so.  Since daemonizing
 125         * is not perfect (child may run before parent finishes exiting), we
 126         * can't rely on parent exiting before us (let alone *cleanly* releasing
 127         * the watchdog fd -- something else that may not even be allowed).
 128         */
 129        if (!(opts & OPT_FOREGROUND))
 130                bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
 131
 132        /* maybe bb_logenv_override(); here for LOGGING=syslog to work? */
 133
 134        if (opts & OPT_HTIMER)
 135                htimer_duration = xatou_sfx(ht_arg, suffixes);
 136        stimer_duration = htimer_duration / 2;
 137        if (opts & OPT_STIMER)
 138                stimer_duration = xatou_sfx(st_arg, suffixes);
 139
 140        bb_signals(BB_FATAL_SIGS, shutdown_on_signal);
 141
 142        watchdog_open(argv[optind]);
 143
 144        /* WDIOC_SETTIMEOUT takes seconds, not milliseconds */
 145        htimer_duration = htimer_duration / 1000;
 146        ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable);
 147        ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration);
 148#if 0
 149        ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration);
 150        printf("watchdog: SW timer is %dms, HW timer is %ds\n",
 151                stimer_duration, htimer_duration);
 152#endif
 153
 154        write_pidfile_std_path_and_ext("watchdog");
 155
 156        while (1) {
 157                /*
 158                 * Make sure we clear the counter before sleeping,
 159                 * as the counter value is undefined at this point -- PFM
 160                 */
 161                write(3, "", 1); /* write zero byte */
 162                msleep(stimer_duration);
 163        }
 164        return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */
 165}
 166