1 /* $OpenBSD: watchdogd.c,v 1.13 2009/06/24 14:28:19 sthen Exp $ */ 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/sysctl.h> 21 #include <sys/mman.h> 22 23 #include <err.h> 24 #include <errno.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <unistd.h> 28 29 volatile sig_atomic_t quit = 0; 30 31 __dead void usage(void); 32 void sighdlr(int); 33 int main(int, char *[]); 34 35 __dead void 36 usage(void) 37 { 38 extern char *__progname; 39 40 fprintf(stderr, "usage: %s [-dnq] [-i interval] [-p period]\n", 41 __progname); 42 exit(1); 43 } 44 45 /* ARGSUSED */ 46 void 47 sighdlr(int signum) 48 { 49 quit = 1; 50 } 51 52 int 53 main(int argc, char *argv[]) 54 { 55 struct rlimit rlim; 56 const char *errstr; 57 size_t len; 58 u_int interval = 0, period = 30, nperiod; 59 int ch, trigauto, sauto, speriod; 60 int quiet = 0, daemonize = 1, retval = 1, do_restore = 1; 61 int mib[3]; 62 63 while ((ch = getopt(argc, argv, "di:np:q")) != -1) { 64 switch (ch) { 65 case 'd': 66 daemonize = 0; 67 break; 68 case 'i': 69 interval = (u_int)strtonum(optarg, 1LL, 86400LL, 70 &errstr); 71 if (errstr) 72 errx(1, "interval is %s: %s", errstr, optarg); 73 break; 74 case 'n': 75 do_restore = 0; 76 break; 77 case 'p': 78 period = (u_int)strtonum(optarg, 2LL, 86400LL, &errstr); 79 if (errstr) 80 errx(1, "period is %s: %s", errstr, optarg); 81 break; 82 case 'q': 83 quiet = 1; 84 break; 85 default: 86 usage(); 87 } 88 } 89 90 argc -= optind; 91 argv += optind; 92 if (argc > 0) 93 usage(); 94 95 if (interval == 0 && (interval = period / 3) == 0) 96 interval = 1; 97 98 if (period <= interval) 99 errx(1, "retrigger interval too long"); 100 101 /* save kern.watchdog.period and kern.watchdog.auto for restore */ 102 mib[0] = CTL_KERN; 103 mib[1] = KERN_WATCHDOG; 104 mib[2] = KERN_WATCHDOG_PERIOD; 105 106 len = sizeof(speriod); 107 if (sysctl(mib, 3, &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 mib[2] = KERN_WATCHDOG_AUTO; 115 len = sizeof(sauto); 116 trigauto = 0; 117 118 if (sysctl(mib, 3, &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 mib[2] = KERN_WATCHDOG_PERIOD; 123 len = sizeof(nperiod); 124 if (sysctl(mib, 3, &nperiod, &len, NULL, 0) == -1) { 125 warnx("can't read back kern.watchdog.period, " 126 "restoring original values"); 127 goto restore; 128 } 129 130 if (nperiod != period && !quiet) 131 warnx("period adjusted to %d by device", nperiod); 132 133 if (nperiod <= interval) { 134 warnx("retrigger interval %d too long, " 135 "restoring original values", interval); 136 goto restore; 137 } 138 139 if (daemonize && daemon(0, 0)) { 140 warn("can't daemonize, restoring original values"); 141 goto restore; 142 } 143 144 /* 145 * mlockall() below will wire the whole stack up to the limit 146 * thus we have to reduce stack size to avoid resource abuse 147 */ 148 rlim.rlim_cur = 256 * 1024; 149 rlim.rlim_max = 256 * 1024; 150 (void)setrlimit(RLIMIT_STACK, &rlim); 151 152 (void)mlockall(MCL_CURRENT | MCL_FUTURE); 153 setpriority(PRIO_PROCESS, getpid(), -5); 154 155 signal(SIGTERM, sighdlr); 156 157 retval = 0; 158 while (!quit) { 159 if (sysctl(mib, 3, NULL, 0, &period, sizeof(period)) == -1) 160 quit = retval = 1; 161 sleep(interval); 162 } 163 164 if (do_restore) { 165 restore: sysctl(mib, 3, NULL, 0, &speriod, sizeof(speriod)); 166 mib[2] = KERN_WATCHDOG_AUTO; 167 sysctl(mib, 3, NULL, 0, &sauto, sizeof(sauto)); 168 } 169 170 return retval; 171 } 172