1210300Sdelphij/*- 2128898Ssmkelly * Copyright (c) 2003-2004 Sean M. Kelly <smkelly@FreeBSD.org> 3247405Salfred * Copyright (c) 2013 iXsystems.com, 4247405Salfred * author: Alfred Perlstein <alfred@freebsd.org> 5247405Salfred * 6116874Ssmkelly * All rights reserved. 7116874Ssmkelly * 8116874Ssmkelly * Redistribution and use in source and binary forms, with or without 9116874Ssmkelly * modification, are permitted provided that the following conditions 10116874Ssmkelly * are met: 11116874Ssmkelly * 1. Redistributions of source code must retain the above copyright 12116874Ssmkelly * notice, this list of conditions and the following disclaimer. 13116874Ssmkelly * 2. Redistributions in binary form must reproduce the above copyright 14116874Ssmkelly * notice, this list of conditions and the following disclaimer in the 15116874Ssmkelly * documentation and/or other materials provided with the distribution. 16116874Ssmkelly * 17116874Ssmkelly * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18116874Ssmkelly * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19116874Ssmkelly * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20116874Ssmkelly * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21116874Ssmkelly * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22116874Ssmkelly * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23116874Ssmkelly * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24116874Ssmkelly * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25116874Ssmkelly * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26116874Ssmkelly * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27116874Ssmkelly * SUCH DAMAGE. 28116874Ssmkelly */ 29116874Ssmkelly 30116874Ssmkelly/* 31116874Ssmkelly * Software watchdog daemon. 32116874Ssmkelly */ 33116874Ssmkelly 34117185Ssmkelly#include <sys/types.h> 35116874Ssmkelly__FBSDID("$FreeBSD$"); 36116874Ssmkelly 37213181Semaste#include <sys/mman.h> 38149434Spjd#include <sys/param.h> 39117185Ssmkelly#include <sys/rtprio.h> 40117185Ssmkelly#include <sys/stat.h> 41116874Ssmkelly#include <sys/time.h> 42253719Salfred#include <sys/sysctl.h> 43126383Sphk#include <sys/watchdog.h> 44116874Ssmkelly 45116874Ssmkelly#include <err.h> 46126383Sphk#include <errno.h> 47126383Sphk#include <fcntl.h> 48149434Spjd#include <libutil.h> 49126383Sphk#include <math.h> 50116874Ssmkelly#include <paths.h> 51117185Ssmkelly#include <signal.h> 52116874Ssmkelly#include <stdio.h> 53253744Sian#include <stdint.h> 54116874Ssmkelly#include <stdlib.h> 55126383Sphk#include <string.h> 56242519Sdelphij#include <strings.h> 57116874Ssmkelly#include <sysexits.h> 58247405Salfred#include <syslog.h> 59116874Ssmkelly#include <unistd.h> 60116874Ssmkelly 61247405Salfred#include <getopt.h> 62247405Salfred 63254173Salfredstatic long fetchtimeout(int opt, 64254173Salfred const char *longopt, const char *myoptarg, int zero_ok); 65116874Ssmkellystatic void parseargs(int, char *[]); 66253719Salfredstatic int seconds_to_pow2ns(int); 67116874Ssmkellystatic void sighandler(int); 68116874Ssmkellystatic void watchdog_loop(void); 69116874Ssmkellystatic int watchdog_init(void); 70116874Ssmkellystatic int watchdog_onoff(int onoff); 71165263Sn_hibmastatic int watchdog_patpat(u_int timeout); 72116874Ssmkellystatic void usage(void); 73253719Salfredstatic int tstotv(struct timeval *tv, struct timespec *ts); 74253719Salfredstatic int tvtohz(struct timeval *tv); 75116874Ssmkelly 76210300Sdelphijstatic int debugging = 0; 77210300Sdelphijstatic int end_program = 0; 78210300Sdelphijstatic const char *pidfile = _PATH_VARRUN "watchdogd.pid"; 79247405Salfredstatic u_int timeout = WD_TO_128SEC; 80247405Salfredstatic u_int pretimeout = 0; 81253719Salfredstatic u_int timeout_sec; 82210300Sdelphijstatic u_int passive = 0; 83210300Sdelphijstatic int is_daemon = 0; 84247405Salfredstatic int is_dry_run = 0; /* do not arm the watchdog, only 85247405Salfred report on timing of the watch 86247405Salfred program */ 87247405Salfredstatic int do_timedog = 0; 88248744Smarkjstatic int do_syslog = 1; 89210300Sdelphijstatic int fd = -1; 90210300Sdelphijstatic int nap = 1; 91247405Salfredstatic int carp_thresh_seconds = -1; 92210300Sdelphijstatic char *test_cmd = NULL; 93116874Ssmkelly 94247405Salfredstatic const char *getopt_shortopts; 95247405Salfred 96247405Salfredstatic int pretimeout_set; 97247405Salfredstatic int pretimeout_act; 98247405Salfredstatic int pretimeout_act_set; 99247405Salfred 100247405Salfredstatic int softtimeout_set; 101247405Salfredstatic int softtimeout_act; 102247405Salfredstatic int softtimeout_act_set; 103247405Salfred 104247405Salfredstatic struct option longopts[] = { 105247405Salfred { "debug", no_argument, &debugging, 1 }, 106247405Salfred { "pretimeout", required_argument, &pretimeout_set, 1 }, 107247405Salfred { "pretimeout-action", required_argument, &pretimeout_act_set, 1 }, 108247405Salfred { "softtimeout", no_argument, &softtimeout_set, 1 }, 109247405Salfred { "softtimeout-action", required_argument, &softtimeout_act_set, 1 }, 110247405Salfred { NULL, 0, NULL, 0} 111247405Salfred}; 112247405Salfred 113116874Ssmkelly/* 114245949Sian * Ask malloc() to map minimum-sized chunks of virtual address space at a time, 115245949Sian * so that mlockall() won't needlessly wire megabytes of unused memory into the 116245949Sian * process. This must be done using the malloc_conf string so that it gets set 117245949Sian * up before the first allocation, which happens before entry to main(). 118245949Sian */ 119245949Sianconst char * malloc_conf = "lg_chunk:0"; 120245949Sian 121245949Sian/* 122128705Ssmkelly * Periodically pat the watchdog, preventing it from firing. 123116874Ssmkelly */ 124116874Ssmkellyint 125116874Ssmkellymain(int argc, char *argv[]) 126116874Ssmkelly{ 127116874Ssmkelly struct rtprio rtp; 128149434Spjd struct pidfh *pfh; 129149434Spjd pid_t otherpid; 130116874Ssmkelly 131116874Ssmkelly if (getuid() != 0) 132116874Ssmkelly errx(EX_SOFTWARE, "not super user"); 133116874Ssmkelly 134116874Ssmkelly parseargs(argc, argv); 135116874Ssmkelly 136248744Smarkj if (do_syslog) 137247405Salfred openlog("watchdogd", LOG_CONS|LOG_NDELAY|LOG_PERROR, 138247405Salfred LOG_DAEMON); 139247405Salfred 140116874Ssmkelly rtp.type = RTP_PRIO_REALTIME; 141116874Ssmkelly rtp.prio = 0; 142116874Ssmkelly if (rtprio(RTP_SET, 0, &rtp) == -1) 143116874Ssmkelly err(EX_OSERR, "rtprio"); 144116874Ssmkelly 145247405Salfred if (!is_dry_run && watchdog_init() == -1) 146117185Ssmkelly errx(EX_SOFTWARE, "unable to initialize watchdog"); 147116874Ssmkelly 148126383Sphk if (is_daemon) { 149126383Sphk if (watchdog_onoff(1) == -1) 150200778Sru err(EX_OSERR, "patting the dog"); 151116874Ssmkelly 152150214Spjd pfh = pidfile_open(pidfile, 0600, &otherpid); 153149434Spjd if (pfh == NULL) { 154149434Spjd if (errno == EEXIST) { 155247405Salfred watchdog_onoff(0); 156149434Spjd errx(EX_SOFTWARE, "%s already running, pid: %d", 157149434Spjd getprogname(), otherpid); 158149434Spjd } 159149434Spjd warn("Cannot open or create pidfile"); 160149434Spjd } 161149434Spjd 162126383Sphk if (debugging == 0 && daemon(0, 0) == -1) { 163126383Sphk watchdog_onoff(0); 164149434Spjd pidfile_remove(pfh); 165126383Sphk err(EX_OSERR, "daemon"); 166126383Sphk } 167116874Ssmkelly 168126383Sphk signal(SIGHUP, SIG_IGN); 169126383Sphk signal(SIGINT, sighandler); 170126383Sphk signal(SIGTERM, sighandler); 171116874Ssmkelly 172149434Spjd pidfile_write(pfh); 173213181Semaste if (madvise(0, 0, MADV_PROTECT) != 0) 174213181Semaste warn("madvise failed"); 175239896Szont if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) 176239769Szont warn("mlockall failed"); 177116874Ssmkelly 178126383Sphk watchdog_loop(); 179116874Ssmkelly 180126383Sphk /* exiting */ 181149434Spjd pidfile_remove(pfh); 182126383Sphk return (EX_OK); 183126383Sphk } else { 184126383Sphk if (passive) 185126383Sphk timeout |= WD_PASSIVE; 186126383Sphk else 187126383Sphk timeout |= WD_ACTIVE; 188165263Sn_hibma if (watchdog_patpat(timeout) < 0) 189126383Sphk err(EX_OSERR, "patting the dog"); 190126383Sphk return (EX_OK); 191126383Sphk } 192116874Ssmkelly} 193116874Ssmkelly 194253719Salfredstatic void 195253719Salfredpow2ns_to_ts(int pow2ns, struct timespec *ts) 196253719Salfred{ 197253719Salfred uint64_t ns; 198253719Salfred 199253719Salfred ns = 1ULL << pow2ns; 200253719Salfred ts->tv_sec = ns / 1000000000ULL; 201253719Salfred ts->tv_nsec = ns % 1000000000ULL; 202253719Salfred} 203253719Salfred 204116874Ssmkelly/* 205253719Salfred * Convert a timeout in seconds to N where 2^N nanoseconds is close to 206253719Salfred * "seconds". 207253719Salfred * 208253719Salfred * The kernel expects the timeouts for watchdogs in "2^N nanosecond format". 209253719Salfred */ 210253719Salfredstatic u_int 211253719Salfredparse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg) 212253719Salfred{ 213253719Salfred double a; 214253719Salfred u_int rv; 215253719Salfred struct timespec ts; 216253719Salfred struct timeval tv; 217253719Salfred int ticks; 218253719Salfred char shortopt[] = "- "; 219253719Salfred 220253719Salfred if (!longopt) 221253719Salfred shortopt[1] = opt; 222253719Salfred 223254173Salfred a = fetchtimeout(opt, longopt, myoptarg, 1); 224253719Salfred 225253719Salfred if (a == 0) 226253719Salfred rv = WD_TO_NEVER; 227253719Salfred else 228253719Salfred rv = seconds_to_pow2ns(a); 229253719Salfred pow2ns_to_ts(rv, &ts); 230253719Salfred tstotv(&tv, &ts); 231253719Salfred ticks = tvtohz(&tv); 232253719Salfred if (debugging) { 233253719Salfred printf("Timeout for %s%s " 234253719Salfred "is 2^%d nanoseconds " 235253744Sian "(in: %s sec -> out: %jd sec %ld ns -> %d ticks)\n", 236253719Salfred longopt ? "-" : "", longopt ? longopt : shortopt, 237253719Salfred rv, 238253744Sian myoptarg, (intmax_t)ts.tv_sec, ts.tv_nsec, ticks); 239253719Salfred } 240253719Salfred if (ticks <= 0) { 241253719Salfred errx(1, "Timeout for %s%s is too small, please choose a higher timeout.", longopt ? "-" : "", longopt ? longopt : shortopt); 242253719Salfred } 243253719Salfred 244253719Salfred return (rv); 245253719Salfred} 246253719Salfred 247253719Salfred/* 248116874Ssmkelly * Catch signals and begin shutdown process. 249116874Ssmkelly */ 250116874Ssmkellystatic void 251116874Ssmkellysighandler(int signum) 252116874Ssmkelly{ 253116874Ssmkelly 254116874Ssmkelly if (signum == SIGINT || signum == SIGTERM) 255116874Ssmkelly end_program = 1; 256116874Ssmkelly} 257116874Ssmkelly 258116874Ssmkelly/* 259128705Ssmkelly * Open the watchdog device. 260116874Ssmkelly */ 261116874Ssmkellystatic int 262201227Sedwatchdog_init(void) 263116874Ssmkelly{ 264116874Ssmkelly 265247405Salfred if (is_dry_run) 266247405Salfred return 0; 267247405Salfred 268126383Sphk fd = open("/dev/" _PATH_WATCHDOG, O_RDWR); 269126383Sphk if (fd >= 0) 270126383Sphk return (0); 271126383Sphk warn("Could not open watchdog device"); 272126383Sphk return (-1); 273116874Ssmkelly} 274116874Ssmkelly 275116874Ssmkelly/* 276247405Salfred * If we are doing timing, then get the time. 277247405Salfred */ 278247405Salfredstatic int 279247405Salfredwatchdog_getuptime(struct timespec *tp) 280247405Salfred{ 281247405Salfred int error; 282247405Salfred 283247405Salfred if (!do_timedog) 284247405Salfred return 0; 285247405Salfred 286247405Salfred error = clock_gettime(CLOCK_UPTIME_FAST, tp); 287247405Salfred if (error) 288247405Salfred warn("clock_gettime"); 289247405Salfred return (error); 290247405Salfred} 291247405Salfred 292247405Salfredstatic long 293247405Salfredwatchdog_check_dogfunction_time(struct timespec *tp_start, 294247405Salfred struct timespec *tp_end) 295247405Salfred{ 296248744Smarkj struct timeval tv_start, tv_end, tv_now, tv; 297247405Salfred const char *cmd_prefix, *cmd; 298248744Smarkj struct timespec tp_now; 299247405Salfred int sec; 300247405Salfred 301247405Salfred if (!do_timedog) 302247405Salfred return (0); 303247405Salfred 304247405Salfred TIMESPEC_TO_TIMEVAL(&tv_start, tp_start); 305247405Salfred TIMESPEC_TO_TIMEVAL(&tv_end, tp_end); 306247405Salfred timersub(&tv_end, &tv_start, &tv); 307247405Salfred sec = tv.tv_sec; 308247405Salfred if (sec < carp_thresh_seconds) 309247405Salfred return (sec); 310247405Salfred 311247405Salfred if (test_cmd) { 312247405Salfred cmd_prefix = "Watchdog program"; 313247405Salfred cmd = test_cmd; 314247405Salfred } else { 315247405Salfred cmd_prefix = "Watchdog operation"; 316247405Salfred cmd = "stat(\"/etc\", &sb)"; 317247405Salfred } 318247405Salfred if (do_syslog) 319247405Salfred syslog(LOG_CRIT, "%s: '%s' took too long: " 320248744Smarkj "%d.%06ld seconds >= %d seconds threshold", 321247405Salfred cmd_prefix, cmd, sec, (long)tv.tv_usec, 322247405Salfred carp_thresh_seconds); 323248744Smarkj else 324248744Smarkj warnx("%s: '%s' took too long: " 325248744Smarkj "%d.%06ld seconds >= %d seconds threshold", 326248744Smarkj cmd_prefix, cmd, sec, (long)tv.tv_usec, 327248744Smarkj carp_thresh_seconds); 328248744Smarkj 329248744Smarkj /* 330248744Smarkj * Adjust the sleep interval again in case syslog(3) took a non-trivial 331248744Smarkj * amount of time to run. 332248744Smarkj */ 333248744Smarkj if (watchdog_getuptime(&tp_now)) 334248744Smarkj return (sec); 335248744Smarkj TIMESPEC_TO_TIMEVAL(&tv_now, &tp_now); 336248744Smarkj timersub(&tv_now, &tv_start, &tv); 337248744Smarkj sec = tv.tv_sec; 338248744Smarkj 339247405Salfred return (sec); 340247405Salfred} 341247405Salfred 342247405Salfred/* 343116874Ssmkelly * Main program loop which is iterated every second. 344116874Ssmkelly */ 345116874Ssmkellystatic void 346116874Ssmkellywatchdog_loop(void) 347116874Ssmkelly{ 348247405Salfred struct timespec ts_start, ts_end; 349116874Ssmkelly struct stat sb; 350247405Salfred long waited; 351247405Salfred int error, failed; 352116874Ssmkelly 353165263Sn_hibma while (end_program != 2) { 354116874Ssmkelly failed = 0; 355116874Ssmkelly 356247405Salfred error = watchdog_getuptime(&ts_start); 357247405Salfred if (error) { 358247405Salfred end_program = 1; 359247405Salfred goto try_end; 360247405Salfred } 361247405Salfred 362126383Sphk if (test_cmd != NULL) 363126383Sphk failed = system(test_cmd); 364126383Sphk else 365126383Sphk failed = stat("/etc", &sb); 366116874Ssmkelly 367247405Salfred error = watchdog_getuptime(&ts_end); 368247405Salfred if (error) { 369247405Salfred end_program = 1; 370247405Salfred goto try_end; 371247405Salfred } 372247405Salfred 373116874Ssmkelly if (failed == 0) 374165263Sn_hibma watchdog_patpat(timeout|WD_ACTIVE); 375248744Smarkj 376248744Smarkj waited = watchdog_check_dogfunction_time(&ts_start, &ts_end); 377247405Salfred if (nap - waited > 0) 378247405Salfred sleep(nap - waited); 379165263Sn_hibma 380247405Salfredtry_end: 381165263Sn_hibma if (end_program != 0) { 382165263Sn_hibma if (watchdog_onoff(0) == 0) { 383165263Sn_hibma end_program = 2; 384165263Sn_hibma } else { 385245951Sian warnx("Could not stop the watchdog, not exiting"); 386165263Sn_hibma end_program = 0; 387165263Sn_hibma } 388165263Sn_hibma } 389116874Ssmkelly } 390116874Ssmkelly} 391116874Ssmkelly 392116874Ssmkelly/* 393116874Ssmkelly * Reset the watchdog timer. This function must be called periodically 394116874Ssmkelly * to keep the watchdog from firing. 395116874Ssmkelly */ 396210300Sdelphijstatic int 397165263Sn_hibmawatchdog_patpat(u_int t) 398116874Ssmkelly{ 399116874Ssmkelly 400247405Salfred if (is_dry_run) 401247405Salfred return 0; 402247405Salfred 403165263Sn_hibma return ioctl(fd, WDIOCPATPAT, &t); 404116874Ssmkelly} 405116874Ssmkelly 406116874Ssmkelly/* 407116874Ssmkelly * Toggle the kernel's watchdog. This routine is used to enable and 408116874Ssmkelly * disable the watchdog. 409116874Ssmkelly */ 410116874Ssmkellystatic int 411116874Ssmkellywatchdog_onoff(int onoff) 412116874Ssmkelly{ 413247405Salfred int error; 414116874Ssmkelly 415247405Salfred /* fake successful watchdog op if a dry run */ 416247405Salfred if (is_dry_run) 417247405Salfred return 0; 418247405Salfred 419247405Salfred if (onoff) { 420247405Salfred /* 421247405Salfred * Call the WDIOC_SETSOFT regardless of softtimeout_set 422247405Salfred * because we'll need to turn it off if someone had turned 423247405Salfred * it on. 424247405Salfred */ 425247405Salfred error = ioctl(fd, WDIOC_SETSOFT, &softtimeout_set); 426247405Salfred if (error) { 427247405Salfred warn("setting WDIOC_SETSOFT %d", softtimeout_set); 428247405Salfred return (error); 429247405Salfred } 430247405Salfred error = watchdog_patpat((timeout|WD_ACTIVE)); 431247405Salfred if (error) { 432247405Salfred warn("watchdog_patpat failed"); 433247405Salfred goto failsafe; 434247405Salfred } 435247405Salfred if (softtimeout_act_set) { 436247405Salfred error = ioctl(fd, WDIOC_SETSOFTTIMEOUTACT, 437247405Salfred &softtimeout_act); 438247405Salfred if (error) { 439247405Salfred warn("setting WDIOC_SETSOFTTIMEOUTACT %d", 440247405Salfred softtimeout_act); 441247405Salfred goto failsafe; 442247405Salfred } 443247405Salfred } 444247405Salfred if (pretimeout_set) { 445247405Salfred error = ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout); 446247405Salfred if (error) { 447247405Salfred warn("setting WDIOC_SETPRETIMEOUT %d", 448247405Salfred pretimeout); 449247405Salfred goto failsafe; 450247405Salfred } 451247405Salfred } 452247405Salfred if (pretimeout_act_set) { 453247405Salfred error = ioctl(fd, WDIOC_SETPRETIMEOUTACT, 454247405Salfred &pretimeout_act); 455247405Salfred if (error) { 456247405Salfred warn("setting WDIOC_SETPRETIMEOUTACT %d", 457247405Salfred pretimeout_act); 458247405Salfred goto failsafe; 459247405Salfred } 460247405Salfred } 461247405Salfred /* pat one more time for good measure */ 462165263Sn_hibma return watchdog_patpat((timeout|WD_ACTIVE)); 463247405Salfred } else { 464165263Sn_hibma return watchdog_patpat(0); 465247405Salfred } 466247405Salfredfailsafe: 467247405Salfred watchdog_patpat(0); 468247405Salfred return (error); 469116874Ssmkelly} 470116874Ssmkelly 471116874Ssmkelly/* 472116874Ssmkelly * Tell user how to use the program. 473116874Ssmkelly */ 474116874Ssmkellystatic void 475201227Sedusage(void) 476116874Ssmkelly{ 477126383Sphk if (is_daemon) 478247405Salfred fprintf(stderr, "usage:\n" 479248744Smarkj" watchdogd [-dnSw] [-e cmd] [-I file] [-s sleep] [-t timeout]\n" 480247405Salfred" [-T script_timeout]\n" 481247405Salfred" [--debug]\n" 482247405Salfred" [--pretimeout seconds] [-pretimeout-action action]\n" 483247405Salfred" [--softtimeout] [-softtimeout-action action]\n" 484247405Salfred); 485126383Sphk else 486156334Sphk fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n"); 487116874Ssmkelly exit(EX_USAGE); 488116874Ssmkelly} 489116874Ssmkelly 490247405Salfredstatic long 491254173Salfredfetchtimeout(int opt, const char *longopt, const char *myoptarg, int zero_ok) 492247405Salfred{ 493247405Salfred const char *errstr; 494247405Salfred char *p; 495247405Salfred long rv; 496247405Salfred 497247405Salfred errstr = NULL; 498247405Salfred p = NULL; 499247405Salfred errno = 0; 500247405Salfred rv = strtol(myoptarg, &p, 0); 501247405Salfred if ((p != NULL && *p != '\0') || errno != 0) 502247405Salfred errstr = "is not a number"; 503254173Salfred if (rv < 0 || (!zero_ok && rv == 0)) 504247405Salfred errstr = "must be greater than zero"; 505247405Salfred if (errstr) { 506247405Salfred if (longopt) 507247405Salfred errx(EX_USAGE, "--%s argument %s", longopt, errstr); 508247405Salfred else 509247405Salfred errx(EX_USAGE, "-%c argument %s", opt, errstr); 510247405Salfred } 511247405Salfred return (rv); 512247405Salfred} 513247405Salfred 514247405Salfredstruct act_tbl { 515247405Salfred const char *at_act; 516247405Salfred int at_value; 517247405Salfred}; 518247405Salfred 519249245Sedstatic const struct act_tbl act_tbl[] = { 520247405Salfred { "panic", WD_SOFT_PANIC }, 521247405Salfred { "ddb", WD_SOFT_DDB }, 522247405Salfred { "log", WD_SOFT_LOG }, 523247405Salfred { "printf", WD_SOFT_PRINTF }, 524247405Salfred { NULL, 0 } 525247405Salfred}; 526247405Salfred 527247405Salfredstatic void 528247405Salfredtimeout_act_error(const char *lopt, const char *badact) 529247405Salfred{ 530247405Salfred char *opts, *oldopts; 531247405Salfred int i; 532247405Salfred 533247405Salfred opts = NULL; 534247405Salfred for (i = 0; act_tbl[i].at_act != NULL; i++) { 535247405Salfred oldopts = opts; 536247405Salfred if (asprintf(&opts, "%s%s%s", 537247405Salfred oldopts == NULL ? "" : oldopts, 538247405Salfred oldopts == NULL ? "" : ", ", 539247405Salfred act_tbl[i].at_act) == -1) 540247405Salfred err(EX_OSERR, "malloc"); 541247405Salfred free(oldopts); 542247405Salfred } 543247405Salfred warnx("bad --%s argument '%s' must be one of (%s).", 544247405Salfred lopt, badact, opts); 545247405Salfred usage(); 546247405Salfred} 547247405Salfred 548116874Ssmkelly/* 549247405Salfred * Take a comma separated list of actions and or the flags 550247405Salfred * together for the ioctl. 551247405Salfred */ 552247405Salfredstatic int 553247405Salfredtimeout_act_str2int(const char *lopt, const char *acts) 554247405Salfred{ 555247405Salfred int i; 556247405Salfred char *dupacts, *tofree; 557247405Salfred char *o; 558247405Salfred int rv = 0; 559247405Salfred 560247405Salfred tofree = dupacts = strdup(acts); 561247405Salfred if (!tofree) 562247405Salfred err(EX_OSERR, "malloc"); 563247405Salfred while ((o = strsep(&dupacts, ",")) != NULL) { 564247405Salfred for (i = 0; act_tbl[i].at_act != NULL; i++) { 565247405Salfred if (!strcmp(o, act_tbl[i].at_act)) { 566247405Salfred rv |= act_tbl[i].at_value; 567247405Salfred break; 568247405Salfred } 569247405Salfred } 570247405Salfred if (act_tbl[i].at_act == NULL) 571247405Salfred timeout_act_error(lopt, o); 572247405Salfred } 573247405Salfred free(tofree); 574247405Salfred return rv; 575247405Salfred} 576247405Salfred 577253719Salfredint 578253719Salfredtstotv(struct timeval *tv, struct timespec *ts) 579253719Salfred{ 580253719Salfred 581253719Salfred tv->tv_sec = ts->tv_sec; 582253719Salfred tv->tv_usec = ts->tv_nsec / 1000; 583253719Salfred return 0; 584253719Salfred} 585253719Salfred 586247405Salfred/* 587253719Salfred * Convert a timeval to a number of ticks. 588253719Salfred * Mostly copied from the kernel. 589253719Salfred */ 590253719Salfredint 591253719Salfredtvtohz(struct timeval *tv) 592253719Salfred{ 593253719Salfred register unsigned long ticks; 594253719Salfred register long sec, usec; 595253719Salfred int hz; 596253719Salfred size_t hzsize; 597253719Salfred int error; 598253719Salfred int tick; 599253719Salfred 600253719Salfred hzsize = sizeof(hz); 601253719Salfred 602253719Salfred error = sysctlbyname("kern.hz", &hz, &hzsize, NULL, 0); 603253719Salfred if (error) 604253719Salfred err(1, "sysctlbyname kern.hz"); 605253719Salfred 606253719Salfred tick = 1000000 / hz; 607253719Salfred 608253719Salfred /* 609253719Salfred * If the number of usecs in the whole seconds part of the time 610253719Salfred * difference fits in a long, then the total number of usecs will 611253719Salfred * fit in an unsigned long. Compute the total and convert it to 612253719Salfred * ticks, rounding up and adding 1 to allow for the current tick 613253719Salfred * to expire. Rounding also depends on unsigned long arithmetic 614253719Salfred * to avoid overflow. 615253719Salfred * 616253719Salfred * Otherwise, if the number of ticks in the whole seconds part of 617253719Salfred * the time difference fits in a long, then convert the parts to 618253719Salfred * ticks separately and add, using similar rounding methods and 619253719Salfred * overflow avoidance. This method would work in the previous 620253719Salfred * case but it is slightly slower and assumes that hz is integral. 621253719Salfred * 622253719Salfred * Otherwise, round the time difference down to the maximum 623253719Salfred * representable value. 624253719Salfred * 625253719Salfred * If ints have 32 bits, then the maximum value for any timeout in 626253719Salfred * 10ms ticks is 248 days. 627253719Salfred */ 628253719Salfred sec = tv->tv_sec; 629253719Salfred usec = tv->tv_usec; 630253719Salfred if (usec < 0) { 631253719Salfred sec--; 632253719Salfred usec += 1000000; 633253719Salfred } 634253719Salfred if (sec < 0) { 635253719Salfred#ifdef DIAGNOSTIC 636253719Salfred if (usec > 0) { 637253719Salfred sec++; 638253719Salfred usec -= 1000000; 639253719Salfred } 640253719Salfred printf("tvotohz: negative time difference %ld sec %ld usec\n", 641253719Salfred sec, usec); 642253719Salfred#endif 643253719Salfred ticks = 1; 644253719Salfred } else if (sec <= LONG_MAX / 1000000) 645253719Salfred ticks = (sec * 1000000 + (unsigned long)usec + (tick - 1)) 646253719Salfred / tick + 1; 647253719Salfred else if (sec <= LONG_MAX / hz) 648253719Salfred ticks = sec * hz 649253719Salfred + ((unsigned long)usec + (tick - 1)) / tick + 1; 650253719Salfred else 651253719Salfred ticks = LONG_MAX; 652253719Salfred if (ticks > INT_MAX) 653253719Salfred ticks = INT_MAX; 654253719Salfred return ((int)ticks); 655253719Salfred} 656253719Salfred 657253719Salfredstatic int 658253719Salfredseconds_to_pow2ns(int seconds) 659253719Salfred{ 660253719Salfred uint64_t power; 661253719Salfred uint64_t ns; 662253719Salfred uint64_t shifted; 663253719Salfred 664253719Salfred if (seconds <= 0) 665253719Salfred errx(1, "seconds %d < 0", seconds); 666253719Salfred ns = ((uint64_t)seconds) * 1000000000ULL; 667253719Salfred power = flsll(ns); 668253719Salfred shifted = 1ULL << power; 669253719Salfred if (shifted <= ns) { 670253719Salfred power++; 671253719Salfred } 672253719Salfred if (debugging) { 673253719Salfred printf("shifted %lld\n", (long long)shifted); 674253719Salfred printf("seconds_to_pow2ns: seconds: %d, ns %lld, power %d\n", 675253719Salfred seconds, (long long)ns, (int)power); 676253719Salfred } 677253719Salfred return (power); 678253719Salfred} 679253719Salfred 680253719Salfred 681253719Salfred/* 682116874Ssmkelly * Handle the few command line arguments supported. 683116874Ssmkelly */ 684116874Ssmkellystatic void 685116874Ssmkellyparseargs(int argc, char *argv[]) 686116874Ssmkelly{ 687247405Salfred int longindex; 688116874Ssmkelly int c; 689247405Salfred const char *lopt; 690116874Ssmkelly 691247405Salfred /* 692247405Salfred * if we end with a 'd' aka 'watchdogd' then we are the daemon program, 693247405Salfred * otherwise run as a command line utility. 694247405Salfred */ 695126383Sphk c = strlen(argv[0]); 696126383Sphk if (argv[0][c - 1] == 'd') 697126383Sphk is_daemon = 1; 698247405Salfred 699247405Salfred if (is_daemon) 700247405Salfred getopt_shortopts = "I:de:ns:t:ST:w?"; 701247405Salfred else 702247405Salfred getopt_shortopts = "dt:?"; 703247405Salfred 704247405Salfred while ((c = getopt_long(argc, argv, getopt_shortopts, longopts, 705247405Salfred &longindex)) != -1) { 706116874Ssmkelly switch (c) { 707116874Ssmkelly case 'I': 708116874Ssmkelly pidfile = optarg; 709116874Ssmkelly break; 710116874Ssmkelly case 'd': 711116874Ssmkelly debugging = 1; 712116874Ssmkelly break; 713126383Sphk case 'e': 714126383Sphk test_cmd = strdup(optarg); 715126383Sphk break; 716247405Salfred case 'n': 717247405Salfred is_dry_run = 1; 718247405Salfred break; 719126383Sphk#ifdef notyet 720126383Sphk case 'p': 721126383Sphk passive = 1; 722126383Sphk break; 723126383Sphk#endif 724126383Sphk case 's': 725254173Salfred nap = fetchtimeout(c, NULL, optarg, 0); 726126383Sphk break; 727247405Salfred case 'S': 728248744Smarkj do_syslog = 0; 729247405Salfred break; 730126383Sphk case 't': 731253719Salfred timeout_sec = atoi(optarg); 732253719Salfred timeout = parse_timeout_to_pow2ns(c, NULL, optarg); 733253719Salfred if (debugging) 734253719Salfred printf("Timeout is 2^%d nanoseconds\n", 735253719Salfred timeout); 736126383Sphk break; 737247405Salfred case 'T': 738254173Salfred carp_thresh_seconds = 739254173Salfred fetchtimeout(c, "NULL", optarg, 0); 740247405Salfred break; 741247405Salfred case 'w': 742247405Salfred do_timedog = 1; 743247405Salfred break; 744247405Salfred case 0: 745247405Salfred lopt = longopts[longindex].name; 746247405Salfred if (!strcmp(lopt, "pretimeout")) { 747254173Salfred pretimeout = fetchtimeout(0, lopt, optarg, 0); 748247405Salfred } else if (!strcmp(lopt, "pretimeout-action")) { 749247405Salfred pretimeout_act = timeout_act_str2int(lopt, 750247405Salfred optarg); 751247405Salfred } else if (!strcmp(lopt, "softtimeout-action")) { 752247405Salfred softtimeout_act = timeout_act_str2int(lopt, 753247405Salfred optarg); 754247405Salfred } else { 755247405Salfred /* warnx("bad option at index %d: %s", optind, 756247405Salfred argv[optind]); 757247405Salfred usage(); 758247405Salfred */ 759247405Salfred } 760247405Salfred break; 761116874Ssmkelly case '?': 762116874Ssmkelly default: 763116874Ssmkelly usage(); 764116874Ssmkelly /* NOTREACHED */ 765116874Ssmkelly } 766116874Ssmkelly } 767247405Salfred 768247405Salfred if (carp_thresh_seconds == -1) 769247405Salfred carp_thresh_seconds = nap; 770247405Salfred 771150747Sphk if (argc != optind) 772150747Sphk errx(EX_USAGE, "extra arguments."); 773126383Sphk if (is_daemon && timeout < WD_TO_1SEC) 774126383Sphk errx(EX_USAGE, "-t argument is less than one second."); 775253719Salfred if (pretimeout_set) { 776253719Salfred struct timespec ts; 777253719Salfred 778253719Salfred pow2ns_to_ts(timeout, &ts); 779253808Sjhb if (pretimeout >= (uintmax_t)ts.tv_sec) { 780253719Salfred errx(EX_USAGE, 781253719Salfred "pretimeout (%d) >= timeout (%d -> %ld)\n" 782253719Salfred "see manual section TIMEOUT RESOLUTION", 783253719Salfred pretimeout, timeout_sec, (long)ts.tv_sec); 784253719Salfred } 785253719Salfred } 786116874Ssmkelly} 787