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$");
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;
80247405Salfredstatic u_int pretimeout = 0;
81253719Salfredstatic u_int timeout_sec;
82210300Sdelphijstatic u_int passive = 0;
83210300Sdelphijstatic int is_daemon = 0;
84247405Salfredstatic int is_dry_run = 0;  /* do not arm the watchdog, only
85247405Salfred			       report on timing of the watch
86247405Salfred			       program */
87247405Salfredstatic int do_timedog = 0;
88248744Smarkjstatic int do_syslog = 1;
89210300Sdelphijstatic int fd = -1;
90210300Sdelphijstatic int nap = 1;
91247405Salfredstatic int carp_thresh_seconds = -1;
92210300Sdelphijstatic char *test_cmd = NULL;
93116874Ssmkelly
94247405Salfredstatic const char *getopt_shortopts;
95247405Salfred
96247405Salfredstatic int pretimeout_set;
97247405Salfredstatic int pretimeout_act;
98247405Salfredstatic int pretimeout_act_set;
99247405Salfred
100247405Salfredstatic int softtimeout_set;
101247405Salfredstatic int softtimeout_act;
102247405Salfredstatic int softtimeout_act_set;
103247405Salfred
104247405Salfredstatic struct option longopts[] = {
105247405Salfred	{ "debug", no_argument, &debugging, 1 },
106247405Salfred	{ "pretimeout", required_argument, &pretimeout_set, 1 },
107247405Salfred	{ "pretimeout-action", required_argument, &pretimeout_act_set, 1 },
108247405Salfred	{ "softtimeout", no_argument, &softtimeout_set, 1 },
109247405Salfred	{ "softtimeout-action", required_argument, &softtimeout_act_set, 1 },
110247405Salfred	{ NULL, 0, NULL, 0}
111247405Salfred};
112247405Salfred
113116874Ssmkelly/*
114245949Sian * Ask malloc() to map minimum-sized chunks of virtual address space at a time,
115245949Sian * so that mlockall() won't needlessly wire megabytes of unused memory into the
116245949Sian * process.  This must be done using the malloc_conf string so that it gets set
117245949Sian * up before the first allocation, which happens before entry to main().
118245949Sian */
119245949Sianconst char * malloc_conf = "lg_chunk:0";
120245949Sian
121245949Sian/*
122128705Ssmkelly * Periodically pat the watchdog, preventing it from firing.
123116874Ssmkelly */
124116874Ssmkellyint
125116874Ssmkellymain(int argc, char *argv[])
126116874Ssmkelly{
127116874Ssmkelly	struct rtprio rtp;
128149434Spjd	struct pidfh *pfh;
129149434Spjd	pid_t otherpid;
130116874Ssmkelly
131116874Ssmkelly	if (getuid() != 0)
132116874Ssmkelly		errx(EX_SOFTWARE, "not super user");
133116874Ssmkelly
134116874Ssmkelly	parseargs(argc, argv);
135116874Ssmkelly
136248744Smarkj	if (do_syslog)
137247405Salfred		openlog("watchdogd", LOG_CONS|LOG_NDELAY|LOG_PERROR,
138247405Salfred		    LOG_DAEMON);
139247405Salfred
140116874Ssmkelly	rtp.type = RTP_PRIO_REALTIME;
141116874Ssmkelly	rtp.prio = 0;
142116874Ssmkelly	if (rtprio(RTP_SET, 0, &rtp) == -1)
143116874Ssmkelly		err(EX_OSERR, "rtprio");
144116874Ssmkelly
145247405Salfred	if (!is_dry_run && watchdog_init() == -1)
146117185Ssmkelly		errx(EX_SOFTWARE, "unable to initialize watchdog");
147116874Ssmkelly
148126383Sphk	if (is_daemon) {
149126383Sphk		if (watchdog_onoff(1) == -1)
150200778Sru			err(EX_OSERR, "patting the dog");
151116874Ssmkelly
152150214Spjd		pfh = pidfile_open(pidfile, 0600, &otherpid);
153149434Spjd		if (pfh == NULL) {
154149434Spjd			if (errno == EEXIST) {
155247405Salfred				watchdog_onoff(0);
156149434Spjd				errx(EX_SOFTWARE, "%s already running, pid: %d",
157149434Spjd				    getprogname(), otherpid);
158149434Spjd			}
159149434Spjd			warn("Cannot open or create pidfile");
160149434Spjd		}
161149434Spjd
162126383Sphk		if (debugging == 0 && daemon(0, 0) == -1) {
163126383Sphk			watchdog_onoff(0);
164149434Spjd			pidfile_remove(pfh);
165126383Sphk			err(EX_OSERR, "daemon");
166126383Sphk		}
167116874Ssmkelly
168126383Sphk		signal(SIGHUP, SIG_IGN);
169126383Sphk		signal(SIGINT, sighandler);
170126383Sphk		signal(SIGTERM, sighandler);
171116874Ssmkelly
172149434Spjd		pidfile_write(pfh);
173213181Semaste		if (madvise(0, 0, MADV_PROTECT) != 0)
174213181Semaste			warn("madvise failed");
175239896Szont		if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0)
176239769Szont			warn("mlockall failed");
177116874Ssmkelly
178126383Sphk		watchdog_loop();
179116874Ssmkelly
180126383Sphk		/* exiting */
181149434Spjd		pidfile_remove(pfh);
182126383Sphk		return (EX_OK);
183126383Sphk	} else {
184126383Sphk		if (passive)
185126383Sphk			timeout |= WD_PASSIVE;
186126383Sphk		else
187126383Sphk			timeout |= WD_ACTIVE;
188165263Sn_hibma		if (watchdog_patpat(timeout) < 0)
189126383Sphk			err(EX_OSERR, "patting the dog");
190126383Sphk		return (EX_OK);
191126383Sphk	}
192116874Ssmkelly}
193116874Ssmkelly
194253719Salfredstatic void
195253719Salfredpow2ns_to_ts(int pow2ns, struct timespec *ts)
196253719Salfred{
197253719Salfred	uint64_t ns;
198253719Salfred
199253719Salfred	ns = 1ULL << pow2ns;
200253719Salfred	ts->tv_sec = ns / 1000000000ULL;
201253719Salfred	ts->tv_nsec = ns % 1000000000ULL;
202253719Salfred}
203253719Salfred
204116874Ssmkelly/*
205253719Salfred * Convert a timeout in seconds to N where 2^N nanoseconds is close to
206253719Salfred * "seconds".
207253719Salfred *
208253719Salfred * The kernel expects the timeouts for watchdogs in "2^N nanosecond format".
209253719Salfred */
210253719Salfredstatic u_int
211253719Salfredparse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg)
212253719Salfred{
213253719Salfred	double a;
214253719Salfred	u_int rv;
215253719Salfred	struct timespec ts;
216253719Salfred	struct timeval tv;
217253719Salfred	int ticks;
218253719Salfred	char shortopt[] = "- ";
219253719Salfred
220253719Salfred	if (!longopt)
221253719Salfred		shortopt[1] = opt;
222253719Salfred
223254173Salfred	a = fetchtimeout(opt, longopt, myoptarg, 1);
224253719Salfred
225253719Salfred	if (a == 0)
226253719Salfred		rv = WD_TO_NEVER;
227253719Salfred	else
228253719Salfred		rv = seconds_to_pow2ns(a);
229253719Salfred	pow2ns_to_ts(rv, &ts);
230253719Salfred	tstotv(&tv, &ts);
231253719Salfred	ticks = tvtohz(&tv);
232253719Salfred	if (debugging) {
233253719Salfred		printf("Timeout for %s%s "
234253719Salfred		    "is 2^%d nanoseconds "
235253744Sian		    "(in: %s sec -> out: %jd sec %ld ns -> %d ticks)\n",
236253719Salfred		    longopt ? "-" : "", longopt ? longopt : shortopt,
237253719Salfred		    rv,
238253744Sian		    myoptarg, (intmax_t)ts.tv_sec, ts.tv_nsec, ticks);
239253719Salfred	}
240253719Salfred	if (ticks <= 0) {
241253719Salfred		errx(1, "Timeout for %s%s is too small, please choose a higher timeout.", longopt ? "-" : "", longopt ? longopt : shortopt);
242253719Salfred	}
243253719Salfred
244253719Salfred	return (rv);
245253719Salfred}
246253719Salfred
247253719Salfred/*
248116874Ssmkelly * Catch signals and begin shutdown process.
249116874Ssmkelly */
250116874Ssmkellystatic void
251116874Ssmkellysighandler(int signum)
252116874Ssmkelly{
253116874Ssmkelly
254116874Ssmkelly	if (signum == SIGINT || signum == SIGTERM)
255116874Ssmkelly		end_program = 1;
256116874Ssmkelly}
257116874Ssmkelly
258116874Ssmkelly/*
259128705Ssmkelly * Open the watchdog device.
260116874Ssmkelly */
261116874Ssmkellystatic int
262201227Sedwatchdog_init(void)
263116874Ssmkelly{
264116874Ssmkelly
265247405Salfred	if (is_dry_run)
266247405Salfred		return 0;
267247405Salfred
268126383Sphk	fd = open("/dev/" _PATH_WATCHDOG, O_RDWR);
269126383Sphk	if (fd >= 0)
270126383Sphk		return (0);
271126383Sphk	warn("Could not open watchdog device");
272126383Sphk	return (-1);
273116874Ssmkelly}
274116874Ssmkelly
275116874Ssmkelly/*
276247405Salfred * If we are doing timing, then get the time.
277247405Salfred */
278247405Salfredstatic int
279247405Salfredwatchdog_getuptime(struct timespec *tp)
280247405Salfred{
281247405Salfred	int error;
282247405Salfred
283247405Salfred	if (!do_timedog)
284247405Salfred		return 0;
285247405Salfred
286247405Salfred	error = clock_gettime(CLOCK_UPTIME_FAST, tp);
287247405Salfred	if (error)
288247405Salfred		warn("clock_gettime");
289247405Salfred	return (error);
290247405Salfred}
291247405Salfred
292247405Salfredstatic long
293247405Salfredwatchdog_check_dogfunction_time(struct timespec *tp_start,
294247405Salfred    struct timespec *tp_end)
295247405Salfred{
296248744Smarkj	struct timeval tv_start, tv_end, tv_now, tv;
297247405Salfred	const char *cmd_prefix, *cmd;
298248744Smarkj	struct timespec tp_now;
299247405Salfred	int sec;
300247405Salfred
301247405Salfred	if (!do_timedog)
302247405Salfred		return (0);
303247405Salfred
304247405Salfred	TIMESPEC_TO_TIMEVAL(&tv_start, tp_start);
305247405Salfred	TIMESPEC_TO_TIMEVAL(&tv_end, tp_end);
306247405Salfred	timersub(&tv_end, &tv_start, &tv);
307247405Salfred	sec = tv.tv_sec;
308247405Salfred	if (sec < carp_thresh_seconds)
309247405Salfred		return (sec);
310247405Salfred
311247405Salfred	if (test_cmd) {
312247405Salfred		cmd_prefix = "Watchdog program";
313247405Salfred		cmd = test_cmd;
314247405Salfred	} else {
315247405Salfred		cmd_prefix = "Watchdog operation";
316247405Salfred		cmd = "stat(\"/etc\", &sb)";
317247405Salfred	}
318247405Salfred	if (do_syslog)
319247405Salfred		syslog(LOG_CRIT, "%s: '%s' took too long: "
320248744Smarkj		    "%d.%06ld seconds >= %d seconds threshold",
321247405Salfred		    cmd_prefix, cmd, sec, (long)tv.tv_usec,
322247405Salfred		    carp_thresh_seconds);
323248744Smarkj	else
324248744Smarkj		warnx("%s: '%s' took too long: "
325248744Smarkj		    "%d.%06ld seconds >= %d seconds threshold",
326248744Smarkj		    cmd_prefix, cmd, sec, (long)tv.tv_usec,
327248744Smarkj		    carp_thresh_seconds);
328248744Smarkj
329248744Smarkj	/*
330248744Smarkj	 * Adjust the sleep interval again in case syslog(3) took a non-trivial
331248744Smarkj	 * amount of time to run.
332248744Smarkj	 */
333248744Smarkj	if (watchdog_getuptime(&tp_now))
334248744Smarkj		return (sec);
335248744Smarkj	TIMESPEC_TO_TIMEVAL(&tv_now, &tp_now);
336248744Smarkj	timersub(&tv_now, &tv_start, &tv);
337248744Smarkj	sec = tv.tv_sec;
338248744Smarkj
339247405Salfred	return (sec);
340247405Salfred}
341247405Salfred
342247405Salfred/*
343116874Ssmkelly * Main program loop which is iterated every second.
344116874Ssmkelly */
345116874Ssmkellystatic void
346116874Ssmkellywatchdog_loop(void)
347116874Ssmkelly{
348247405Salfred	struct timespec ts_start, ts_end;
349116874Ssmkelly	struct stat sb;
350247405Salfred	long waited;
351247405Salfred	int error, failed;
352116874Ssmkelly
353165263Sn_hibma	while (end_program != 2) {
354116874Ssmkelly		failed = 0;
355116874Ssmkelly
356247405Salfred		error = watchdog_getuptime(&ts_start);
357247405Salfred		if (error) {
358247405Salfred			end_program = 1;
359247405Salfred			goto try_end;
360247405Salfred		}
361247405Salfred
362126383Sphk		if (test_cmd != NULL)
363126383Sphk			failed = system(test_cmd);
364126383Sphk		else
365126383Sphk			failed = stat("/etc", &sb);
366116874Ssmkelly
367247405Salfred		error = watchdog_getuptime(&ts_end);
368247405Salfred		if (error) {
369247405Salfred			end_program = 1;
370247405Salfred			goto try_end;
371247405Salfred		}
372247405Salfred
373116874Ssmkelly		if (failed == 0)
374165263Sn_hibma			watchdog_patpat(timeout|WD_ACTIVE);
375248744Smarkj
376248744Smarkj		waited = watchdog_check_dogfunction_time(&ts_start, &ts_end);
377247405Salfred		if (nap - waited > 0)
378247405Salfred			sleep(nap - waited);
379165263Sn_hibma
380247405Salfredtry_end:
381165263Sn_hibma		if (end_program != 0) {
382165263Sn_hibma			if (watchdog_onoff(0) == 0) {
383165263Sn_hibma				end_program = 2;
384165263Sn_hibma			} else {
385245951Sian				warnx("Could not stop the watchdog, not exiting");
386165263Sn_hibma				end_program = 0;
387165263Sn_hibma			}
388165263Sn_hibma		}
389116874Ssmkelly	}
390116874Ssmkelly}
391116874Ssmkelly
392116874Ssmkelly/*
393116874Ssmkelly * Reset the watchdog timer. This function must be called periodically
394116874Ssmkelly * to keep the watchdog from firing.
395116874Ssmkelly */
396210300Sdelphijstatic int
397165263Sn_hibmawatchdog_patpat(u_int t)
398116874Ssmkelly{
399116874Ssmkelly
400247405Salfred	if (is_dry_run)
401247405Salfred		return 0;
402247405Salfred
403165263Sn_hibma	return ioctl(fd, WDIOCPATPAT, &t);
404116874Ssmkelly}
405116874Ssmkelly
406116874Ssmkelly/*
407116874Ssmkelly * Toggle the kernel's watchdog. This routine is used to enable and
408116874Ssmkelly * disable the watchdog.
409116874Ssmkelly */
410116874Ssmkellystatic int
411116874Ssmkellywatchdog_onoff(int onoff)
412116874Ssmkelly{
413247405Salfred	int error;
414116874Ssmkelly
415247405Salfred	/* fake successful watchdog op if a dry run */
416247405Salfred	if (is_dry_run)
417247405Salfred		return 0;
418247405Salfred
419247405Salfred	if (onoff) {
420247405Salfred		/*
421247405Salfred		 * Call the WDIOC_SETSOFT regardless of softtimeout_set
422247405Salfred		 * because we'll need to turn it off if someone had turned
423247405Salfred		 * it on.
424247405Salfred		 */
425247405Salfred		error = ioctl(fd, WDIOC_SETSOFT, &softtimeout_set);
426247405Salfred		if (error) {
427247405Salfred			warn("setting WDIOC_SETSOFT %d", softtimeout_set);
428247405Salfred			return (error);
429247405Salfred		}
430247405Salfred		error = watchdog_patpat((timeout|WD_ACTIVE));
431247405Salfred		if (error) {
432247405Salfred			warn("watchdog_patpat failed");
433247405Salfred			goto failsafe;
434247405Salfred		}
435247405Salfred		if (softtimeout_act_set) {
436247405Salfred			error = ioctl(fd, WDIOC_SETSOFTTIMEOUTACT,
437247405Salfred			    &softtimeout_act);
438247405Salfred			if (error) {
439247405Salfred				warn("setting WDIOC_SETSOFTTIMEOUTACT %d",
440247405Salfred				    softtimeout_act);
441247405Salfred				goto failsafe;
442247405Salfred			}
443247405Salfred		}
444247405Salfred		if (pretimeout_set) {
445247405Salfred			error = ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);
446247405Salfred			if (error) {
447247405Salfred				warn("setting WDIOC_SETPRETIMEOUT %d",
448247405Salfred				    pretimeout);
449247405Salfred				goto failsafe;
450247405Salfred			}
451247405Salfred		}
452247405Salfred		if (pretimeout_act_set) {
453247405Salfred			error = ioctl(fd, WDIOC_SETPRETIMEOUTACT,
454247405Salfred			    &pretimeout_act);
455247405Salfred			if (error) {
456247405Salfred				warn("setting WDIOC_SETPRETIMEOUTACT %d",
457247405Salfred				    pretimeout_act);
458247405Salfred				goto failsafe;
459247405Salfred			}
460247405Salfred		}
461247405Salfred		/* pat one more time for good measure */
462165263Sn_hibma		return watchdog_patpat((timeout|WD_ACTIVE));
463247405Salfred	 } else {
464165263Sn_hibma		return watchdog_patpat(0);
465247405Salfred	 }
466247405Salfredfailsafe:
467247405Salfred	watchdog_patpat(0);
468247405Salfred	return (error);
469116874Ssmkelly}
470116874Ssmkelly
471116874Ssmkelly/*
472116874Ssmkelly * Tell user how to use the program.
473116874Ssmkelly */
474116874Ssmkellystatic void
475201227Sedusage(void)
476116874Ssmkelly{
477126383Sphk	if (is_daemon)
478247405Salfred		fprintf(stderr, "usage:\n"
479248744Smarkj"  watchdogd [-dnSw] [-e cmd] [-I file] [-s sleep] [-t timeout]\n"
480247405Salfred"            [-T script_timeout]\n"
481247405Salfred"            [--debug]\n"
482247405Salfred"            [--pretimeout seconds] [-pretimeout-action action]\n"
483247405Salfred"            [--softtimeout] [-softtimeout-action action]\n"
484247405Salfred);
485126383Sphk	else
486156334Sphk		fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n");
487116874Ssmkelly	exit(EX_USAGE);
488116874Ssmkelly}
489116874Ssmkelly
490247405Salfredstatic long
491254173Salfredfetchtimeout(int opt, const char *longopt, const char *myoptarg, int zero_ok)
492247405Salfred{
493247405Salfred	const char *errstr;
494247405Salfred	char *p;
495247405Salfred	long rv;
496247405Salfred
497247405Salfred	errstr = NULL;
498247405Salfred	p = NULL;
499247405Salfred	errno = 0;
500247405Salfred	rv = strtol(myoptarg, &p, 0);
501247405Salfred	if ((p != NULL && *p != '\0') || errno != 0)
502247405Salfred		errstr = "is not a number";
503254173Salfred	if (rv < 0 || (!zero_ok && rv == 0))
504247405Salfred		errstr = "must be greater than zero";
505247405Salfred	if (errstr) {
506247405Salfred		if (longopt)
507247405Salfred			errx(EX_USAGE, "--%s argument %s", longopt, errstr);
508247405Salfred		else
509247405Salfred			errx(EX_USAGE, "-%c argument %s", opt, errstr);
510247405Salfred	}
511247405Salfred	return (rv);
512247405Salfred}
513247405Salfred
514247405Salfredstruct act_tbl {
515247405Salfred	const char *at_act;
516247405Salfred	int at_value;
517247405Salfred};
518247405Salfred
519249245Sedstatic const struct act_tbl act_tbl[] = {
520247405Salfred	{ "panic", WD_SOFT_PANIC },
521247405Salfred	{ "ddb", WD_SOFT_DDB },
522247405Salfred	{ "log", WD_SOFT_LOG },
523247405Salfred	{ "printf", WD_SOFT_PRINTF },
524247405Salfred	{ NULL, 0 }
525247405Salfred};
526247405Salfred
527247405Salfredstatic void
528247405Salfredtimeout_act_error(const char *lopt, const char *badact)
529247405Salfred{
530247405Salfred	char *opts, *oldopts;
531247405Salfred	int i;
532247405Salfred
533247405Salfred	opts = NULL;
534247405Salfred	for (i = 0; act_tbl[i].at_act != NULL; i++) {
535247405Salfred		oldopts = opts;
536247405Salfred		if (asprintf(&opts, "%s%s%s",
537247405Salfred		    oldopts == NULL ? "" : oldopts,
538247405Salfred		    oldopts == NULL ? "" : ", ",
539247405Salfred		    act_tbl[i].at_act) == -1)
540247405Salfred			err(EX_OSERR, "malloc");
541247405Salfred		free(oldopts);
542247405Salfred	}
543247405Salfred	warnx("bad --%s argument '%s' must be one of (%s).",
544247405Salfred	    lopt, badact, opts);
545247405Salfred	usage();
546247405Salfred}
547247405Salfred
548116874Ssmkelly/*
549247405Salfred * Take a comma separated list of actions and or the flags
550247405Salfred * together for the ioctl.
551247405Salfred */
552247405Salfredstatic int
553247405Salfredtimeout_act_str2int(const char *lopt, const char *acts)
554247405Salfred{
555247405Salfred	int i;
556247405Salfred	char *dupacts, *tofree;
557247405Salfred	char *o;
558247405Salfred	int rv = 0;
559247405Salfred
560247405Salfred	tofree = dupacts = strdup(acts);
561247405Salfred	if (!tofree)
562247405Salfred		err(EX_OSERR, "malloc");
563247405Salfred	while ((o = strsep(&dupacts, ",")) != NULL) {
564247405Salfred		for (i = 0; act_tbl[i].at_act != NULL; i++) {
565247405Salfred			if (!strcmp(o, act_tbl[i].at_act)) {
566247405Salfred				rv |= act_tbl[i].at_value;
567247405Salfred				break;
568247405Salfred			}
569247405Salfred		}
570247405Salfred		if (act_tbl[i].at_act == NULL)
571247405Salfred			timeout_act_error(lopt, o);
572247405Salfred	}
573247405Salfred	free(tofree);
574247405Salfred	return rv;
575247405Salfred}
576247405Salfred
577253719Salfredint
578253719Salfredtstotv(struct timeval *tv, struct timespec *ts)
579253719Salfred{
580253719Salfred
581253719Salfred	tv->tv_sec = ts->tv_sec;
582253719Salfred	tv->tv_usec = ts->tv_nsec / 1000;
583253719Salfred	return 0;
584253719Salfred}
585253719Salfred
586247405Salfred/*
587253719Salfred * Convert a timeval to a number of ticks.
588253719Salfred * Mostly copied from the kernel.
589253719Salfred */
590253719Salfredint
591253719Salfredtvtohz(struct timeval *tv)
592253719Salfred{
593253719Salfred	register unsigned long ticks;
594253719Salfred	register long sec, usec;
595253719Salfred	int hz;
596253719Salfred	size_t hzsize;
597253719Salfred	int error;
598253719Salfred	int tick;
599253719Salfred
600253719Salfred	hzsize = sizeof(hz);
601253719Salfred
602253719Salfred	error = sysctlbyname("kern.hz", &hz, &hzsize, NULL, 0);
603253719Salfred	if (error)
604253719Salfred		err(1, "sysctlbyname kern.hz");
605253719Salfred
606253719Salfred	tick = 1000000 / hz;
607253719Salfred
608253719Salfred	/*
609253719Salfred	 * If the number of usecs in the whole seconds part of the time
610253719Salfred	 * difference fits in a long, then the total number of usecs will
611253719Salfred	 * fit in an unsigned long.  Compute the total and convert it to
612253719Salfred	 * ticks, rounding up and adding 1 to allow for the current tick
613253719Salfred	 * to expire.  Rounding also depends on unsigned long arithmetic
614253719Salfred	 * to avoid overflow.
615253719Salfred	 *
616253719Salfred	 * Otherwise, if the number of ticks in the whole seconds part of
617253719Salfred	 * the time difference fits in a long, then convert the parts to
618253719Salfred	 * ticks separately and add, using similar rounding methods and
619253719Salfred	 * overflow avoidance.  This method would work in the previous
620253719Salfred	 * case but it is slightly slower and assumes that hz is integral.
621253719Salfred	 *
622253719Salfred	 * Otherwise, round the time difference down to the maximum
623253719Salfred	 * representable value.
624253719Salfred	 *
625253719Salfred	 * If ints have 32 bits, then the maximum value for any timeout in
626253719Salfred	 * 10ms ticks is 248 days.
627253719Salfred	 */
628253719Salfred	sec = tv->tv_sec;
629253719Salfred	usec = tv->tv_usec;
630253719Salfred	if (usec < 0) {
631253719Salfred		sec--;
632253719Salfred		usec += 1000000;
633253719Salfred	}
634253719Salfred	if (sec < 0) {
635253719Salfred#ifdef DIAGNOSTIC
636253719Salfred		if (usec > 0) {
637253719Salfred			sec++;
638253719Salfred			usec -= 1000000;
639253719Salfred		}
640253719Salfred		printf("tvotohz: negative time difference %ld sec %ld usec\n",
641253719Salfred		    sec, usec);
642253719Salfred#endif
643253719Salfred		ticks = 1;
644253719Salfred	} else if (sec <= LONG_MAX / 1000000)
645253719Salfred		ticks = (sec * 1000000 + (unsigned long)usec + (tick - 1))
646253719Salfred		    / tick + 1;
647253719Salfred	else if (sec <= LONG_MAX / hz)
648253719Salfred		ticks = sec * hz
649253719Salfred		    + ((unsigned long)usec + (tick - 1)) / tick + 1;
650253719Salfred	else
651253719Salfred		ticks = LONG_MAX;
652253719Salfred	if (ticks > INT_MAX)
653253719Salfred		ticks = INT_MAX;
654253719Salfred	return ((int)ticks);
655253719Salfred}
656253719Salfred
657253719Salfredstatic int
658253719Salfredseconds_to_pow2ns(int seconds)
659253719Salfred{
660253719Salfred	uint64_t power;
661253719Salfred	uint64_t ns;
662253719Salfred	uint64_t shifted;
663253719Salfred
664253719Salfred	if (seconds <= 0)
665253719Salfred		errx(1, "seconds %d < 0", seconds);
666253719Salfred	ns = ((uint64_t)seconds) * 1000000000ULL;
667253719Salfred	power = flsll(ns);
668253719Salfred	shifted = 1ULL << power;
669253719Salfred	if (shifted <= ns) {
670253719Salfred		power++;
671253719Salfred	}
672253719Salfred	if (debugging) {
673253719Salfred		printf("shifted %lld\n", (long long)shifted);
674253719Salfred		printf("seconds_to_pow2ns: seconds: %d, ns %lld, power %d\n",
675253719Salfred		    seconds, (long long)ns, (int)power);
676253719Salfred	}
677253719Salfred	return (power);
678253719Salfred}
679253719Salfred
680253719Salfred
681253719Salfred/*
682116874Ssmkelly * Handle the few command line arguments supported.
683116874Ssmkelly */
684116874Ssmkellystatic void
685116874Ssmkellyparseargs(int argc, char *argv[])
686116874Ssmkelly{
687247405Salfred	int longindex;
688116874Ssmkelly	int c;
689247405Salfred	const char *lopt;
690116874Ssmkelly
691247405Salfred	/*
692247405Salfred	 * if we end with a 'd' aka 'watchdogd' then we are the daemon program,
693247405Salfred	 * otherwise run as a command line utility.
694247405Salfred	 */
695126383Sphk	c = strlen(argv[0]);
696126383Sphk	if (argv[0][c - 1] == 'd')
697126383Sphk		is_daemon = 1;
698247405Salfred
699247405Salfred	if (is_daemon)
700247405Salfred		getopt_shortopts = "I:de:ns:t:ST:w?";
701247405Salfred	else
702247405Salfred		getopt_shortopts = "dt:?";
703247405Salfred
704247405Salfred	while ((c = getopt_long(argc, argv, getopt_shortopts, longopts,
705247405Salfred		    &longindex)) != -1) {
706116874Ssmkelly		switch (c) {
707116874Ssmkelly		case 'I':
708116874Ssmkelly			pidfile = optarg;
709116874Ssmkelly			break;
710116874Ssmkelly		case 'd':
711116874Ssmkelly			debugging = 1;
712116874Ssmkelly			break;
713126383Sphk		case 'e':
714126383Sphk			test_cmd = strdup(optarg);
715126383Sphk			break;
716247405Salfred		case 'n':
717247405Salfred			is_dry_run = 1;
718247405Salfred			break;
719126383Sphk#ifdef notyet
720126383Sphk		case 'p':
721126383Sphk			passive = 1;
722126383Sphk			break;
723126383Sphk#endif
724126383Sphk		case 's':
725254173Salfred			nap = fetchtimeout(c, NULL, optarg, 0);
726126383Sphk			break;
727247405Salfred		case 'S':
728248744Smarkj			do_syslog = 0;
729247405Salfred			break;
730126383Sphk		case 't':
731253719Salfred			timeout_sec = atoi(optarg);
732253719Salfred			timeout = parse_timeout_to_pow2ns(c, NULL, optarg);
733253719Salfred 			if (debugging)
734253719Salfred 				printf("Timeout is 2^%d nanoseconds\n",
735253719Salfred 				    timeout);
736126383Sphk			break;
737247405Salfred		case 'T':
738254173Salfred			carp_thresh_seconds =
739254173Salfred			    fetchtimeout(c, "NULL", optarg, 0);
740247405Salfred			break;
741247405Salfred		case 'w':
742247405Salfred			do_timedog = 1;
743247405Salfred			break;
744247405Salfred		case 0:
745247405Salfred			lopt = longopts[longindex].name;
746247405Salfred			if (!strcmp(lopt, "pretimeout")) {
747254173Salfred				pretimeout = fetchtimeout(0, lopt, optarg, 0);
748247405Salfred			} else if (!strcmp(lopt, "pretimeout-action")) {
749247405Salfred				pretimeout_act = timeout_act_str2int(lopt,
750247405Salfred				    optarg);
751247405Salfred			} else if (!strcmp(lopt, "softtimeout-action")) {
752247405Salfred				softtimeout_act = timeout_act_str2int(lopt,
753247405Salfred				    optarg);
754247405Salfred			} else {
755247405Salfred		/*		warnx("bad option at index %d: %s", optind,
756247405Salfred				    argv[optind]);
757247405Salfred				usage();
758247405Salfred				*/
759247405Salfred			}
760247405Salfred			break;
761116874Ssmkelly		case '?':
762116874Ssmkelly		default:
763116874Ssmkelly			usage();
764116874Ssmkelly			/* NOTREACHED */
765116874Ssmkelly		}
766116874Ssmkelly	}
767247405Salfred
768247405Salfred	if (carp_thresh_seconds == -1)
769247405Salfred		carp_thresh_seconds = nap;
770247405Salfred
771150747Sphk	if (argc != optind)
772150747Sphk		errx(EX_USAGE, "extra arguments.");
773126383Sphk	if (is_daemon && timeout < WD_TO_1SEC)
774126383Sphk		errx(EX_USAGE, "-t argument is less than one second.");
775253719Salfred	if (pretimeout_set) {
776253719Salfred		struct timespec ts;
777253719Salfred
778253719Salfred		pow2ns_to_ts(timeout, &ts);
779253808Sjhb		if (pretimeout >= (uintmax_t)ts.tv_sec) {
780253719Salfred			errx(EX_USAGE,
781253719Salfred			    "pretimeout (%d) >= timeout (%d -> %ld)\n"
782253719Salfred			    "see manual section TIMEOUT RESOLUTION",
783253719Salfred			    pretimeout, timeout_sec, (long)ts.tv_sec);
784253719Salfred		}
785253719Salfred	}
786116874Ssmkelly}
787