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