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 int main(int, char *[]); 40 41 void 42 usage(void) 43 { 44 fprintf(stderr, "usage: %s [-dnq] [-i interval] [-p period]\n", 45 getprogname()); 46 exit(1); 47 } 48 49 /* ARGSUSED */ 50 void 51 sighdlr(__unused int signum) 52 { 53 quit = 1; 54 } 55 56 int 57 main(int argc, char *argv[]) 58 { 59 struct rlimit rlim; 60 const char *errstr; 61 size_t len; 62 u_int interval = 0, period = 30, nperiod; 63 int fd, ch, trigauto, sauto, speriod; 64 int quiet = 0, daemonize = 1, retval = 1, do_restore = 1; 65 66 while ((ch = getopt(argc, argv, "di:np:q")) != -1) { 67 switch (ch) { 68 case 'd': 69 daemonize = 0; 70 break; 71 case 'i': 72 interval = (u_int)strtonum(optarg, 1LL, 86400LL, 73 &errstr); 74 if (errstr) 75 errx(1, "interval is %s: %s", errstr, optarg); 76 break; 77 case 'n': 78 do_restore = 0; 79 break; 80 case 'p': 81 period = (u_int)strtonum(optarg, 2LL, 86400LL, &errstr); 82 if (errstr) 83 errx(1, "period is %s: %s", errstr, optarg); 84 break; 85 case 'q': 86 quiet = 1; 87 break; 88 default: 89 usage(); 90 } 91 } 92 93 argc -= optind; 94 argv += optind; 95 if (argc > 0) 96 usage(); 97 98 if (interval == 0 && (interval = period / 3) == 0) 99 interval = 1; 100 101 if (period <= interval) 102 errx(1, "retrigger interval too long"); 103 104 /* save kern.watchdog.period and kern.watchdog.auto for restore */ 105 106 len = sizeof(speriod); 107 if (sysctlbyname("kern.watchdog.period", &speriod, &len, &period, sizeof(period)) == -1) { 108 if (errno == EOPNOTSUPP) 109 errx(1, "no watchdog timer available"); 110 else 111 err(1, "can't access kern.watchdog.period"); 112 } 113 114 len = sizeof(sauto); 115 trigauto = 0; 116 117 if (sysctlbyname("kern.watchdog.auto", &sauto, &len, &trigauto, sizeof(trigauto)) == -1) 118 err(1, "can't access kern.watchdog.auto"); 119 120 /* Double check the timeout period, some devices change the value */ 121 len = sizeof(nperiod); 122 if (sysctlbyname("kern.watchdog.period", &nperiod, &len, NULL, 0) == -1) { 123 warnx("can't read back kern.watchdog.period, " 124 "restoring original values"); 125 goto restore; 126 } 127 128 if (nperiod != period && !quiet) 129 warnx("period adjusted to %d by device", nperiod); 130 131 if (nperiod <= interval) { 132 warnx("retrigger interval %d too long, " 133 "restoring original values", interval); 134 goto restore; 135 } 136 137 if ((fd = open("/dev/wdog", O_RDWR)) == -1) { 138 err(1, "can't open /dev/wdog"); 139 } 140 141 if (daemonize && daemon(0, 0)) { 142 warn("can't daemonize, restoring original values"); 143 goto restore; 144 } 145 146 /* 147 * mlockall() below will wire the whole stack up to the limit 148 * thus we have to reduce stack size to avoid resource abuse 149 */ 150 rlim.rlim_cur = 256 * 1024; 151 rlim.rlim_max = 256 * 1024; 152 (void)setrlimit(RLIMIT_STACK, &rlim); 153 154 setpriority(PRIO_PROCESS, getpid(), -5); 155 156 signal(SIGTERM, sighdlr); 157 158 retval = 0; 159 while (!quit) { 160 if (ioctl(fd, WDIOCRESET, &period, sizeof(period)) == -1) 161 quit = retval = 1; 162 sleep(interval); 163 } 164 165 close(fd); 166 167 if (do_restore) { 168 restore: sysctlbyname("kern.watchdog.period", NULL, 0, &speriod, sizeof(speriod)); 169 sysctlbyname("kern.watchdog.auto", NULL, 0, &sauto, sizeof(sauto)); 170 } 171 return retval; 172 } 173