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