1210300Sdelphij/*-
2128898Ssmkelly * Copyright (c) 2003-2004  Sean M. Kelly <smkelly@FreeBSD.org>
3116874Ssmkelly * All rights reserved.
4116874Ssmkelly *
5116874Ssmkelly * Redistribution and use in source and binary forms, with or without
6116874Ssmkelly * modification, are permitted provided that the following conditions
7116874Ssmkelly * are met:
8116874Ssmkelly * 1. Redistributions of source code must retain the above copyright
9116874Ssmkelly *    notice, this list of conditions and the following disclaimer.
10116874Ssmkelly * 2. Redistributions in binary form must reproduce the above copyright
11116874Ssmkelly *    notice, this list of conditions and the following disclaimer in the
12116874Ssmkelly *    documentation and/or other materials provided with the distribution.
13116874Ssmkelly *
14116874Ssmkelly * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15116874Ssmkelly * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16116874Ssmkelly * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17116874Ssmkelly * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18116874Ssmkelly * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19116874Ssmkelly * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20116874Ssmkelly * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21116874Ssmkelly * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22116874Ssmkelly * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23116874Ssmkelly * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24116874Ssmkelly * SUCH DAMAGE.
25116874Ssmkelly */
26116874Ssmkelly
27116874Ssmkelly/*
28116874Ssmkelly * Software watchdog daemon.
29116874Ssmkelly */
30116874Ssmkelly
31117185Ssmkelly#include <sys/types.h>
32116874Ssmkelly__FBSDID("$FreeBSD$");
33116874Ssmkelly
34213181Semaste#include <sys/mman.h>
35149434Spjd#include <sys/param.h>
36117185Ssmkelly#include <sys/rtprio.h>
37117185Ssmkelly#include <sys/stat.h>
38116874Ssmkelly#include <sys/time.h>
39126383Sphk#include <sys/watchdog.h>
40116874Ssmkelly
41116874Ssmkelly#include <err.h>
42126383Sphk#include <errno.h>
43126383Sphk#include <fcntl.h>
44149434Spjd#include <libutil.h>
45126383Sphk#include <math.h>
46116874Ssmkelly#include <paths.h>
47117185Ssmkelly#include <signal.h>
48116874Ssmkelly#include <stdio.h>
49116874Ssmkelly#include <stdlib.h>
50126383Sphk#include <string.h>
51243820Sdelphij#include <strings.h>
52116874Ssmkelly#include <sysexits.h>
53116874Ssmkelly#include <unistd.h>
54116874Ssmkelly
55116874Ssmkellystatic void	parseargs(int, char *[]);
56116874Ssmkellystatic void	sighandler(int);
57116874Ssmkellystatic void	watchdog_loop(void);
58116874Ssmkellystatic int	watchdog_init(void);
59116874Ssmkellystatic int	watchdog_onoff(int onoff);
60165263Sn_hibmastatic int	watchdog_patpat(u_int timeout);
61116874Ssmkellystatic void	usage(void);
62116874Ssmkelly
63210300Sdelphijstatic int debugging = 0;
64210300Sdelphijstatic int end_program = 0;
65210300Sdelphijstatic const char *pidfile = _PATH_VARRUN "watchdogd.pid";
66210300Sdelphijstatic u_int timeout = WD_TO_16SEC;
67210300Sdelphijstatic u_int passive = 0;
68210300Sdelphijstatic int is_daemon = 0;
69210300Sdelphijstatic int fd = -1;
70210300Sdelphijstatic int nap = 1;
71210300Sdelphijstatic char *test_cmd = NULL;
72116874Ssmkelly
73116874Ssmkelly/*
74128705Ssmkelly * Periodically pat the watchdog, preventing it from firing.
75116874Ssmkelly */
76116874Ssmkellyint
77116874Ssmkellymain(int argc, char *argv[])
78116874Ssmkelly{
79116874Ssmkelly	struct rtprio rtp;
80149434Spjd	struct pidfh *pfh;
81149434Spjd	pid_t otherpid;
82116874Ssmkelly
83116874Ssmkelly	if (getuid() != 0)
84116874Ssmkelly		errx(EX_SOFTWARE, "not super user");
85116874Ssmkelly
86116874Ssmkelly	parseargs(argc, argv);
87116874Ssmkelly
88116874Ssmkelly	rtp.type = RTP_PRIO_REALTIME;
89116874Ssmkelly	rtp.prio = 0;
90116874Ssmkelly	if (rtprio(RTP_SET, 0, &rtp) == -1)
91116874Ssmkelly		err(EX_OSERR, "rtprio");
92116874Ssmkelly
93116874Ssmkelly	if (watchdog_init() == -1)
94117185Ssmkelly		errx(EX_SOFTWARE, "unable to initialize watchdog");
95116874Ssmkelly
96126383Sphk	if (is_daemon) {
97126383Sphk		if (watchdog_onoff(1) == -1)
98200778Sru			err(EX_OSERR, "patting the dog");
99116874Ssmkelly
100150214Spjd		pfh = pidfile_open(pidfile, 0600, &otherpid);
101149434Spjd		if (pfh == NULL) {
102149434Spjd			if (errno == EEXIST) {
103149434Spjd				errx(EX_SOFTWARE, "%s already running, pid: %d",
104149434Spjd				    getprogname(), otherpid);
105149434Spjd			}
106149434Spjd			warn("Cannot open or create pidfile");
107149434Spjd		}
108149434Spjd
109126383Sphk		if (debugging == 0 && daemon(0, 0) == -1) {
110126383Sphk			watchdog_onoff(0);
111149434Spjd			pidfile_remove(pfh);
112126383Sphk			err(EX_OSERR, "daemon");
113126383Sphk		}
114116874Ssmkelly
115126383Sphk		signal(SIGHUP, SIG_IGN);
116126383Sphk		signal(SIGINT, sighandler);
117126383Sphk		signal(SIGTERM, sighandler);
118116874Ssmkelly
119149434Spjd		pidfile_write(pfh);
120213181Semaste		if (madvise(0, 0, MADV_PROTECT) != 0)
121213181Semaste			warn("madvise failed");
122240242Szont		if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0)
123240242Szont			warn("mlockall failed");
124116874Ssmkelly
125126383Sphk		watchdog_loop();
126116874Ssmkelly
127126383Sphk		/* exiting */
128149434Spjd		pidfile_remove(pfh);
129126383Sphk		return (EX_OK);
130126383Sphk	} else {
131126383Sphk		if (passive)
132126383Sphk			timeout |= WD_PASSIVE;
133126383Sphk		else
134126383Sphk			timeout |= WD_ACTIVE;
135165263Sn_hibma		if (watchdog_patpat(timeout) < 0)
136126383Sphk			err(EX_OSERR, "patting the dog");
137126383Sphk		return (EX_OK);
138126383Sphk	}
139116874Ssmkelly}
140116874Ssmkelly
141116874Ssmkelly/*
142116874Ssmkelly * Catch signals and begin shutdown process.
143116874Ssmkelly */
144116874Ssmkellystatic void
145116874Ssmkellysighandler(int signum)
146116874Ssmkelly{
147116874Ssmkelly
148116874Ssmkelly	if (signum == SIGINT || signum == SIGTERM)
149116874Ssmkelly		end_program = 1;
150116874Ssmkelly}
151116874Ssmkelly
152116874Ssmkelly/*
153128705Ssmkelly * Open the watchdog device.
154116874Ssmkelly */
155116874Ssmkellystatic int
156201227Sedwatchdog_init(void)
157116874Ssmkelly{
158116874Ssmkelly
159126383Sphk	fd = open("/dev/" _PATH_WATCHDOG, O_RDWR);
160126383Sphk	if (fd >= 0)
161126383Sphk		return (0);
162126383Sphk	warn("Could not open watchdog device");
163126383Sphk	return (-1);
164116874Ssmkelly}
165116874Ssmkelly
166116874Ssmkelly/*
167116874Ssmkelly * Main program loop which is iterated every second.
168116874Ssmkelly */
169116874Ssmkellystatic void
170116874Ssmkellywatchdog_loop(void)
171116874Ssmkelly{
172116874Ssmkelly	struct stat sb;
173116874Ssmkelly	int failed;
174116874Ssmkelly
175165263Sn_hibma	while (end_program != 2) {
176116874Ssmkelly		failed = 0;
177116874Ssmkelly
178126383Sphk		if (test_cmd != NULL)
179126383Sphk			failed = system(test_cmd);
180126383Sphk		else
181126383Sphk			failed = stat("/etc", &sb);
182116874Ssmkelly
183116874Ssmkelly		if (failed == 0)
184165263Sn_hibma			watchdog_patpat(timeout|WD_ACTIVE);
185126383Sphk		sleep(nap);
186165263Sn_hibma
187165263Sn_hibma		if (end_program != 0) {
188165263Sn_hibma			if (watchdog_onoff(0) == 0) {
189165263Sn_hibma				end_program = 2;
190165263Sn_hibma			} else {
191165263Sn_hibma				warnx("Could not stop the watchdog, not exitting");
192165263Sn_hibma				end_program = 0;
193165263Sn_hibma			}
194165263Sn_hibma		}
195116874Ssmkelly	}
196116874Ssmkelly}
197116874Ssmkelly
198116874Ssmkelly/*
199116874Ssmkelly * Reset the watchdog timer. This function must be called periodically
200116874Ssmkelly * to keep the watchdog from firing.
201116874Ssmkelly */
202210300Sdelphijstatic int
203165263Sn_hibmawatchdog_patpat(u_int t)
204116874Ssmkelly{
205116874Ssmkelly
206165263Sn_hibma	return ioctl(fd, WDIOCPATPAT, &t);
207116874Ssmkelly}
208116874Ssmkelly
209116874Ssmkelly/*
210116874Ssmkelly * Toggle the kernel's watchdog. This routine is used to enable and
211116874Ssmkelly * disable the watchdog.
212116874Ssmkelly */
213116874Ssmkellystatic int
214116874Ssmkellywatchdog_onoff(int onoff)
215116874Ssmkelly{
216116874Ssmkelly
217126383Sphk	if (onoff)
218165263Sn_hibma		return watchdog_patpat((timeout|WD_ACTIVE));
219126383Sphk	else
220165263Sn_hibma		return watchdog_patpat(0);
221116874Ssmkelly}
222116874Ssmkelly
223116874Ssmkelly/*
224116874Ssmkelly * Tell user how to use the program.
225116874Ssmkelly */
226116874Ssmkellystatic void
227201227Sedusage(void)
228116874Ssmkelly{
229126383Sphk	if (is_daemon)
230165263Sn_hibma		fprintf(stderr, "usage: watchdogd [-d] [-e cmd] [-I file] [-s sleep] [-t timeout]\n");
231126383Sphk	else
232156334Sphk		fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n");
233116874Ssmkelly	exit(EX_USAGE);
234116874Ssmkelly}
235116874Ssmkelly
236116874Ssmkelly/*
237116874Ssmkelly * Handle the few command line arguments supported.
238116874Ssmkelly */
239116874Ssmkellystatic void
240116874Ssmkellyparseargs(int argc, char *argv[])
241116874Ssmkelly{
242116874Ssmkelly	int c;
243126383Sphk	char *p;
244126383Sphk	double a;
245116874Ssmkelly
246126383Sphk	c = strlen(argv[0]);
247126383Sphk	if (argv[0][c - 1] == 'd')
248126383Sphk		is_daemon = 1;
249126383Sphk	while ((c = getopt(argc, argv,
250126383Sphk	    is_daemon ? "I:de:s:t:?" : "dt:?")) != -1) {
251116874Ssmkelly		switch (c) {
252116874Ssmkelly		case 'I':
253116874Ssmkelly			pidfile = optarg;
254116874Ssmkelly			break;
255116874Ssmkelly		case 'd':
256116874Ssmkelly			debugging = 1;
257116874Ssmkelly			break;
258126383Sphk		case 'e':
259126383Sphk			test_cmd = strdup(optarg);
260126383Sphk			break;
261126383Sphk#ifdef notyet
262126383Sphk		case 'p':
263126383Sphk			passive = 1;
264126383Sphk			break;
265126383Sphk#endif
266126383Sphk		case 's':
267126383Sphk			p = NULL;
268126383Sphk			errno = 0;
269126383Sphk			nap = strtol(optarg, &p, 0);
270126383Sphk			if ((p != NULL && *p != '\0') || errno != 0)
271126383Sphk				errx(EX_USAGE, "-s argument is not a number");
272126383Sphk			break;
273126383Sphk		case 't':
274126383Sphk			p = NULL;
275126383Sphk			errno = 0;
276126383Sphk			a = strtod(optarg, &p);
277126383Sphk			if ((p != NULL && *p != '\0') || errno != 0)
278126383Sphk				errx(EX_USAGE, "-t argument is not a number");
279126383Sphk			if (a < 0)
280126383Sphk				errx(EX_USAGE, "-t argument must be positive");
281126383Sphk			if (a == 0)
282126383Sphk				timeout = WD_TO_NEVER;
283126383Sphk			else
284243820Sdelphij				timeout = flsll(a * 1e9);
285126383Sphk			if (debugging)
286126383Sphk				printf("Timeout is 2^%d nanoseconds\n",
287126383Sphk				    timeout);
288126383Sphk			break;
289116874Ssmkelly		case '?':
290116874Ssmkelly		default:
291116874Ssmkelly			usage();
292116874Ssmkelly			/* NOTREACHED */
293116874Ssmkelly		}
294116874Ssmkelly	}
295150747Sphk	if (argc != optind)
296150747Sphk		errx(EX_USAGE, "extra arguments.");
297126383Sphk	if (is_daemon && timeout < WD_TO_1SEC)
298126383Sphk		errx(EX_USAGE, "-t argument is less than one second.");
299116874Ssmkelly}
300