xref: /openbsd-src/usr.sbin/watchdogd/watchdogd.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: watchdogd.c,v 1.16 2023/03/08 04:43:15 guenther Exp $ */
21469ed6eSmbalmer 
31469ed6eSmbalmer /*
4a9c1aa39Smbalmer  * Copyright (c) 2005 Marc Balmer <mbalmer@openbsd.org>
51469ed6eSmbalmer  *
61469ed6eSmbalmer  * Permission to use, copy, modify, and distribute this software for any
71469ed6eSmbalmer  * purpose with or without fee is hereby granted, provided that the above
81469ed6eSmbalmer  * copyright notice and this permission notice appear in all copies.
91469ed6eSmbalmer  *
101469ed6eSmbalmer  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111469ed6eSmbalmer  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121469ed6eSmbalmer  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131469ed6eSmbalmer  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141469ed6eSmbalmer  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151469ed6eSmbalmer  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161469ed6eSmbalmer  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171469ed6eSmbalmer  */
181469ed6eSmbalmer 
19b9fc9a72Sderaadt #include <sys/types.h>
20991f7995Smillert #include <sys/resource.h>
21b9fc9a72Sderaadt #include <sys/signal.h>
221469ed6eSmbalmer #include <sys/sysctl.h>
231469ed6eSmbalmer #include <sys/mman.h>
241469ed6eSmbalmer 
251469ed6eSmbalmer #include <err.h>
261469ed6eSmbalmer #include <errno.h>
271469ed6eSmbalmer #include <stdio.h>
281469ed6eSmbalmer #include <stdlib.h>
291469ed6eSmbalmer #include <unistd.h>
301469ed6eSmbalmer 
311469ed6eSmbalmer volatile sig_atomic_t	quit = 0;
321469ed6eSmbalmer 
33de70259aShenning __dead void	usage(void);
34de70259aShenning void		sighdlr(int);
35de70259aShenning int		main(int, char *[]);
36de70259aShenning 
371469ed6eSmbalmer __dead void
usage(void)381469ed6eSmbalmer usage(void)
391469ed6eSmbalmer {
401469ed6eSmbalmer 	extern char *__progname;
411469ed6eSmbalmer 
4217046616Smbalmer 	fprintf(stderr, "usage: %s [-dnq] [-i interval] [-p period]\n",
431469ed6eSmbalmer 	    __progname);
441469ed6eSmbalmer 	exit(1);
451469ed6eSmbalmer }
461469ed6eSmbalmer 
471469ed6eSmbalmer void
sighdlr(int signum)481469ed6eSmbalmer sighdlr(int signum)
491469ed6eSmbalmer {
501469ed6eSmbalmer 	quit = 1;
511469ed6eSmbalmer }
521469ed6eSmbalmer 
531469ed6eSmbalmer int
main(int argc,char * argv[])541469ed6eSmbalmer main(int argc, char *argv[])
551469ed6eSmbalmer {
56d6f310deSsthen 	struct rlimit	 rlim;
571469ed6eSmbalmer 	const char	*errstr;
58de70259aShenning 	size_t		 len;
59bd755cd0Smbalmer 	u_int		 interval = 0, period = 30, nperiod;
60c1c14ec1Smbalmer 	int		 ch, trigauto, sauto, speriod;
6125f7cff5Smbalmer 	int		 quiet = 0, daemonize = 1, retval = 1, do_restore = 1;
62de70259aShenning 	int		 mib[3];
631469ed6eSmbalmer 
6417046616Smbalmer 	while ((ch = getopt(argc, argv, "di:np:q")) != -1) {
651469ed6eSmbalmer 		switch (ch) {
661469ed6eSmbalmer 		case 'd':
671469ed6eSmbalmer 			daemonize = 0;
681469ed6eSmbalmer 			break;
691469ed6eSmbalmer 		case 'i':
70bd755cd0Smbalmer 			interval = (u_int)strtonum(optarg, 1LL, 86400LL,
71bd755cd0Smbalmer 			    &errstr);
721469ed6eSmbalmer 			if (errstr)
731469ed6eSmbalmer 				errx(1, "interval is %s: %s", errstr, optarg);
741469ed6eSmbalmer 			break;
7517046616Smbalmer 		case 'n':
7625f7cff5Smbalmer 			do_restore = 0;
7717046616Smbalmer 			break;
781469ed6eSmbalmer 		case 'p':
79bd755cd0Smbalmer 			period = (u_int)strtonum(optarg, 2LL, 86400LL, &errstr);
801469ed6eSmbalmer 			if (errstr)
811469ed6eSmbalmer 				errx(1, "period is %s: %s", errstr, optarg);
821469ed6eSmbalmer 			break;
83a0bc59dfSmbalmer 		case 'q':
84a0bc59dfSmbalmer 			quiet = 1;
85a0bc59dfSmbalmer 			break;
861469ed6eSmbalmer 		default:
871469ed6eSmbalmer 			usage();
881469ed6eSmbalmer 		}
891469ed6eSmbalmer 	}
901469ed6eSmbalmer 
91fde55bc4Spyr 	argc -= optind;
92fde55bc4Spyr 	argv += optind;
93fde55bc4Spyr 	if (argc > 0)
94fde55bc4Spyr 		usage();
95fde55bc4Spyr 
96de70259aShenning 	if (interval == 0 && (interval = period / 3) == 0)
971469ed6eSmbalmer 		interval = 1;
981469ed6eSmbalmer 
991469ed6eSmbalmer 	if (period <= interval)
1001469ed6eSmbalmer 		errx(1, "retrigger interval too long");
1011469ed6eSmbalmer 
1021469ed6eSmbalmer 	/* save kern.watchdog.period and kern.watchdog.auto for restore */
1031469ed6eSmbalmer 	mib[0] = CTL_KERN;
1041469ed6eSmbalmer 	mib[1] = KERN_WATCHDOG;
1051469ed6eSmbalmer 	mib[2] = KERN_WATCHDOG_PERIOD;
1061469ed6eSmbalmer 
1071469ed6eSmbalmer 	len = sizeof(speriod);
1081469ed6eSmbalmer 	if (sysctl(mib, 3, &speriod, &len, &period, sizeof(period)) == -1) {
1091469ed6eSmbalmer 		if (errno == EOPNOTSUPP)
1101469ed6eSmbalmer 			errx(1, "no watchdog timer available");
1111469ed6eSmbalmer 		else
1121469ed6eSmbalmer 			err(1, "can't access kern.watchdog.period");
1131469ed6eSmbalmer 	}
1141469ed6eSmbalmer 
1151469ed6eSmbalmer 	mib[2] = KERN_WATCHDOG_AUTO;
1161469ed6eSmbalmer 	len = sizeof(sauto);
1171469ed6eSmbalmer 	trigauto = 0;
1181469ed6eSmbalmer 
1191469ed6eSmbalmer 	if (sysctl(mib, 3, &sauto, &len, &trigauto, sizeof(trigauto)) == -1)
1201469ed6eSmbalmer 		err(1, "can't access kern.watchdog.auto");
1211469ed6eSmbalmer 
1221469ed6eSmbalmer 	/* Double check the timeout period, some devices change the value */
1231469ed6eSmbalmer 	mib[2] = KERN_WATCHDOG_PERIOD;
1241469ed6eSmbalmer 	len = sizeof(nperiod);
1251469ed6eSmbalmer 	if (sysctl(mib, 3, &nperiod, &len, NULL, 0) == -1) {
1261469ed6eSmbalmer 		warnx("can't read back kern.watchdog.period, "
1271469ed6eSmbalmer 		    "restoring original values");
1281469ed6eSmbalmer 		goto restore;
1291469ed6eSmbalmer 	}
1301469ed6eSmbalmer 
131a0bc59dfSmbalmer 	if (nperiod != period && !quiet)
1321469ed6eSmbalmer 		warnx("period adjusted to %d by device", nperiod);
1331469ed6eSmbalmer 
1341469ed6eSmbalmer 	if (nperiod <= interval) {
1351469ed6eSmbalmer 		warnx("retrigger interval %d too long, "
1361469ed6eSmbalmer 		    "restoring original values", interval);
1371469ed6eSmbalmer 		goto restore;
1381469ed6eSmbalmer 	}
1391469ed6eSmbalmer 
1401469ed6eSmbalmer 	if (daemonize && daemon(0, 0)) {
1411469ed6eSmbalmer 		warn("can't daemonize, restoring original values");
1421469ed6eSmbalmer 		goto restore;
1431469ed6eSmbalmer 	}
1441469ed6eSmbalmer 
145d6f310deSsthen 	/*
146d6f310deSsthen 	 * mlockall() below will wire the whole stack up to the limit
147d6f310deSsthen 	 * thus we have to reduce stack size to avoid resource abuse
148d6f310deSsthen 	 */
149d6f310deSsthen 	rlim.rlim_cur = 256 * 1024;
150d6f310deSsthen 	rlim.rlim_max = 256 * 1024;
151d6f310deSsthen 	(void)setrlimit(RLIMIT_STACK, &rlim);
152d6f310deSsthen 
15382846cdaSmickey 	(void)mlockall(MCL_CURRENT | MCL_FUTURE);
154fb815d12Smickey 	setpriority(PRIO_PROCESS, getpid(), -5);
15582846cdaSmickey 
1561469ed6eSmbalmer 	signal(SIGTERM, sighdlr);
1571469ed6eSmbalmer 
1581469ed6eSmbalmer 	retval = 0;
1591469ed6eSmbalmer 	while (!quit) {
1601469ed6eSmbalmer 		if (sysctl(mib, 3, NULL, 0, &period, sizeof(period)) == -1)
161fb815d12Smickey 			quit = retval = 1;
1621469ed6eSmbalmer 		sleep(interval);
1631469ed6eSmbalmer 	}
1641469ed6eSmbalmer 
16525f7cff5Smbalmer 	if (do_restore) {
16617046616Smbalmer restore:	sysctl(mib, 3, NULL, 0, &speriod, sizeof(speriod));
1671469ed6eSmbalmer 		mib[2] = KERN_WATCHDOG_AUTO;
1681469ed6eSmbalmer 		sysctl(mib, 3, NULL, 0, &sauto, sizeof(sauto));
16917046616Smbalmer 	}
1701469ed6eSmbalmer 
171c4c4febbSmbalmer 	return retval;
1721469ed6eSmbalmer }
173