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