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:       select PLATFORM_LINUX
  15//config:       help
  16//config:       The watchdog utility is used with hardware or software watchdog
  17//config:       device drivers. It opens the specified watchdog device special file
  18//config:       and periodically writes a magic character to the device. If the
  19//config:       watchdog applet ever fails to write the magic character within a
  20//config:       certain amount of time, the watchdog device assumes the system has
  21//config:       hung, and will cause the hardware to reboot.
  22
  23//applet:IF_WATCHDOG(APPLET(watchdog, BB_DIR_SBIN, BB_SUID_DROP))
  24
  25//kbuild:lib-$(CONFIG_WATCHDOG) += watchdog.o
  26
  27//usage:#define watchdog_trivial_usage
  28//usage:       "[-t N[ms]] [-T N[ms]] [-F] DEV"
  29//usage:#define watchdog_full_usage "\n\n"
  30//usage:       "Periodically write to watchdog device DEV\n"
  31//usage:     "\n        -T N    Reboot after N seconds if not reset (default 60)"
  32//usage:     "\n        -t N    Reset every N seconds (default 30)"
  33//usage:     "\n        -F      Run in foreground"
  34//usage:     "\n"
  35//usage:     "\nUse 500ms to specify period in milliseconds"
  36
  37#include "libbb.h"
  38#include <linux/types.h> /* for __u32 */
  39#include <linux/watchdog.h>
  40
  41#ifndef WDIOC_SETOPTIONS
  42# define WDIOC_SETOPTIONS 0x5704
  43#endif
  44#ifndef WDIOC_SETTIMEOUT
  45# define WDIOC_SETTIMEOUT 0x5706
  46#endif
  47#ifndef WDIOC_GETTIMEOUT
  48# define WDIOC_GETTIMEOUT 0x5707
  49#endif
  50#ifndef WDIOS_ENABLECARD
  51# define WDIOS_ENABLECARD 2
  52#endif
  53
  54#define OPT_FOREGROUND  (1 << 0)
  55#define OPT_STIMER      (1 << 1)
  56#define OPT_HTIMER      (1 << 2)
  57
  58static void shutdown_watchdog(void)
  59{
  60        static const char V = 'V';
  61        write(3, &V, 1);  /* Magic, see watchdog-api.txt in kernel */
  62        close(3);
  63}
  64
  65static void shutdown_on_signal(int sig UNUSED_PARAM)
  66{
  67        remove_pidfile_std_path_and_ext("watchdog");
  68        shutdown_watchdog();
  69        _exit(EXIT_SUCCESS);
  70}
  71
  72static void watchdog_open(const char* device)
  73{
  74        /* Use known fd # - avoid needing global 'int fd' */
  75        xmove_fd(xopen(device, O_WRONLY), 3);
  76
  77        /* If the watchdog driver can do something other than cause a reboot
  78         * on a timeout, then it's possible this program may be starting from
  79         * a state when the watchdog hadn't been previously stopped with
  80         * the magic write followed by a close.  In this case the driver may
  81         * not start properly, so always do the proper stop first just in case.
  82         */
  83        shutdown_watchdog();
  84
  85        xmove_fd(xopen(device, O_WRONLY), 3);
  86}
  87
  88int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  89int watchdog_main(int argc UNUSED_PARAM, char **argv)
  90{
  91        static const int enable = WDIOS_ENABLECARD;
  92        static const struct suffix_mult suffixes[] = {
  93                { "ms", 1 },
  94                { "", 1000 },
  95                { "", 0 }
  96        };
  97
  98        unsigned opts;
  99        unsigned stimer_duration; /* how often to restart */
 100        unsigned htimer_duration = 60000; /* reboots after N ms if not restarted */
 101        char *st_arg;
 102        char *ht_arg;
 103
 104        opts = getopt32(argv, "^" "Ft:T:" "\0" "=1"/*must have exactly 1 arg*/,
 105                                &st_arg, &ht_arg
 106        );
 107
 108        /* We need to daemonize *before* opening the watchdog as many drivers
 109         * will only allow one process at a time to do so.  Since daemonizing
 110         * is not perfect (child may run before parent finishes exiting), we
 111         * can't rely on parent exiting before us (let alone *cleanly* releasing
 112         * the watchdog fd -- something else that may not even be allowed).
 113         */
 114        if (!(opts & OPT_FOREGROUND))
 115                bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
 116
 117        /* maybe bb_logenv_override(); here for LOGGING=syslog to work? */
 118
 119        if (opts & OPT_HTIMER)
 120                htimer_duration = xatou_sfx(ht_arg, suffixes);
 121        stimer_duration = htimer_duration / 2;
 122        if (opts & OPT_STIMER)
 123                stimer_duration = xatou_sfx(st_arg, suffixes);
 124
 125        bb_signals(BB_FATAL_SIGS, shutdown_on_signal);
 126
 127        watchdog_open(argv[optind]);
 128
 129        /* WDIOC_SETTIMEOUT takes seconds, not milliseconds */
 130        htimer_duration = htimer_duration / 1000;
 131        ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable);
 132        ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration);
 133#if 0
 134        ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration);
 135        printf("watchdog: SW timer is %dms, HW timer is %ds\n",
 136                stimer_duration, htimer_duration * 1000);
 137#endif
 138
 139        write_pidfile_std_path_and_ext("watchdog");
 140
 141        while (1) {
 142                /*
 143                 * Make sure we clear the counter before sleeping,
 144                 * as the counter value is undefined at this point -- PFM
 145                 */
 146                write(3, "", 1); /* write zero byte */
 147                usleep(stimer_duration * 1000L);
 148        }
 149        return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */
 150}
 151