watchdogd.c revision 117185
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 117185 2003-07-03 03:37:04Z smkelly $");
33
34#include <sys/rtprio.h>
35#include <sys/stat.h>
36#include <sys/sysctl.h>
37#include <sys/time.h>
38
39#include <err.h>
40#include <paths.h>
41#include <signal.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <sysexits.h>
45#include <unistd.h>
46
47static void	parseargs(int, char *[]);
48static void	sighandler(int);
49static void	watchdog_loop(void);
50static int	watchdog_init(void);
51static int	watchdog_onoff(int onoff);
52static int	watchdog_tickle(void);
53static void	usage(void);
54
55int debugging = 0;
56int end_program = 0;
57const char *pidfile = _PATH_VARRUN "watchdogd.pid";
58int reset_mib[3];
59size_t reset_miblen = 3;
60
61/*
62 * Periodically write to the debug.watchdog.reset sysctl OID
63 * to keep the software watchdog from firing.
64 */
65int
66main(int argc, char *argv[])
67{
68	struct rtprio rtp;
69	FILE *fp;
70
71	if (getuid() != 0)
72		errx(EX_SOFTWARE, "not super user");
73
74	parseargs(argc, argv);
75
76	rtp.type = RTP_PRIO_REALTIME;
77	rtp.prio = 0;
78	if (rtprio(RTP_SET, 0, &rtp) == -1)
79		err(EX_OSERR, "rtprio");
80
81	if (watchdog_init() == -1)
82		errx(EX_SOFTWARE, "unable to initialize watchdog");
83
84	if (watchdog_onoff(1) == -1)
85		exit(EX_SOFTWARE);
86
87	if (debugging == 0 && daemon(0, 0) == -1) {
88		watchdog_onoff(0);
89		err(EX_OSERR, "daemon");
90	}
91
92	signal(SIGHUP, SIG_IGN);
93	signal(SIGINT, sighandler);
94	signal(SIGTERM, sighandler);
95
96	fp = fopen(pidfile, "w");
97	if (fp != NULL) {
98		fprintf(fp, "%d\n", getpid());
99		fclose(fp);
100	}
101
102	watchdog_loop();
103
104	/* exiting */
105	watchdog_onoff(0);
106	unlink(pidfile);
107	return (EX_OK);
108}
109
110/*
111 * Catch signals and begin shutdown process.
112 */
113static void
114sighandler(int signum)
115{
116
117	if (signum == SIGINT || signum == SIGTERM)
118		end_program = 1;
119}
120
121/*
122 * Locate the OID for the 'debug.watchdog.reset' sysctl setting.
123 * Upon finding it, do an initial reset on the watchdog.
124 */
125static int
126watchdog_init()
127{
128	int error;
129
130	error = sysctlnametomib("debug.watchdog.reset", reset_mib,
131		&reset_miblen);
132	if (error == -1) {
133		warn("could not find reset OID");
134		return (error);
135	}
136	return watchdog_tickle();
137}
138
139/*
140 * Main program loop which is iterated every second.
141 */
142static void
143watchdog_loop(void)
144{
145	struct stat sb;
146	int failed;
147
148	while (end_program == 0) {
149		failed = 0;
150
151		failed = stat("/etc", &sb);
152
153		if (failed == 0)
154			watchdog_tickle();
155		sleep(1);
156	}
157}
158
159/*
160 * Reset the watchdog timer. This function must be called periodically
161 * to keep the watchdog from firing.
162 */
163int
164watchdog_tickle(void)
165{
166
167	return sysctl(reset_mib, reset_miblen, NULL, NULL, NULL, 0);
168}
169
170/*
171 * Toggle the kernel's watchdog. This routine is used to enable and
172 * disable the watchdog.
173 */
174static int
175watchdog_onoff(int onoff)
176{
177	int mib[3];
178	int error;
179	size_t len;
180
181	len = 3;
182
183	error = sysctlnametomib("debug.watchdog.enabled", mib, &len);
184	if (error == 0)
185		error = sysctl(mib, len, NULL, NULL, &onoff, sizeof(onoff));
186
187	if (error == -1) {
188		warn("could not %s watchdog",
189			(onoff > 0) ? "enable" : "disable");
190		return (error);
191	}
192	return (0);
193}
194
195/*
196 * Tell user how to use the program.
197 */
198static void
199usage()
200{
201	fprintf(stderr, "usage: watchdogd [-d] [-I file]\n");
202	exit(EX_USAGE);
203}
204
205/*
206 * Handle the few command line arguments supported.
207 */
208static void
209parseargs(int argc, char *argv[])
210{
211	int c;
212
213	while ((c = getopt(argc, argv, "I:d?")) != -1) {
214		switch (c) {
215		case 'I':
216			pidfile = optarg;
217			break;
218		case 'd':
219			debugging = 1;
220			break;
221		case '?':
222		default:
223			usage();
224			/* NOTREACHED */
225		}
226	}
227}
228