1210300Sdelphij/*- 2128898Ssmkelly * Copyright (c) 2003-2004 Sean M. Kelly <smkelly@FreeBSD.org> 3116874Ssmkelly * All rights reserved. 4116874Ssmkelly * 5116874Ssmkelly * Redistribution and use in source and binary forms, with or without 6116874Ssmkelly * modification, are permitted provided that the following conditions 7116874Ssmkelly * are met: 8116874Ssmkelly * 1. Redistributions of source code must retain the above copyright 9116874Ssmkelly * notice, this list of conditions and the following disclaimer. 10116874Ssmkelly * 2. Redistributions in binary form must reproduce the above copyright 11116874Ssmkelly * notice, this list of conditions and the following disclaimer in the 12116874Ssmkelly * documentation and/or other materials provided with the distribution. 13116874Ssmkelly * 14116874Ssmkelly * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15116874Ssmkelly * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16116874Ssmkelly * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17116874Ssmkelly * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18116874Ssmkelly * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19116874Ssmkelly * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20116874Ssmkelly * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21116874Ssmkelly * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22116874Ssmkelly * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23116874Ssmkelly * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24116874Ssmkelly * SUCH DAMAGE. 25116874Ssmkelly */ 26116874Ssmkelly 27116874Ssmkelly/* 28116874Ssmkelly * Software watchdog daemon. 29116874Ssmkelly */ 30116874Ssmkelly 31117185Ssmkelly#include <sys/types.h> 32116874Ssmkelly__FBSDID("$FreeBSD$"); 33116874Ssmkelly 34213181Semaste#include <sys/mman.h> 35149434Spjd#include <sys/param.h> 36117185Ssmkelly#include <sys/rtprio.h> 37117185Ssmkelly#include <sys/stat.h> 38116874Ssmkelly#include <sys/time.h> 39126383Sphk#include <sys/watchdog.h> 40116874Ssmkelly 41116874Ssmkelly#include <err.h> 42126383Sphk#include <errno.h> 43126383Sphk#include <fcntl.h> 44149434Spjd#include <libutil.h> 45126383Sphk#include <math.h> 46116874Ssmkelly#include <paths.h> 47117185Ssmkelly#include <signal.h> 48116874Ssmkelly#include <stdio.h> 49116874Ssmkelly#include <stdlib.h> 50126383Sphk#include <string.h> 51243820Sdelphij#include <strings.h> 52116874Ssmkelly#include <sysexits.h> 53116874Ssmkelly#include <unistd.h> 54116874Ssmkelly 55116874Ssmkellystatic void parseargs(int, char *[]); 56116874Ssmkellystatic void sighandler(int); 57116874Ssmkellystatic void watchdog_loop(void); 58116874Ssmkellystatic int watchdog_init(void); 59116874Ssmkellystatic int watchdog_onoff(int onoff); 60165263Sn_hibmastatic int watchdog_patpat(u_int timeout); 61116874Ssmkellystatic void usage(void); 62116874Ssmkelly 63210300Sdelphijstatic int debugging = 0; 64210300Sdelphijstatic int end_program = 0; 65210300Sdelphijstatic const char *pidfile = _PATH_VARRUN "watchdogd.pid"; 66210300Sdelphijstatic u_int timeout = WD_TO_16SEC; 67210300Sdelphijstatic u_int passive = 0; 68210300Sdelphijstatic int is_daemon = 0; 69210300Sdelphijstatic int fd = -1; 70210300Sdelphijstatic int nap = 1; 71210300Sdelphijstatic char *test_cmd = NULL; 72116874Ssmkelly 73116874Ssmkelly/* 74128705Ssmkelly * Periodically pat the watchdog, preventing it from firing. 75116874Ssmkelly */ 76116874Ssmkellyint 77116874Ssmkellymain(int argc, char *argv[]) 78116874Ssmkelly{ 79116874Ssmkelly struct rtprio rtp; 80149434Spjd struct pidfh *pfh; 81149434Spjd pid_t otherpid; 82116874Ssmkelly 83116874Ssmkelly if (getuid() != 0) 84116874Ssmkelly errx(EX_SOFTWARE, "not super user"); 85116874Ssmkelly 86116874Ssmkelly parseargs(argc, argv); 87116874Ssmkelly 88116874Ssmkelly rtp.type = RTP_PRIO_REALTIME; 89116874Ssmkelly rtp.prio = 0; 90116874Ssmkelly if (rtprio(RTP_SET, 0, &rtp) == -1) 91116874Ssmkelly err(EX_OSERR, "rtprio"); 92116874Ssmkelly 93116874Ssmkelly if (watchdog_init() == -1) 94117185Ssmkelly errx(EX_SOFTWARE, "unable to initialize watchdog"); 95116874Ssmkelly 96126383Sphk if (is_daemon) { 97126383Sphk if (watchdog_onoff(1) == -1) 98200778Sru err(EX_OSERR, "patting the dog"); 99116874Ssmkelly 100150214Spjd pfh = pidfile_open(pidfile, 0600, &otherpid); 101149434Spjd if (pfh == NULL) { 102149434Spjd if (errno == EEXIST) { 103149434Spjd errx(EX_SOFTWARE, "%s already running, pid: %d", 104149434Spjd getprogname(), otherpid); 105149434Spjd } 106149434Spjd warn("Cannot open or create pidfile"); 107149434Spjd } 108149434Spjd 109126383Sphk if (debugging == 0 && daemon(0, 0) == -1) { 110126383Sphk watchdog_onoff(0); 111149434Spjd pidfile_remove(pfh); 112126383Sphk err(EX_OSERR, "daemon"); 113126383Sphk } 114116874Ssmkelly 115126383Sphk signal(SIGHUP, SIG_IGN); 116126383Sphk signal(SIGINT, sighandler); 117126383Sphk signal(SIGTERM, sighandler); 118116874Ssmkelly 119149434Spjd pidfile_write(pfh); 120213181Semaste if (madvise(0, 0, MADV_PROTECT) != 0) 121213181Semaste warn("madvise failed"); 122240242Szont if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) 123240242Szont warn("mlockall failed"); 124116874Ssmkelly 125126383Sphk watchdog_loop(); 126116874Ssmkelly 127126383Sphk /* exiting */ 128149434Spjd pidfile_remove(pfh); 129126383Sphk return (EX_OK); 130126383Sphk } else { 131126383Sphk if (passive) 132126383Sphk timeout |= WD_PASSIVE; 133126383Sphk else 134126383Sphk timeout |= WD_ACTIVE; 135165263Sn_hibma if (watchdog_patpat(timeout) < 0) 136126383Sphk err(EX_OSERR, "patting the dog"); 137126383Sphk return (EX_OK); 138126383Sphk } 139116874Ssmkelly} 140116874Ssmkelly 141116874Ssmkelly/* 142116874Ssmkelly * Catch signals and begin shutdown process. 143116874Ssmkelly */ 144116874Ssmkellystatic void 145116874Ssmkellysighandler(int signum) 146116874Ssmkelly{ 147116874Ssmkelly 148116874Ssmkelly if (signum == SIGINT || signum == SIGTERM) 149116874Ssmkelly end_program = 1; 150116874Ssmkelly} 151116874Ssmkelly 152116874Ssmkelly/* 153128705Ssmkelly * Open the watchdog device. 154116874Ssmkelly */ 155116874Ssmkellystatic int 156201227Sedwatchdog_init(void) 157116874Ssmkelly{ 158116874Ssmkelly 159126383Sphk fd = open("/dev/" _PATH_WATCHDOG, O_RDWR); 160126383Sphk if (fd >= 0) 161126383Sphk return (0); 162126383Sphk warn("Could not open watchdog device"); 163126383Sphk return (-1); 164116874Ssmkelly} 165116874Ssmkelly 166116874Ssmkelly/* 167116874Ssmkelly * Main program loop which is iterated every second. 168116874Ssmkelly */ 169116874Ssmkellystatic void 170116874Ssmkellywatchdog_loop(void) 171116874Ssmkelly{ 172116874Ssmkelly struct stat sb; 173116874Ssmkelly int failed; 174116874Ssmkelly 175165263Sn_hibma while (end_program != 2) { 176116874Ssmkelly failed = 0; 177116874Ssmkelly 178126383Sphk if (test_cmd != NULL) 179126383Sphk failed = system(test_cmd); 180126383Sphk else 181126383Sphk failed = stat("/etc", &sb); 182116874Ssmkelly 183116874Ssmkelly if (failed == 0) 184165263Sn_hibma watchdog_patpat(timeout|WD_ACTIVE); 185126383Sphk sleep(nap); 186165263Sn_hibma 187165263Sn_hibma if (end_program != 0) { 188165263Sn_hibma if (watchdog_onoff(0) == 0) { 189165263Sn_hibma end_program = 2; 190165263Sn_hibma } else { 191165263Sn_hibma warnx("Could not stop the watchdog, not exitting"); 192165263Sn_hibma end_program = 0; 193165263Sn_hibma } 194165263Sn_hibma } 195116874Ssmkelly } 196116874Ssmkelly} 197116874Ssmkelly 198116874Ssmkelly/* 199116874Ssmkelly * Reset the watchdog timer. This function must be called periodically 200116874Ssmkelly * to keep the watchdog from firing. 201116874Ssmkelly */ 202210300Sdelphijstatic int 203165263Sn_hibmawatchdog_patpat(u_int t) 204116874Ssmkelly{ 205116874Ssmkelly 206165263Sn_hibma return ioctl(fd, WDIOCPATPAT, &t); 207116874Ssmkelly} 208116874Ssmkelly 209116874Ssmkelly/* 210116874Ssmkelly * Toggle the kernel's watchdog. This routine is used to enable and 211116874Ssmkelly * disable the watchdog. 212116874Ssmkelly */ 213116874Ssmkellystatic int 214116874Ssmkellywatchdog_onoff(int onoff) 215116874Ssmkelly{ 216116874Ssmkelly 217126383Sphk if (onoff) 218165263Sn_hibma return watchdog_patpat((timeout|WD_ACTIVE)); 219126383Sphk else 220165263Sn_hibma return watchdog_patpat(0); 221116874Ssmkelly} 222116874Ssmkelly 223116874Ssmkelly/* 224116874Ssmkelly * Tell user how to use the program. 225116874Ssmkelly */ 226116874Ssmkellystatic void 227201227Sedusage(void) 228116874Ssmkelly{ 229126383Sphk if (is_daemon) 230165263Sn_hibma fprintf(stderr, "usage: watchdogd [-d] [-e cmd] [-I file] [-s sleep] [-t timeout]\n"); 231126383Sphk else 232156334Sphk fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n"); 233116874Ssmkelly exit(EX_USAGE); 234116874Ssmkelly} 235116874Ssmkelly 236116874Ssmkelly/* 237116874Ssmkelly * Handle the few command line arguments supported. 238116874Ssmkelly */ 239116874Ssmkellystatic void 240116874Ssmkellyparseargs(int argc, char *argv[]) 241116874Ssmkelly{ 242116874Ssmkelly int c; 243126383Sphk char *p; 244126383Sphk double a; 245116874Ssmkelly 246126383Sphk c = strlen(argv[0]); 247126383Sphk if (argv[0][c - 1] == 'd') 248126383Sphk is_daemon = 1; 249126383Sphk while ((c = getopt(argc, argv, 250126383Sphk is_daemon ? "I:de:s:t:?" : "dt:?")) != -1) { 251116874Ssmkelly switch (c) { 252116874Ssmkelly case 'I': 253116874Ssmkelly pidfile = optarg; 254116874Ssmkelly break; 255116874Ssmkelly case 'd': 256116874Ssmkelly debugging = 1; 257116874Ssmkelly break; 258126383Sphk case 'e': 259126383Sphk test_cmd = strdup(optarg); 260126383Sphk break; 261126383Sphk#ifdef notyet 262126383Sphk case 'p': 263126383Sphk passive = 1; 264126383Sphk break; 265126383Sphk#endif 266126383Sphk case 's': 267126383Sphk p = NULL; 268126383Sphk errno = 0; 269126383Sphk nap = strtol(optarg, &p, 0); 270126383Sphk if ((p != NULL && *p != '\0') || errno != 0) 271126383Sphk errx(EX_USAGE, "-s argument is not a number"); 272126383Sphk break; 273126383Sphk case 't': 274126383Sphk p = NULL; 275126383Sphk errno = 0; 276126383Sphk a = strtod(optarg, &p); 277126383Sphk if ((p != NULL && *p != '\0') || errno != 0) 278126383Sphk errx(EX_USAGE, "-t argument is not a number"); 279126383Sphk if (a < 0) 280126383Sphk errx(EX_USAGE, "-t argument must be positive"); 281126383Sphk if (a == 0) 282126383Sphk timeout = WD_TO_NEVER; 283126383Sphk else 284243820Sdelphij timeout = flsll(a * 1e9); 285126383Sphk if (debugging) 286126383Sphk printf("Timeout is 2^%d nanoseconds\n", 287126383Sphk timeout); 288126383Sphk break; 289116874Ssmkelly case '?': 290116874Ssmkelly default: 291116874Ssmkelly usage(); 292116874Ssmkelly /* NOTREACHED */ 293116874Ssmkelly } 294116874Ssmkelly } 295150747Sphk if (argc != optind) 296150747Sphk errx(EX_USAGE, "extra arguments."); 297126383Sphk if (is_daemon && timeout < WD_TO_1SEC) 298126383Sphk errx(EX_USAGE, "-t argument is less than one second."); 299116874Ssmkelly} 300