wdogctl.c revision 1.2 1 /* $NetBSD: wdogctl.c,v 1.2 2000/11/06 07:48:28 minoura 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 int
63 main(int argc, char *argv[])
64 {
65 int cmds = 0, kflag = 0, uflag = 0, dflag = 0, ch;
66 u_int period = WDOG_PERIOD_DEFAULT;
67
68 while ((ch = getopt(argc, argv, "Adkp:u")) != -1) {
69 switch (ch) {
70 case 'A':
71 if (cmds == 0 || dflag)
72 usage();
73 Aflag = 1;
74 break;
75
76 case 'd':
77 dflag = 1;
78 cmds++;
79 break;
80
81 case 'k':
82 kflag = 1;
83 cmds++;
84 break;
85
86 case 'p':
87 if (cmds == 0 || dflag)
88 usage();
89 period = atoi(optarg);
90 if (period == -1)
91 usage();
92 break;
93
94 case 'u':
95 uflag = 1;
96 cmds++;
97 break;
98
99 default:
100 usage();
101 }
102 }
103
104 argc -= optind;
105 argv += optind;
106
107 if (cmds > 1)
108 usage();
109
110 if (kflag) {
111 if (argc != 1)
112 usage();
113 enable_kernel(argv[0], period);
114 } else if (uflag) {
115 if (argc != 1)
116 usage();
117 enable_user(argv[0], period);
118 } else if (dflag) {
119 if (argc != 0)
120 usage();
121 disable();
122 } else
123 list_timers();
124
125 exit(0);
126 }
127
128 void
129 enable_kernel(const char *name, u_int period)
130 {
131 struct wdog_mode wm;
132 int fd;
133
134 if (strlen(name) >= WDOG_NAMESIZE)
135 errx(1, "invalid watchdog timer name: %s", name);
136 strcpy(wm.wm_name, name);
137 wm.wm_mode = WDOG_MODE_KTICKLE;
138 wm.wm_period = period;
139
140 if (Aflag)
141 wm.wm_mode |= WDOG_FEATURE_ALARM;
142
143 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
144 if (fd == -1)
145 err(1, "open %s", _PATH_WATCHDOG);
146
147 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1)
148 err(1, "WDOGIOC_SMODE");
149 }
150
151 void
152 enable_user(const char *name, u_int period)
153 {
154 extern const char *__progname;
155 struct wdog_mode wm;
156 struct timespec ts;
157 pid_t tickler;
158 int fd, rv;
159
160 if (strlen(name) >= WDOG_NAMESIZE)
161 errx(1, "invalid watchdog timer name: %s", name);
162 strcpy(wm.wm_name, name);
163 wm.wm_mode = WDOG_MODE_UTICKLE;
164 wm.wm_period = period;
165
166 if (Aflag)
167 wm.wm_mode |= WDOG_FEATURE_ALARM;
168
169 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
170 if (fd == -1)
171 err(1, "open %s", _PATH_WATCHDOG);
172
173 /* ...so we can log failures to tickle the timer. */
174 openlog(__progname, LOG_CONS|LOG_PERROR|LOG_PID, LOG_DAEMON);
175
176 /*
177 * We fork a child process which detaches from the controlling
178 * terminal once the timer is armed, and tickles the timer
179 * until we send it a SIGTERM.
180 */
181 tickler = fork();
182 if (tickler == -1)
183 err(1, "unable to fork tickler process");
184 else if (tickler != 0) {
185 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1) {
186 err(1, "WDOGIOC_SMODE");
187 (void) kill(tickler, SIGTERM);
188 }
189 (void) close(fd);
190 return;
191 }
192
193
194 /*
195 * Wait for the watchdog to be armed. When it is, loop,
196 * tickling the timer, then waiting 1/2 the period before
197 * doing it again.
198 *
199 * If the parent fails to enable the watchdog, it will kill
200 * us.
201 */
202 do {
203 rv = ioctl(fd, WDOGIOC_WHICH, &wm);
204 } while (rv == -1);
205
206 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
207 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m",
208 wm.wm_name);
209
210 /*
211 * Now detach from the controlling terminal, and just run
212 * in the background. The kernel will keep track of who
213 * we are, each time we tickle the timer.
214 */
215 if (daemon(0, 0) == -1) {
216 /*
217 * We weren't able to go into the background. When
218 * we exit, the kernel will disable the watchdog so
219 * that the system won't die.
220 */
221 err(1, "unable to deatch from terminal");
222 }
223
224 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
225 syslog(LOG_EMERG, "unable to tickle watchdog timer %s: %m",
226 wm.wm_name);
227
228 for (;;) {
229 ts.tv_sec = wm.wm_period / 2;
230 ts.tv_nsec = 0;
231 (void) nanosleep(&ts, NULL);
232
233 if (ioctl(fd, WDOGIOC_TICKLE) == -1)
234 syslog(LOG_EMERG,
235 "unable to tickle watchdog timer %s: %m",
236 wm.wm_name);
237 }
238 /* NOTREACHED */
239 }
240
241 void
242 disable(void)
243 {
244 struct wdog_mode wm;
245 pid_t tickler;
246 int fd;
247
248 fd = open(_PATH_WATCHDOG, O_RDWR, 0644);
249 if (fd == -1)
250 err(1, "open %s", _PATH_WATCHDOG);
251
252 if (ioctl(fd, WDOGIOC_WHICH, &wm) == -1) {
253 printf("No watchdog timer running.\n");
254 return;
255 }
256
257 /*
258 * If the timer is running in UTICKLE mode, we need
259 * to kill the wdogctl(8) process that is tickling
260 * the timer.
261 */
262 if (wm.wm_mode == WDOG_MODE_UTICKLE) {
263 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1)
264 err(1, "WDOGIOC_GTICKLER");
265 (void) close(fd);
266 (void) kill(tickler, SIGTERM);
267 } else {
268 wm.wm_mode = WDOG_MODE_DISARMED;
269 if (ioctl(fd, WDOGIOC_SMODE, &wm) == -1)
270 err(1, "unable to disarm watchdog %s", wm.wm_name);
271 (void) close(fd);
272 }
273 }
274
275 void
276 list_timers(void)
277 {
278 struct wdog_conf wc;
279 struct wdog_mode wm;
280 char *buf, *cp;
281 int fd, count, i;
282 pid_t tickler;
283
284 fd = open(_PATH_WATCHDOG, O_RDONLY, 0644);
285 if (fd == -1)
286 err(1, "open %s", _PATH_WATCHDOG);
287
288 wc.wc_names = NULL;
289 wc.wc_count = 0;
290
291 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1)
292 err(1, "ioctl WDOGIOC_GWDOGS for count");
293
294 count = wc.wc_count;
295 if (count == 0) {
296 printf("No watchdog timers present.\n");
297 goto out;
298 }
299
300 buf = malloc(count * WDOG_NAMESIZE);
301 if (buf == NULL)
302 err(1, "malloc %d byte for watchdog names",
303 count * WDOG_NAMESIZE);
304
305 wc.wc_names = buf;
306 if (ioctl(fd, WDOGIOC_GWDOGS, &wc) == -1)
307 err(1, "ioctl WDOGIOC_GWDOGS for names");
308
309 count = wc.wc_count;
310 if (count == 0) {
311 printf("No watchdog timers present.\n");
312 free(buf);
313 goto out;
314 }
315
316 printf("Available watchdog timers:\n");
317 for (i = 0, cp = buf; i < count; i++, cp += WDOG_NAMESIZE) {
318 cp[WDOG_NAMESIZE - 1] = '\0';
319 strcpy(wm.wm_name, cp);
320
321 if (ioctl(fd, WDOGIOC_GMODE, &wm) == -1)
322 wm.wm_mode = -1;
323 else if (wm.wm_mode == WDOG_MODE_UTICKLE) {
324 if (ioctl(fd, WDOGIOC_GTICKLER, &tickler) == -1)
325 tickler = (pid_t) -1;
326 }
327
328 printf("\t%s, %u second period", cp, wm.wm_period);
329 if (wm.wm_mode != WDOG_MODE_DISARMED) {
330 printf(" [armed, %s tickle",
331 wm.wm_mode == WDOG_MODE_KTICKLE ?
332 "kernel" : "user");
333 if (wm.wm_mode == WDOG_MODE_UTICKLE &&
334 tickler != (pid_t) -1)
335 printf(", pid %d", tickler);
336 printf("]");
337 }
338 printf("\n");
339 }
340 out:
341 (void) close(fd);
342 }
343
344 void
345 usage(void)
346 {
347 extern const char *__progname;
348
349 fprintf(stderr, "usage: %s\n", __progname);
350 fprintf(stderr, " %s -k [-A] [-p seconds] timer\n", __progname);
351 fprintf(stderr, " %s -u [-A] [-p seconds] timer\n", __progname);
352 fprintf(stderr, " %s -d\n", __progname);
353
354 exit(1);
355 }
356