1210300Sdelphij/*-
2128898Ssmkelly * Copyright (c) 2003-2004  Sean M. Kelly <smkelly@FreeBSD.org>
3247405Salfred * Copyright (c) 2013 iXsystems.com,
4247405Salfred *                    author: Alfred Perlstein <alfred@freebsd.org>
5247405Salfred *
6116874Ssmkelly * All rights reserved.
7116874Ssmkelly *
8116874Ssmkelly * Redistribution and use in source and binary forms, with or without
9116874Ssmkelly * modification, are permitted provided that the following conditions
10116874Ssmkelly * are met:
11116874Ssmkelly * 1. Redistributions of source code must retain the above copyright
12116874Ssmkelly *    notice, this list of conditions and the following disclaimer.
13116874Ssmkelly * 2. Redistributions in binary form must reproduce the above copyright
14116874Ssmkelly *    notice, this list of conditions and the following disclaimer in the
15116874Ssmkelly *    documentation and/or other materials provided with the distribution.
16116874Ssmkelly *
17116874Ssmkelly * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18116874Ssmkelly * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19116874Ssmkelly * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20116874Ssmkelly * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21116874Ssmkelly * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22116874Ssmkelly * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23116874Ssmkelly * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24116874Ssmkelly * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25116874Ssmkelly * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26116874Ssmkelly * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27116874Ssmkelly * SUCH DAMAGE.
28116874Ssmkelly */
29116874Ssmkelly
30116874Ssmkelly/*
31116874Ssmkelly * Software watchdog daemon.
32116874Ssmkelly */
33116874Ssmkelly
34117185Ssmkelly#include <sys/types.h>
35116874Ssmkelly__FBSDID("$FreeBSD: releng/11.0/usr.sbin/watchdogd/watchdogd.c 286947 2015-08-19 21:46:12Z ian $");
36116874Ssmkelly
37213181Semaste#include <sys/mman.h>
38149434Spjd#include <sys/param.h>
39117185Ssmkelly#include <sys/rtprio.h>
40117185Ssmkelly#include <sys/stat.h>
41116874Ssmkelly#include <sys/time.h>
42253719Salfred#include <sys/sysctl.h>
43126383Sphk#include <sys/watchdog.h>
44116874Ssmkelly
45116874Ssmkelly#include <err.h>
46126383Sphk#include <errno.h>
47126383Sphk#include <fcntl.h>
48149434Spjd#include <libutil.h>
49126383Sphk#include <math.h>
50116874Ssmkelly#include <paths.h>
51117185Ssmkelly#include <signal.h>
52116874Ssmkelly#include <stdio.h>
53253744Sian#include <stdint.h>
54116874Ssmkelly#include <stdlib.h>
55126383Sphk#include <string.h>
56242519Sdelphij#include <strings.h>
57116874Ssmkelly#include <sysexits.h>
58247405Salfred#include <syslog.h>
59116874Ssmkelly#include <unistd.h>
60116874Ssmkelly
61247405Salfred#include <getopt.h>
62247405Salfred
63254173Salfredstatic long	fetchtimeout(int opt,
64254173Salfred    const char *longopt, const char *myoptarg, int zero_ok);
65116874Ssmkellystatic void	parseargs(int, char *[]);
66253719Salfredstatic int	seconds_to_pow2ns(int);
67116874Ssmkellystatic void	sighandler(int);
68116874Ssmkellystatic void	watchdog_loop(void);
69116874Ssmkellystatic int	watchdog_init(void);
70116874Ssmkellystatic int	watchdog_onoff(int onoff);
71165263Sn_hibmastatic int	watchdog_patpat(u_int timeout);
72116874Ssmkellystatic void	usage(void);
73253719Salfredstatic int	tstotv(struct timeval *tv, struct timespec *ts);
74253719Salfredstatic int	tvtohz(struct timeval *tv);
75116874Ssmkelly
76210300Sdelphijstatic int debugging = 0;
77210300Sdelphijstatic int end_program = 0;
78210300Sdelphijstatic const char *pidfile = _PATH_VARRUN "watchdogd.pid";
79247405Salfredstatic u_int timeout = WD_TO_128SEC;
80286947Sianstatic u_int exit_timeout = WD_TO_NEVER;
81247405Salfredstatic u_int pretimeout = 0;
82253719Salfredstatic u_int timeout_sec;
83210300Sdelphijstatic u_int passive = 0;
84210300Sdelphijstatic int is_daemon = 0;
85247405Salfredstatic int is_dry_run = 0;  /* do not arm the watchdog, only
86247405Salfred			       report on timing of the watch
87247405Salfred			       program */
88247405Salfredstatic int do_timedog = 0;
89248744Smarkjstatic int do_syslog = 1;
90210300Sdelphijstatic int fd = -1;
91274583Sdelphijstatic int nap = 10;
92247405Salfredstatic int carp_thresh_seconds = -1;
93210300Sdelphijstatic char *test_cmd = NULL;
94116874Ssmkelly
95247405Salfredstatic const char *getopt_shortopts;
96247405Salfred
97247405Salfredstatic int pretimeout_set;
98247405Salfredstatic int pretimeout_act;
99247405Salfredstatic int pretimeout_act_set;
100247405Salfred
101247405Salfredstatic int softtimeout_set;
102247405Salfredstatic int softtimeout_act;
103247405Salfredstatic int softtimeout_act_set;
104247405Salfred
105247405Salfredstatic struct option longopts[] = {
106247405Salfred	{ "debug", no_argument, &debugging, 1 },
107247405Salfred	{ "pretimeout", required_argument, &pretimeout_set, 1 },
108247405Salfred	{ "pretimeout-action", required_argument, &pretimeout_act_set, 1 },
109247405Salfred	{ "softtimeout", no_argument, &softtimeout_set, 1 },
110247405Salfred	{ "softtimeout-action", required_argument, &softtimeout_act_set, 1 },
111247405Salfred	{ NULL, 0, NULL, 0}
112247405Salfred};
113247405Salfred
114116874Ssmkelly/*
115245949Sian * Ask malloc() to map minimum-sized chunks of virtual address space at a time,
116245949Sian * so that mlockall() won't needlessly wire megabytes of unused memory into the
117245949Sian * process.  This must be done using the malloc_conf string so that it gets set
118245949Sian * up before the first allocation, which happens before entry to main().
119245949Sian */
120245949Sianconst char * malloc_conf = "lg_chunk:0";
121245949Sian
122245949Sian/*
123128705Ssmkelly * Periodically pat the watchdog, preventing it from firing.
124116874Ssmkelly */
125116874Ssmkellyint
126116874Ssmkellymain(int argc, char *argv[])
127116874Ssmkelly{
128116874Ssmkelly	struct rtprio rtp;
129149434Spjd	struct pidfh *pfh;
130149434Spjd	pid_t otherpid;
131116874Ssmkelly
132116874Ssmkelly	if (getuid() != 0)
133116874Ssmkelly		errx(EX_SOFTWARE, "not super user");
134116874Ssmkelly
135116874Ssmkelly	parseargs(argc, argv);
136116874Ssmkelly
137248744Smarkj	if (do_syslog)
138247405Salfred		openlog("watchdogd", LOG_CONS|LOG_NDELAY|LOG_PERROR,
139247405Salfred		    LOG_DAEMON);
140247405Salfred
141116874Ssmkelly	rtp.type = RTP_PRIO_REALTIME;
142116874Ssmkelly	rtp.prio = 0;
143116874Ssmkelly	if (rtprio(RTP_SET, 0, &rtp) == -1)
144116874Ssmkelly		err(EX_OSERR, "rtprio");
145116874Ssmkelly
146247405Salfred	if (!is_dry_run && watchdog_init() == -1)
147117185Ssmkelly		errx(EX_SOFTWARE, "unable to initialize watchdog");
148116874Ssmkelly
149126383Sphk	if (is_daemon) {
150126383Sphk		if (watchdog_onoff(1) == -1)
151200778Sru			err(EX_OSERR, "patting the dog");
152116874Ssmkelly
153150214Spjd		pfh = pidfile_open(pidfile, 0600, &otherpid);
154149434Spjd		if (pfh == NULL) {
155149434Spjd			if (errno == EEXIST) {
156247405Salfred				watchdog_onoff(0);
157149434Spjd				errx(EX_SOFTWARE, "%s already running, pid: %d",
158149434Spjd				    getprogname(), otherpid);
159149434Spjd			}
160149434Spjd			warn("Cannot open or create pidfile");
161149434Spjd		}
162149434Spjd
163126383Sphk		if (debugging == 0 && daemon(0, 0) == -1) {
164126383Sphk			watchdog_onoff(0);
165149434Spjd			pidfile_remove(pfh);
166126383Sphk			err(EX_OSERR, "daemon");
167126383Sphk		}
168116874Ssmkelly
169126383Sphk		signal(SIGHUP, SIG_IGN);
170126383Sphk		signal(SIGINT, sighandler);
171126383Sphk		signal(SIGTERM, sighandler);
172116874Ssmkelly
173149434Spjd		pidfile_write(pfh);
174213181Semaste		if (madvise(0, 0, MADV_PROTECT) != 0)
175213181Semaste			warn("madvise failed");
176239896Szont		if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0)
177239769Szont			warn("mlockall failed");
178116874Ssmkelly
179126383Sphk		watchdog_loop();
180116874Ssmkelly
181126383Sphk		/* exiting */
182149434Spjd		pidfile_remove(pfh);
183126383Sphk		return (EX_OK);
184126383Sphk	} else {
185126383Sphk		if (passive)
186126383Sphk			timeout |= WD_PASSIVE;
187126383Sphk		else
188126383Sphk			timeout |= WD_ACTIVE;
189165263Sn_hibma		if (watchdog_patpat(timeout) < 0)
190126383Sphk			err(EX_OSERR, "patting the dog");
191126383Sphk		return (EX_OK);
192126383Sphk	}
193116874Ssmkelly}
194116874Ssmkelly
195253719Salfredstatic void
196253719Salfredpow2ns_to_ts(int pow2ns, struct timespec *ts)
197253719Salfred{
198253719Salfred	uint64_t ns;
199253719Salfred
200253719Salfred	ns = 1ULL << pow2ns;
201253719Salfred	ts->tv_sec = ns / 1000000000ULL;
202253719Salfred	ts->tv_nsec = ns % 1000000000ULL;
203253719Salfred}
204253719Salfred
205116874Ssmkelly/*
206253719Salfred * Convert a timeout in seconds to N where 2^N nanoseconds is close to
207253719Salfred * "seconds".
208253719Salfred *
209253719Salfred * The kernel expects the timeouts for watchdogs in "2^N nanosecond format".
210253719Salfred */
211253719Salfredstatic u_int
212253719Salfredparse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg)
213253719Salfred{
214253719Salfred	double a;
215253719Salfred	u_int rv;
216253719Salfred	struct timespec ts;
217253719Salfred	struct timeval tv;
218253719Salfred	int ticks;
219253719Salfred	char shortopt[] = "- ";
220253719Salfred
221253719Salfred	if (!longopt)
222253719Salfred		shortopt[1] = opt;
223253719Salfred
224254173Salfred	a = fetchtimeout(opt, longopt, myoptarg, 1);
225253719Salfred
226253719Salfred	if (a == 0)
227253719Salfred		rv = WD_TO_NEVER;
228253719Salfred	else
229253719Salfred		rv = seconds_to_pow2ns(a);
230253719Salfred	pow2ns_to_ts(rv, &ts);
231253719Salfred	tstotv(&tv, &ts);
232253719Salfred	ticks = tvtohz(&tv);
233253719Salfred	if (debugging) {
234253719Salfred		printf("Timeout for %s%s "
235253719Salfred		    "is 2^%d nanoseconds "
236253744Sian		    "(in: %s sec -> out: %jd sec %ld ns -> %d ticks)\n",
237253719Salfred		    longopt ? "-" : "", longopt ? longopt : shortopt,
238253719Salfred		    rv,
239253744Sian		    myoptarg, (intmax_t)ts.tv_sec, ts.tv_nsec, ticks);
240253719Salfred	}
241253719Salfred	if (ticks <= 0) {
242253719Salfred		errx(1, "Timeout for %s%s is too small, please choose a higher timeout.", longopt ? "-" : "", longopt ? longopt : shortopt);
243253719Salfred	}
244253719Salfred
245253719Salfred	return (rv);
246253719Salfred}
247253719Salfred
248253719Salfred/*
249116874Ssmkelly * Catch signals and begin shutdown process.
250116874Ssmkelly */
251116874Ssmkellystatic void
252116874Ssmkellysighandler(int signum)
253116874Ssmkelly{
254116874Ssmkelly
255116874Ssmkelly	if (signum == SIGINT || signum == SIGTERM)
256116874Ssmkelly		end_program = 1;
257116874Ssmkelly}
258116874Ssmkelly
259116874Ssmkelly/*
260128705Ssmkelly * Open the watchdog device.
261116874Ssmkelly */
262116874Ssmkellystatic int
263201227Sedwatchdog_init(void)
264116874Ssmkelly{
265116874Ssmkelly
266247405Salfred	if (is_dry_run)
267247405Salfred		return 0;
268247405Salfred
269126383Sphk	fd = open("/dev/" _PATH_WATCHDOG, O_RDWR);
270126383Sphk	if (fd >= 0)
271126383Sphk		return (0);
272126383Sphk	warn("Could not open watchdog device");
273126383Sphk	return (-1);
274116874Ssmkelly}
275116874Ssmkelly
276116874Ssmkelly/*
277247405Salfred * If we are doing timing, then get the time.
278247405Salfred */
279247405Salfredstatic int
280247405Salfredwatchdog_getuptime(struct timespec *tp)
281247405Salfred{
282247405Salfred	int error;
283247405Salfred
284247405Salfred	if (!do_timedog)
285247405Salfred		return 0;
286247405Salfred
287247405Salfred	error = clock_gettime(CLOCK_UPTIME_FAST, tp);
288247405Salfred	if (error)
289247405Salfred		warn("clock_gettime");
290247405Salfred	return (error);
291247405Salfred}
292247405Salfred
293247405Salfredstatic long
294247405Salfredwatchdog_check_dogfunction_time(struct timespec *tp_start,
295247405Salfred    struct timespec *tp_end)
296247405Salfred{
297248744Smarkj	struct timeval tv_start, tv_end, tv_now, tv;
298247405Salfred	const char *cmd_prefix, *cmd;
299248744Smarkj	struct timespec tp_now;
300247405Salfred	int sec;
301247405Salfred
302247405Salfred	if (!do_timedog)
303247405Salfred		return (0);
304247405Salfred
305247405Salfred	TIMESPEC_TO_TIMEVAL(&tv_start, tp_start);
306247405Salfred	TIMESPEC_TO_TIMEVAL(&tv_end, tp_end);
307247405Salfred	timersub(&tv_end, &tv_start, &tv);
308247405Salfred	sec = tv.tv_sec;
309247405Salfred	if (sec < carp_thresh_seconds)
310247405Salfred		return (sec);
311247405Salfred
312247405Salfred	if (test_cmd) {
313247405Salfred		cmd_prefix = "Watchdog program";
314247405Salfred		cmd = test_cmd;
315247405Salfred	} else {
316247405Salfred		cmd_prefix = "Watchdog operation";
317247405Salfred		cmd = "stat(\"/etc\", &sb)";
318247405Salfred	}
319247405Salfred	if (do_syslog)
320247405Salfred		syslog(LOG_CRIT, "%s: '%s' took too long: "
321248744Smarkj		    "%d.%06ld seconds >= %d seconds threshold",
322247405Salfred		    cmd_prefix, cmd, sec, (long)tv.tv_usec,
323247405Salfred		    carp_thresh_seconds);
324248744Smarkj	else
325248744Smarkj		warnx("%s: '%s' took too long: "
326248744Smarkj		    "%d.%06ld seconds >= %d seconds threshold",
327248744Smarkj		    cmd_prefix, cmd, sec, (long)tv.tv_usec,
328248744Smarkj		    carp_thresh_seconds);
329248744Smarkj
330248744Smarkj	/*
331248744Smarkj	 * Adjust the sleep interval again in case syslog(3) took a non-trivial
332248744Smarkj	 * amount of time to run.
333248744Smarkj	 */
334248744Smarkj	if (watchdog_getuptime(&tp_now))
335248744Smarkj		return (sec);
336248744Smarkj	TIMESPEC_TO_TIMEVAL(&tv_now, &tp_now);
337248744Smarkj	timersub(&tv_now, &tv_start, &tv);
338248744Smarkj	sec = tv.tv_sec;
339248744Smarkj
340247405Salfred	return (sec);
341247405Salfred}
342247405Salfred
343247405Salfred/*
344116874Ssmkelly * Main program loop which is iterated every second.
345116874Ssmkelly */
346116874Ssmkellystatic void
347116874Ssmkellywatchdog_loop(void)
348116874Ssmkelly{
349247405Salfred	struct timespec ts_start, ts_end;
350116874Ssmkelly	struct stat sb;
351247405Salfred	long waited;
352247405Salfred	int error, failed;
353116874Ssmkelly
354165263Sn_hibma	while (end_program != 2) {
355116874Ssmkelly		failed = 0;
356116874Ssmkelly
357247405Salfred		error = watchdog_getuptime(&ts_start);
358247405Salfred		if (error) {
359247405Salfred			end_program = 1;
360247405Salfred			goto try_end;
361247405Salfred		}
362247405Salfred
363126383Sphk		if (test_cmd != NULL)
364126383Sphk			failed = system(test_cmd);
365126383Sphk		else
366126383Sphk			failed = stat("/etc", &sb);
367116874Ssmkelly
368247405Salfred		error = watchdog_getuptime(&ts_end);
369247405Salfred		if (error) {
370247405Salfred			end_program = 1;
371247405Salfred			goto try_end;
372247405Salfred		}
373247405Salfred
374116874Ssmkelly		if (failed == 0)
375165263Sn_hibma			watchdog_patpat(timeout|WD_ACTIVE);
376248744Smarkj
377248744Smarkj		waited = watchdog_check_dogfunction_time(&ts_start, &ts_end);
378247405Salfred		if (nap - waited > 0)
379247405Salfred			sleep(nap - waited);
380165263Sn_hibma
381247405Salfredtry_end:
382165263Sn_hibma		if (end_program != 0) {
383165263Sn_hibma			if (watchdog_onoff(0) == 0) {
384165263Sn_hibma				end_program = 2;
385165263Sn_hibma			} else {
386245951Sian				warnx("Could not stop the watchdog, not exiting");
387165263Sn_hibma				end_program = 0;
388165263Sn_hibma			}
389165263Sn_hibma		}
390116874Ssmkelly	}
391116874Ssmkelly}
392116874Ssmkelly
393116874Ssmkelly/*
394116874Ssmkelly * Reset the watchdog timer. This function must be called periodically
395116874Ssmkelly * to keep the watchdog from firing.
396116874Ssmkelly */
397210300Sdelphijstatic int
398165263Sn_hibmawatchdog_patpat(u_int t)
399116874Ssmkelly{
400116874Ssmkelly
401247405Salfred	if (is_dry_run)
402247405Salfred		return 0;
403247405Salfred
404165263Sn_hibma	return ioctl(fd, WDIOCPATPAT, &t);
405116874Ssmkelly}
406116874Ssmkelly
407116874Ssmkelly/*
408116874Ssmkelly * Toggle the kernel's watchdog. This routine is used to enable and
409116874Ssmkelly * disable the watchdog.
410116874Ssmkelly */
411116874Ssmkellystatic int
412116874Ssmkellywatchdog_onoff(int onoff)
413116874Ssmkelly{
414247405Salfred	int error;
415116874Ssmkelly
416247405Salfred	/* fake successful watchdog op if a dry run */
417247405Salfred	if (is_dry_run)
418247405Salfred		return 0;
419247405Salfred
420247405Salfred	if (onoff) {
421247405Salfred		/*
422247405Salfred		 * Call the WDIOC_SETSOFT regardless of softtimeout_set
423247405Salfred		 * because we'll need to turn it off if someone had turned
424247405Salfred		 * it on.
425247405Salfred		 */
426247405Salfred		error = ioctl(fd, WDIOC_SETSOFT, &softtimeout_set);
427247405Salfred		if (error) {
428247405Salfred			warn("setting WDIOC_SETSOFT %d", softtimeout_set);
429247405Salfred			return (error);
430247405Salfred		}
431247405Salfred		error = watchdog_patpat((timeout|WD_ACTIVE));
432247405Salfred		if (error) {
433247405Salfred			warn("watchdog_patpat failed");
434247405Salfred			goto failsafe;
435247405Salfred		}
436247405Salfred		if (softtimeout_act_set) {
437247405Salfred			error = ioctl(fd, WDIOC_SETSOFTTIMEOUTACT,
438247405Salfred			    &softtimeout_act);
439247405Salfred			if (error) {
440247405Salfred				warn("setting WDIOC_SETSOFTTIMEOUTACT %d",
441247405Salfred				    softtimeout_act);
442247405Salfred				goto failsafe;
443247405Salfred			}
444247405Salfred		}
445247405Salfred		if (pretimeout_set) {
446247405Salfred			error = ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);
447247405Salfred			if (error) {
448247405Salfred				warn("setting WDIOC_SETPRETIMEOUT %d",
449247405Salfred				    pretimeout);
450247405Salfred				goto failsafe;
451247405Salfred			}
452247405Salfred		}
453247405Salfred		if (pretimeout_act_set) {
454247405Salfred			error = ioctl(fd, WDIOC_SETPRETIMEOUTACT,
455247405Salfred			    &pretimeout_act);
456247405Salfred			if (error) {
457247405Salfred				warn("setting WDIOC_SETPRETIMEOUTACT %d",
458247405Salfred				    pretimeout_act);
459247405Salfred				goto failsafe;
460247405Salfred			}
461247405Salfred		}
462247405Salfred		/* pat one more time for good measure */
463165263Sn_hibma		return watchdog_patpat((timeout|WD_ACTIVE));
464247405Salfred	 } else {
465286947Sian		return watchdog_patpat(exit_timeout);
466247405Salfred	 }
467247405Salfredfailsafe:
468286947Sian	watchdog_patpat(exit_timeout);
469247405Salfred	return (error);
470116874Ssmkelly}
471116874Ssmkelly
472116874Ssmkelly/*
473116874Ssmkelly * Tell user how to use the program.
474116874Ssmkelly */
475116874Ssmkellystatic void
476201227Sedusage(void)
477116874Ssmkelly{
478126383Sphk	if (is_daemon)
479247405Salfred		fprintf(stderr, "usage:\n"
480286947Sian"  watchdogd [-dnSw] [-e cmd] [-I pidfile] [-s sleep] [-t timeout]\n"
481286947Sian"            [-T script_timeout] [-x exit_timeout]\n"
482247405Salfred"            [--debug]\n"
483247405Salfred"            [--pretimeout seconds] [-pretimeout-action action]\n"
484247405Salfred"            [--softtimeout] [-softtimeout-action action]\n"
485247405Salfred);
486126383Sphk	else
487156334Sphk		fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n");
488116874Ssmkelly	exit(EX_USAGE);
489116874Ssmkelly}
490116874Ssmkelly
491247405Salfredstatic long
492254173Salfredfetchtimeout(int opt, const char *longopt, const char *myoptarg, int zero_ok)
493247405Salfred{
494247405Salfred	const char *errstr;
495247405Salfred	char *p;
496247405Salfred	long rv;
497247405Salfred
498247405Salfred	errstr = NULL;
499247405Salfred	p = NULL;
500247405Salfred	errno = 0;
501247405Salfred	rv = strtol(myoptarg, &p, 0);
502247405Salfred	if ((p != NULL && *p != '\0') || errno != 0)
503247405Salfred		errstr = "is not a number";
504254173Salfred	if (rv < 0 || (!zero_ok && rv == 0))
505247405Salfred		errstr = "must be greater than zero";
506247405Salfred	if (errstr) {
507247405Salfred		if (longopt)
508247405Salfred			errx(EX_USAGE, "--%s argument %s", longopt, errstr);
509247405Salfred		else
510247405Salfred			errx(EX_USAGE, "-%c argument %s", opt, errstr);
511247405Salfred	}
512247405Salfred	return (rv);
513247405Salfred}
514247405Salfred
515247405Salfredstruct act_tbl {
516247405Salfred	const char *at_act;
517247405Salfred	int at_value;
518247405Salfred};
519247405Salfred
520249245Sedstatic const struct act_tbl act_tbl[] = {
521247405Salfred	{ "panic", WD_SOFT_PANIC },
522247405Salfred	{ "ddb", WD_SOFT_DDB },
523247405Salfred	{ "log", WD_SOFT_LOG },
524247405Salfred	{ "printf", WD_SOFT_PRINTF },
525247405Salfred	{ NULL, 0 }
526247405Salfred};
527247405Salfred
528247405Salfredstatic void
529247405Salfredtimeout_act_error(const char *lopt, const char *badact)
530247405Salfred{
531247405Salfred	char *opts, *oldopts;
532247405Salfred	int i;
533247405Salfred
534247405Salfred	opts = NULL;
535247405Salfred	for (i = 0; act_tbl[i].at_act != NULL; i++) {
536247405Salfred		oldopts = opts;
537247405Salfred		if (asprintf(&opts, "%s%s%s",
538247405Salfred		    oldopts == NULL ? "" : oldopts,
539247405Salfred		    oldopts == NULL ? "" : ", ",
540247405Salfred		    act_tbl[i].at_act) == -1)
541247405Salfred			err(EX_OSERR, "malloc");
542247405Salfred		free(oldopts);
543247405Salfred	}
544247405Salfred	warnx("bad --%s argument '%s' must be one of (%s).",
545247405Salfred	    lopt, badact, opts);
546247405Salfred	usage();
547247405Salfred}
548247405Salfred
549116874Ssmkelly/*
550247405Salfred * Take a comma separated list of actions and or the flags
551247405Salfred * together for the ioctl.
552247405Salfred */
553247405Salfredstatic int
554247405Salfredtimeout_act_str2int(const char *lopt, const char *acts)
555247405Salfred{
556247405Salfred	int i;
557247405Salfred	char *dupacts, *tofree;
558247405Salfred	char *o;
559247405Salfred	int rv = 0;
560247405Salfred
561247405Salfred	tofree = dupacts = strdup(acts);
562247405Salfred	if (!tofree)
563247405Salfred		err(EX_OSERR, "malloc");
564247405Salfred	while ((o = strsep(&dupacts, ",")) != NULL) {
565247405Salfred		for (i = 0; act_tbl[i].at_act != NULL; i++) {
566247405Salfred			if (!strcmp(o, act_tbl[i].at_act)) {
567247405Salfred				rv |= act_tbl[i].at_value;
568247405Salfred				break;
569247405Salfred			}
570247405Salfred		}
571247405Salfred		if (act_tbl[i].at_act == NULL)
572247405Salfred			timeout_act_error(lopt, o);
573247405Salfred	}
574247405Salfred	free(tofree);
575247405Salfred	return rv;
576247405Salfred}
577247405Salfred
578253719Salfredint
579253719Salfredtstotv(struct timeval *tv, struct timespec *ts)
580253719Salfred{
581253719Salfred
582253719Salfred	tv->tv_sec = ts->tv_sec;
583253719Salfred	tv->tv_usec = ts->tv_nsec / 1000;
584253719Salfred	return 0;
585253719Salfred}
586253719Salfred
587247405Salfred/*
588253719Salfred * Convert a timeval to a number of ticks.
589253719Salfred * Mostly copied from the kernel.
590253719Salfred */
591253719Salfredint
592253719Salfredtvtohz(struct timeval *tv)
593253719Salfred{
594253719Salfred	register unsigned long ticks;
595253719Salfred	register long sec, usec;
596253719Salfred	int hz;
597253719Salfred	size_t hzsize;
598253719Salfred	int error;
599253719Salfred	int tick;
600253719Salfred
601253719Salfred	hzsize = sizeof(hz);
602253719Salfred
603253719Salfred	error = sysctlbyname("kern.hz", &hz, &hzsize, NULL, 0);
604253719Salfred	if (error)
605253719Salfred		err(1, "sysctlbyname kern.hz");
606253719Salfred
607253719Salfred	tick = 1000000 / hz;
608253719Salfred
609253719Salfred	/*
610253719Salfred	 * If the number of usecs in the whole seconds part of the time
611253719Salfred	 * difference fits in a long, then the total number of usecs will
612253719Salfred	 * fit in an unsigned long.  Compute the total and convert it to
613253719Salfred	 * ticks, rounding up and adding 1 to allow for the current tick
614253719Salfred	 * to expire.  Rounding also depends on unsigned long arithmetic
615253719Salfred	 * to avoid overflow.
616253719Salfred	 *
617253719Salfred	 * Otherwise, if the number of ticks in the whole seconds part of
618253719Salfred	 * the time difference fits in a long, then convert the parts to
619253719Salfred	 * ticks separately and add, using similar rounding methods and
620253719Salfred	 * overflow avoidance.  This method would work in the previous
621253719Salfred	 * case but it is slightly slower and assumes that hz is integral.
622253719Salfred	 *
623253719Salfred	 * Otherwise, round the time difference down to the maximum
624253719Salfred	 * representable value.
625253719Salfred	 *
626253719Salfred	 * If ints have 32 bits, then the maximum value for any timeout in
627253719Salfred	 * 10ms ticks is 248 days.
628253719Salfred	 */
629253719Salfred	sec = tv->tv_sec;
630253719Salfred	usec = tv->tv_usec;
631253719Salfred	if (usec < 0) {
632253719Salfred		sec--;
633253719Salfred		usec += 1000000;
634253719Salfred	}
635253719Salfred	if (sec < 0) {
636253719Salfred#ifdef DIAGNOSTIC
637253719Salfred		if (usec > 0) {
638253719Salfred			sec++;
639253719Salfred			usec -= 1000000;
640253719Salfred		}
641253719Salfred		printf("tvotohz: negative time difference %ld sec %ld usec\n",
642253719Salfred		    sec, usec);
643253719Salfred#endif
644253719Salfred		ticks = 1;
645253719Salfred	} else if (sec <= LONG_MAX / 1000000)
646253719Salfred		ticks = (sec * 1000000 + (unsigned long)usec + (tick - 1))
647253719Salfred		    / tick + 1;
648253719Salfred	else if (sec <= LONG_MAX / hz)
649253719Salfred		ticks = sec * hz
650253719Salfred		    + ((unsigned long)usec + (tick - 1)) / tick + 1;
651253719Salfred	else
652253719Salfred		ticks = LONG_MAX;
653253719Salfred	if (ticks > INT_MAX)
654253719Salfred		ticks = INT_MAX;
655253719Salfred	return ((int)ticks);
656253719Salfred}
657253719Salfred
658253719Salfredstatic int
659253719Salfredseconds_to_pow2ns(int seconds)
660253719Salfred{
661253719Salfred	uint64_t power;
662253719Salfred	uint64_t ns;
663253719Salfred	uint64_t shifted;
664253719Salfred
665253719Salfred	if (seconds <= 0)
666253719Salfred		errx(1, "seconds %d < 0", seconds);
667253719Salfred	ns = ((uint64_t)seconds) * 1000000000ULL;
668253719Salfred	power = flsll(ns);
669253719Salfred	shifted = 1ULL << power;
670253719Salfred	if (shifted <= ns) {
671253719Salfred		power++;
672253719Salfred	}
673253719Salfred	if (debugging) {
674253719Salfred		printf("shifted %lld\n", (long long)shifted);
675253719Salfred		printf("seconds_to_pow2ns: seconds: %d, ns %lld, power %d\n",
676253719Salfred		    seconds, (long long)ns, (int)power);
677253719Salfred	}
678253719Salfred	return (power);
679253719Salfred}
680253719Salfred
681253719Salfred
682253719Salfred/*
683116874Ssmkelly * Handle the few command line arguments supported.
684116874Ssmkelly */
685116874Ssmkellystatic void
686116874Ssmkellyparseargs(int argc, char *argv[])
687116874Ssmkelly{
688247405Salfred	int longindex;
689116874Ssmkelly	int c;
690247405Salfred	const char *lopt;
691116874Ssmkelly
692247405Salfred	/*
693247405Salfred	 * if we end with a 'd' aka 'watchdogd' then we are the daemon program,
694247405Salfred	 * otherwise run as a command line utility.
695247405Salfred	 */
696126383Sphk	c = strlen(argv[0]);
697126383Sphk	if (argv[0][c - 1] == 'd')
698126383Sphk		is_daemon = 1;
699247405Salfred
700247405Salfred	if (is_daemon)
701286947Sian		getopt_shortopts = "I:de:ns:t:ST:wx:?";
702247405Salfred	else
703247405Salfred		getopt_shortopts = "dt:?";
704247405Salfred
705247405Salfred	while ((c = getopt_long(argc, argv, getopt_shortopts, longopts,
706247405Salfred		    &longindex)) != -1) {
707116874Ssmkelly		switch (c) {
708116874Ssmkelly		case 'I':
709116874Ssmkelly			pidfile = optarg;
710116874Ssmkelly			break;
711116874Ssmkelly		case 'd':
712116874Ssmkelly			debugging = 1;
713116874Ssmkelly			break;
714126383Sphk		case 'e':
715126383Sphk			test_cmd = strdup(optarg);
716126383Sphk			break;
717247405Salfred		case 'n':
718247405Salfred			is_dry_run = 1;
719247405Salfred			break;
720126383Sphk#ifdef notyet
721126383Sphk		case 'p':
722126383Sphk			passive = 1;
723126383Sphk			break;
724126383Sphk#endif
725126383Sphk		case 's':
726254173Salfred			nap = fetchtimeout(c, NULL, optarg, 0);
727126383Sphk			break;
728247405Salfred		case 'S':
729248744Smarkj			do_syslog = 0;
730247405Salfred			break;
731126383Sphk		case 't':
732253719Salfred			timeout_sec = atoi(optarg);
733253719Salfred			timeout = parse_timeout_to_pow2ns(c, NULL, optarg);
734253719Salfred 			if (debugging)
735253719Salfred 				printf("Timeout is 2^%d nanoseconds\n",
736253719Salfred 				    timeout);
737126383Sphk			break;
738247405Salfred		case 'T':
739254173Salfred			carp_thresh_seconds =
740254173Salfred			    fetchtimeout(c, "NULL", optarg, 0);
741247405Salfred			break;
742247405Salfred		case 'w':
743247405Salfred			do_timedog = 1;
744247405Salfred			break;
745286947Sian		case 'x':
746286947Sian			exit_timeout = parse_timeout_to_pow2ns(c, NULL, optarg);
747286947Sian			if (exit_timeout != 0)
748286947Sian				exit_timeout |= WD_ACTIVE;
749286947Sian			break;
750247405Salfred		case 0:
751247405Salfred			lopt = longopts[longindex].name;
752247405Salfred			if (!strcmp(lopt, "pretimeout")) {
753254173Salfred				pretimeout = fetchtimeout(0, lopt, optarg, 0);
754247405Salfred			} else if (!strcmp(lopt, "pretimeout-action")) {
755247405Salfred				pretimeout_act = timeout_act_str2int(lopt,
756247405Salfred				    optarg);
757247405Salfred			} else if (!strcmp(lopt, "softtimeout-action")) {
758247405Salfred				softtimeout_act = timeout_act_str2int(lopt,
759247405Salfred				    optarg);
760247405Salfred			} else {
761247405Salfred		/*		warnx("bad option at index %d: %s", optind,
762247405Salfred				    argv[optind]);
763247405Salfred				usage();
764247405Salfred				*/
765247405Salfred			}
766247405Salfred			break;
767116874Ssmkelly		case '?':
768116874Ssmkelly		default:
769116874Ssmkelly			usage();
770116874Ssmkelly			/* NOTREACHED */
771116874Ssmkelly		}
772116874Ssmkelly	}
773247405Salfred
774247405Salfred	if (carp_thresh_seconds == -1)
775247405Salfred		carp_thresh_seconds = nap;
776247405Salfred
777150747Sphk	if (argc != optind)
778150747Sphk		errx(EX_USAGE, "extra arguments.");
779126383Sphk	if (is_daemon && timeout < WD_TO_1SEC)
780126383Sphk		errx(EX_USAGE, "-t argument is less than one second.");
781253719Salfred	if (pretimeout_set) {
782253719Salfred		struct timespec ts;
783253719Salfred
784253719Salfred		pow2ns_to_ts(timeout, &ts);
785253808Sjhb		if (pretimeout >= (uintmax_t)ts.tv_sec) {
786253719Salfred			errx(EX_USAGE,
787253719Salfred			    "pretimeout (%d) >= timeout (%d -> %ld)\n"
788253719Salfred			    "see manual section TIMEOUT RESOLUTION",
789253719Salfred			    pretimeout, timeout_sec, (long)ts.tv_sec);
790253719Salfred		}
791253719Salfred	}
792116874Ssmkelly}
793