1 /* $OpenBSD: watchdogd.c,v 1.15 2015/01/16 06:40:22 deraadt 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/types.h> 20 #include <sys/resource.h> 21 #include <sys/signal.h> 22 #include <sys/sysctl.h> 23 #include <sys/mman.h> 24 25 #include <err.h> 26 #include <errno.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 31 volatile sig_atomic_t quit = 0; 32 33 __dead void usage(void); 34 void sighdlr(int); 35 int main(int, char *[]); 36 37 __dead void 38 usage(void) 39 { 40 extern char *__progname; 41 42 fprintf(stderr, "usage: %s [-dnq] [-i interval] [-p period]\n", 43 __progname); 44 exit(1); 45 } 46 47 /* ARGSUSED */ 48 void 49 sighdlr(int signum) 50 { 51 quit = 1; 52 } 53 54 int 55 main(int argc, char *argv[]) 56 { 57 struct rlimit rlim; 58 const char *errstr; 59 size_t len; 60 u_int interval = 0, period = 30, nperiod; 61 int ch, trigauto, sauto, speriod; 62 int quiet = 0, daemonize = 1, retval = 1, do_restore = 1; 63 int mib[3]; 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 mib[0] = CTL_KERN; 105 mib[1] = KERN_WATCHDOG; 106 mib[2] = KERN_WATCHDOG_PERIOD; 107 108 len = sizeof(speriod); 109 if (sysctl(mib, 3, &speriod, &len, &period, sizeof(period)) == -1) { 110 if (errno == EOPNOTSUPP) 111 errx(1, "no watchdog timer available"); 112 else 113 err(1, "can't access kern.watchdog.period"); 114 } 115 116 mib[2] = KERN_WATCHDOG_AUTO; 117 len = sizeof(sauto); 118 trigauto = 0; 119 120 if (sysctl(mib, 3, &sauto, &len, &trigauto, sizeof(trigauto)) == -1) 121 err(1, "can't access kern.watchdog.auto"); 122 123 /* Double check the timeout period, some devices change the value */ 124 mib[2] = KERN_WATCHDOG_PERIOD; 125 len = sizeof(nperiod); 126 if (sysctl(mib, 3, &nperiod, &len, NULL, 0) == -1) { 127 warnx("can't read back kern.watchdog.period, " 128 "restoring original values"); 129 goto restore; 130 } 131 132 if (nperiod != period && !quiet) 133 warnx("period adjusted to %d by device", nperiod); 134 135 if (nperiod <= interval) { 136 warnx("retrigger interval %d too long, " 137 "restoring original values", interval); 138 goto restore; 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 (void)mlockall(MCL_CURRENT | MCL_FUTURE); 155 setpriority(PRIO_PROCESS, getpid(), -5); 156 157 signal(SIGTERM, sighdlr); 158 159 retval = 0; 160 while (!quit) { 161 if (sysctl(mib, 3, NULL, 0, &period, sizeof(period)) == -1) 162 quit = retval = 1; 163 sleep(interval); 164 } 165 166 if (do_restore) { 167 restore: sysctl(mib, 3, NULL, 0, &speriod, sizeof(speriod)); 168 mib[2] = KERN_WATCHDOG_AUTO; 169 sysctl(mib, 3, NULL, 0, &sauto, sizeof(sauto)); 170 } 171 172 return retval; 173 } 174