xref: /netbsd/sbin/wdogctl/wdogctl.c (revision bf9ec67e)
1 /*	$NetBSD: wdogctl.c,v 1.7 2001/02/20 23:25:29 cgd 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 
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 	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("wdogctl", 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 detach 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 	fprintf(stderr, "Usage: %s\n", getprogname());
347 	fprintf(stderr, "       %s -k [-A] [-p seconds] timer\n",
348 	    getprogname());
349 	fprintf(stderr, "       %s -u [-A] [-p seconds] timer\n",
350 	    getprogname());
351 	fprintf(stderr, "       %s -d\n", getprogname());
352 
353 	exit(1);
354 }
355