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