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: releng/11.0/usr.sbin/watchdogd/watchdogd.c 286947 2015-08-19 21:46:12Z ian $"); 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; 80286947Sianstatic u_int exit_timeout = WD_TO_NEVER; 81247405Salfredstatic u_int pretimeout = 0; 82253719Salfredstatic u_int timeout_sec; 83210300Sdelphijstatic u_int passive = 0; 84210300Sdelphijstatic int is_daemon = 0; 85247405Salfredstatic int is_dry_run = 0; /* do not arm the watchdog, only 86247405Salfred report on timing of the watch 87247405Salfred program */ 88247405Salfredstatic int do_timedog = 0; 89248744Smarkjstatic int do_syslog = 1; 90210300Sdelphijstatic int fd = -1; 91274583Sdelphijstatic int nap = 10; 92247405Salfredstatic int carp_thresh_seconds = -1; 93210300Sdelphijstatic char *test_cmd = NULL; 94116874Ssmkelly 95247405Salfredstatic const char *getopt_shortopts; 96247405Salfred 97247405Salfredstatic int pretimeout_set; 98247405Salfredstatic int pretimeout_act; 99247405Salfredstatic int pretimeout_act_set; 100247405Salfred 101247405Salfredstatic int softtimeout_set; 102247405Salfredstatic int softtimeout_act; 103247405Salfredstatic int softtimeout_act_set; 104247405Salfred 105247405Salfredstatic struct option longopts[] = { 106247405Salfred { "debug", no_argument, &debugging, 1 }, 107247405Salfred { "pretimeout", required_argument, &pretimeout_set, 1 }, 108247405Salfred { "pretimeout-action", required_argument, &pretimeout_act_set, 1 }, 109247405Salfred { "softtimeout", no_argument, &softtimeout_set, 1 }, 110247405Salfred { "softtimeout-action", required_argument, &softtimeout_act_set, 1 }, 111247405Salfred { NULL, 0, NULL, 0} 112247405Salfred}; 113247405Salfred 114116874Ssmkelly/* 115245949Sian * Ask malloc() to map minimum-sized chunks of virtual address space at a time, 116245949Sian * so that mlockall() won't needlessly wire megabytes of unused memory into the 117245949Sian * process. This must be done using the malloc_conf string so that it gets set 118245949Sian * up before the first allocation, which happens before entry to main(). 119245949Sian */ 120245949Sianconst char * malloc_conf = "lg_chunk:0"; 121245949Sian 122245949Sian/* 123128705Ssmkelly * Periodically pat the watchdog, preventing it from firing. 124116874Ssmkelly */ 125116874Ssmkellyint 126116874Ssmkellymain(int argc, char *argv[]) 127116874Ssmkelly{ 128116874Ssmkelly struct rtprio rtp; 129149434Spjd struct pidfh *pfh; 130149434Spjd pid_t otherpid; 131116874Ssmkelly 132116874Ssmkelly if (getuid() != 0) 133116874Ssmkelly errx(EX_SOFTWARE, "not super user"); 134116874Ssmkelly 135116874Ssmkelly parseargs(argc, argv); 136116874Ssmkelly 137248744Smarkj if (do_syslog) 138247405Salfred openlog("watchdogd", LOG_CONS|LOG_NDELAY|LOG_PERROR, 139247405Salfred LOG_DAEMON); 140247405Salfred 141116874Ssmkelly rtp.type = RTP_PRIO_REALTIME; 142116874Ssmkelly rtp.prio = 0; 143116874Ssmkelly if (rtprio(RTP_SET, 0, &rtp) == -1) 144116874Ssmkelly err(EX_OSERR, "rtprio"); 145116874Ssmkelly 146247405Salfred if (!is_dry_run && watchdog_init() == -1) 147117185Ssmkelly errx(EX_SOFTWARE, "unable to initialize watchdog"); 148116874Ssmkelly 149126383Sphk if (is_daemon) { 150126383Sphk if (watchdog_onoff(1) == -1) 151200778Sru err(EX_OSERR, "patting the dog"); 152116874Ssmkelly 153150214Spjd pfh = pidfile_open(pidfile, 0600, &otherpid); 154149434Spjd if (pfh == NULL) { 155149434Spjd if (errno == EEXIST) { 156247405Salfred watchdog_onoff(0); 157149434Spjd errx(EX_SOFTWARE, "%s already running, pid: %d", 158149434Spjd getprogname(), otherpid); 159149434Spjd } 160149434Spjd warn("Cannot open or create pidfile"); 161149434Spjd } 162149434Spjd 163126383Sphk if (debugging == 0 && daemon(0, 0) == -1) { 164126383Sphk watchdog_onoff(0); 165149434Spjd pidfile_remove(pfh); 166126383Sphk err(EX_OSERR, "daemon"); 167126383Sphk } 168116874Ssmkelly 169126383Sphk signal(SIGHUP, SIG_IGN); 170126383Sphk signal(SIGINT, sighandler); 171126383Sphk signal(SIGTERM, sighandler); 172116874Ssmkelly 173149434Spjd pidfile_write(pfh); 174213181Semaste if (madvise(0, 0, MADV_PROTECT) != 0) 175213181Semaste warn("madvise failed"); 176239896Szont if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) 177239769Szont warn("mlockall failed"); 178116874Ssmkelly 179126383Sphk watchdog_loop(); 180116874Ssmkelly 181126383Sphk /* exiting */ 182149434Spjd pidfile_remove(pfh); 183126383Sphk return (EX_OK); 184126383Sphk } else { 185126383Sphk if (passive) 186126383Sphk timeout |= WD_PASSIVE; 187126383Sphk else 188126383Sphk timeout |= WD_ACTIVE; 189165263Sn_hibma if (watchdog_patpat(timeout) < 0) 190126383Sphk err(EX_OSERR, "patting the dog"); 191126383Sphk return (EX_OK); 192126383Sphk } 193116874Ssmkelly} 194116874Ssmkelly 195253719Salfredstatic void 196253719Salfredpow2ns_to_ts(int pow2ns, struct timespec *ts) 197253719Salfred{ 198253719Salfred uint64_t ns; 199253719Salfred 200253719Salfred ns = 1ULL << pow2ns; 201253719Salfred ts->tv_sec = ns / 1000000000ULL; 202253719Salfred ts->tv_nsec = ns % 1000000000ULL; 203253719Salfred} 204253719Salfred 205116874Ssmkelly/* 206253719Salfred * Convert a timeout in seconds to N where 2^N nanoseconds is close to 207253719Salfred * "seconds". 208253719Salfred * 209253719Salfred * The kernel expects the timeouts for watchdogs in "2^N nanosecond format". 210253719Salfred */ 211253719Salfredstatic u_int 212253719Salfredparse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg) 213253719Salfred{ 214253719Salfred double a; 215253719Salfred u_int rv; 216253719Salfred struct timespec ts; 217253719Salfred struct timeval tv; 218253719Salfred int ticks; 219253719Salfred char shortopt[] = "- "; 220253719Salfred 221253719Salfred if (!longopt) 222253719Salfred shortopt[1] = opt; 223253719Salfred 224254173Salfred a = fetchtimeout(opt, longopt, myoptarg, 1); 225253719Salfred 226253719Salfred if (a == 0) 227253719Salfred rv = WD_TO_NEVER; 228253719Salfred else 229253719Salfred rv = seconds_to_pow2ns(a); 230253719Salfred pow2ns_to_ts(rv, &ts); 231253719Salfred tstotv(&tv, &ts); 232253719Salfred ticks = tvtohz(&tv); 233253719Salfred if (debugging) { 234253719Salfred printf("Timeout for %s%s " 235253719Salfred "is 2^%d nanoseconds " 236253744Sian "(in: %s sec -> out: %jd sec %ld ns -> %d ticks)\n", 237253719Salfred longopt ? "-" : "", longopt ? longopt : shortopt, 238253719Salfred rv, 239253744Sian myoptarg, (intmax_t)ts.tv_sec, ts.tv_nsec, ticks); 240253719Salfred } 241253719Salfred if (ticks <= 0) { 242253719Salfred errx(1, "Timeout for %s%s is too small, please choose a higher timeout.", longopt ? "-" : "", longopt ? longopt : shortopt); 243253719Salfred } 244253719Salfred 245253719Salfred return (rv); 246253719Salfred} 247253719Salfred 248253719Salfred/* 249116874Ssmkelly * Catch signals and begin shutdown process. 250116874Ssmkelly */ 251116874Ssmkellystatic void 252116874Ssmkellysighandler(int signum) 253116874Ssmkelly{ 254116874Ssmkelly 255116874Ssmkelly if (signum == SIGINT || signum == SIGTERM) 256116874Ssmkelly end_program = 1; 257116874Ssmkelly} 258116874Ssmkelly 259116874Ssmkelly/* 260128705Ssmkelly * Open the watchdog device. 261116874Ssmkelly */ 262116874Ssmkellystatic int 263201227Sedwatchdog_init(void) 264116874Ssmkelly{ 265116874Ssmkelly 266247405Salfred if (is_dry_run) 267247405Salfred return 0; 268247405Salfred 269126383Sphk fd = open("/dev/" _PATH_WATCHDOG, O_RDWR); 270126383Sphk if (fd >= 0) 271126383Sphk return (0); 272126383Sphk warn("Could not open watchdog device"); 273126383Sphk return (-1); 274116874Ssmkelly} 275116874Ssmkelly 276116874Ssmkelly/* 277247405Salfred * If we are doing timing, then get the time. 278247405Salfred */ 279247405Salfredstatic int 280247405Salfredwatchdog_getuptime(struct timespec *tp) 281247405Salfred{ 282247405Salfred int error; 283247405Salfred 284247405Salfred if (!do_timedog) 285247405Salfred return 0; 286247405Salfred 287247405Salfred error = clock_gettime(CLOCK_UPTIME_FAST, tp); 288247405Salfred if (error) 289247405Salfred warn("clock_gettime"); 290247405Salfred return (error); 291247405Salfred} 292247405Salfred 293247405Salfredstatic long 294247405Salfredwatchdog_check_dogfunction_time(struct timespec *tp_start, 295247405Salfred struct timespec *tp_end) 296247405Salfred{ 297248744Smarkj struct timeval tv_start, tv_end, tv_now, tv; 298247405Salfred const char *cmd_prefix, *cmd; 299248744Smarkj struct timespec tp_now; 300247405Salfred int sec; 301247405Salfred 302247405Salfred if (!do_timedog) 303247405Salfred return (0); 304247405Salfred 305247405Salfred TIMESPEC_TO_TIMEVAL(&tv_start, tp_start); 306247405Salfred TIMESPEC_TO_TIMEVAL(&tv_end, tp_end); 307247405Salfred timersub(&tv_end, &tv_start, &tv); 308247405Salfred sec = tv.tv_sec; 309247405Salfred if (sec < carp_thresh_seconds) 310247405Salfred return (sec); 311247405Salfred 312247405Salfred if (test_cmd) { 313247405Salfred cmd_prefix = "Watchdog program"; 314247405Salfred cmd = test_cmd; 315247405Salfred } else { 316247405Salfred cmd_prefix = "Watchdog operation"; 317247405Salfred cmd = "stat(\"/etc\", &sb)"; 318247405Salfred } 319247405Salfred if (do_syslog) 320247405Salfred syslog(LOG_CRIT, "%s: '%s' took too long: " 321248744Smarkj "%d.%06ld seconds >= %d seconds threshold", 322247405Salfred cmd_prefix, cmd, sec, (long)tv.tv_usec, 323247405Salfred carp_thresh_seconds); 324248744Smarkj else 325248744Smarkj warnx("%s: '%s' took too long: " 326248744Smarkj "%d.%06ld seconds >= %d seconds threshold", 327248744Smarkj cmd_prefix, cmd, sec, (long)tv.tv_usec, 328248744Smarkj carp_thresh_seconds); 329248744Smarkj 330248744Smarkj /* 331248744Smarkj * Adjust the sleep interval again in case syslog(3) took a non-trivial 332248744Smarkj * amount of time to run. 333248744Smarkj */ 334248744Smarkj if (watchdog_getuptime(&tp_now)) 335248744Smarkj return (sec); 336248744Smarkj TIMESPEC_TO_TIMEVAL(&tv_now, &tp_now); 337248744Smarkj timersub(&tv_now, &tv_start, &tv); 338248744Smarkj sec = tv.tv_sec; 339248744Smarkj 340247405Salfred return (sec); 341247405Salfred} 342247405Salfred 343247405Salfred/* 344116874Ssmkelly * Main program loop which is iterated every second. 345116874Ssmkelly */ 346116874Ssmkellystatic void 347116874Ssmkellywatchdog_loop(void) 348116874Ssmkelly{ 349247405Salfred struct timespec ts_start, ts_end; 350116874Ssmkelly struct stat sb; 351247405Salfred long waited; 352247405Salfred int error, failed; 353116874Ssmkelly 354165263Sn_hibma while (end_program != 2) { 355116874Ssmkelly failed = 0; 356116874Ssmkelly 357247405Salfred error = watchdog_getuptime(&ts_start); 358247405Salfred if (error) { 359247405Salfred end_program = 1; 360247405Salfred goto try_end; 361247405Salfred } 362247405Salfred 363126383Sphk if (test_cmd != NULL) 364126383Sphk failed = system(test_cmd); 365126383Sphk else 366126383Sphk failed = stat("/etc", &sb); 367116874Ssmkelly 368247405Salfred error = watchdog_getuptime(&ts_end); 369247405Salfred if (error) { 370247405Salfred end_program = 1; 371247405Salfred goto try_end; 372247405Salfred } 373247405Salfred 374116874Ssmkelly if (failed == 0) 375165263Sn_hibma watchdog_patpat(timeout|WD_ACTIVE); 376248744Smarkj 377248744Smarkj waited = watchdog_check_dogfunction_time(&ts_start, &ts_end); 378247405Salfred if (nap - waited > 0) 379247405Salfred sleep(nap - waited); 380165263Sn_hibma 381247405Salfredtry_end: 382165263Sn_hibma if (end_program != 0) { 383165263Sn_hibma if (watchdog_onoff(0) == 0) { 384165263Sn_hibma end_program = 2; 385165263Sn_hibma } else { 386245951Sian warnx("Could not stop the watchdog, not exiting"); 387165263Sn_hibma end_program = 0; 388165263Sn_hibma } 389165263Sn_hibma } 390116874Ssmkelly } 391116874Ssmkelly} 392116874Ssmkelly 393116874Ssmkelly/* 394116874Ssmkelly * Reset the watchdog timer. This function must be called periodically 395116874Ssmkelly * to keep the watchdog from firing. 396116874Ssmkelly */ 397210300Sdelphijstatic int 398165263Sn_hibmawatchdog_patpat(u_int t) 399116874Ssmkelly{ 400116874Ssmkelly 401247405Salfred if (is_dry_run) 402247405Salfred return 0; 403247405Salfred 404165263Sn_hibma return ioctl(fd, WDIOCPATPAT, &t); 405116874Ssmkelly} 406116874Ssmkelly 407116874Ssmkelly/* 408116874Ssmkelly * Toggle the kernel's watchdog. This routine is used to enable and 409116874Ssmkelly * disable the watchdog. 410116874Ssmkelly */ 411116874Ssmkellystatic int 412116874Ssmkellywatchdog_onoff(int onoff) 413116874Ssmkelly{ 414247405Salfred int error; 415116874Ssmkelly 416247405Salfred /* fake successful watchdog op if a dry run */ 417247405Salfred if (is_dry_run) 418247405Salfred return 0; 419247405Salfred 420247405Salfred if (onoff) { 421247405Salfred /* 422247405Salfred * Call the WDIOC_SETSOFT regardless of softtimeout_set 423247405Salfred * because we'll need to turn it off if someone had turned 424247405Salfred * it on. 425247405Salfred */ 426247405Salfred error = ioctl(fd, WDIOC_SETSOFT, &softtimeout_set); 427247405Salfred if (error) { 428247405Salfred warn("setting WDIOC_SETSOFT %d", softtimeout_set); 429247405Salfred return (error); 430247405Salfred } 431247405Salfred error = watchdog_patpat((timeout|WD_ACTIVE)); 432247405Salfred if (error) { 433247405Salfred warn("watchdog_patpat failed"); 434247405Salfred goto failsafe; 435247405Salfred } 436247405Salfred if (softtimeout_act_set) { 437247405Salfred error = ioctl(fd, WDIOC_SETSOFTTIMEOUTACT, 438247405Salfred &softtimeout_act); 439247405Salfred if (error) { 440247405Salfred warn("setting WDIOC_SETSOFTTIMEOUTACT %d", 441247405Salfred softtimeout_act); 442247405Salfred goto failsafe; 443247405Salfred } 444247405Salfred } 445247405Salfred if (pretimeout_set) { 446247405Salfred error = ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout); 447247405Salfred if (error) { 448247405Salfred warn("setting WDIOC_SETPRETIMEOUT %d", 449247405Salfred pretimeout); 450247405Salfred goto failsafe; 451247405Salfred } 452247405Salfred } 453247405Salfred if (pretimeout_act_set) { 454247405Salfred error = ioctl(fd, WDIOC_SETPRETIMEOUTACT, 455247405Salfred &pretimeout_act); 456247405Salfred if (error) { 457247405Salfred warn("setting WDIOC_SETPRETIMEOUTACT %d", 458247405Salfred pretimeout_act); 459247405Salfred goto failsafe; 460247405Salfred } 461247405Salfred } 462247405Salfred /* pat one more time for good measure */ 463165263Sn_hibma return watchdog_patpat((timeout|WD_ACTIVE)); 464247405Salfred } else { 465286947Sian return watchdog_patpat(exit_timeout); 466247405Salfred } 467247405Salfredfailsafe: 468286947Sian watchdog_patpat(exit_timeout); 469247405Salfred return (error); 470116874Ssmkelly} 471116874Ssmkelly 472116874Ssmkelly/* 473116874Ssmkelly * Tell user how to use the program. 474116874Ssmkelly */ 475116874Ssmkellystatic void 476201227Sedusage(void) 477116874Ssmkelly{ 478126383Sphk if (is_daemon) 479247405Salfred fprintf(stderr, "usage:\n" 480286947Sian" watchdogd [-dnSw] [-e cmd] [-I pidfile] [-s sleep] [-t timeout]\n" 481286947Sian" [-T script_timeout] [-x exit_timeout]\n" 482247405Salfred" [--debug]\n" 483247405Salfred" [--pretimeout seconds] [-pretimeout-action action]\n" 484247405Salfred" [--softtimeout] [-softtimeout-action action]\n" 485247405Salfred); 486126383Sphk else 487156334Sphk fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n"); 488116874Ssmkelly exit(EX_USAGE); 489116874Ssmkelly} 490116874Ssmkelly 491247405Salfredstatic long 492254173Salfredfetchtimeout(int opt, const char *longopt, const char *myoptarg, int zero_ok) 493247405Salfred{ 494247405Salfred const char *errstr; 495247405Salfred char *p; 496247405Salfred long rv; 497247405Salfred 498247405Salfred errstr = NULL; 499247405Salfred p = NULL; 500247405Salfred errno = 0; 501247405Salfred rv = strtol(myoptarg, &p, 0); 502247405Salfred if ((p != NULL && *p != '\0') || errno != 0) 503247405Salfred errstr = "is not a number"; 504254173Salfred if (rv < 0 || (!zero_ok && rv == 0)) 505247405Salfred errstr = "must be greater than zero"; 506247405Salfred if (errstr) { 507247405Salfred if (longopt) 508247405Salfred errx(EX_USAGE, "--%s argument %s", longopt, errstr); 509247405Salfred else 510247405Salfred errx(EX_USAGE, "-%c argument %s", opt, errstr); 511247405Salfred } 512247405Salfred return (rv); 513247405Salfred} 514247405Salfred 515247405Salfredstruct act_tbl { 516247405Salfred const char *at_act; 517247405Salfred int at_value; 518247405Salfred}; 519247405Salfred 520249245Sedstatic const struct act_tbl act_tbl[] = { 521247405Salfred { "panic", WD_SOFT_PANIC }, 522247405Salfred { "ddb", WD_SOFT_DDB }, 523247405Salfred { "log", WD_SOFT_LOG }, 524247405Salfred { "printf", WD_SOFT_PRINTF }, 525247405Salfred { NULL, 0 } 526247405Salfred}; 527247405Salfred 528247405Salfredstatic void 529247405Salfredtimeout_act_error(const char *lopt, const char *badact) 530247405Salfred{ 531247405Salfred char *opts, *oldopts; 532247405Salfred int i; 533247405Salfred 534247405Salfred opts = NULL; 535247405Salfred for (i = 0; act_tbl[i].at_act != NULL; i++) { 536247405Salfred oldopts = opts; 537247405Salfred if (asprintf(&opts, "%s%s%s", 538247405Salfred oldopts == NULL ? "" : oldopts, 539247405Salfred oldopts == NULL ? "" : ", ", 540247405Salfred act_tbl[i].at_act) == -1) 541247405Salfred err(EX_OSERR, "malloc"); 542247405Salfred free(oldopts); 543247405Salfred } 544247405Salfred warnx("bad --%s argument '%s' must be one of (%s).", 545247405Salfred lopt, badact, opts); 546247405Salfred usage(); 547247405Salfred} 548247405Salfred 549116874Ssmkelly/* 550247405Salfred * Take a comma separated list of actions and or the flags 551247405Salfred * together for the ioctl. 552247405Salfred */ 553247405Salfredstatic int 554247405Salfredtimeout_act_str2int(const char *lopt, const char *acts) 555247405Salfred{ 556247405Salfred int i; 557247405Salfred char *dupacts, *tofree; 558247405Salfred char *o; 559247405Salfred int rv = 0; 560247405Salfred 561247405Salfred tofree = dupacts = strdup(acts); 562247405Salfred if (!tofree) 563247405Salfred err(EX_OSERR, "malloc"); 564247405Salfred while ((o = strsep(&dupacts, ",")) != NULL) { 565247405Salfred for (i = 0; act_tbl[i].at_act != NULL; i++) { 566247405Salfred if (!strcmp(o, act_tbl[i].at_act)) { 567247405Salfred rv |= act_tbl[i].at_value; 568247405Salfred break; 569247405Salfred } 570247405Salfred } 571247405Salfred if (act_tbl[i].at_act == NULL) 572247405Salfred timeout_act_error(lopt, o); 573247405Salfred } 574247405Salfred free(tofree); 575247405Salfred return rv; 576247405Salfred} 577247405Salfred 578253719Salfredint 579253719Salfredtstotv(struct timeval *tv, struct timespec *ts) 580253719Salfred{ 581253719Salfred 582253719Salfred tv->tv_sec = ts->tv_sec; 583253719Salfred tv->tv_usec = ts->tv_nsec / 1000; 584253719Salfred return 0; 585253719Salfred} 586253719Salfred 587247405Salfred/* 588253719Salfred * Convert a timeval to a number of ticks. 589253719Salfred * Mostly copied from the kernel. 590253719Salfred */ 591253719Salfredint 592253719Salfredtvtohz(struct timeval *tv) 593253719Salfred{ 594253719Salfred register unsigned long ticks; 595253719Salfred register long sec, usec; 596253719Salfred int hz; 597253719Salfred size_t hzsize; 598253719Salfred int error; 599253719Salfred int tick; 600253719Salfred 601253719Salfred hzsize = sizeof(hz); 602253719Salfred 603253719Salfred error = sysctlbyname("kern.hz", &hz, &hzsize, NULL, 0); 604253719Salfred if (error) 605253719Salfred err(1, "sysctlbyname kern.hz"); 606253719Salfred 607253719Salfred tick = 1000000 / hz; 608253719Salfred 609253719Salfred /* 610253719Salfred * If the number of usecs in the whole seconds part of the time 611253719Salfred * difference fits in a long, then the total number of usecs will 612253719Salfred * fit in an unsigned long. Compute the total and convert it to 613253719Salfred * ticks, rounding up and adding 1 to allow for the current tick 614253719Salfred * to expire. Rounding also depends on unsigned long arithmetic 615253719Salfred * to avoid overflow. 616253719Salfred * 617253719Salfred * Otherwise, if the number of ticks in the whole seconds part of 618253719Salfred * the time difference fits in a long, then convert the parts to 619253719Salfred * ticks separately and add, using similar rounding methods and 620253719Salfred * overflow avoidance. This method would work in the previous 621253719Salfred * case but it is slightly slower and assumes that hz is integral. 622253719Salfred * 623253719Salfred * Otherwise, round the time difference down to the maximum 624253719Salfred * representable value. 625253719Salfred * 626253719Salfred * If ints have 32 bits, then the maximum value for any timeout in 627253719Salfred * 10ms ticks is 248 days. 628253719Salfred */ 629253719Salfred sec = tv->tv_sec; 630253719Salfred usec = tv->tv_usec; 631253719Salfred if (usec < 0) { 632253719Salfred sec--; 633253719Salfred usec += 1000000; 634253719Salfred } 635253719Salfred if (sec < 0) { 636253719Salfred#ifdef DIAGNOSTIC 637253719Salfred if (usec > 0) { 638253719Salfred sec++; 639253719Salfred usec -= 1000000; 640253719Salfred } 641253719Salfred printf("tvotohz: negative time difference %ld sec %ld usec\n", 642253719Salfred sec, usec); 643253719Salfred#endif 644253719Salfred ticks = 1; 645253719Salfred } else if (sec <= LONG_MAX / 1000000) 646253719Salfred ticks = (sec * 1000000 + (unsigned long)usec + (tick - 1)) 647253719Salfred / tick + 1; 648253719Salfred else if (sec <= LONG_MAX / hz) 649253719Salfred ticks = sec * hz 650253719Salfred + ((unsigned long)usec + (tick - 1)) / tick + 1; 651253719Salfred else 652253719Salfred ticks = LONG_MAX; 653253719Salfred if (ticks > INT_MAX) 654253719Salfred ticks = INT_MAX; 655253719Salfred return ((int)ticks); 656253719Salfred} 657253719Salfred 658253719Salfredstatic int 659253719Salfredseconds_to_pow2ns(int seconds) 660253719Salfred{ 661253719Salfred uint64_t power; 662253719Salfred uint64_t ns; 663253719Salfred uint64_t shifted; 664253719Salfred 665253719Salfred if (seconds <= 0) 666253719Salfred errx(1, "seconds %d < 0", seconds); 667253719Salfred ns = ((uint64_t)seconds) * 1000000000ULL; 668253719Salfred power = flsll(ns); 669253719Salfred shifted = 1ULL << power; 670253719Salfred if (shifted <= ns) { 671253719Salfred power++; 672253719Salfred } 673253719Salfred if (debugging) { 674253719Salfred printf("shifted %lld\n", (long long)shifted); 675253719Salfred printf("seconds_to_pow2ns: seconds: %d, ns %lld, power %d\n", 676253719Salfred seconds, (long long)ns, (int)power); 677253719Salfred } 678253719Salfred return (power); 679253719Salfred} 680253719Salfred 681253719Salfred 682253719Salfred/* 683116874Ssmkelly * Handle the few command line arguments supported. 684116874Ssmkelly */ 685116874Ssmkellystatic void 686116874Ssmkellyparseargs(int argc, char *argv[]) 687116874Ssmkelly{ 688247405Salfred int longindex; 689116874Ssmkelly int c; 690247405Salfred const char *lopt; 691116874Ssmkelly 692247405Salfred /* 693247405Salfred * if we end with a 'd' aka 'watchdogd' then we are the daemon program, 694247405Salfred * otherwise run as a command line utility. 695247405Salfred */ 696126383Sphk c = strlen(argv[0]); 697126383Sphk if (argv[0][c - 1] == 'd') 698126383Sphk is_daemon = 1; 699247405Salfred 700247405Salfred if (is_daemon) 701286947Sian getopt_shortopts = "I:de:ns:t:ST:wx:?"; 702247405Salfred else 703247405Salfred getopt_shortopts = "dt:?"; 704247405Salfred 705247405Salfred while ((c = getopt_long(argc, argv, getopt_shortopts, longopts, 706247405Salfred &longindex)) != -1) { 707116874Ssmkelly switch (c) { 708116874Ssmkelly case 'I': 709116874Ssmkelly pidfile = optarg; 710116874Ssmkelly break; 711116874Ssmkelly case 'd': 712116874Ssmkelly debugging = 1; 713116874Ssmkelly break; 714126383Sphk case 'e': 715126383Sphk test_cmd = strdup(optarg); 716126383Sphk break; 717247405Salfred case 'n': 718247405Salfred is_dry_run = 1; 719247405Salfred break; 720126383Sphk#ifdef notyet 721126383Sphk case 'p': 722126383Sphk passive = 1; 723126383Sphk break; 724126383Sphk#endif 725126383Sphk case 's': 726254173Salfred nap = fetchtimeout(c, NULL, optarg, 0); 727126383Sphk break; 728247405Salfred case 'S': 729248744Smarkj do_syslog = 0; 730247405Salfred break; 731126383Sphk case 't': 732253719Salfred timeout_sec = atoi(optarg); 733253719Salfred timeout = parse_timeout_to_pow2ns(c, NULL, optarg); 734253719Salfred if (debugging) 735253719Salfred printf("Timeout is 2^%d nanoseconds\n", 736253719Salfred timeout); 737126383Sphk break; 738247405Salfred case 'T': 739254173Salfred carp_thresh_seconds = 740254173Salfred fetchtimeout(c, "NULL", optarg, 0); 741247405Salfred break; 742247405Salfred case 'w': 743247405Salfred do_timedog = 1; 744247405Salfred break; 745286947Sian case 'x': 746286947Sian exit_timeout = parse_timeout_to_pow2ns(c, NULL, optarg); 747286947Sian if (exit_timeout != 0) 748286947Sian exit_timeout |= WD_ACTIVE; 749286947Sian break; 750247405Salfred case 0: 751247405Salfred lopt = longopts[longindex].name; 752247405Salfred if (!strcmp(lopt, "pretimeout")) { 753254173Salfred pretimeout = fetchtimeout(0, lopt, optarg, 0); 754247405Salfred } else if (!strcmp(lopt, "pretimeout-action")) { 755247405Salfred pretimeout_act = timeout_act_str2int(lopt, 756247405Salfred optarg); 757247405Salfred } else if (!strcmp(lopt, "softtimeout-action")) { 758247405Salfred softtimeout_act = timeout_act_str2int(lopt, 759247405Salfred optarg); 760247405Salfred } else { 761247405Salfred /* warnx("bad option at index %d: %s", optind, 762247405Salfred argv[optind]); 763247405Salfred usage(); 764247405Salfred */ 765247405Salfred } 766247405Salfred break; 767116874Ssmkelly case '?': 768116874Ssmkelly default: 769116874Ssmkelly usage(); 770116874Ssmkelly /* NOTREACHED */ 771116874Ssmkelly } 772116874Ssmkelly } 773247405Salfred 774247405Salfred if (carp_thresh_seconds == -1) 775247405Salfred carp_thresh_seconds = nap; 776247405Salfred 777150747Sphk if (argc != optind) 778150747Sphk errx(EX_USAGE, "extra arguments."); 779126383Sphk if (is_daemon && timeout < WD_TO_1SEC) 780126383Sphk errx(EX_USAGE, "-t argument is less than one second."); 781253719Salfred if (pretimeout_set) { 782253719Salfred struct timespec ts; 783253719Salfred 784253719Salfred pow2ns_to_ts(timeout, &ts); 785253808Sjhb if (pretimeout >= (uintmax_t)ts.tv_sec) { 786253719Salfred errx(EX_USAGE, 787253719Salfred "pretimeout (%d) >= timeout (%d -> %ld)\n" 788253719Salfred "see manual section TIMEOUT RESOLUTION", 789253719Salfred pretimeout, timeout_sec, (long)ts.tv_sec); 790253719Salfred } 791253719Salfred } 792116874Ssmkelly} 793