xref: /openbsd-src/usr.sbin/ifstated/ifstated.c (revision e6c7c102cf5d9891f32552a42895134a59937045)
1*e6c7c102Sjsg /*	$OpenBSD: ifstated.c,v 1.68 2024/04/23 13:34:51 jsg Exp $	*/
26dbcce3fSmcbride 
36dbcce3fSmcbride /*
46dbcce3fSmcbride  * Copyright (c) 2004 Marco Pfatschbacher <mpf@openbsd.org>
5b73b88efSmcbride  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
66dbcce3fSmcbride  *
76dbcce3fSmcbride  * Permission to use, copy, modify, and distribute this software for any
86dbcce3fSmcbride  * purpose with or without fee is hereby granted, provided that the above
96dbcce3fSmcbride  * copyright notice and this permission notice appear in all copies.
106dbcce3fSmcbride  *
116dbcce3fSmcbride  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
126dbcce3fSmcbride  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
136dbcce3fSmcbride  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
146dbcce3fSmcbride  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
156dbcce3fSmcbride  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
166dbcce3fSmcbride  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
176dbcce3fSmcbride  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
186dbcce3fSmcbride  */
196dbcce3fSmcbride 
206dbcce3fSmcbride /*
216dbcce3fSmcbride  * ifstated listens to link_state transitions on interfaces
226dbcce3fSmcbride  * and executes predefined commands.
236dbcce3fSmcbride  */
246dbcce3fSmcbride 
256dbcce3fSmcbride #include <sys/types.h>
266dbcce3fSmcbride #include <sys/time.h>
276dbcce3fSmcbride #include <sys/socket.h>
28b73b88efSmcbride #include <sys/wait.h>
296dbcce3fSmcbride 
306dbcce3fSmcbride #include <net/if.h>
316dbcce3fSmcbride #include <net/route.h>
326dbcce3fSmcbride #include <netinet/in.h>
336dbcce3fSmcbride 
34a4459108Smestre #include <paths.h>
356dbcce3fSmcbride #include <stdio.h>
366dbcce3fSmcbride #include <stdlib.h>
376dbcce3fSmcbride #include <string.h>
386dbcce3fSmcbride #include <signal.h>
393c0535b0Sbenno #include <stdint.h>
40bfdb9ad4Sbenno #include <syslog.h>
41089b0f81Srob #include <errno.h>
42b73b88efSmcbride #include <event.h>
436dbcce3fSmcbride #include <unistd.h>
44b73b88efSmcbride #include <ifaddrs.h>
45b73b88efSmcbride 
46b73b88efSmcbride #include "ifstated.h"
47bfdb9ad4Sbenno #include "log.h"
486dbcce3fSmcbride 
49d64c7ac9Sderaadt struct	 ifsd_config *conf, *newconf;
506dbcce3fSmcbride 
51d64c7ac9Sderaadt int	 opts;
52d64c7ac9Sderaadt int	 opt_inhibit;
536dbcce3fSmcbride char	*configfile = "/etc/ifstated.conf";
548bb56c35Smcbride struct event	rt_msg_ev, sighup_ev, startup_ev, sigchld_ev;
556dbcce3fSmcbride 
56b73b88efSmcbride void		startup_handler(int, short, void *);
57b73b88efSmcbride void		sighup_handler(int, short, void *);
588bb56c35Smcbride int		load_config(void);
59b73b88efSmcbride void		sigchld_handler(int, short, void *);
60b73b88efSmcbride void		rt_msg_handler(int, short, void *);
61b73b88efSmcbride void		external_handler(int, short, void *);
62b688c394Smpf void		external_exec(struct ifsd_external *, int);
63b73b88efSmcbride void		check_external_status(struct ifsd_state *);
6440699dccSrob void		check_ifdeparture(void);
65b73b88efSmcbride void		external_evtimer_setup(struct ifsd_state *, int);
6618e4260cSrob void		scan_ifstate(const char *, int, int);
6718e4260cSrob int		scan_ifstate_single(const char *, int, struct ifsd_state *);
68089b0f81Srob void		fetch_ifstate(int);
69bce9047dSbenno __dead void	usage(void);
70b73b88efSmcbride void		adjust_expressions(struct ifsd_expression_list *, int);
71b688c394Smpf void		adjust_external_expressions(struct ifsd_state *);
72b73b88efSmcbride void		eval_state(struct ifsd_state *);
73b688c394Smpf int		state_change(void);
74b73b88efSmcbride void		do_action(struct ifsd_action *);
75b73b88efSmcbride void		remove_action(struct ifsd_action *, struct ifsd_state *);
767c875bb8Sbenno void		remove_expression(struct ifsd_expression *,
777c875bb8Sbenno 		    struct ifsd_state *);
786dbcce3fSmcbride 
79bce9047dSbenno __dead void
usage(void)806dbcce3fSmcbride usage(void)
816dbcce3fSmcbride {
82fb210867Smcbride 	extern char *__progname;
83fb210867Smcbride 
8497bef895Sjmc 	fprintf(stderr, "usage: %s [-dhinv] [-D macro=value] [-f file]\n",
85fb210867Smcbride 	    __progname);
866dbcce3fSmcbride 	exit(1);
876dbcce3fSmcbride }
886dbcce3fSmcbride 
896dbcce3fSmcbride int
main(int argc,char * argv[])906dbcce3fSmcbride main(int argc, char *argv[])
916dbcce3fSmcbride {
92b73b88efSmcbride 	struct timeval tv;
933c0535b0Sbenno 	int ch, rt_fd;
9435360db1Spyr 	int debug = 0;
953c0535b0Sbenno 	unsigned int rtfilter;
9635360db1Spyr 
97bfdb9ad4Sbenno 	log_init(1, LOG_DAEMON);	/* log to stderr until daemonized */
98bfdb9ad4Sbenno 	log_setverbose(1);
996dbcce3fSmcbride 
10084f5df3aSmcbride 	while ((ch = getopt(argc, argv, "dD:f:hniv")) != -1) {
1016dbcce3fSmcbride 		switch (ch) {
1026dbcce3fSmcbride 		case 'd':
10335360db1Spyr 			debug = 1;
1046dbcce3fSmcbride 			break;
105b73b88efSmcbride 		case 'D':
106b73b88efSmcbride 			if (cmdline_symset(optarg) < 0)
10795b4ea67Srob 				fatalx("could not parse macro definition %s",
108b73b88efSmcbride 				    optarg);
109b73b88efSmcbride 			break;
110b73b88efSmcbride 		case 'f':
111b73b88efSmcbride 			configfile = optarg;
112b73b88efSmcbride 			break;
113b73b88efSmcbride 		case 'h':
114b73b88efSmcbride 			usage();
115b73b88efSmcbride 			break;
11684f5df3aSmcbride 		case 'n':
1178bb56c35Smcbride 			opts |= IFSD_OPT_NOACTION;
11884f5df3aSmcbride 			break;
1196dbcce3fSmcbride 		case 'i':
1206dbcce3fSmcbride 			opt_inhibit = 1;
1216dbcce3fSmcbride 			break;
122b73b88efSmcbride 		case 'v':
1238bb56c35Smcbride 			if (opts & IFSD_OPT_VERBOSE)
1248bb56c35Smcbride 				opts |= IFSD_OPT_VERBOSE2;
1258bb56c35Smcbride 			opts |= IFSD_OPT_VERBOSE;
1266dbcce3fSmcbride 			break;
1276dbcce3fSmcbride 		default:
1286dbcce3fSmcbride 			usage();
1296dbcce3fSmcbride 		}
1306dbcce3fSmcbride 	}
1316dbcce3fSmcbride 
132fde55bc4Spyr 	argc -= optind;
133fde55bc4Spyr 	argv += optind;
134fde55bc4Spyr 	if (argc > 0)
135fde55bc4Spyr 		usage();
136fde55bc4Spyr 
1378bb56c35Smcbride 	if (opts & IFSD_OPT_NOACTION) {
1388bb56c35Smcbride 		if ((newconf = parse_config(configfile, opts)) == NULL)
13984f5df3aSmcbride 			exit(1);
14095b4ea67Srob 		fprintf(stderr, "configuration OK\n");
14184f5df3aSmcbride 		exit(0);
14284f5df3aSmcbride 	}
1436dbcce3fSmcbride 
14482e24455Sderaadt 	if (!debug)
1452f505238Scamield 		daemon(1, 0);
1466dbcce3fSmcbride 
1473d2bb036Smarkus 	event_init();
148bfdb9ad4Sbenno 	log_init(debug, LOG_DAEMON);
149bfdb9ad4Sbenno 	log_setverbose(opts & IFSD_OPT_VERBOSE);
1503d2bb036Smarkus 
151df69c215Sderaadt 	if ((rt_fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
15295b4ea67Srob 		fatal("no routing socket");
153b2017614Smpf 
15440699dccSrob 	rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE);
155b47fcd70Skrw 	if (setsockopt(rt_fd, AF_ROUTE, ROUTE_MSGFILTER,
156aad308e5Ssthen 	    &rtfilter, sizeof(rtfilter)) == -1)	/* not fatal */
157bfdb9ad4Sbenno 		log_warn("%s: setsockopt msgfilter", __func__);
15887df0332Shenning 
15987df0332Shenning 	rtfilter = RTABLE_ANY;
160b47fcd70Skrw 	if (setsockopt(rt_fd, AF_ROUTE, ROUTE_TABLEFILTER,
16187df0332Shenning 	    &rtfilter, sizeof(rtfilter)) == -1)	/* not fatal */
162bfdb9ad4Sbenno 		log_warn("%s: setsockopt tablefilter", __func__);
163aad308e5Ssthen 
164a4459108Smestre 	if (unveil(configfile, "r") == -1)
165bc5a8259Sbeck 		fatal("unveil %s", configfile);
166a4459108Smestre 	if (unveil(_PATH_BSHELL, "x") == -1)
167bc5a8259Sbeck 		fatal("unveil %s", _PATH_BSHELL);
1680eb8ec17Sjca 	if (pledge("stdio rpath route proc exec", NULL) == -1)
1690eb8ec17Sjca 		fatal("pledge");
1700eb8ec17Sjca 
1713c0535b0Sbenno 	signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL);
1723c0535b0Sbenno 	signal_add(&sigchld_ev, NULL);
1733c0535b0Sbenno 
1743c0535b0Sbenno 	/* Loading the config needs to happen in the event loop */
1753c0535b0Sbenno 	timerclear(&tv);
1763c0535b0Sbenno 	evtimer_set(&startup_ev, startup_handler, (void *)(long)rt_fd);
1773c0535b0Sbenno 	evtimer_add(&startup_ev, &tv);
1783c0535b0Sbenno 
1793c0535b0Sbenno 	event_loop(0);
1803c0535b0Sbenno 	exit(0);
1813c0535b0Sbenno }
1823c0535b0Sbenno 
1833c0535b0Sbenno void
startup_handler(int fd,short event,void * arg)1843c0535b0Sbenno startup_handler(int fd, short event, void *arg)
1853c0535b0Sbenno {
1863c0535b0Sbenno 	int rfd = (int)(long)arg;
1873c0535b0Sbenno 
1883c0535b0Sbenno 	if (load_config() != 0) {
1893c0535b0Sbenno 		log_warnx("unable to load config");
1903c0535b0Sbenno 		exit(1);
1913c0535b0Sbenno 	}
1923c0535b0Sbenno 
1933c0535b0Sbenno 	event_set(&rt_msg_ev, rfd, EV_READ|EV_PERSIST, rt_msg_handler, NULL);
1948bb56c35Smcbride 	event_add(&rt_msg_ev, NULL);
1958bb56c35Smcbride 
1964dab0c72Smcbride 	signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL);
1978bb56c35Smcbride 	signal_add(&sighup_ev, NULL);
1988bb56c35Smcbride 
19935360db1Spyr 	log_info("started");
2006dbcce3fSmcbride }
2016dbcce3fSmcbride 
2026dbcce3fSmcbride void
sighup_handler(int fd,short event,void * arg)203b73b88efSmcbride sighup_handler(int fd, short event, void *arg)
204b73b88efSmcbride {
20535360db1Spyr 	log_info("reloading config");
2068bb56c35Smcbride 	if (load_config() != 0)
20735360db1Spyr 		log_warnx("unable to reload config");
208b73b88efSmcbride }
209b73b88efSmcbride 
2108bb56c35Smcbride int
load_config(void)211b73b88efSmcbride load_config(void)
212b73b88efSmcbride {
2138bb56c35Smcbride 	if ((newconf = parse_config(configfile, opts)) == NULL)
2148bb56c35Smcbride 		return (-1);
2158bb56c35Smcbride 	if (conf != NULL)
2168bb56c35Smcbride 		clear_config(conf);
2178bb56c35Smcbride 	conf = newconf;
21820863243Sbenno 	conf->initstate.entered = time(NULL);
219089b0f81Srob 	fetch_ifstate(0);
22020863243Sbenno 	external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_ADD);
22120863243Sbenno 	adjust_external_expressions(&conf->initstate);
22220863243Sbenno 	eval_state(&conf->initstate);
2238bb56c35Smcbride 	if (conf->curstate != NULL) {
22435360db1Spyr 		log_info("initial state: %s", conf->curstate->name);
2258bb56c35Smcbride 		conf->curstate->entered = time(NULL);
22622794b72Smpf 		conf->nextstate = conf->curstate;
22722794b72Smpf 		conf->curstate = NULL;
228988c4ffaSbenno 		while (state_change()) {
229988c4ffaSbenno 			do_action(conf->curstate->init);
230138e089fSbenno 			do_action(conf->curstate->body);
231b73b88efSmcbride 		}
232988c4ffaSbenno 	}
2338bb56c35Smcbride 	return (0);
234b73b88efSmcbride }
235b73b88efSmcbride 
236b73b88efSmcbride void
rt_msg_handler(int fd,short event,void * arg)237b73b88efSmcbride rt_msg_handler(int fd, short event, void *arg)
2386dbcce3fSmcbride {
239b73b88efSmcbride 	char msg[2048];
240b73b88efSmcbride 	struct rt_msghdr *rtm = (struct rt_msghdr *)&msg;
241aba3295aSderaadt 	struct if_msghdr ifm;
24240699dccSrob 	struct if_announcemsghdr ifan;
24318e4260cSrob 	char ifnamebuf[IFNAMSIZ];
24418e4260cSrob 	char *ifname;
24577c6c732Sderaadt 	ssize_t len;
246b73b88efSmcbride 
247089b0f81Srob 	if ((len = read(fd, msg, sizeof(msg))) == -1) {
248089b0f81Srob 		if (errno == EAGAIN || errno == EINTR)
2496dbcce3fSmcbride 			return;
250089b0f81Srob 		fatal("%s: routing socket read error", __func__);
251089b0f81Srob 	}
252089b0f81Srob 
253089b0f81Srob 	if (len == 0)
254089b0f81Srob 		fatal("%s: routing socket closed", __func__);
2556dbcce3fSmcbride 
2566dbcce3fSmcbride 	if (rtm->rtm_version != RTM_VERSION)
2576dbcce3fSmcbride 		return;
2586dbcce3fSmcbride 
259089b0f81Srob 	switch (rtm->rtm_type) {
260089b0f81Srob 	case RTM_IFINFO:
261f442093cShenning 		memcpy(&ifm, rtm, sizeof(ifm));
26218e4260cSrob 		ifname = if_indextoname(ifm.ifm_index, ifnamebuf);
26318e4260cSrob 		/* ifname is NULL on interface departure */
26418e4260cSrob 		if (ifname != NULL)
26518e4260cSrob 			scan_ifstate(ifname, ifm.ifm_data.ifi_link_state, 1);
266089b0f81Srob 		break;
26740699dccSrob 	case RTM_IFANNOUNCE:
26840699dccSrob 		memcpy(&ifan, rtm, sizeof(ifan));
26940699dccSrob 		switch (ifan.ifan_what) {
27040699dccSrob 		case IFAN_DEPARTURE:
27140699dccSrob 			log_warnx("interface %s departed", ifan.ifan_name);
27240699dccSrob 			check_ifdeparture();
27340699dccSrob 			break;
27440699dccSrob 		case IFAN_ARRIVAL:
27540699dccSrob 			log_warnx("interface %s arrived", ifan.ifan_name);
276089b0f81Srob 			fetch_ifstate(1);
277089b0f81Srob 			break;
27840699dccSrob 		}
27940699dccSrob 		break;
28040699dccSrob 	case RTM_DESYNC:
28140699dccSrob 		/* we lost some routing messages so rescan interfaces */
28240699dccSrob 		check_ifdeparture();
28340699dccSrob 		fetch_ifstate(1);
284089b0f81Srob 		break;
285089b0f81Srob 	}
286089b0f81Srob 	return;
2876dbcce3fSmcbride }
2886dbcce3fSmcbride 
289b73b88efSmcbride void
sigchld_handler(int fd,short event,void * arg)290b73b88efSmcbride sigchld_handler(int fd, short event, void *arg)
291b73b88efSmcbride {
29220863243Sbenno 	check_external_status(&conf->initstate);
2938bb56c35Smcbride 	if (conf->curstate != NULL)
2948bb56c35Smcbride 		check_external_status(conf->curstate);
295b73b88efSmcbride }
296b73b88efSmcbride 
297b73b88efSmcbride void
external_handler(int fd,short event,void * arg)298b73b88efSmcbride external_handler(int fd, short event, void *arg)
299b73b88efSmcbride {
300b73b88efSmcbride 	struct ifsd_external *external = (struct ifsd_external *)arg;
301b73b88efSmcbride 	struct timeval tv;
302b73b88efSmcbride 
303b73b88efSmcbride 	/* re-schedule */
30432a8a117Sokan 	timerclear(&tv);
305b73b88efSmcbride 	tv.tv_sec = external->frequency;
306b73b88efSmcbride 	evtimer_set(&external->ev, external_handler, external);
307b73b88efSmcbride 	evtimer_add(&external->ev, &tv);
308b73b88efSmcbride 
309b73b88efSmcbride 	/* execute */
310b688c394Smpf 	external_exec(external, 1);
311b73b88efSmcbride }
312b73b88efSmcbride 
313b73b88efSmcbride void
external_exec(struct ifsd_external * external,int async)314b688c394Smpf external_exec(struct ifsd_external *external, int async)
315b73b88efSmcbride {
316b73b88efSmcbride 	char *argp[] = {"sh", "-c", NULL, NULL};
317aba3295aSderaadt 	pid_t pid;
318172fe27aSmpf 	int s;
319b73b88efSmcbride 
320b73b88efSmcbride 	if (external->pid > 0) {
3218c3698cfSstsp 		log_debug("previous command %s [%d] still running, killing it",
32222794b72Smpf 		    external->command, external->pid);
323b73b88efSmcbride 		kill(external->pid, SIGKILL);
324172fe27aSmpf 		waitpid(external->pid, &s, 0);
325b73b88efSmcbride 		external->pid = 0;
326b73b88efSmcbride 	}
327b73b88efSmcbride 
328b73b88efSmcbride 	argp[2] = external->command;
32935360db1Spyr 	log_debug("running %s", external->command);
330b73b88efSmcbride 	pid = fork();
331df69c215Sderaadt 	if (pid == -1) {
33235360db1Spyr 		log_warn("fork error");
333b73b88efSmcbride 	} else if (pid == 0) {
334a4459108Smestre 		execv(_PATH_BSHELL, argp);
335b73b88efSmcbride 		_exit(1);
336b73b88efSmcbride 		/* NOTREACHED */
337b73b88efSmcbride 	} else {
338b73b88efSmcbride 		external->pid = pid;
339b73b88efSmcbride 	}
340b688c394Smpf 	if (!async) {
341b688c394Smpf 		waitpid(external->pid, &s, 0);
342b688c394Smpf 		external->pid = 0;
343b688c394Smpf 		if (WIFEXITED(s))
344b688c394Smpf 			external->prevstatus = WEXITSTATUS(s);
345b688c394Smpf 	}
346b688c394Smpf }
347b688c394Smpf 
348b688c394Smpf void
adjust_external_expressions(struct ifsd_state * state)349b688c394Smpf adjust_external_expressions(struct ifsd_state *state)
350b688c394Smpf {
351b688c394Smpf 	struct ifsd_external *external;
352b688c394Smpf 	struct ifsd_expression_list expressions;
353b688c394Smpf 
354b688c394Smpf 	TAILQ_INIT(&expressions);
355b688c394Smpf 	TAILQ_FOREACH(external, &state->external_tests, entries) {
356b688c394Smpf 		struct ifsd_expression *expression;
357b688c394Smpf 
358b688c394Smpf 		if (external->prevstatus == -1)
359b688c394Smpf 			continue;
360b688c394Smpf 
361b688c394Smpf 		TAILQ_FOREACH(expression, &external->expressions, entries) {
362b688c394Smpf 			TAILQ_INSERT_TAIL(&expressions,
363b688c394Smpf 			    expression, eval);
3646ee78c37Smpf 			expression->truth = !external->prevstatus;
365b688c394Smpf 		}
366b688c394Smpf 		adjust_expressions(&expressions, conf->maxdepth);
367b688c394Smpf 	}
368b73b88efSmcbride }
369b73b88efSmcbride 
370b73b88efSmcbride void
check_external_status(struct ifsd_state * state)371b73b88efSmcbride check_external_status(struct ifsd_state *state)
372b73b88efSmcbride {
373b73b88efSmcbride 	struct ifsd_external *external, *end = NULL;
374b73b88efSmcbride 	int status, s, changed = 0;
375b73b88efSmcbride 
376b73b88efSmcbride 	/* Do this manually; change ordering so the oldest is first */
377b73b88efSmcbride 	external = TAILQ_FIRST(&state->external_tests);
378b73b88efSmcbride 	while (external != NULL && external != end) {
379b73b88efSmcbride 		struct ifsd_external *newexternal;
380b73b88efSmcbride 
381b73b88efSmcbride 		newexternal = TAILQ_NEXT(external, entries);
382b73b88efSmcbride 
383b73b88efSmcbride 		if (external->pid <= 0)
384b73b88efSmcbride 			goto loop;
385b73b88efSmcbride 
386b73b88efSmcbride 		if (wait4(external->pid, &s, WNOHANG, NULL) == 0)
387b73b88efSmcbride 			goto loop;
388b73b88efSmcbride 
389b73b88efSmcbride 		external->pid = 0;
390b73b88efSmcbride 		if (end == NULL)
391b73b88efSmcbride 			end = external;
392b73b88efSmcbride 		if (WIFEXITED(s))
393b73b88efSmcbride 			status = WEXITSTATUS(s);
394b73b88efSmcbride 		else {
39535360db1Spyr 			log_warnx("%s exited abnormally", external->command);
396b73b88efSmcbride 			goto loop;
397b73b88efSmcbride 		}
398b73b88efSmcbride 
399b73b88efSmcbride 		if (external->prevstatus != status &&
400b73b88efSmcbride 		    (external->prevstatus != -1 || !opt_inhibit)) {
401b73b88efSmcbride 			changed = 1;
402b688c394Smpf 			external->prevstatus = status;
403b73b88efSmcbride 		}
404b73b88efSmcbride 		external->lastexec = time(NULL);
405b73b88efSmcbride 		TAILQ_REMOVE(&state->external_tests, external, entries);
406b73b88efSmcbride 		TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
407b73b88efSmcbride loop:
408b73b88efSmcbride 		external = newexternal;
409b73b88efSmcbride 	}
410b73b88efSmcbride 
411b73b88efSmcbride 	if (changed) {
412b688c394Smpf 		adjust_external_expressions(state);
413b73b88efSmcbride 		eval_state(state);
414b73b88efSmcbride 	}
415b73b88efSmcbride }
416b73b88efSmcbride 
417b73b88efSmcbride void
external_evtimer_setup(struct ifsd_state * state,int action)418b73b88efSmcbride external_evtimer_setup(struct ifsd_state *state, int action)
419b73b88efSmcbride {
420b73b88efSmcbride 	struct ifsd_external *external;
421172fe27aSmpf 	int s;
422b73b88efSmcbride 
423b73b88efSmcbride 	if (state != NULL) {
424b73b88efSmcbride 		switch (action) {
425b73b88efSmcbride 		case IFSD_EVTIMER_ADD:
426b73b88efSmcbride 			TAILQ_FOREACH(external,
427b73b88efSmcbride 			    &state->external_tests, entries) {
428b73b88efSmcbride 				struct timeval tv;
429b73b88efSmcbride 
430b73b88efSmcbride 				/* run it once right away */
431b688c394Smpf 				external_exec(external, 0);
432b73b88efSmcbride 
433b73b88efSmcbride 				/* schedule it for later */
43432a8a117Sokan 				timerclear(&tv);
435b73b88efSmcbride 				tv.tv_sec = external->frequency;
436b73b88efSmcbride 				evtimer_set(&external->ev, external_handler,
437b73b88efSmcbride 				    external);
438b73b88efSmcbride 				evtimer_add(&external->ev, &tv);
439b73b88efSmcbride 			}
440b73b88efSmcbride 			break;
441b73b88efSmcbride 		case IFSD_EVTIMER_DEL:
442b73b88efSmcbride 			TAILQ_FOREACH(external,
443b73b88efSmcbride 			    &state->external_tests, entries) {
444b73b88efSmcbride 				if (external->pid > 0) {
445b73b88efSmcbride 					kill(external->pid, SIGKILL);
446172fe27aSmpf 					waitpid(external->pid, &s, 0);
447b73b88efSmcbride 					external->pid = 0;
448b73b88efSmcbride 				}
449b73b88efSmcbride 				evtimer_del(&external->ev);
450b73b88efSmcbride 			}
451b73b88efSmcbride 			break;
452b73b88efSmcbride 		}
453b73b88efSmcbride 	}
454b73b88efSmcbride }
455b73b88efSmcbride 
4569a2e0324Sclaudio #define	LINK_STATE_IS_DOWN(_s)		(!LINK_STATE_IS_UP((_s)))
457a3630be1Sstevesk 
458b73b88efSmcbride int
scan_ifstate_single(const char * ifname,int s,struct ifsd_state * state)45918e4260cSrob scan_ifstate_single(const char *ifname, int s, struct ifsd_state *state)
460b73b88efSmcbride {
461b73b88efSmcbride 	struct ifsd_ifstate *ifstate;
462b73b88efSmcbride 	struct ifsd_expression_list expressions;
463b73b88efSmcbride 	int changed = 0;
464b73b88efSmcbride 
465b73b88efSmcbride 	TAILQ_INIT(&expressions);
466b73b88efSmcbride 
467b73b88efSmcbride 	TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
46818e4260cSrob 		if (strcmp(ifstate->ifname, ifname) == 0) {
469b73b88efSmcbride 			if (ifstate->prevstate != s &&
470b73b88efSmcbride 			    (ifstate->prevstate != -1 || !opt_inhibit)) {
471b73b88efSmcbride 				struct ifsd_expression *expression;
472b73b88efSmcbride 				int truth;
473b73b88efSmcbride 
474a3630be1Sstevesk 				truth =
475a3630be1Sstevesk 				    (ifstate->ifstate == IFSD_LINKUNKNOWN &&
476a3630be1Sstevesk 				    s == LINK_STATE_UNKNOWN) ||
477a3630be1Sstevesk 				    (ifstate->ifstate == IFSD_LINKDOWN &&
478a3630be1Sstevesk 				    LINK_STATE_IS_DOWN(s)) ||
479cda33248Sreyk 				    (ifstate->ifstate == IFSD_LINKUP &&
480cda33248Sreyk 				    LINK_STATE_IS_UP(s));
481b73b88efSmcbride 
482b73b88efSmcbride 				TAILQ_FOREACH(expression,
483b73b88efSmcbride 				    &ifstate->expressions, entries) {
484b73b88efSmcbride 					expression->truth = truth;
485b73b88efSmcbride 					TAILQ_INSERT_TAIL(&expressions,
486b73b88efSmcbride 					    expression, eval);
487b73b88efSmcbride 					changed = 1;
488b73b88efSmcbride 				}
489b73b88efSmcbride 				ifstate->prevstate = s;
490b73b88efSmcbride 			}
491b73b88efSmcbride 		}
492b73b88efSmcbride 	}
493b73b88efSmcbride 
494b73b88efSmcbride 	if (changed)
4958bb56c35Smcbride 		adjust_expressions(&expressions, conf->maxdepth);
496b73b88efSmcbride 	return (changed);
497b73b88efSmcbride }
4986dbcce3fSmcbride 
499616cc397Smpf void
scan_ifstate(const char * ifname,int s,int do_eval)50018e4260cSrob scan_ifstate(const char *ifname, int s, int do_eval)
501616cc397Smpf {
502616cc397Smpf 	struct ifsd_state *state;
503616cc397Smpf 	int cur_eval = 0;
504616cc397Smpf 
50518e4260cSrob 	if (scan_ifstate_single(ifname, s, &conf->initstate) && do_eval)
50620863243Sbenno 		eval_state(&conf->initstate);
507616cc397Smpf 	TAILQ_FOREACH(state, &conf->states, entries) {
50818e4260cSrob 		if (scan_ifstate_single(ifname, s, state) &&
509616cc397Smpf 		    (do_eval && state == conf->curstate))
510616cc397Smpf 			cur_eval = 1;
511616cc397Smpf 	}
512616cc397Smpf 	/* execute actions _after_ all expressions have been adjusted */
513616cc397Smpf 	if (cur_eval)
514616cc397Smpf 		eval_state(conf->curstate);
515616cc397Smpf }
516616cc397Smpf 
5176dbcce3fSmcbride /*
5183a50f0a9Sjmc  * Do a bottom-up adjustment of the expression tree's truth value,
519b73b88efSmcbride  * level-by-level to ensure that each expression's subexpressions have been
520b73b88efSmcbride  * evaluated.
5216dbcce3fSmcbride  */
5226dbcce3fSmcbride void
adjust_expressions(struct ifsd_expression_list * expressions,int depth)523b73b88efSmcbride adjust_expressions(struct ifsd_expression_list *expressions, int depth)
5246dbcce3fSmcbride {
525b73b88efSmcbride 	struct ifsd_expression_list nexpressions;
526b73b88efSmcbride 	struct ifsd_expression *expression;
5276dbcce3fSmcbride 
528b73b88efSmcbride 	TAILQ_INIT(&nexpressions);
529b73b88efSmcbride 	while ((expression = TAILQ_FIRST(expressions)) != NULL) {
530b73b88efSmcbride 		TAILQ_REMOVE(expressions, expression, eval);
531b73b88efSmcbride 		if (expression->depth == depth) {
532b73b88efSmcbride 			struct ifsd_expression *te;
5336dbcce3fSmcbride 
534b73b88efSmcbride 			switch (expression->type) {
535b73b88efSmcbride 			case IFSD_OPER_AND:
5366ee78c37Smpf 				expression->truth = expression->left->truth &&
5376ee78c37Smpf 				    expression->right->truth;
538b73b88efSmcbride 				break;
539b73b88efSmcbride 			case IFSD_OPER_OR:
5406ee78c37Smpf 				expression->truth = expression->left->truth ||
5416ee78c37Smpf 				    expression->right->truth;
542b73b88efSmcbride 				break;
543b73b88efSmcbride 			case IFSD_OPER_NOT:
5446ee78c37Smpf 				expression->truth = !expression->right->truth;
545b73b88efSmcbride 				break;
546b73b88efSmcbride 			default:
547b73b88efSmcbride 				break;
5486dbcce3fSmcbride 			}
549b73b88efSmcbride 			if (expression->parent != NULL) {
550b73b88efSmcbride 				if (TAILQ_EMPTY(&nexpressions))
551b73b88efSmcbride 					te = NULL;
552b73b88efSmcbride 				TAILQ_FOREACH(te, &nexpressions, eval)
553b73b88efSmcbride 					if (expression->parent == te)
554b73b88efSmcbride 						break;
555b73b88efSmcbride 				if (te == NULL)
556b73b88efSmcbride 					TAILQ_INSERT_TAIL(&nexpressions,
557b73b88efSmcbride 					    expression->parent, eval);
5586dbcce3fSmcbride 			}
559b73b88efSmcbride 		} else
560b73b88efSmcbride 			TAILQ_INSERT_TAIL(&nexpressions, expression, eval);
561b73b88efSmcbride 	}
562b73b88efSmcbride 	if (depth > 0)
563b73b88efSmcbride 		adjust_expressions(&nexpressions, depth - 1);
5646dbcce3fSmcbride }
5656dbcce3fSmcbride 
566b73b88efSmcbride void
eval_state(struct ifsd_state * state)567b73b88efSmcbride eval_state(struct ifsd_state *state)
568b73b88efSmcbride {
5692f67262dSbenno 	struct ifsd_external *external;
5702f67262dSbenno 
5712f67262dSbenno 	external = TAILQ_FIRST(&state->external_tests);
57222794b72Smpf 	if (external == NULL || external->lastexec >= state->entered ||
57322794b72Smpf 	    external->lastexec == 0) {
574138e089fSbenno 		do_action(state->body);
575988c4ffaSbenno 		while (state_change()) {
576988c4ffaSbenno 			do_action(conf->curstate->init);
577138e089fSbenno 			do_action(conf->curstate->body);
578b73b88efSmcbride 		}
579b73b88efSmcbride 	}
580988c4ffaSbenno }
581b73b88efSmcbride 
582b688c394Smpf int
state_change(void)583b73b88efSmcbride state_change(void)
584b73b88efSmcbride {
5858bb56c35Smcbride 	if (conf->nextstate != NULL && conf->curstate != conf->nextstate) {
58635360db1Spyr 		log_info("changing state to %s", conf->nextstate->name);
58722794b72Smpf 		if (conf->curstate != NULL) {
5888bb56c35Smcbride 			evtimer_del(&conf->curstate->ev);
5898bb56c35Smcbride 			external_evtimer_setup(conf->curstate,
5908bb56c35Smcbride 			    IFSD_EVTIMER_DEL);
59122794b72Smpf 		}
5928bb56c35Smcbride 		conf->curstate = conf->nextstate;
5938bb56c35Smcbride 		conf->nextstate = NULL;
5948bb56c35Smcbride 		conf->curstate->entered = time(NULL);
5958bb56c35Smcbride 		external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD);
596b688c394Smpf 		adjust_external_expressions(conf->curstate);
597b688c394Smpf 		return (1);
598b73b88efSmcbride 	}
599b688c394Smpf 	return (0);
600b73b88efSmcbride }
601b73b88efSmcbride 
602b73b88efSmcbride /*
603b73b88efSmcbride  * Run recursively through the tree of actions.
604b73b88efSmcbride  */
605b73b88efSmcbride void
do_action(struct ifsd_action * action)606b73b88efSmcbride do_action(struct ifsd_action *action)
607b73b88efSmcbride {
608b73b88efSmcbride 	struct ifsd_action *subaction;
609b73b88efSmcbride 
610b73b88efSmcbride 	switch (action->type) {
611b73b88efSmcbride 	case IFSD_ACTION_COMMAND:
6128c3698cfSstsp 		log_debug("running %s", action->act.command);
613b73b88efSmcbride 		system(action->act.command);
614b73b88efSmcbride 		break;
615b73b88efSmcbride 	case IFSD_ACTION_CHANGESTATE:
6168bb56c35Smcbride 		conf->nextstate = action->act.nextstate;
617b73b88efSmcbride 		break;
618b73b88efSmcbride 	case IFSD_ACTION_CONDITION:
619b73b88efSmcbride 		if ((action->act.c.expression != NULL &&
620b73b88efSmcbride 		    action->act.c.expression->truth) ||
621b73b88efSmcbride 		    action->act.c.expression == NULL) {
622b73b88efSmcbride 			TAILQ_FOREACH(subaction, &action->act.c.actions,
623b73b88efSmcbride 			    entries)
624b73b88efSmcbride 				do_action(subaction);
625b73b88efSmcbride 		}
626b73b88efSmcbride 		break;
627b73b88efSmcbride 	default:
628bfdb9ad4Sbenno 		log_debug("%s: unknown action %d", __func__, action->type);
629b73b88efSmcbride 		break;
630b73b88efSmcbride 	}
631b73b88efSmcbride }
632b73b88efSmcbride 
633b73b88efSmcbride /*
634b73b88efSmcbride  * Fetch the current link states.
635b73b88efSmcbride  */
6366dbcce3fSmcbride void
fetch_ifstate(int do_eval)637089b0f81Srob fetch_ifstate(int do_eval)
6386dbcce3fSmcbride {
639b73b88efSmcbride 	struct ifaddrs *ifap, *ifa;
6406dbcce3fSmcbride 
641b73b88efSmcbride 	if (getifaddrs(&ifap) != 0)
64295b4ea67Srob 		fatal("getifaddrs");
643b73b88efSmcbride 
644b73b88efSmcbride 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
6450aada395Sbenno 		if (ifa->ifa_addr != NULL &&
6460aada395Sbenno 		    ifa->ifa_addr->sa_family == AF_LINK) {
6476b38c177Sjca 			struct if_data *ifdata = ifa->ifa_data;
64818e4260cSrob 			scan_ifstate(ifa->ifa_name, ifdata->ifi_link_state,
64918e4260cSrob 			    do_eval);
6506b38c177Sjca 		}
6516dbcce3fSmcbride 	}
6524baea900Sjca 
6539b890fceSmcbride 	freeifaddrs(ifap);
6546dbcce3fSmcbride }
655b73b88efSmcbride 
656b73b88efSmcbride void
check_ifdeparture(void)65740699dccSrob check_ifdeparture(void)
65840699dccSrob {
65940699dccSrob 	struct ifsd_state *state;
66040699dccSrob 	struct ifsd_ifstate *ifstate;
66140699dccSrob 
66240699dccSrob 	TAILQ_FOREACH(state, &conf->states, entries) {
66340699dccSrob 		TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
66440699dccSrob 			if (if_nametoindex(ifstate->ifname) == 0)
66540699dccSrob 				scan_ifstate(ifstate->ifname,
66640699dccSrob 				    LINK_STATE_DOWN, 1);
66740699dccSrob 		}
66840699dccSrob 	}
66940699dccSrob }
67040699dccSrob 
67140699dccSrob void
clear_config(struct ifsd_config * oconf)672b73b88efSmcbride clear_config(struct ifsd_config *oconf)
673b73b88efSmcbride {
674b73b88efSmcbride 	struct ifsd_state *state;
675b73b88efSmcbride 
67620863243Sbenno 	external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_DEL);
6771d74c151Smcbride 	if (conf != NULL && conf->curstate != NULL)
6788bb56c35Smcbride 		external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL);
679b73b88efSmcbride 	while ((state = TAILQ_FIRST(&oconf->states)) != NULL) {
680b73b88efSmcbride 		TAILQ_REMOVE(&oconf->states, state, entries);
681b73b88efSmcbride 		remove_action(state->init, state);
682138e089fSbenno 		remove_action(state->body, state);
683b73b88efSmcbride 		free(state->name);
684b73b88efSmcbride 		free(state);
685b73b88efSmcbride 	}
68620863243Sbenno 	remove_action(oconf->initstate.init, &oconf->initstate);
68720863243Sbenno 	remove_action(oconf->initstate.body, &oconf->initstate);
688346e1d62Smcbride 	free(oconf);
689b73b88efSmcbride }
690b73b88efSmcbride 
691b73b88efSmcbride void
remove_action(struct ifsd_action * action,struct ifsd_state * state)692b73b88efSmcbride remove_action(struct ifsd_action *action, struct ifsd_state *state)
693b73b88efSmcbride {
694b73b88efSmcbride 	struct ifsd_action *subaction;
695b73b88efSmcbride 
696b73b88efSmcbride 	if (action == NULL || state == NULL)
697b73b88efSmcbride 		return;
698b73b88efSmcbride 
699b73b88efSmcbride 	switch (action->type) {
700b73b88efSmcbride 	case IFSD_ACTION_COMMAND:
701b73b88efSmcbride 		free(action->act.command);
702b73b88efSmcbride 		break;
703b73b88efSmcbride 	case IFSD_ACTION_CHANGESTATE:
704b73b88efSmcbride 		break;
705b73b88efSmcbride 	case IFSD_ACTION_CONDITION:
706b73b88efSmcbride 		if (action->act.c.expression != NULL)
707b73b88efSmcbride 			remove_expression(action->act.c.expression, state);
708b73b88efSmcbride 		while ((subaction =
709b73b88efSmcbride 		    TAILQ_FIRST(&action->act.c.actions)) != NULL) {
710b73b88efSmcbride 			TAILQ_REMOVE(&action->act.c.actions,
711b73b88efSmcbride 			    subaction, entries);
712b73b88efSmcbride 			remove_action(subaction, state);
713b73b88efSmcbride 		}
714b73b88efSmcbride 	}
715b73b88efSmcbride 	free(action);
716b73b88efSmcbride }
717b73b88efSmcbride 
718b73b88efSmcbride void
remove_expression(struct ifsd_expression * expression,struct ifsd_state * state)719b73b88efSmcbride remove_expression(struct ifsd_expression *expression,
720b73b88efSmcbride     struct ifsd_state *state)
721b73b88efSmcbride {
722b73b88efSmcbride 	switch (expression->type) {
723b73b88efSmcbride 	case IFSD_OPER_IFSTATE:
724b73b88efSmcbride 		TAILQ_REMOVE(&expression->u.ifstate->expressions, expression,
725b73b88efSmcbride 		    entries);
726b73b88efSmcbride 		if (--expression->u.ifstate->refcount == 0) {
727b73b88efSmcbride 			TAILQ_REMOVE(&state->interface_states,
728b73b88efSmcbride 			    expression->u.ifstate, entries);
729b73b88efSmcbride 			free(expression->u.ifstate);
730b73b88efSmcbride 		}
731b73b88efSmcbride 		break;
732b73b88efSmcbride 	case IFSD_OPER_EXTERNAL:
733b73b88efSmcbride 		TAILQ_REMOVE(&expression->u.external->expressions, expression,
734b73b88efSmcbride 		    entries);
735b73b88efSmcbride 		if (--expression->u.external->refcount == 0) {
736b73b88efSmcbride 			TAILQ_REMOVE(&state->external_tests,
737b73b88efSmcbride 			    expression->u.external, entries);
738b73b88efSmcbride 			free(expression->u.external->command);
739b73b88efSmcbride 			event_del(&expression->u.external->ev);
740b73b88efSmcbride 			free(expression->u.external);
741b73b88efSmcbride 		}
742b73b88efSmcbride 		break;
743b73b88efSmcbride 	default:
744b73b88efSmcbride 		if (expression->left != NULL)
745b73b88efSmcbride 			remove_expression(expression->left, state);
746b73b88efSmcbride 		if (expression->right != NULL)
747b73b88efSmcbride 			remove_expression(expression->right, state);
748b73b88efSmcbride 		break;
749b73b88efSmcbride 	}
750b73b88efSmcbride 	free(expression);
751b73b88efSmcbride }
752