xref: /dragonfly/usr.sbin/watchdogd/watchdogd.c (revision fe08e20ddf9efaf51ce4fbe089d786a1989e1262)
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 static volatile sig_atomic_t  quit = 0;
36 
37 static void         usage(void) __dead2;
38 static void         sighdlr(int);
39 
40 static void
usage(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 static void
sighdlr(__unused int signum)50 sighdlr(__unused int signum)
51 {
52           quit = 1;
53 }
54 
55 int
main(int argc,char * argv[])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