1 /*- 2 * Copyright (c) 2003-2004 Sean M. Kelly <smkelly@FreeBSD.org> 3 * Copyright (c) 2013 iXsystems.com, 4 * author: Alfred Perlstein <alfred@freebsd.org> 5 * 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* 31 * Software watchdog daemon. 32 */ 33 34 #include <sys/types.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/mman.h> 38 #include <sys/param.h> 39 #include <sys/rtprio.h> 40 #include <sys/stat.h> 41 #include <sys/time.h> 42 #include <sys/sysctl.h> 43 #include <sys/watchdog.h> 44 45 #include <err.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <libutil.h> 49 #include <math.h> 50 #include <paths.h> 51 #include <signal.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <strings.h> 56 #include <sysexits.h> 57 #include <syslog.h> 58 #include <unistd.h> 59 60 #include <getopt.h> 61 62 static long fetchtimeout(int opt, const char *longopt, const char *myoptarg); 63 static void parseargs(int, char *[]); 64 static int seconds_to_pow2ns(int); 65 static void sighandler(int); 66 static void watchdog_loop(void); 67 static int watchdog_init(void); 68 static int watchdog_onoff(int onoff); 69 static int watchdog_patpat(u_int timeout); 70 static void usage(void); 71 static int tstotv(struct timeval *tv, struct timespec *ts); 72 static int tvtohz(struct timeval *tv); 73 74 static int debugging = 0; 75 static int end_program = 0; 76 static const char *pidfile = _PATH_VARRUN "watchdogd.pid"; 77 static u_int timeout = WD_TO_128SEC; 78 static u_int pretimeout = 0; 79 static u_int timeout_sec; 80 static u_int passive = 0; 81 static int is_daemon = 0; 82 static int is_dry_run = 0; /* do not arm the watchdog, only 83 report on timing of the watch 84 program */ 85 static int do_timedog = 0; 86 static int do_syslog = 1; 87 static int fd = -1; 88 static int nap = 1; 89 static int carp_thresh_seconds = -1; 90 static char *test_cmd = NULL; 91 92 static const char *getopt_shortopts; 93 94 static int pretimeout_set; 95 static int pretimeout_act; 96 static int pretimeout_act_set; 97 98 static int softtimeout_set; 99 static int softtimeout_act; 100 static int softtimeout_act_set; 101 102 static struct option longopts[] = { 103 { "debug", no_argument, &debugging, 1 }, 104 { "pretimeout", required_argument, &pretimeout_set, 1 }, 105 { "pretimeout-action", required_argument, &pretimeout_act_set, 1 }, 106 { "softtimeout", no_argument, &softtimeout_set, 1 }, 107 { "softtimeout-action", required_argument, &softtimeout_act_set, 1 }, 108 { NULL, 0, NULL, 0} 109 }; 110 111 /* 112 * Ask malloc() to map minimum-sized chunks of virtual address space at a time, 113 * so that mlockall() won't needlessly wire megabytes of unused memory into the 114 * process. This must be done using the malloc_conf string so that it gets set 115 * up before the first allocation, which happens before entry to main(). 116 */ 117 const char * malloc_conf = "lg_chunk:0"; 118 119 /* 120 * Periodically pat the watchdog, preventing it from firing. 121 */ 122 int 123 main(int argc, char *argv[]) 124 { 125 struct rtprio rtp; 126 struct pidfh *pfh; 127 pid_t otherpid; 128 129 if (getuid() != 0) 130 errx(EX_SOFTWARE, "not super user"); 131 132 parseargs(argc, argv); 133 134 if (do_syslog) 135 openlog("watchdogd", LOG_CONS|LOG_NDELAY|LOG_PERROR, 136 LOG_DAEMON); 137 138 rtp.type = RTP_PRIO_REALTIME; 139 rtp.prio = 0; 140 if (rtprio(RTP_SET, 0, &rtp) == -1) 141 err(EX_OSERR, "rtprio"); 142 143 if (!is_dry_run && watchdog_init() == -1) 144 errx(EX_SOFTWARE, "unable to initialize watchdog"); 145 146 if (is_daemon) { 147 if (watchdog_onoff(1) == -1) 148 err(EX_OSERR, "patting the dog"); 149 150 pfh = pidfile_open(pidfile, 0600, &otherpid); 151 if (pfh == NULL) { 152 if (errno == EEXIST) { 153 watchdog_onoff(0); 154 errx(EX_SOFTWARE, "%s already running, pid: %d", 155 getprogname(), otherpid); 156 } 157 warn("Cannot open or create pidfile"); 158 } 159 160 if (debugging == 0 && daemon(0, 0) == -1) { 161 watchdog_onoff(0); 162 pidfile_remove(pfh); 163 err(EX_OSERR, "daemon"); 164 } 165 166 signal(SIGHUP, SIG_IGN); 167 signal(SIGINT, sighandler); 168 signal(SIGTERM, sighandler); 169 170 pidfile_write(pfh); 171 if (madvise(0, 0, MADV_PROTECT) != 0) 172 warn("madvise failed"); 173 if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) 174 warn("mlockall failed"); 175 176 watchdog_loop(); 177 178 /* exiting */ 179 pidfile_remove(pfh); 180 return (EX_OK); 181 } else { 182 if (passive) 183 timeout |= WD_PASSIVE; 184 else 185 timeout |= WD_ACTIVE; 186 if (watchdog_patpat(timeout) < 0) 187 err(EX_OSERR, "patting the dog"); 188 return (EX_OK); 189 } 190 } 191 192 static void 193 pow2ns_to_ts(int pow2ns, struct timespec *ts) 194 { 195 uint64_t ns; 196 197 ns = 1ULL << pow2ns; 198 ts->tv_sec = ns / 1000000000ULL; 199 ts->tv_nsec = ns % 1000000000ULL; 200 } 201 202 /* 203 * Convert a timeout in seconds to N where 2^N nanoseconds is close to 204 * "seconds". 205 * 206 * The kernel expects the timeouts for watchdogs in "2^N nanosecond format". 207 */ 208 static u_int 209 parse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg) 210 { 211 double a; 212 u_int rv; 213 struct timespec ts; 214 struct timeval tv; 215 int ticks; 216 char shortopt[] = "- "; 217 218 if (!longopt) 219 shortopt[1] = opt; 220 221 a = fetchtimeout(opt, longopt, myoptarg); 222 223 if (a == 0) 224 rv = WD_TO_NEVER; 225 else 226 rv = seconds_to_pow2ns(a); 227 pow2ns_to_ts(rv, &ts); 228 tstotv(&tv, &ts); 229 ticks = tvtohz(&tv); 230 if (debugging) { 231 printf("Timeout for %s%s " 232 "is 2^%d nanoseconds " 233 "(in: %s sec -> out: %ld sec %ld ns -> %d ticks)\n", 234 longopt ? "-" : "", longopt ? longopt : shortopt, 235 rv, 236 myoptarg, ts.tv_sec, ts.tv_nsec, ticks); 237 } 238 if (ticks <= 0) { 239 errx(1, "Timeout for %s%s is too small, please choose a higher timeout.", longopt ? "-" : "", longopt ? longopt : shortopt); 240 } 241 242 return (rv); 243 } 244 245 /* 246 * Catch signals and begin shutdown process. 247 */ 248 static void 249 sighandler(int signum) 250 { 251 252 if (signum == SIGINT || signum == SIGTERM) 253 end_program = 1; 254 } 255 256 /* 257 * Open the watchdog device. 258 */ 259 static int 260 watchdog_init(void) 261 { 262 263 if (is_dry_run) 264 return 0; 265 266 fd = open("/dev/" _PATH_WATCHDOG, O_RDWR); 267 if (fd >= 0) 268 return (0); 269 warn("Could not open watchdog device"); 270 return (-1); 271 } 272 273 /* 274 * If we are doing timing, then get the time. 275 */ 276 static int 277 watchdog_getuptime(struct timespec *tp) 278 { 279 int error; 280 281 if (!do_timedog) 282 return 0; 283 284 error = clock_gettime(CLOCK_UPTIME_FAST, tp); 285 if (error) 286 warn("clock_gettime"); 287 return (error); 288 } 289 290 static long 291 watchdog_check_dogfunction_time(struct timespec *tp_start, 292 struct timespec *tp_end) 293 { 294 struct timeval tv_start, tv_end, tv_now, tv; 295 const char *cmd_prefix, *cmd; 296 struct timespec tp_now; 297 int sec; 298 299 if (!do_timedog) 300 return (0); 301 302 TIMESPEC_TO_TIMEVAL(&tv_start, tp_start); 303 TIMESPEC_TO_TIMEVAL(&tv_end, tp_end); 304 timersub(&tv_end, &tv_start, &tv); 305 sec = tv.tv_sec; 306 if (sec < carp_thresh_seconds) 307 return (sec); 308 309 if (test_cmd) { 310 cmd_prefix = "Watchdog program"; 311 cmd = test_cmd; 312 } else { 313 cmd_prefix = "Watchdog operation"; 314 cmd = "stat(\"/etc\", &sb)"; 315 } 316 if (do_syslog) 317 syslog(LOG_CRIT, "%s: '%s' took too long: " 318 "%d.%06ld seconds >= %d seconds threshold", 319 cmd_prefix, cmd, sec, (long)tv.tv_usec, 320 carp_thresh_seconds); 321 else 322 warnx("%s: '%s' took too long: " 323 "%d.%06ld seconds >= %d seconds threshold", 324 cmd_prefix, cmd, sec, (long)tv.tv_usec, 325 carp_thresh_seconds); 326 327 /* 328 * Adjust the sleep interval again in case syslog(3) took a non-trivial 329 * amount of time to run. 330 */ 331 if (watchdog_getuptime(&tp_now)) 332 return (sec); 333 TIMESPEC_TO_TIMEVAL(&tv_now, &tp_now); 334 timersub(&tv_now, &tv_start, &tv); 335 sec = tv.tv_sec; 336 337 return (sec); 338 } 339 340 /* 341 * Main program loop which is iterated every second. 342 */ 343 static void 344 watchdog_loop(void) 345 { 346 struct timespec ts_start, ts_end; 347 struct stat sb; 348 long waited; 349 int error, failed; 350 351 while (end_program != 2) { 352 failed = 0; 353 354 error = watchdog_getuptime(&ts_start); 355 if (error) { 356 end_program = 1; 357 goto try_end; 358 } 359 360 if (test_cmd != NULL) 361 failed = system(test_cmd); 362 else 363 failed = stat("/etc", &sb); 364 365 error = watchdog_getuptime(&ts_end); 366 if (error) { 367 end_program = 1; 368 goto try_end; 369 } 370 371 if (failed == 0) 372 watchdog_patpat(timeout|WD_ACTIVE); 373 374 waited = watchdog_check_dogfunction_time(&ts_start, &ts_end); 375 if (nap - waited > 0) 376 sleep(nap - waited); 377 378 try_end: 379 if (end_program != 0) { 380 if (watchdog_onoff(0) == 0) { 381 end_program = 2; 382 } else { 383 warnx("Could not stop the watchdog, not exiting"); 384 end_program = 0; 385 } 386 } 387 } 388 } 389 390 /* 391 * Reset the watchdog timer. This function must be called periodically 392 * to keep the watchdog from firing. 393 */ 394 static int 395 watchdog_patpat(u_int t) 396 { 397 398 if (is_dry_run) 399 return 0; 400 401 return ioctl(fd, WDIOCPATPAT, &t); 402 } 403 404 /* 405 * Toggle the kernel's watchdog. This routine is used to enable and 406 * disable the watchdog. 407 */ 408 static int 409 watchdog_onoff(int onoff) 410 { 411 int error; 412 413 /* fake successful watchdog op if a dry run */ 414 if (is_dry_run) 415 return 0; 416 417 if (onoff) { 418 /* 419 * Call the WDIOC_SETSOFT regardless of softtimeout_set 420 * because we'll need to turn it off if someone had turned 421 * it on. 422 */ 423 error = ioctl(fd, WDIOC_SETSOFT, &softtimeout_set); 424 if (error) { 425 warn("setting WDIOC_SETSOFT %d", softtimeout_set); 426 return (error); 427 } 428 error = watchdog_patpat((timeout|WD_ACTIVE)); 429 if (error) { 430 warn("watchdog_patpat failed"); 431 goto failsafe; 432 } 433 if (softtimeout_act_set) { 434 error = ioctl(fd, WDIOC_SETSOFTTIMEOUTACT, 435 &softtimeout_act); 436 if (error) { 437 warn("setting WDIOC_SETSOFTTIMEOUTACT %d", 438 softtimeout_act); 439 goto failsafe; 440 } 441 } 442 if (pretimeout_set) { 443 error = ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout); 444 if (error) { 445 warn("setting WDIOC_SETPRETIMEOUT %d", 446 pretimeout); 447 goto failsafe; 448 } 449 } 450 if (pretimeout_act_set) { 451 error = ioctl(fd, WDIOC_SETPRETIMEOUTACT, 452 &pretimeout_act); 453 if (error) { 454 warn("setting WDIOC_SETPRETIMEOUTACT %d", 455 pretimeout_act); 456 goto failsafe; 457 } 458 } 459 /* pat one more time for good measure */ 460 return watchdog_patpat((timeout|WD_ACTIVE)); 461 } else { 462 return watchdog_patpat(0); 463 } 464 failsafe: 465 watchdog_patpat(0); 466 return (error); 467 } 468 469 /* 470 * Tell user how to use the program. 471 */ 472 static void 473 usage(void) 474 { 475 if (is_daemon) 476 fprintf(stderr, "usage:\n" 477 " watchdogd [-dnSw] [-e cmd] [-I file] [-s sleep] [-t timeout]\n" 478 " [-T script_timeout]\n" 479 " [--debug]\n" 480 " [--pretimeout seconds] [-pretimeout-action action]\n" 481 " [--softtimeout] [-softtimeout-action action]\n" 482 ); 483 else 484 fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n"); 485 exit(EX_USAGE); 486 } 487 488 static long 489 fetchtimeout(int opt, const char *longopt, const char *myoptarg) 490 { 491 const char *errstr; 492 char *p; 493 long rv; 494 495 errstr = NULL; 496 p = NULL; 497 errno = 0; 498 rv = strtol(myoptarg, &p, 0); 499 if ((p != NULL && *p != '\0') || errno != 0) 500 errstr = "is not a number"; 501 if (rv <= 0) 502 errstr = "must be greater than zero"; 503 if (errstr) { 504 if (longopt) 505 errx(EX_USAGE, "--%s argument %s", longopt, errstr); 506 else 507 errx(EX_USAGE, "-%c argument %s", opt, errstr); 508 } 509 return (rv); 510 } 511 512 struct act_tbl { 513 const char *at_act; 514 int at_value; 515 }; 516 517 static const struct act_tbl act_tbl[] = { 518 { "panic", WD_SOFT_PANIC }, 519 { "ddb", WD_SOFT_DDB }, 520 { "log", WD_SOFT_LOG }, 521 { "printf", WD_SOFT_PRINTF }, 522 { NULL, 0 } 523 }; 524 525 static void 526 timeout_act_error(const char *lopt, const char *badact) 527 { 528 char *opts, *oldopts; 529 int i; 530 531 opts = NULL; 532 for (i = 0; act_tbl[i].at_act != NULL; i++) { 533 oldopts = opts; 534 if (asprintf(&opts, "%s%s%s", 535 oldopts == NULL ? "" : oldopts, 536 oldopts == NULL ? "" : ", ", 537 act_tbl[i].at_act) == -1) 538 err(EX_OSERR, "malloc"); 539 free(oldopts); 540 } 541 warnx("bad --%s argument '%s' must be one of (%s).", 542 lopt, badact, opts); 543 usage(); 544 } 545 546 /* 547 * Take a comma separated list of actions and or the flags 548 * together for the ioctl. 549 */ 550 static int 551 timeout_act_str2int(const char *lopt, const char *acts) 552 { 553 int i; 554 char *dupacts, *tofree; 555 char *o; 556 int rv = 0; 557 558 tofree = dupacts = strdup(acts); 559 if (!tofree) 560 err(EX_OSERR, "malloc"); 561 while ((o = strsep(&dupacts, ",")) != NULL) { 562 for (i = 0; act_tbl[i].at_act != NULL; i++) { 563 if (!strcmp(o, act_tbl[i].at_act)) { 564 rv |= act_tbl[i].at_value; 565 break; 566 } 567 } 568 if (act_tbl[i].at_act == NULL) 569 timeout_act_error(lopt, o); 570 } 571 free(tofree); 572 return rv; 573 } 574 575 int 576 tstotv(struct timeval *tv, struct timespec *ts) 577 { 578 579 tv->tv_sec = ts->tv_sec; 580 tv->tv_usec = ts->tv_nsec / 1000; 581 return 0; 582 } 583 584 /* 585 * Convert a timeval to a number of ticks. 586 * Mostly copied from the kernel. 587 */ 588 int 589 tvtohz(struct timeval *tv) 590 { 591 register unsigned long ticks; 592 register long sec, usec; 593 int hz; 594 size_t hzsize; 595 int error; 596 int tick; 597 598 hzsize = sizeof(hz); 599 600 error = sysctlbyname("kern.hz", &hz, &hzsize, NULL, 0); 601 if (error) 602 err(1, "sysctlbyname kern.hz"); 603 604 tick = 1000000 / hz; 605 606 /* 607 * If the number of usecs in the whole seconds part of the time 608 * difference fits in a long, then the total number of usecs will 609 * fit in an unsigned long. Compute the total and convert it to 610 * ticks, rounding up and adding 1 to allow for the current tick 611 * to expire. Rounding also depends on unsigned long arithmetic 612 * to avoid overflow. 613 * 614 * Otherwise, if the number of ticks in the whole seconds part of 615 * the time difference fits in a long, then convert the parts to 616 * ticks separately and add, using similar rounding methods and 617 * overflow avoidance. This method would work in the previous 618 * case but it is slightly slower and assumes that hz is integral. 619 * 620 * Otherwise, round the time difference down to the maximum 621 * representable value. 622 * 623 * If ints have 32 bits, then the maximum value for any timeout in 624 * 10ms ticks is 248 days. 625 */ 626 sec = tv->tv_sec; 627 usec = tv->tv_usec; 628 if (usec < 0) { 629 sec--; 630 usec += 1000000; 631 } 632 if (sec < 0) { 633 #ifdef DIAGNOSTIC 634 if (usec > 0) { 635 sec++; 636 usec -= 1000000; 637 } 638 printf("tvotohz: negative time difference %ld sec %ld usec\n", 639 sec, usec); 640 #endif 641 ticks = 1; 642 } else if (sec <= LONG_MAX / 1000000) 643 ticks = (sec * 1000000 + (unsigned long)usec + (tick - 1)) 644 / tick + 1; 645 else if (sec <= LONG_MAX / hz) 646 ticks = sec * hz 647 + ((unsigned long)usec + (tick - 1)) / tick + 1; 648 else 649 ticks = LONG_MAX; 650 if (ticks > INT_MAX) 651 ticks = INT_MAX; 652 return ((int)ticks); 653 } 654 655 static int 656 seconds_to_pow2ns(int seconds) 657 { 658 uint64_t power; 659 uint64_t ns; 660 uint64_t shifted; 661 662 if (seconds <= 0) 663 errx(1, "seconds %d < 0", seconds); 664 ns = ((uint64_t)seconds) * 1000000000ULL; 665 power = flsll(ns); 666 shifted = 1ULL << power; 667 if (shifted <= ns) { 668 power++; 669 } 670 if (debugging) { 671 printf("shifted %lld\n", (long long)shifted); 672 printf("seconds_to_pow2ns: seconds: %d, ns %lld, power %d\n", 673 seconds, (long long)ns, (int)power); 674 } 675 return (power); 676 } 677 678 679 /* 680 * Handle the few command line arguments supported. 681 */ 682 static void 683 parseargs(int argc, char *argv[]) 684 { 685 int longindex; 686 int c; 687 const char *lopt; 688 689 /* 690 * if we end with a 'd' aka 'watchdogd' then we are the daemon program, 691 * otherwise run as a command line utility. 692 */ 693 c = strlen(argv[0]); 694 if (argv[0][c - 1] == 'd') 695 is_daemon = 1; 696 697 if (is_daemon) 698 getopt_shortopts = "I:de:ns:t:ST:w?"; 699 else 700 getopt_shortopts = "dt:?"; 701 702 while ((c = getopt_long(argc, argv, getopt_shortopts, longopts, 703 &longindex)) != -1) { 704 switch (c) { 705 case 'I': 706 pidfile = optarg; 707 break; 708 case 'd': 709 debugging = 1; 710 break; 711 case 'e': 712 test_cmd = strdup(optarg); 713 break; 714 case 'n': 715 is_dry_run = 1; 716 break; 717 #ifdef notyet 718 case 'p': 719 passive = 1; 720 break; 721 #endif 722 case 's': 723 nap = fetchtimeout(c, NULL, optarg); 724 break; 725 case 'S': 726 do_syslog = 0; 727 break; 728 case 't': 729 timeout_sec = atoi(optarg); 730 timeout = parse_timeout_to_pow2ns(c, NULL, optarg); 731 if (debugging) 732 printf("Timeout is 2^%d nanoseconds\n", 733 timeout); 734 break; 735 case 'T': 736 carp_thresh_seconds = fetchtimeout(c, "NULL", optarg); 737 break; 738 case 'w': 739 do_timedog = 1; 740 break; 741 case 0: 742 lopt = longopts[longindex].name; 743 if (!strcmp(lopt, "pretimeout")) { 744 pretimeout = fetchtimeout(0, lopt, optarg); 745 } else if (!strcmp(lopt, "pretimeout-action")) { 746 pretimeout_act = timeout_act_str2int(lopt, 747 optarg); 748 } else if (!strcmp(lopt, "softtimeout-action")) { 749 softtimeout_act = timeout_act_str2int(lopt, 750 optarg); 751 } else { 752 /* warnx("bad option at index %d: %s", optind, 753 argv[optind]); 754 usage(); 755 */ 756 } 757 break; 758 case '?': 759 default: 760 usage(); 761 /* NOTREACHED */ 762 } 763 } 764 765 if (carp_thresh_seconds == -1) 766 carp_thresh_seconds = nap; 767 768 if (argc != optind) 769 errx(EX_USAGE, "extra arguments."); 770 if (is_daemon && timeout < WD_TO_1SEC) 771 errx(EX_USAGE, "-t argument is less than one second."); 772 if (pretimeout_set) { 773 struct timespec ts; 774 775 pow2ns_to_ts(timeout, &ts); 776 if (pretimeout >= ts.tv_sec) { 777 errx(EX_USAGE, 778 "pretimeout (%d) >= timeout (%d -> %ld)\n" 779 "see manual section TIMEOUT RESOLUTION", 780 pretimeout, timeout_sec, (long)ts.tv_sec); 781 } 782 } 783 } 784