1 /* $OpenBSD: sthen $ */ 2 3 /* 4 * Copyright (c) 2005 Marc Balmer <mbalmer@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/ioctl.h> 21 #include <sys/mman.h> 22 #include <sys/resource.h> 23 #include <sys/sysctl.h> 24 #include <sys/time.h> 25 #include <sys/types.h> 26 #include <sys/wdog.h> 27 28 #include <err.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 35 volatile sig_atomic_t quit = 0; 36 37 void usage(void); 38 void sighdlr(int); 39 40 void 41 usage(void) 42 { 43 fprintf(stderr, "usage: %s [-dnq] [-i interval] [-p period]\n", 44 getprogname()); 45 exit(1); 46 } 47 48 /* ARGSUSED */ 49 void 50 sighdlr(__unused int signum) 51 { 52 quit = 1; 53 } 54 55 int 56 main(int argc, char *argv[]) 57 { 58 struct rlimit rlim; 59 const char *errstr; 60 size_t len; 61 u_int interval = 0, period = 30, nperiod; 62 int fd, ch, trigauto, sauto, speriod; 63 int quiet = 0, daemonize = 1, retval = 1, do_restore = 1; 64 65 while ((ch = getopt(argc, argv, "di:np:q")) != -1) { 66 switch (ch) { 67 case 'd': 68 daemonize = 0; 69 break; 70 case 'i': 71 interval = (u_int)strtonum(optarg, 1LL, 86400LL, 72 &errstr); 73 if (errstr) 74 errx(1, "interval is %s: %s", errstr, optarg); 75 break; 76 case 'n': 77 do_restore = 0; 78 break; 79 case 'p': 80 period = (u_int)strtonum(optarg, 2LL, 86400LL, &errstr); 81 if (errstr) 82 errx(1, "period is %s: %s", errstr, optarg); 83 break; 84 case 'q': 85 quiet = 1; 86 break; 87 default: 88 usage(); 89 } 90 } 91 92 argc -= optind; 93 argv += optind; 94 if (argc > 0) 95 usage(); 96 97 if (interval == 0 && (interval = period / 3) == 0) 98 interval = 1; 99 100 if (period <= interval) 101 errx(1, "retrigger interval too long"); 102 103 /* save kern.watchdog.period and kern.watchdog.auto for restore */ 104 105 len = sizeof(speriod); 106 if (sysctlbyname("kern.watchdog.period", &speriod, &len, &period, sizeof(period)) == -1) { 107 if (errno == EOPNOTSUPP) 108 errx(1, "no watchdog timer available"); 109 else 110 err(1, "can't access kern.watchdog.period"); 111 } 112 113 len = sizeof(sauto); 114 trigauto = 0; 115 116 if (sysctlbyname("kern.watchdog.auto", &sauto, &len, &trigauto, sizeof(trigauto)) == -1) 117 err(1, "can't access kern.watchdog.auto"); 118 119 /* Double check the timeout period, some devices change the value */ 120 len = sizeof(nperiod); 121 if (sysctlbyname("kern.watchdog.period", &nperiod, &len, NULL, 0) == -1) { 122 warnx("can't read back kern.watchdog.period, " 123 "restoring original values"); 124 goto restore; 125 } 126 127 if (nperiod != period && !quiet) 128 warnx("period adjusted to %d by device", nperiod); 129 130 if (nperiod <= interval) { 131 warnx("retrigger interval %d too long, " 132 "restoring original values", interval); 133 goto restore; 134 } 135 136 if ((fd = open("/dev/wdog", O_RDWR)) == -1) { 137 err(1, "can't open /dev/wdog"); 138 } 139 140 if (daemonize && daemon(0, 0)) { 141 warn("can't daemonize, restoring original values"); 142 goto restore; 143 } 144 145 /* 146 * mlockall() below will wire the whole stack up to the limit 147 * thus we have to reduce stack size to avoid resource abuse 148 */ 149 rlim.rlim_cur = 256 * 1024; 150 rlim.rlim_max = 256 * 1024; 151 (void)setrlimit(RLIMIT_STACK, &rlim); 152 153 setpriority(PRIO_PROCESS, getpid(), -5); 154 155 signal(SIGTERM, sighdlr); 156 157 retval = 0; 158 while (!quit) { 159 if (ioctl(fd, WDIOCRESET, &period, sizeof(period)) == -1) 160 quit = retval = 1; 161 sleep(interval); 162 } 163 164 close(fd); 165 166 if (do_restore) { 167 restore: sysctlbyname("kern.watchdog.period", NULL, 0, &speriod, sizeof(speriod)); 168 sysctlbyname("kern.watchdog.auto", NULL, 0, &sauto, sizeof(sauto)); 169 } 170 return retval; 171 } 172