xref: /openbsd/usr.sbin/ifstated/ifstated.c (revision e6c7c102)
1 /*	$OpenBSD: ifstated.c,v 1.68 2024/04/23 13:34:51 jsg Exp $	*/
2 
3 /*
4  * Copyright (c) 2004 Marco Pfatschbacher <mpf@openbsd.org>
5  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*
21  * ifstated listens to link_state transitions on interfaces
22  * and executes predefined commands.
23  */
24 
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #include <sys/socket.h>
28 #include <sys/wait.h>
29 
30 #include <net/if.h>
31 #include <net/route.h>
32 #include <netinet/in.h>
33 
34 #include <paths.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <signal.h>
39 #include <stdint.h>
40 #include <syslog.h>
41 #include <errno.h>
42 #include <event.h>
43 #include <unistd.h>
44 #include <ifaddrs.h>
45 
46 #include "ifstated.h"
47 #include "log.h"
48 
49 struct	 ifsd_config *conf, *newconf;
50 
51 int	 opts;
52 int	 opt_inhibit;
53 char	*configfile = "/etc/ifstated.conf";
54 struct event	rt_msg_ev, sighup_ev, startup_ev, sigchld_ev;
55 
56 void		startup_handler(int, short, void *);
57 void		sighup_handler(int, short, void *);
58 int		load_config(void);
59 void		sigchld_handler(int, short, void *);
60 void		rt_msg_handler(int, short, void *);
61 void		external_handler(int, short, void *);
62 void		external_exec(struct ifsd_external *, int);
63 void		check_external_status(struct ifsd_state *);
64 void		check_ifdeparture(void);
65 void		external_evtimer_setup(struct ifsd_state *, int);
66 void		scan_ifstate(const char *, int, int);
67 int		scan_ifstate_single(const char *, int, struct ifsd_state *);
68 void		fetch_ifstate(int);
69 __dead void	usage(void);
70 void		adjust_expressions(struct ifsd_expression_list *, int);
71 void		adjust_external_expressions(struct ifsd_state *);
72 void		eval_state(struct ifsd_state *);
73 int		state_change(void);
74 void		do_action(struct ifsd_action *);
75 void		remove_action(struct ifsd_action *, struct ifsd_state *);
76 void		remove_expression(struct ifsd_expression *,
77 		    struct ifsd_state *);
78 
79 __dead void
usage(void)80 usage(void)
81 {
82 	extern char *__progname;
83 
84 	fprintf(stderr, "usage: %s [-dhinv] [-D macro=value] [-f file]\n",
85 	    __progname);
86 	exit(1);
87 }
88 
89 int
main(int argc,char * argv[])90 main(int argc, char *argv[])
91 {
92 	struct timeval tv;
93 	int ch, rt_fd;
94 	int debug = 0;
95 	unsigned int rtfilter;
96 
97 	log_init(1, LOG_DAEMON);	/* log to stderr until daemonized */
98 	log_setverbose(1);
99 
100 	while ((ch = getopt(argc, argv, "dD:f:hniv")) != -1) {
101 		switch (ch) {
102 		case 'd':
103 			debug = 1;
104 			break;
105 		case 'D':
106 			if (cmdline_symset(optarg) < 0)
107 				fatalx("could not parse macro definition %s",
108 				    optarg);
109 			break;
110 		case 'f':
111 			configfile = optarg;
112 			break;
113 		case 'h':
114 			usage();
115 			break;
116 		case 'n':
117 			opts |= IFSD_OPT_NOACTION;
118 			break;
119 		case 'i':
120 			opt_inhibit = 1;
121 			break;
122 		case 'v':
123 			if (opts & IFSD_OPT_VERBOSE)
124 				opts |= IFSD_OPT_VERBOSE2;
125 			opts |= IFSD_OPT_VERBOSE;
126 			break;
127 		default:
128 			usage();
129 		}
130 	}
131 
132 	argc -= optind;
133 	argv += optind;
134 	if (argc > 0)
135 		usage();
136 
137 	if (opts & IFSD_OPT_NOACTION) {
138 		if ((newconf = parse_config(configfile, opts)) == NULL)
139 			exit(1);
140 		fprintf(stderr, "configuration OK\n");
141 		exit(0);
142 	}
143 
144 	if (!debug)
145 		daemon(1, 0);
146 
147 	event_init();
148 	log_init(debug, LOG_DAEMON);
149 	log_setverbose(opts & IFSD_OPT_VERBOSE);
150 
151 	if ((rt_fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
152 		fatal("no routing socket");
153 
154 	rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE);
155 	if (setsockopt(rt_fd, AF_ROUTE, ROUTE_MSGFILTER,
156 	    &rtfilter, sizeof(rtfilter)) == -1)	/* not fatal */
157 		log_warn("%s: setsockopt msgfilter", __func__);
158 
159 	rtfilter = RTABLE_ANY;
160 	if (setsockopt(rt_fd, AF_ROUTE, ROUTE_TABLEFILTER,
161 	    &rtfilter, sizeof(rtfilter)) == -1)	/* not fatal */
162 		log_warn("%s: setsockopt tablefilter", __func__);
163 
164 	if (unveil(configfile, "r") == -1)
165 		fatal("unveil %s", configfile);
166 	if (unveil(_PATH_BSHELL, "x") == -1)
167 		fatal("unveil %s", _PATH_BSHELL);
168 	if (pledge("stdio rpath route proc exec", NULL) == -1)
169 		fatal("pledge");
170 
171 	signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL);
172 	signal_add(&sigchld_ev, NULL);
173 
174 	/* Loading the config needs to happen in the event loop */
175 	timerclear(&tv);
176 	evtimer_set(&startup_ev, startup_handler, (void *)(long)rt_fd);
177 	evtimer_add(&startup_ev, &tv);
178 
179 	event_loop(0);
180 	exit(0);
181 }
182 
183 void
startup_handler(int fd,short event,void * arg)184 startup_handler(int fd, short event, void *arg)
185 {
186 	int rfd = (int)(long)arg;
187 
188 	if (load_config() != 0) {
189 		log_warnx("unable to load config");
190 		exit(1);
191 	}
192 
193 	event_set(&rt_msg_ev, rfd, EV_READ|EV_PERSIST, rt_msg_handler, NULL);
194 	event_add(&rt_msg_ev, NULL);
195 
196 	signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL);
197 	signal_add(&sighup_ev, NULL);
198 
199 	log_info("started");
200 }
201 
202 void
sighup_handler(int fd,short event,void * arg)203 sighup_handler(int fd, short event, void *arg)
204 {
205 	log_info("reloading config");
206 	if (load_config() != 0)
207 		log_warnx("unable to reload config");
208 }
209 
210 int
load_config(void)211 load_config(void)
212 {
213 	if ((newconf = parse_config(configfile, opts)) == NULL)
214 		return (-1);
215 	if (conf != NULL)
216 		clear_config(conf);
217 	conf = newconf;
218 	conf->initstate.entered = time(NULL);
219 	fetch_ifstate(0);
220 	external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_ADD);
221 	adjust_external_expressions(&conf->initstate);
222 	eval_state(&conf->initstate);
223 	if (conf->curstate != NULL) {
224 		log_info("initial state: %s", conf->curstate->name);
225 		conf->curstate->entered = time(NULL);
226 		conf->nextstate = conf->curstate;
227 		conf->curstate = NULL;
228 		while (state_change()) {
229 			do_action(conf->curstate->init);
230 			do_action(conf->curstate->body);
231 		}
232 	}
233 	return (0);
234 }
235 
236 void
rt_msg_handler(int fd,short event,void * arg)237 rt_msg_handler(int fd, short event, void *arg)
238 {
239 	char msg[2048];
240 	struct rt_msghdr *rtm = (struct rt_msghdr *)&msg;
241 	struct if_msghdr ifm;
242 	struct if_announcemsghdr ifan;
243 	char ifnamebuf[IFNAMSIZ];
244 	char *ifname;
245 	ssize_t len;
246 
247 	if ((len = read(fd, msg, sizeof(msg))) == -1) {
248 		if (errno == EAGAIN || errno == EINTR)
249 			return;
250 		fatal("%s: routing socket read error", __func__);
251 	}
252 
253 	if (len == 0)
254 		fatal("%s: routing socket closed", __func__);
255 
256 	if (rtm->rtm_version != RTM_VERSION)
257 		return;
258 
259 	switch (rtm->rtm_type) {
260 	case RTM_IFINFO:
261 		memcpy(&ifm, rtm, sizeof(ifm));
262 		ifname = if_indextoname(ifm.ifm_index, ifnamebuf);
263 		/* ifname is NULL on interface departure */
264 		if (ifname != NULL)
265 			scan_ifstate(ifname, ifm.ifm_data.ifi_link_state, 1);
266 		break;
267 	case RTM_IFANNOUNCE:
268 		memcpy(&ifan, rtm, sizeof(ifan));
269 		switch (ifan.ifan_what) {
270 		case IFAN_DEPARTURE:
271 			log_warnx("interface %s departed", ifan.ifan_name);
272 			check_ifdeparture();
273 			break;
274 		case IFAN_ARRIVAL:
275 			log_warnx("interface %s arrived", ifan.ifan_name);
276 			fetch_ifstate(1);
277 			break;
278 		}
279 		break;
280 	case RTM_DESYNC:
281 		/* we lost some routing messages so rescan interfaces */
282 		check_ifdeparture();
283 		fetch_ifstate(1);
284 		break;
285 	}
286 	return;
287 }
288 
289 void
sigchld_handler(int fd,short event,void * arg)290 sigchld_handler(int fd, short event, void *arg)
291 {
292 	check_external_status(&conf->initstate);
293 	if (conf->curstate != NULL)
294 		check_external_status(conf->curstate);
295 }
296 
297 void
external_handler(int fd,short event,void * arg)298 external_handler(int fd, short event, void *arg)
299 {
300 	struct ifsd_external *external = (struct ifsd_external *)arg;
301 	struct timeval tv;
302 
303 	/* re-schedule */
304 	timerclear(&tv);
305 	tv.tv_sec = external->frequency;
306 	evtimer_set(&external->ev, external_handler, external);
307 	evtimer_add(&external->ev, &tv);
308 
309 	/* execute */
310 	external_exec(external, 1);
311 }
312 
313 void
external_exec(struct ifsd_external * external,int async)314 external_exec(struct ifsd_external *external, int async)
315 {
316 	char *argp[] = {"sh", "-c", NULL, NULL};
317 	pid_t pid;
318 	int s;
319 
320 	if (external->pid > 0) {
321 		log_debug("previous command %s [%d] still running, killing it",
322 		    external->command, external->pid);
323 		kill(external->pid, SIGKILL);
324 		waitpid(external->pid, &s, 0);
325 		external->pid = 0;
326 	}
327 
328 	argp[2] = external->command;
329 	log_debug("running %s", external->command);
330 	pid = fork();
331 	if (pid == -1) {
332 		log_warn("fork error");
333 	} else if (pid == 0) {
334 		execv(_PATH_BSHELL, argp);
335 		_exit(1);
336 		/* NOTREACHED */
337 	} else {
338 		external->pid = pid;
339 	}
340 	if (!async) {
341 		waitpid(external->pid, &s, 0);
342 		external->pid = 0;
343 		if (WIFEXITED(s))
344 			external->prevstatus = WEXITSTATUS(s);
345 	}
346 }
347 
348 void
adjust_external_expressions(struct ifsd_state * state)349 adjust_external_expressions(struct ifsd_state *state)
350 {
351 	struct ifsd_external *external;
352 	struct ifsd_expression_list expressions;
353 
354 	TAILQ_INIT(&expressions);
355 	TAILQ_FOREACH(external, &state->external_tests, entries) {
356 		struct ifsd_expression *expression;
357 
358 		if (external->prevstatus == -1)
359 			continue;
360 
361 		TAILQ_FOREACH(expression, &external->expressions, entries) {
362 			TAILQ_INSERT_TAIL(&expressions,
363 			    expression, eval);
364 			expression->truth = !external->prevstatus;
365 		}
366 		adjust_expressions(&expressions, conf->maxdepth);
367 	}
368 }
369 
370 void
check_external_status(struct ifsd_state * state)371 check_external_status(struct ifsd_state *state)
372 {
373 	struct ifsd_external *external, *end = NULL;
374 	int status, s, changed = 0;
375 
376 	/* Do this manually; change ordering so the oldest is first */
377 	external = TAILQ_FIRST(&state->external_tests);
378 	while (external != NULL && external != end) {
379 		struct ifsd_external *newexternal;
380 
381 		newexternal = TAILQ_NEXT(external, entries);
382 
383 		if (external->pid <= 0)
384 			goto loop;
385 
386 		if (wait4(external->pid, &s, WNOHANG, NULL) == 0)
387 			goto loop;
388 
389 		external->pid = 0;
390 		if (end == NULL)
391 			end = external;
392 		if (WIFEXITED(s))
393 			status = WEXITSTATUS(s);
394 		else {
395 			log_warnx("%s exited abnormally", external->command);
396 			goto loop;
397 		}
398 
399 		if (external->prevstatus != status &&
400 		    (external->prevstatus != -1 || !opt_inhibit)) {
401 			changed = 1;
402 			external->prevstatus = status;
403 		}
404 		external->lastexec = time(NULL);
405 		TAILQ_REMOVE(&state->external_tests, external, entries);
406 		TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
407 loop:
408 		external = newexternal;
409 	}
410 
411 	if (changed) {
412 		adjust_external_expressions(state);
413 		eval_state(state);
414 	}
415 }
416 
417 void
external_evtimer_setup(struct ifsd_state * state,int action)418 external_evtimer_setup(struct ifsd_state *state, int action)
419 {
420 	struct ifsd_external *external;
421 	int s;
422 
423 	if (state != NULL) {
424 		switch (action) {
425 		case IFSD_EVTIMER_ADD:
426 			TAILQ_FOREACH(external,
427 			    &state->external_tests, entries) {
428 				struct timeval tv;
429 
430 				/* run it once right away */
431 				external_exec(external, 0);
432 
433 				/* schedule it for later */
434 				timerclear(&tv);
435 				tv.tv_sec = external->frequency;
436 				evtimer_set(&external->ev, external_handler,
437 				    external);
438 				evtimer_add(&external->ev, &tv);
439 			}
440 			break;
441 		case IFSD_EVTIMER_DEL:
442 			TAILQ_FOREACH(external,
443 			    &state->external_tests, entries) {
444 				if (external->pid > 0) {
445 					kill(external->pid, SIGKILL);
446 					waitpid(external->pid, &s, 0);
447 					external->pid = 0;
448 				}
449 				evtimer_del(&external->ev);
450 			}
451 			break;
452 		}
453 	}
454 }
455 
456 #define	LINK_STATE_IS_DOWN(_s)		(!LINK_STATE_IS_UP((_s)))
457 
458 int
scan_ifstate_single(const char * ifname,int s,struct ifsd_state * state)459 scan_ifstate_single(const char *ifname, int s, struct ifsd_state *state)
460 {
461 	struct ifsd_ifstate *ifstate;
462 	struct ifsd_expression_list expressions;
463 	int changed = 0;
464 
465 	TAILQ_INIT(&expressions);
466 
467 	TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
468 		if (strcmp(ifstate->ifname, ifname) == 0) {
469 			if (ifstate->prevstate != s &&
470 			    (ifstate->prevstate != -1 || !opt_inhibit)) {
471 				struct ifsd_expression *expression;
472 				int truth;
473 
474 				truth =
475 				    (ifstate->ifstate == IFSD_LINKUNKNOWN &&
476 				    s == LINK_STATE_UNKNOWN) ||
477 				    (ifstate->ifstate == IFSD_LINKDOWN &&
478 				    LINK_STATE_IS_DOWN(s)) ||
479 				    (ifstate->ifstate == IFSD_LINKUP &&
480 				    LINK_STATE_IS_UP(s));
481 
482 				TAILQ_FOREACH(expression,
483 				    &ifstate->expressions, entries) {
484 					expression->truth = truth;
485 					TAILQ_INSERT_TAIL(&expressions,
486 					    expression, eval);
487 					changed = 1;
488 				}
489 				ifstate->prevstate = s;
490 			}
491 		}
492 	}
493 
494 	if (changed)
495 		adjust_expressions(&expressions, conf->maxdepth);
496 	return (changed);
497 }
498 
499 void
scan_ifstate(const char * ifname,int s,int do_eval)500 scan_ifstate(const char *ifname, int s, int do_eval)
501 {
502 	struct ifsd_state *state;
503 	int cur_eval = 0;
504 
505 	if (scan_ifstate_single(ifname, s, &conf->initstate) && do_eval)
506 		eval_state(&conf->initstate);
507 	TAILQ_FOREACH(state, &conf->states, entries) {
508 		if (scan_ifstate_single(ifname, s, state) &&
509 		    (do_eval && state == conf->curstate))
510 			cur_eval = 1;
511 	}
512 	/* execute actions _after_ all expressions have been adjusted */
513 	if (cur_eval)
514 		eval_state(conf->curstate);
515 }
516 
517 /*
518  * Do a bottom-up adjustment of the expression tree's truth value,
519  * level-by-level to ensure that each expression's subexpressions have been
520  * evaluated.
521  */
522 void
adjust_expressions(struct ifsd_expression_list * expressions,int depth)523 adjust_expressions(struct ifsd_expression_list *expressions, int depth)
524 {
525 	struct ifsd_expression_list nexpressions;
526 	struct ifsd_expression *expression;
527 
528 	TAILQ_INIT(&nexpressions);
529 	while ((expression = TAILQ_FIRST(expressions)) != NULL) {
530 		TAILQ_REMOVE(expressions, expression, eval);
531 		if (expression->depth == depth) {
532 			struct ifsd_expression *te;
533 
534 			switch (expression->type) {
535 			case IFSD_OPER_AND:
536 				expression->truth = expression->left->truth &&
537 				    expression->right->truth;
538 				break;
539 			case IFSD_OPER_OR:
540 				expression->truth = expression->left->truth ||
541 				    expression->right->truth;
542 				break;
543 			case IFSD_OPER_NOT:
544 				expression->truth = !expression->right->truth;
545 				break;
546 			default:
547 				break;
548 			}
549 			if (expression->parent != NULL) {
550 				if (TAILQ_EMPTY(&nexpressions))
551 					te = NULL;
552 				TAILQ_FOREACH(te, &nexpressions, eval)
553 					if (expression->parent == te)
554 						break;
555 				if (te == NULL)
556 					TAILQ_INSERT_TAIL(&nexpressions,
557 					    expression->parent, eval);
558 			}
559 		} else
560 			TAILQ_INSERT_TAIL(&nexpressions, expression, eval);
561 	}
562 	if (depth > 0)
563 		adjust_expressions(&nexpressions, depth - 1);
564 }
565 
566 void
eval_state(struct ifsd_state * state)567 eval_state(struct ifsd_state *state)
568 {
569 	struct ifsd_external *external;
570 
571 	external = TAILQ_FIRST(&state->external_tests);
572 	if (external == NULL || external->lastexec >= state->entered ||
573 	    external->lastexec == 0) {
574 		do_action(state->body);
575 		while (state_change()) {
576 			do_action(conf->curstate->init);
577 			do_action(conf->curstate->body);
578 		}
579 	}
580 }
581 
582 int
state_change(void)583 state_change(void)
584 {
585 	if (conf->nextstate != NULL && conf->curstate != conf->nextstate) {
586 		log_info("changing state to %s", conf->nextstate->name);
587 		if (conf->curstate != NULL) {
588 			evtimer_del(&conf->curstate->ev);
589 			external_evtimer_setup(conf->curstate,
590 			    IFSD_EVTIMER_DEL);
591 		}
592 		conf->curstate = conf->nextstate;
593 		conf->nextstate = NULL;
594 		conf->curstate->entered = time(NULL);
595 		external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD);
596 		adjust_external_expressions(conf->curstate);
597 		return (1);
598 	}
599 	return (0);
600 }
601 
602 /*
603  * Run recursively through the tree of actions.
604  */
605 void
do_action(struct ifsd_action * action)606 do_action(struct ifsd_action *action)
607 {
608 	struct ifsd_action *subaction;
609 
610 	switch (action->type) {
611 	case IFSD_ACTION_COMMAND:
612 		log_debug("running %s", action->act.command);
613 		system(action->act.command);
614 		break;
615 	case IFSD_ACTION_CHANGESTATE:
616 		conf->nextstate = action->act.nextstate;
617 		break;
618 	case IFSD_ACTION_CONDITION:
619 		if ((action->act.c.expression != NULL &&
620 		    action->act.c.expression->truth) ||
621 		    action->act.c.expression == NULL) {
622 			TAILQ_FOREACH(subaction, &action->act.c.actions,
623 			    entries)
624 				do_action(subaction);
625 		}
626 		break;
627 	default:
628 		log_debug("%s: unknown action %d", __func__, action->type);
629 		break;
630 	}
631 }
632 
633 /*
634  * Fetch the current link states.
635  */
636 void
fetch_ifstate(int do_eval)637 fetch_ifstate(int do_eval)
638 {
639 	struct ifaddrs *ifap, *ifa;
640 
641 	if (getifaddrs(&ifap) != 0)
642 		fatal("getifaddrs");
643 
644 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
645 		if (ifa->ifa_addr != NULL &&
646 		    ifa->ifa_addr->sa_family == AF_LINK) {
647 			struct if_data *ifdata = ifa->ifa_data;
648 			scan_ifstate(ifa->ifa_name, ifdata->ifi_link_state,
649 			    do_eval);
650 		}
651 	}
652 
653 	freeifaddrs(ifap);
654 }
655 
656 void
check_ifdeparture(void)657 check_ifdeparture(void)
658 {
659 	struct ifsd_state *state;
660 	struct ifsd_ifstate *ifstate;
661 
662 	TAILQ_FOREACH(state, &conf->states, entries) {
663 		TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
664 			if (if_nametoindex(ifstate->ifname) == 0)
665 				scan_ifstate(ifstate->ifname,
666 				    LINK_STATE_DOWN, 1);
667 		}
668 	}
669 }
670 
671 void
clear_config(struct ifsd_config * oconf)672 clear_config(struct ifsd_config *oconf)
673 {
674 	struct ifsd_state *state;
675 
676 	external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_DEL);
677 	if (conf != NULL && conf->curstate != NULL)
678 		external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL);
679 	while ((state = TAILQ_FIRST(&oconf->states)) != NULL) {
680 		TAILQ_REMOVE(&oconf->states, state, entries);
681 		remove_action(state->init, state);
682 		remove_action(state->body, state);
683 		free(state->name);
684 		free(state);
685 	}
686 	remove_action(oconf->initstate.init, &oconf->initstate);
687 	remove_action(oconf->initstate.body, &oconf->initstate);
688 	free(oconf);
689 }
690 
691 void
remove_action(struct ifsd_action * action,struct ifsd_state * state)692 remove_action(struct ifsd_action *action, struct ifsd_state *state)
693 {
694 	struct ifsd_action *subaction;
695 
696 	if (action == NULL || state == NULL)
697 		return;
698 
699 	switch (action->type) {
700 	case IFSD_ACTION_COMMAND:
701 		free(action->act.command);
702 		break;
703 	case IFSD_ACTION_CHANGESTATE:
704 		break;
705 	case IFSD_ACTION_CONDITION:
706 		if (action->act.c.expression != NULL)
707 			remove_expression(action->act.c.expression, state);
708 		while ((subaction =
709 		    TAILQ_FIRST(&action->act.c.actions)) != NULL) {
710 			TAILQ_REMOVE(&action->act.c.actions,
711 			    subaction, entries);
712 			remove_action(subaction, state);
713 		}
714 	}
715 	free(action);
716 }
717 
718 void
remove_expression(struct ifsd_expression * expression,struct ifsd_state * state)719 remove_expression(struct ifsd_expression *expression,
720     struct ifsd_state *state)
721 {
722 	switch (expression->type) {
723 	case IFSD_OPER_IFSTATE:
724 		TAILQ_REMOVE(&expression->u.ifstate->expressions, expression,
725 		    entries);
726 		if (--expression->u.ifstate->refcount == 0) {
727 			TAILQ_REMOVE(&state->interface_states,
728 			    expression->u.ifstate, entries);
729 			free(expression->u.ifstate);
730 		}
731 		break;
732 	case IFSD_OPER_EXTERNAL:
733 		TAILQ_REMOVE(&expression->u.external->expressions, expression,
734 		    entries);
735 		if (--expression->u.external->refcount == 0) {
736 			TAILQ_REMOVE(&state->external_tests,
737 			    expression->u.external, entries);
738 			free(expression->u.external->command);
739 			event_del(&expression->u.external->ev);
740 			free(expression->u.external);
741 		}
742 		break;
743 	default:
744 		if (expression->left != NULL)
745 			remove_expression(expression->left, state);
746 		if (expression->right != NULL)
747 			remove_expression(expression->right, state);
748 		break;
749 	}
750 	free(expression);
751 }
752