1 /*	$OpenBSD: ifstated.c,v 1.40 2011/07/04 04:34:14 claudio 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/ioctl.h>
28 #include <sys/socket.h>
29 #include <sys/sysctl.h>
30 #include <sys/wait.h>
31 
32 #include <net/if.h>
33 #include <net/if_mib.h>
34 #include <net/route.h>
35 #include <netinet/in.h>
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <signal.h>
41 #include <err.h>
42 #include <event.h>
43 #include <unistd.h>
44 #include <ifaddrs.h>
45 
46 #include "ifstated.h"
47 
48 struct	 ifsd_config *conf = NULL, *newconf = NULL;
49 
50 int	 opts = 0;
51 int	 opt_inhibit = 0;
52 char	*configfile = "/usr/local/etc/ifstated.conf";
53 char	*pidfile = NULL;
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	external_evtimer_setup(struct ifsd_state *, int);
65 void	scan_ifstate(int, int, int);
66 int	scan_ifstate_single(int, int, struct ifsd_state *);
67 int	get_ifcount(void);
68 int	get_ifmib_general(int row, struct ifmibdata *ifmd);
69 void	fetch_state(void);
70 void	usage(void);
71 void	adjust_expressions(struct ifsd_expression_list *, int);
72 void	adjust_external_expressions(struct ifsd_state *);
73 void	eval_state(struct ifsd_state *);
74 int	state_change(void);
75 void	do_action(struct ifsd_action *);
76 void	remove_action(struct ifsd_action *, struct ifsd_state *);
77 void	remove_expression(struct ifsd_expression *, struct ifsd_state *);
78 void	remove_pidfile(int);
79 
80 void
usage(void)81 usage(void)
82 {
83 	extern char *__progname;
84 
85 	fprintf(stderr, "usage: %s [-dhinv] [-D macro=value] [-f file] [-p pidfile]\n",
86 	    __progname);
87 	exit(1);
88 }
89 
90 int
main(int argc,char * argv[])91 main(int argc, char *argv[])
92 {
93 	struct timeval tv;
94 	int ch;
95 	int debug = 0;
96 
97 	log_init(1);
98 
99 	while ((ch = getopt(argc, argv, "dD:f:p:hniv")) != -1) {
100 		switch (ch) {
101 		case 'd':
102 			debug = 1;
103 			break;
104 		case 'D':
105 			if (cmdline_symset(optarg) < 0)
106 				errx(1, "could not parse macro definition %s",
107 				    optarg);
108 			break;
109 		case 'f':
110 			configfile = optarg;
111 			break;
112 		case 'h':
113 			usage();
114 			break;
115 		case 'n':
116 			opts |= IFSD_OPT_NOACTION;
117 			break;
118 		case 'i':
119 			opt_inhibit = 1;
120 			break;
121 		case 'p':
122 			pidfile = optarg;
123 			break;
124 		case 'v':
125 			if (opts & IFSD_OPT_VERBOSE)
126 				opts |= IFSD_OPT_VERBOSE2;
127 			opts |= IFSD_OPT_VERBOSE;
128 			break;
129 		default:
130 			usage();
131 		}
132 	}
133 
134 	argc -= optind;
135 	argv += optind;
136 	if (argc > 0)
137 		usage();
138 
139 	if (opts & IFSD_OPT_NOACTION) {
140 		if ((newconf = parse_config(configfile, opts)) == NULL)
141 			exit(1);
142 		warnx("configuration OK");
143 		exit(0);
144 	}
145 
146 	if (!debug)
147 		daemon(1, 0);
148 
149 	event_init();
150 	log_init(debug);
151 
152 	signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL);
153 	signal_add(&sigchld_ev, NULL);
154 
155 	/* Loading the config needs to happen in the event loop */
156 	timerclear(&tv);
157 	evtimer_set(&startup_ev, startup_handler, NULL);
158 	evtimer_add(&startup_ev, &tv);
159 
160 	event_loop(0);
161 	exit(0);
162 }
163 
164 void
startup_handler(int fd,short event,void * arg)165 startup_handler(int fd, short event, void *arg)
166 {
167 	int rt_fd;
168 
169 	if ((rt_fd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0)
170 		err(1, "no routing socket");
171 
172 	if (load_config() != 0) {
173 		log_warnx("unable to load config");
174 		exit(1);
175 	}
176 
177 	if (pidfile != NULL) {
178 		FILE* file = fopen(pidfile, "w");
179 
180 		if (file == NULL) {
181 			log_warnx("unable to open pidfile");
182 		}
183 		else {
184 			fprintf(file, "%ld\n", (long)getpid());
185 			fclose(file);
186 			log_debug("wrote pidfile %s", pidfile);
187 
188 			signal(SIGINT, remove_pidfile);
189 			signal(SIGQUIT, remove_pidfile);
190 			signal(SIGABRT, remove_pidfile);
191 			signal(SIGTERM, remove_pidfile);
192 		}
193 	}
194 
195 	event_set(&rt_msg_ev, rt_fd, EV_READ|EV_PERSIST, rt_msg_handler, NULL);
196 	event_add(&rt_msg_ev, NULL);
197 
198 	signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL);
199 	signal_add(&sighup_ev, NULL);
200 
201 	log_info("started");
202 }
203 
204 void
sighup_handler(int fd,short event,void * arg)205 sighup_handler(int fd, short event, void *arg)
206 {
207 	log_info("reloading config");
208 	if (load_config() != 0)
209 		log_warnx("unable to reload config");
210 }
211 
212 int
load_config(void)213 load_config(void)
214 {
215 	if ((newconf = parse_config(configfile, opts)) == NULL)
216 		return (-1);
217 	if (conf != NULL)
218 		clear_config(conf);
219 	conf = newconf;
220 	conf->always.entered = time(NULL);
221 	fetch_state();
222 	external_evtimer_setup(&conf->always, IFSD_EVTIMER_ADD);
223 	adjust_external_expressions(&conf->always);
224 	eval_state(&conf->always);
225 	if (conf->curstate != NULL) {
226 		log_info("initial state: %s", conf->curstate->name);
227 		conf->curstate->entered = time(NULL);
228 		conf->nextstate = conf->curstate;
229 		conf->curstate = NULL;
230 		while (state_change())
231 			do_action(conf->curstate->always);
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 	int len;
243 
244 	len = read(fd, msg, sizeof(msg));
245 
246 	/* XXX ignore errors? */
247 	if (len < sizeof(struct rt_msghdr))
248 		return;
249 
250 	if (rtm->rtm_version != RTM_VERSION)
251 		return;
252 
253 	if (rtm->rtm_type != RTM_IFINFO)
254 		return;
255 
256 	memcpy(&ifm, rtm, sizeof(ifm));
257 	scan_ifstate(ifm.ifm_index, ifm.ifm_data.ifi_link_state, 1);
258 }
259 
260 void
sigchld_handler(int fd,short event,void * arg)261 sigchld_handler(int fd, short event, void *arg)
262 {
263 	check_external_status(&conf->always);
264 	if (conf->curstate != NULL)
265 		check_external_status(conf->curstate);
266 }
267 
268 void
external_handler(int fd,short event,void * arg)269 external_handler(int fd, short event, void *arg)
270 {
271 	struct ifsd_external *external = (struct ifsd_external *)arg;
272 	struct timeval tv;
273 
274 	/* re-schedule */
275 	timerclear(&tv);
276 	tv.tv_sec = external->frequency;
277 	evtimer_set(&external->ev, external_handler, external);
278 	evtimer_add(&external->ev, &tv);
279 
280 	/* execute */
281 	external_exec(external, 1);
282 }
283 
284 void
external_exec(struct ifsd_external * external,int async)285 external_exec(struct ifsd_external *external, int async)
286 {
287 	char *argp[] = {"sh", "-c", NULL, NULL};
288 	pid_t pid;
289 	int s;
290 
291 	if (external->pid > 0) {
292 		log_debug("previous command %s [%d] still running, killing it",
293 		    external->command, external->pid);
294 		kill(external->pid, SIGKILL);
295 		waitpid(external->pid, &s, 0);
296 		external->pid = 0;
297 	}
298 
299 	argp[2] = external->command;
300 	log_debug("running %s", external->command);
301 	pid = fork();
302 	if (pid < 0) {
303 		log_warn("fork error");
304 	} else if (pid == 0) {
305 		execv("/bin/sh", argp);
306 		_exit(1);
307 		/* NOTREACHED */
308 	} else {
309 		external->pid = pid;
310 	}
311 	if (!async) {
312 		waitpid(external->pid, &s, 0);
313 		external->pid = 0;
314 		if (WIFEXITED(s))
315 			external->prevstatus = WEXITSTATUS(s);
316 	}
317 }
318 
319 void
adjust_external_expressions(struct ifsd_state * state)320 adjust_external_expressions(struct ifsd_state *state)
321 {
322 	struct ifsd_external *external;
323 	struct ifsd_expression_list expressions;
324 
325 	TAILQ_INIT(&expressions);
326 	TAILQ_FOREACH(external, &state->external_tests, entries) {
327 		struct ifsd_expression *expression;
328 
329 		if (external->prevstatus == -1)
330 			continue;
331 
332 		TAILQ_FOREACH(expression, &external->expressions, entries) {
333 			TAILQ_INSERT_TAIL(&expressions,
334 			    expression, eval);
335 			expression->truth = !external->prevstatus;
336 		}
337 		adjust_expressions(&expressions, conf->maxdepth);
338 	}
339 }
340 
341 void
check_external_status(struct ifsd_state * state)342 check_external_status(struct ifsd_state *state)
343 {
344 	struct ifsd_external *external, *end = NULL;
345 	int status, s, changed = 0;
346 
347 	/* Do this manually; change ordering so the oldest is first */
348 	external = TAILQ_FIRST(&state->external_tests);
349 	while (external != NULL && external != end) {
350 		struct ifsd_external *newexternal;
351 
352 		newexternal = TAILQ_NEXT(external, entries);
353 
354 		if (external->pid <= 0)
355 			goto loop;
356 
357 		if (wait4(external->pid, &s, WNOHANG, NULL) == 0)
358 			goto loop;
359 
360 		external->pid = 0;
361 		if (end == NULL)
362 			end = external;
363 		if (WIFEXITED(s))
364 			status = WEXITSTATUS(s);
365 		else {
366 			log_warnx("%s exited abnormally", external->command);
367 			goto loop;
368 		}
369 
370 		if (external->prevstatus != status &&
371 		    (external->prevstatus != -1 || !opt_inhibit)) {
372 			changed = 1;
373 			external->prevstatus = status;
374 		}
375 		external->lastexec = time(NULL);
376 		TAILQ_REMOVE(&state->external_tests, external, entries);
377 		TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
378 loop:
379 		external = newexternal;
380 	}
381 
382 	if (changed) {
383 		adjust_external_expressions(state);
384 		eval_state(state);
385 	}
386 }
387 
388 void
external_evtimer_setup(struct ifsd_state * state,int action)389 external_evtimer_setup(struct ifsd_state *state, int action)
390 {
391 	struct ifsd_external *external;
392 	int s;
393 
394 	if (state != NULL) {
395 		switch (action) {
396 		case IFSD_EVTIMER_ADD:
397 			TAILQ_FOREACH(external,
398 			    &state->external_tests, entries) {
399 				struct timeval tv;
400 
401 				/* run it once right away */
402 				external_exec(external, 0);
403 
404 				/* schedule it for later */
405 				timerclear(&tv);
406 				tv.tv_sec = external->frequency;
407 				evtimer_set(&external->ev, external_handler,
408 				    external);
409 				evtimer_add(&external->ev, &tv);
410 			}
411 			break;
412 		case IFSD_EVTIMER_DEL:
413 			TAILQ_FOREACH(external,
414 			    &state->external_tests, entries) {
415 				if (external->pid > 0) {
416 					kill(external->pid, SIGKILL);
417 					waitpid(external->pid, &s, 0);
418 					external->pid = 0;
419 				}
420 				if (event_initialized(&external->ev))
421 					evtimer_del(&external->ev);
422 			}
423 			break;
424 		}
425 	}
426 }
427 
428 #define LINK_STATE_IS_UP(_s)		((_s) >= LINK_STATE_UP)
429 #define	LINK_STATE_IS_DOWN(_s)		(!LINK_STATE_IS_UP((_s)))
430 
431 int
scan_ifstate_single(int ifindex,int s,struct ifsd_state * state)432 scan_ifstate_single(int ifindex, int s, struct ifsd_state *state)
433 {
434 	struct ifsd_ifstate *ifstate;
435 	struct ifsd_expression_list expressions;
436 	int changed = 0;
437 
438 	TAILQ_INIT(&expressions);
439 
440 	TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
441 		if (ifstate->ifindex == ifindex) {
442 			if (ifstate->prevstate != s &&
443 			    (ifstate->prevstate != -1 || !opt_inhibit)) {
444 				struct ifsd_expression *expression;
445 				int truth;
446 
447 				truth =
448 				    (ifstate->ifstate == IFSD_LINKUNKNOWN &&
449 				    s == LINK_STATE_UNKNOWN) ||
450 				    (ifstate->ifstate == IFSD_LINKDOWN &&
451 				    LINK_STATE_IS_DOWN(s)) ||
452 				    (ifstate->ifstate == IFSD_LINKUP &&
453 				    LINK_STATE_IS_UP(s));
454 
455 				TAILQ_FOREACH(expression,
456 				    &ifstate->expressions, entries) {
457 					expression->truth = truth;
458 					TAILQ_INSERT_TAIL(&expressions,
459 					    expression, eval);
460 					changed = 1;
461 				}
462 				ifstate->prevstate = s;
463 			}
464 		}
465 	}
466 
467 	if (changed)
468 		adjust_expressions(&expressions, conf->maxdepth);
469 	return (changed);
470 }
471 
472 void
scan_ifstate(int ifindex,int s,int do_eval)473 scan_ifstate(int ifindex, int s, int do_eval)
474 {
475 	struct ifsd_state *state;
476 	int cur_eval = 0;
477 
478 	if (scan_ifstate_single(ifindex, s, &conf->always) && do_eval)
479 		eval_state(&conf->always);
480 	TAILQ_FOREACH(state, &conf->states, entries) {
481 		if (scan_ifstate_single(ifindex, s, state) &&
482 		    (do_eval && state == conf->curstate))
483 			cur_eval = 1;
484 	}
485 	/* execute actions _after_ all expressions have been adjusted */
486 	if (cur_eval)
487 		eval_state(conf->curstate);
488 }
489 
490 /*
491  * Do a bottom-up ajustment of the expression tree's truth value,
492  * level-by-level to ensure that each expression's subexpressions have been
493  * evaluated.
494  */
495 void
adjust_expressions(struct ifsd_expression_list * expressions,int depth)496 adjust_expressions(struct ifsd_expression_list *expressions, int depth)
497 {
498 	struct ifsd_expression_list nexpressions;
499 	struct ifsd_expression *expression;
500 
501 	TAILQ_INIT(&nexpressions);
502 	while ((expression = TAILQ_FIRST(expressions)) != NULL) {
503 		TAILQ_REMOVE(expressions, expression, eval);
504 		if (expression->depth == depth) {
505 			struct ifsd_expression *te;
506 
507 			switch (expression->type) {
508 			case IFSD_OPER_AND:
509 				expression->truth = expression->left->truth &&
510 				    expression->right->truth;
511 				break;
512 			case IFSD_OPER_OR:
513 				expression->truth = expression->left->truth ||
514 				    expression->right->truth;
515 				break;
516 			case IFSD_OPER_NOT:
517 				expression->truth = !expression->right->truth;
518 				break;
519 			default:
520 				break;
521 			}
522 			if (expression->parent != NULL) {
523 				if (TAILQ_EMPTY(&nexpressions))
524 				te = NULL;
525 				TAILQ_FOREACH(te, &nexpressions, eval)
526 					if (expression->parent == te)
527 						break;
528 				if (te == NULL)
529 					TAILQ_INSERT_TAIL(&nexpressions,
530 					    expression->parent, eval);
531 			}
532 		} else
533 			TAILQ_INSERT_TAIL(&nexpressions, expression, eval);
534 	}
535 	if (depth > 0)
536 		adjust_expressions(&nexpressions, depth - 1);
537 }
538 
539 void
eval_state(struct ifsd_state * state)540 eval_state(struct ifsd_state *state)
541 {
542 	struct ifsd_external *external = TAILQ_FIRST(&state->external_tests);
543 	if (external == NULL || external->lastexec >= state->entered ||
544 	    external->lastexec == 0) {
545 		do_action(state->always);
546 		while (state_change())
547 			do_action(conf->curstate->always);
548 	}
549 }
550 
551 /*
552  *If a previous action included a state change, process it.
553  */
554 int
state_change(void)555 state_change(void)
556 {
557 	if (conf->nextstate != NULL && conf->curstate != conf->nextstate) {
558 		log_info("changing state to %s", conf->nextstate->name);
559 		if (conf->curstate != NULL) {
560 			if (event_initialized (&conf->curstate->ev))
561 				evtimer_del(&conf->curstate->ev);
562 			external_evtimer_setup(conf->curstate,
563 			    IFSD_EVTIMER_DEL);
564 		}
565 		conf->curstate = conf->nextstate;
566 		conf->nextstate = NULL;
567 		conf->curstate->entered = time(NULL);
568 		external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD);
569 		adjust_external_expressions(conf->curstate);
570 		do_action(conf->curstate->init);
571 		return (1);
572 	}
573 	return (0);
574 }
575 
576 /*
577  * Run recursively through the tree of actions.
578  */
579 void
do_action(struct ifsd_action * action)580 do_action(struct ifsd_action *action)
581 {
582 	struct ifsd_action *subaction;
583 
584 	switch (action->type) {
585 	case IFSD_ACTION_COMMAND:
586 		log_debug("running %s", action->act.command);
587 		system(action->act.command);
588 		break;
589 	case IFSD_ACTION_CHANGESTATE:
590 		conf->nextstate = action->act.nextstate;
591 		break;
592 	case IFSD_ACTION_CONDITION:
593 		if ((action->act.c.expression != NULL &&
594 		    action->act.c.expression->truth) ||
595 		    action->act.c.expression == NULL) {
596 			TAILQ_FOREACH(subaction, &action->act.c.actions,
597 			    entries)
598 				do_action(subaction);
599 		}
600 		break;
601 	default:
602 		log_debug("do_action: unknown action %d", action->type);
603 		break;
604 	}
605 }
606 
607 int
get_ifcount(void)608 get_ifcount(void)
609 {
610   int name[5], count;
611   size_t len;
612 
613   name[0] = CTL_NET;
614   name[1] = PF_LINK;
615   name[2] = NETLINK_GENERIC;
616   name[3] = IFMIB_SYSTEM;
617   name[4] = IFMIB_IFCOUNT;
618 
619   len = sizeof(int);
620 
621   if (sysctl(name, 5, &count, &len, NULL, 0) != -1)
622     return(count);
623   else
624     return(-1);
625 }
626 
627 int
get_ifmib_general(int row,struct ifmibdata * ifmd)628 get_ifmib_general(int row, struct ifmibdata *ifmd)
629 {
630   int name[6];
631   size_t len;
632 
633   name[0] = CTL_NET;
634   name[1] = PF_LINK;
635   name[2] = NETLINK_GENERIC;
636   name[3] = IFMIB_IFDATA;
637   name[4] = row;
638   name[5] = IFDATA_GENERAL;
639 
640   len = sizeof(*ifmd);
641 
642   return sysctl(name, 6, ifmd, &len, (void *)0, 0);
643 }
644 
645 /*
646  * Fetch the current link states.
647  */
648 void
fetch_state(void)649 fetch_state(void)
650 {
651 	struct ifaddrs *ifap, *ifa;
652 	char *oname = NULL;
653 	int sock = socket(AF_INET, SOCK_DGRAM, 0);
654         int ifcount = get_ifcount();
655         int i;
656 
657 	if (getifaddrs(&ifap) != 0 || ifcount == -1)
658 		err(1, "getifaddrs");
659 
660 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
661 		struct ifmibdata ifmd;
662 		struct if_data  ifdata;
663 
664 		if (oname && !strcmp(oname, ifa->ifa_name))
665 			continue;
666 		oname = ifa->ifa_name;
667 
668 		for (i = 1; i <= ifcount; i++)
669 		  {
670 		    get_ifmib_general(i, &ifmd);
671 		    if (! strcmp(ifmd.ifmd_name, oname))
672 		      break;
673 		  }
674 
675 		ifdata = ifmd.ifmd_data;
676 
677 		scan_ifstate(if_nametoindex(ifa->ifa_name),
678 		    ifdata.ifi_link_state, 0);
679 	}
680 	freeifaddrs(ifap);
681 	close(sock);
682 }
683 
684 
685 
686 /*
687  * Clear the config.
688  */
689 void
clear_config(struct ifsd_config * oconf)690 clear_config(struct ifsd_config *oconf)
691 {
692 	struct ifsd_state *state;
693 
694 	external_evtimer_setup(&conf->always, IFSD_EVTIMER_DEL);
695 	if (conf != NULL && conf->curstate != NULL)
696 		external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL);
697 	while ((state = TAILQ_FIRST(&oconf->states)) != NULL) {
698 		TAILQ_REMOVE(&oconf->states, state, entries);
699 		remove_action(state->init, state);
700 		remove_action(state->always, state);
701 		free(state->name);
702 		free(state);
703 	}
704 	remove_action(oconf->always.init, &oconf->always);
705 	remove_action(oconf->always.always, &oconf->always);
706 	free(oconf);
707 }
708 
709 void
remove_action(struct ifsd_action * action,struct ifsd_state * state)710 remove_action(struct ifsd_action *action, struct ifsd_state *state)
711 {
712 	struct ifsd_action *subaction;
713 
714 	if (action == NULL || state == NULL)
715 		return;
716 
717 	switch (action->type) {
718 	case IFSD_ACTION_LOG:
719 		free(action->act.logmessage);
720 		break;
721 	case IFSD_ACTION_COMMAND:
722 		free(action->act.command);
723 		break;
724 	case IFSD_ACTION_CHANGESTATE:
725 		break;
726 	case IFSD_ACTION_CONDITION:
727 		if (action->act.c.expression != NULL)
728 			remove_expression(action->act.c.expression, state);
729 		while ((subaction =
730 		    TAILQ_FIRST(&action->act.c.actions)) != NULL) {
731 			TAILQ_REMOVE(&action->act.c.actions,
732 			    subaction, entries);
733 			remove_action(subaction, state);
734 		}
735 	}
736 	free(action);
737 }
738 
739 void
remove_expression(struct ifsd_expression * expression,struct ifsd_state * state)740 remove_expression(struct ifsd_expression *expression,
741     struct ifsd_state *state)
742 {
743 	switch (expression->type) {
744 	case IFSD_OPER_IFSTATE:
745 		TAILQ_REMOVE(&expression->u.ifstate->expressions, expression,
746 		    entries);
747 		if (--expression->u.ifstate->refcount == 0) {
748 			TAILQ_REMOVE(&state->interface_states,
749 			    expression->u.ifstate, entries);
750 			free(expression->u.ifstate);
751 		}
752 		break;
753 	case IFSD_OPER_EXTERNAL:
754 		TAILQ_REMOVE(&expression->u.external->expressions, expression,
755 		    entries);
756 		if (--expression->u.external->refcount == 0) {
757 			TAILQ_REMOVE(&state->external_tests,
758 			    expression->u.external, entries);
759 			free(expression->u.external->command);
760 			event_del(&expression->u.external->ev);
761 			free(expression->u.external);
762 		}
763 		break;
764 	default:
765 		if (expression->left != NULL)
766 			remove_expression(expression->left, state);
767 		if (expression->right != NULL)
768 			remove_expression(expression->right, state);
769 		break;
770 	}
771 	free(expression);
772 }
773 
774 void
remove_pidfile(int code)775 remove_pidfile(int code)
776 {
777 	if ((pidfile != NULL) && unlink(pidfile)) {
778 		log_warnx("could not remove pidfile");
779 	}
780 
781 	exit(code);
782 }
783