wdogctl.c revision 1.16 1 /* $NetBSD: wdogctl.c,v 1.16 2006/08/13 01:11:01 dyoung 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 #include <sys/cdefs.h>
36
37 #ifndef lint
38 __RCSID("$NetBSD: wdogctl.c,v 1.16 2006/08/13 01:11:01 dyoung Exp $");
39 #endif
40
41
42 #include <sys/param.h>
43 #include <sys/ioctl.h>
44 #include <sys/wdog.h>
45
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <time.h>
52 #include <signal.h>
53 #include <syslog.h>
54 #include <unistd.h>
55 #include <string.h>
56
57 #define _PATH_WATCHDOG "/dev/watchdog"
58
59 int main(int, char *[]);
60 void enable_kernel(const char *, u_int);
61 void enable_user(const char *, u_int, int);
62 void enable_ext(const char *, u_int);
63 void tickle_ext(void);
64 void disable(void);
65 void prep_wmode(struct wdog_mode *, int, const char *, u_int);
66 void list_timers(void);
67 void usage(void);
68
69 int Aflag;
70
71 /* Caution -- ordered list; entries >= CMD_EXT_TICKLE set timers */
72 enum cmd {
73 CMD_NONE, /* No verb given */
74 CMD_DISABLE,
75 CMD_DOTICKLE,
76 CMD_EXT_TICKLE,
77 CMD_KERN_TICKLE,
78 CMD_NOCANCEL_TICKLE,
79 CMD_USER_TICKLE
80 };
81
82 int
83 main(int argc, char *argv[])
84 {
85 enum cmd command = CMD_NONE;
86 int period_flag = 0;
87 int ch;
88 u_int period = WDOG_PERIOD_DEFAULT;
89
90 while ((ch = getopt(argc, argv, "Adekp:utx")) != -1) {
91 switch (ch) {
92 case 'A':
93 Aflag = 1;
94 break;
95
96 case 'd':
97 if (command != CMD_NONE)
98 usage();
99 command = CMD_DISABLE;
100 break;
101
102 case 'e':
103 if (command != CMD_NONE)
104 usage();
105 command = CMD_EXT_TICKLE;
106 break;
107
108 case 'k':
109 if (command != CMD_NONE)
110 usage();
111 command = CMD_KERN_TICKLE;
112 break;
113
114 case 't':
115 if (command != CMD_NONE)
116 usage();
117 command = CMD_DOTICKLE;
118 break;
119
120 case 'p':
121 period_flag = 1;
122 period = atoi(optarg);
123 if (period == -1)
124 usage();
125 break;
126
127 case 'x':
128 case 'u':
129 if (command != CMD_NONE)
130 usage();
131 command =
132 (ch == 'u') ? CMD_USER_TICKLE : CMD_NOCANCEL_TICKLE;
133 break;
134
135 default:
136 usage();
137 }
138 }
139
140 argc -= optind;
141 argv += optind;
142
143 if (command < CMD_EXT_TICKLE) {
144 if (Aflag || period_flag)
145 usage();
146 if (argc != 0)
147 usage();
148 } else if (argc != 1)
149 usage();
150
151 switch (command) {
152 case CMD_NONE:
153 list_timers();
154 break;
155 case CMD_DISABLE:
156 disable();
157 break;
158 case CMD_DOTICKLE:
159 tickle_ext();
160 break;
161 case CMD_EXT_TICKLE:
162 enable_ext(argv[0], period);
163 break;
164 case CMD_KERN_TICKLE:
165 enable_kernel(argv[0], period);
166 break;
167 case CMD_NOCANCEL_TICKLE:
168 case CMD_USER_TICKLE:
169 enable_user(argv[0], period, command == CMD_USER_TICKLE);
170 break;
171 }
172 exit(EXIT_SUCCESS);
173 }
174
175 void
176 prep_wmode(struct wdog_mode *wp, int mode, const char *name, u_int period)
177 {
178 if (strlen(name) >= WDOG_NAMESIZE)
179 errx(EXIT_FAILURE, "invalid watchdog timer name: %s", name);
180
181 strlcpy(wp->wm_name, name, sizeof(wp->wm_name));
182 wp->wm_mode = mode;
183 wp->wm_period = period;
184 if (Aflag)
185 wp->wm_mode |= WDOG_FEATURE_ALARM;
186 }
187
188 void
189 enable_kernel(const char *name, u_int period)
190 {
191 struct wdog_mode wm;
192 int fd;
193
194 prep_wmode(&wm, WDOG_MODE_KTICKLE, name, period);
195
196 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
197 if (fd == -1)
198 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG);
199
200 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1)
201 err(EXIT_FAILURE, "WDOGIOC_SMODE");
202 }
203
204 void
205 enable_ext(const char *name, u_int period)
206 {
207 struct wdog_mode wm;
208 int fd;
209
210 prep_wmode(&wm, WDOG_MODE_ETICKLE, name, period);
211
212 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
213 if (fd == -1)
214 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG);
215 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) {
216 err(EXIT_FAILURE, "WDOGIOC_SMODE");
217 }
218 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
219 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m",
220 wm.wm_name);
221 return;
222 }
223
224 void
225 enable_user(const char *name, u_int period, int cancel_on_close)
226 {
227 struct wdog_mode wm;
228 struct timespec ts;
229 pid_t tickler;
230 int fd, rv;
231
232 prep_wmode(&wm,
233 (cancel_on_close) ? WDOG_MODE_UTICKLE : WDOG_MODE_ETICKLE, name,
234 period);
235
236 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
237 if (fd == -1)
238 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG);
239
240 /* ...so we can log failures to tickle the timer. */
241 openlog("wdogctl", LOG_PERROR|LOG_PID, LOG_DAEMON);
242
243 /*
244 * We fork a child process which detaches from the controlling
245 * terminal once the timer is armed, and tickles the timer
246 * until we send it a SIGTERM.
247 */
248 tickler = fork();
249 if (tickler == -1)
250 err(EXIT_FAILURE, "unable to fork tickler process");
251 else if (tickler != 0) {
252 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) {
253 (void)kill(tickler, SIGTERM);
254 err(EXIT_FAILURE, "WDOGIOC_SMODE");
255 }
256 (void)close(fd);
257 return;
258 }
259
260
261 /*
262 * Wait for the watchdog to be armed. When it is, loop,
263 * tickling the timer, then waiting 1/2 the period before
264 * doing it again.
265 *
266 * If the parent fails to enable the watchdog, it will kill
267 * us.
268 */
269 do {
270 rv = ioctl(fd, WDOGIOC_WHICH, &wm);
271 } while (rv == -1);
272
273 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
274 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m",
275 wm.wm_name);
276
277 /*
278 * Now detach from the controlling terminal, and just run
279 * in the background. The kernel will keep track of who
280 * we are, each time we tickle the timer.
281 */
282 if (daemon(0, 0) == -1) {
283 /*
284 * We weren't able to go into the background. When
285 * we exit, the kernel will disable the watchdog so
286 * that the system won't die.
287 */
288 err(EXIT_FAILURE, "unable to detach from terminal");
289 }
290
291 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
292 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m",
293 wm.wm_name);
294
295 for (;;) {
296 ts.tv_sec = wm.wm_period / 2;
297 ts.tv_nsec = 0;
298 (void)nanosleep(&ts, NULL);
299
300 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
301 syslog(LOG_EMERG,
302 "unable to tickle watchdog timer %s: %m",
303 wm.wm_name);
304 }
305 /* NOTREACHED */
306 }
307
308 void
309 tickle_ext()
310 {
311 int fd;
312
313 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
314 if (fd == -1)
315 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG);
316 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
317 fprintf(stderr, "Cannot tickle timer\n");
318 }
319
320 void
321 disable(void)
322 {
323 struct wdog_mode wm;
324 pid_t tickler;
325 int fd, mode;
326
327 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
328 if (fd == -1)
329 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG);
330
331 if (ioctl(fd, WDOGIOC_WHICH, &wm) == -1) {
332 printf("No watchdog timer running.\n");
333 return;
334 }
335 mode = wm.wm_mode & WDOG_MODE_MASK;
336
337 /*
338 * If the timer is running in UTICKLE mode, we need
339 * to kill the wdogctl(8) process that is tickling
340 * the timer.
341 */
342 if (mode == WDOG_MODE_UTICKLE) {
343 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1)
344 err(EXIT_FAILURE, "WDOGIOC_GTICKLER");
345 (void)close(fd);
346 (void)kill(tickler, SIGTERM);
347 } else {
348 wm.wm_mode = WDOG_MODE_DISARMED;
349 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) {
350 err(EXIT_FAILURE, "unable to disarm watchdog %s",
351 wm.wm_name);
352 }
353 (void)close(fd);
354 }
355 }
356
357 void
358 list_timers(void)
359 {
360 struct wdog_conf wc;
361 struct wdog_mode wm;
362 char *buf, *cp;
363 int fd, count, i, mode;
364 pid_t tickler;
365
366 fd = open(_PATH_WATCHDOG, O_RDONLY, 0644);
367 if (fd == -1)
368 err(EXIT_FAILURE, "open %s", _PATH_WATCHDOG);
369
370 wc.wc_names = NULL;
371 wc.wc_count = 0;
372
373 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1)
374 err(EXIT_FAILURE, "ioctl WDOGIOC_GWDOGS for count");
375
376 count = wc.wc_count;
377 if (count == 0) {
378 printf("No watchdog timers present.\n");
379 goto out;
380 }
381
382 buf = malloc(count * WDOG_NAMESIZE);
383 if (buf == NULL)
384 err(EXIT_FAILURE, "malloc %d byte for watchdog names",
385 count * WDOG_NAMESIZE);
386
387 wc.wc_names = buf;
388 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1)
389 err(EXIT_FAILURE, "ioctl WDOGIOC_GWDOGS for names");
390
391 count = wc.wc_count;
392 if (count == 0) {
393 printf("No watchdog timers present.\n");
394 free(buf);
395 goto out;
396 }
397
398 printf("Available watchdog timers:\n");
399 for (i = 0, cp = buf; i < count; i++, cp += WDOG_NAMESIZE) {
400 cp[WDOG_NAMESIZE - 1] = '\0';
401 strlcpy(wm.wm_name, cp, sizeof(wm.wm_name));
402
403 if (ioctl(fd, WDOGIOC_GMODE, &wm) == -1)
404 continue;
405 mode = wm.wm_mode & WDOG_MODE_MASK;
406 if (mode == WDOG_MODE_UTICKLE) {
407 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1)
408 tickler = (pid_t) -1;
409 }
410
411 printf("\t%s, %u second period", cp, wm.wm_period);
412 if (mode != WDOG_MODE_DISARMED) {
413 switch(mode) {
414 case WDOG_MODE_KTICKLE:
415 printf(" [armed, kernel tickle");
416 break;
417 case WDOG_MODE_UTICKLE:
418 printf(" [armed, user tickle");
419 if (tickler != (pid_t) -1)
420 printf(", pid %d", tickler);
421 break;
422 case WDOG_MODE_ETICKLE:
423 printf(" [armed, external tickle");
424 break;
425 }
426 printf("]");
427 }
428 printf("\n");
429 }
430 out:
431 (void)close(fd);
432 }
433
434 void
435 usage(void)
436 {
437
438 fprintf(stderr, "usage: %s\n", getprogname());
439 fprintf(stderr, " %s -d\n", getprogname());
440 fprintf(stderr, " %s -e [-A] [-p seconds] timer\n",
441 getprogname());
442 fprintf(stderr, " %s -k [-A] [-p seconds] timer\n",
443 getprogname());
444 fprintf(stderr, " %s -t\n", getprogname());
445 fprintf(stderr, " %s -u [-A] [-p seconds] timer\n",
446 getprogname());
447
448 exit(1);
449 }
450