GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/ifstated/ifstated.c Lines: 0 334 0.0 %
Date: 2017-11-07 Branches: 0 240 0.0 %

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