wdogctl.c revision 1.6 1 /* $NetBSD: wdogctl.c,v 1.6 2001/02/04 21:14:32 christos Exp $ */
2
3 /*-
4 * Copyright (c) 2000 Zembu Labs, Inc.
5 * All rights reserved.
6 *
7 * Author: Jason R. Thorpe <thorpej (at) zembu.com>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Zembu Labs, Inc.
20 * 4. Neither the name of Zembu Labs nor the names of its employees may
21 * be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS
25 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR-
26 * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS-
27 * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <sys/param.h>
37 #include <sys/ioctl.h>
38 #include <sys/wdog.h>
39
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <time.h>
46 #include <signal.h>
47 #include <syslog.h>
48 #include <unistd.h>
49 #include <string.h>
50
51 #define _PATH_WATCHDOG "/dev/watchdog"
52
53 int main(int, char *[]);
54 void enable_kernel(const char *, u_int);
55 void enable_user(const char *, u_int);
56 void disable(void);
57 void list_timers(void);
58 void usage(void);
59
60 int Aflag;
61
62 extern const char *__progname;
63
64 int
65 main(int argc, char *argv[])
66 {
67 int cmds = 0, kflag = 0, uflag = 0, dflag = 0, ch;
68 u_int period = WDOG_PERIOD_DEFAULT;
69
70 while ((ch = getopt(argc, argv, "Adkp:u")) != -1) {
71 switch (ch) {
72 case 'A':
73 if (cmds == 0 || dflag)
74 usage();
75 Aflag = 1;
76 break;
77
78 case 'd':
79 dflag = 1;
80 cmds++;
81 break;
82
83 case 'k':
84 kflag = 1;
85 cmds++;
86 break;
87
88 case 'p':
89 if (cmds == 0 || dflag)
90 usage();
91 period = atoi(optarg);
92 if (period == -1)
93 usage();
94 break;
95
96 case 'u':
97 uflag = 1;
98 cmds++;
99 break;
100
101 default:
102 usage();
103 }
104 }
105
106 argc -= optind;
107 argv += optind;
108
109 if (cmds > 1)
110 usage();
111
112 if (kflag) {
113 if (argc != 1)
114 usage();
115 enable_kernel(argv[0], period);
116 } else if (uflag) {
117 if (argc != 1)
118 usage();
119 enable_user(argv[0], period);
120 } else if (dflag) {
121 if (argc != 0)
122 usage();
123 disable();
124 } else
125 list_timers();
126
127 exit(0);
128 }
129
130 void
131 enable_kernel(const char *name, u_int period)
132 {
133 struct wdog_mode wm;
134 int fd;
135
136 if (strlen(name) >= WDOG_NAMESIZE)
137 errx(1, "invalid watchdog timer name: %s", name);
138 strcpy(wm.wm_name, name);
139 wm.wm_mode = WDOG_MODE_KTICKLE;
140 wm.wm_period = period;
141
142 if (Aflag)
143 wm.wm_mode |= WDOG_FEATURE_ALARM;
144
145 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
146 if (fd == -1)
147 err(1, "open %s", _PATH_WATCHDOG);
148
149 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1)
150 err(1, "WDOGIOC_SMODE");
151 }
152
153 void
154 enable_user(const char *name, u_int period)
155 {
156 struct wdog_mode wm;
157 struct timespec ts;
158 pid_t tickler;
159 int fd, rv;
160
161 if (strlen(name) >= WDOG_NAMESIZE)
162 errx(1, "invalid watchdog timer name: %s", name);
163 strcpy(wm.wm_name, name);
164 wm.wm_mode = WDOG_MODE_UTICKLE;
165 wm.wm_period = period;
166
167 if (Aflag)
168 wm.wm_mode |= WDOG_FEATURE_ALARM;
169
170 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
171 if (fd == -1)
172 err(1, "open %s", _PATH_WATCHDOG);
173
174 /* ...so we can log failures to tickle the timer. */
175 openlog("wdogctl", LOG_PERROR|LOG_PID, LOG_DAEMON);
176
177 /*
178 * We fork a child process which detaches from the controlling
179 * terminal once the timer is armed, and tickles the timer
180 * until we send it a SIGTERM.
181 */
182 tickler = fork();
183 if (tickler == -1)
184 err(1, "unable to fork tickler process");
185 else if (tickler != 0) {
186 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) {
187 err(1, "WDOGIOC_SMODE");
188 (void) kill(tickler, SIGTERM);
189 }
190 (void) close(fd);
191 return;
192 }
193
194
195 /*
196 * Wait for the watchdog to be armed. When it is, loop,
197 * tickling the timer, then waiting 1/2 the period before
198 * doing it again.
199 *
200 * If the parent fails to enable the watchdog, it will kill
201 * us.
202 */
203 do {
204 rv = ioctl(fd, WDOGIOC_WHICH, &wm);
205 } while (rv == -1);
206
207 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
208 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m",
209 wm.wm_name);
210
211 /*
212 * Now detach from the controlling terminal, and just run
213 * in the background. The kernel will keep track of who
214 * we are, each time we tickle the timer.
215 */
216 if (daemon(0, 0) == -1) {
217 /*
218 * We weren't able to go into the background. When
219 * we exit, the kernel will disable the watchdog so
220 * that the system won't die.
221 */
222 err(1, "unable to detach from terminal");
223 }
224
225 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
226 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m",
227 wm.wm_name);
228
229 for (;;) {
230 ts.tv_sec = wm.wm_period / 2;
231 ts.tv_nsec = 0;
232 (void) nanosleep(&ts, NULL);
233
234 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
235 syslog(LOG_EMERG,
236 "unable to tickle watchdog timer %s: %m",
237 wm.wm_name);
238 }
239 /* NOTREACHED */
240 }
241
242 void
243 disable(void)
244 {
245 struct wdog_mode wm;
246 pid_t tickler;
247 int fd;
248
249 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
250 if (fd == -1)
251 err(1, "open %s", _PATH_WATCHDOG);
252
253 if (ioctl(fd, WDOGIOC_WHICH, &wm) == -1) {
254 printf("No watchdog timer running.\n");
255 return;
256 }
257
258 /*
259 * If the timer is running in UTICKLE mode, we need
260 * to kill the wdogctl(8) process that is tickling
261 * the timer.
262 */
263 if (wm.wm_mode == WDOG_MODE_UTICKLE) {
264 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1)
265 err(1, "WDOGIOC_GTICKLER");
266 (void) close(fd);
267 (void) kill(tickler, SIGTERM);
268 } else {
269 wm.wm_mode = WDOG_MODE_DISARMED;
270 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1)
271 err(1, "unable to disarm watchdog %s", wm.wm_name);
272 (void) close(fd);
273 }
274 }
275
276 void
277 list_timers(void)
278 {
279 struct wdog_conf wc;
280 struct wdog_mode wm;
281 char *buf, *cp;
282 int fd, count, i;
283 pid_t tickler;
284
285 fd = open(_PATH_WATCHDOG, O_RDONLY, 0644);
286 if (fd == -1)
287 err(1, "open %s", _PATH_WATCHDOG);
288
289 wc.wc_names = NULL;
290 wc.wc_count = 0;
291
292 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1)
293 err(1, "ioctl WDOGIOC_GWDOGS for count");
294
295 count = wc.wc_count;
296 if (count == 0) {
297 printf("No watchdog timers present.\n");
298 goto out;
299 }
300
301 buf = malloc(count * WDOG_NAMESIZE);
302 if (buf == NULL)
303 err(1, "malloc %d byte for watchdog names",
304 count * WDOG_NAMESIZE);
305
306 wc.wc_names = buf;
307 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1)
308 err(1, "ioctl WDOGIOC_GWDOGS for names");
309
310 count = wc.wc_count;
311 if (count == 0) {
312 printf("No watchdog timers present.\n");
313 free(buf);
314 goto out;
315 }
316
317 printf("Available watchdog timers:\n");
318 for (i = 0, cp = buf; i < count; i++, cp += WDOG_NAMESIZE) {
319 cp[WDOG_NAMESIZE - 1] = '\0';
320 strcpy(wm.wm_name, cp);
321
322 if (ioctl(fd, WDOGIOC_GMODE, &wm) == -1)
323 wm.wm_mode = -1;
324 else if (wm.wm_mode == WDOG_MODE_UTICKLE) {
325 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1)
326 tickler = (pid_t) -1;
327 }
328
329 printf("\t%s, %u second period", cp, wm.wm_period);
330 if (wm.wm_mode != WDOG_MODE_DISARMED) {
331 printf(" [armed, %s tickle",
332 wm.wm_mode == WDOG_MODE_KTICKLE ?
333 "kernel" : "user");
334 if (wm.wm_mode == WDOG_MODE_UTICKLE &&
335 tickler != (pid_t) -1)
336 printf(", pid %d", tickler);
337 printf("]");
338 }
339 printf("\n");
340 }
341 out:
342 (void) close(fd);
343 }
344
345 void
346 usage(void)
347 {
348 fprintf(stderr, "Usage: %s\n", __progname);
349 fprintf(stderr, " %s -k [-A] [-p seconds] timer\n", __progname);
350 fprintf(stderr, " %s -u [-A] [-p seconds] timer\n", __progname);
351 fprintf(stderr, " %s -d\n", __progname);
352
353 exit(1);
354 }
355