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