watchdogd.c revision 126383
1/*
2 * Copyright (c) 2003  Sean M. Kelly <smkelly@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * Software watchdog daemon.
29 */
30
31#include <sys/types.h>
32__FBSDID("$FreeBSD: head/usr.sbin/watchdogd/watchdogd.c 126383 2004-02-28 20:56:35Z phk $");
33
34#include <sys/rtprio.h>
35#include <sys/stat.h>
36#include <sys/sysctl.h>
37#include <sys/time.h>
38#include <sys/watchdog.h>
39
40#include <err.h>
41#include <errno.h>
42#include <fcntl.h>
43#include <math.h>
44#include <paths.h>
45#include <signal.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <sysexits.h>
50#include <unistd.h>
51
52static void	parseargs(int, char *[]);
53static void	sighandler(int);
54static void	watchdog_loop(void);
55static int	watchdog_init(void);
56static int	watchdog_onoff(int onoff);
57static int	watchdog_patpat(void);
58static void	usage(void);
59
60int debugging = 0;
61int end_program = 0;
62const char *pidfile = _PATH_VARRUN "watchdogd.pid";
63int reset_mib[3];
64size_t reset_miblen = 3;
65u_int timeout = WD_TO_16SEC;
66u_int passive = 0;
67int is_daemon = 0;
68int fd = -1;
69int nap = 1;
70char *test_cmd = NULL;
71
72/*
73 * Periodically write to the debug.watchdog.reset sysctl OID
74 * to keep the software watchdog from firing.
75 */
76int
77main(int argc, char *argv[])
78{
79	struct rtprio rtp;
80	FILE *fp;
81
82	if (getuid() != 0)
83		errx(EX_SOFTWARE, "not super user");
84
85	parseargs(argc, argv);
86
87	rtp.type = RTP_PRIO_REALTIME;
88	rtp.prio = 0;
89	if (rtprio(RTP_SET, 0, &rtp) == -1)
90		err(EX_OSERR, "rtprio");
91
92	if (watchdog_init() == -1)
93		errx(EX_SOFTWARE, "unable to initialize watchdog");
94
95	if (is_daemon) {
96		if (watchdog_onoff(1) == -1)
97			exit(EX_SOFTWARE);
98
99		if (debugging == 0 && daemon(0, 0) == -1) {
100			watchdog_onoff(0);
101			err(EX_OSERR, "daemon");
102		}
103
104		signal(SIGHUP, SIG_IGN);
105		signal(SIGINT, sighandler);
106		signal(SIGTERM, sighandler);
107
108		fp = fopen(pidfile, "w");
109		if (fp != NULL) {
110			fprintf(fp, "%d\n", getpid());
111			fclose(fp);
112		}
113
114		watchdog_loop();
115
116		/* exiting */
117		watchdog_onoff(0);
118		unlink(pidfile);
119		return (EX_OK);
120	} else {
121		if (passive)
122			timeout |= WD_PASSIVE;
123		else
124			timeout |= WD_ACTIVE;
125		if (watchdog_patpat() < 0)
126			err(EX_OSERR, "patting the dog");
127		return (EX_OK);
128	}
129}
130
131/*
132 * Catch signals and begin shutdown process.
133 */
134static void
135sighandler(int signum)
136{
137
138	if (signum == SIGINT || signum == SIGTERM)
139		end_program = 1;
140}
141
142/*
143 * Locate the OID for the 'debug.watchdog.reset' sysctl setting.
144 * Upon finding it, do an initial reset on the watchdog.
145 */
146static int
147watchdog_init()
148{
149
150	fd = open("/dev/" _PATH_WATCHDOG, O_RDWR);
151	if (fd >= 0)
152		return (0);
153	warn("Could not open watchdog device");
154	return (-1);
155}
156
157/*
158 * Main program loop which is iterated every second.
159 */
160static void
161watchdog_loop(void)
162{
163	struct stat sb;
164	int failed;
165
166	while (end_program == 0) {
167		failed = 0;
168
169		if (test_cmd != NULL)
170			failed = system(test_cmd);
171		else
172			failed = stat("/etc", &sb);
173
174		if (failed == 0)
175			watchdog_patpat();
176		sleep(nap);
177	}
178}
179
180/*
181 * Reset the watchdog timer. This function must be called periodically
182 * to keep the watchdog from firing.
183 */
184int
185watchdog_patpat(void)
186{
187
188	return ioctl(fd, WDIOCPATPAT, &timeout);
189}
190
191/*
192 * Toggle the kernel's watchdog. This routine is used to enable and
193 * disable the watchdog.
194 */
195static int
196watchdog_onoff(int onoff)
197{
198
199	if (onoff)
200		timeout |= WD_ACTIVE;
201	else
202		timeout &= ~WD_ACTIVE;
203	return watchdog_patpat();
204}
205
206/*
207 * Tell user how to use the program.
208 */
209static void
210usage()
211{
212	if (is_daemon)
213		fprintf(stderr, "usage: watchdogd [-d] [-e cmd] [-I file]\n");
214	else
215		fprintf(stderr, "usage: watchdog [-d] [-t]\n");
216	exit(EX_USAGE);
217}
218
219/*
220 * Handle the few command line arguments supported.
221 */
222static void
223parseargs(int argc, char *argv[])
224{
225	int c;
226	char *p;
227	double a;
228
229	c = strlen(argv[0]);
230	if (argv[0][c - 1] == 'd')
231		is_daemon = 1;
232	while ((c = getopt(argc, argv,
233	    is_daemon ? "I:de:s:t:?" : "dt:?")) != -1) {
234		switch (c) {
235		case 'I':
236			pidfile = optarg;
237			break;
238		case 'd':
239			debugging = 1;
240			break;
241		case 'e':
242			test_cmd = strdup(optarg);
243			break;
244#ifdef notyet
245		case 'p':
246			passive = 1;
247			break;
248#endif
249		case 's':
250			p = NULL;
251			errno = 0;
252			nap = strtol(optarg, &p, 0);
253			if ((p != NULL && *p != '\0') || errno != 0)
254				errx(EX_USAGE, "-s argument is not a number");
255			break;
256		case 't':
257			p = NULL;
258			errno = 0;
259			a = strtod(optarg, &p);
260			if ((p != NULL && *p != '\0') || errno != 0)
261				errx(EX_USAGE, "-t argument is not a number");
262			if (a < 0)
263				errx(EX_USAGE, "-t argument must be positive");
264			if (a == 0)
265				timeout = WD_TO_NEVER;
266			else
267				timeout = 1.0 + log(a * 1e9) / log(2.0);
268			if (debugging)
269				printf("Timeout is 2^%d nanoseconds\n",
270				    timeout);
271			break;
272		case '?':
273		default:
274			usage();
275			/* NOTREACHED */
276		}
277	}
278	if (is_daemon && timeout < WD_TO_1SEC)
279		errx(EX_USAGE, "-t argument is less than one second.");
280}
281