watchdogd - Import from OpenBSD
[dragonfly.git] / usr.sbin / watchdogd / watchdogd.c
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/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 }