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(CONFIG_PID_FILE_PATH "/watchdog.pid"); 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(CONFIG_PID_FILE_PATH "/watchdog.pid"); 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