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