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