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