752 lines
18 KiB
C
752 lines
18 KiB
C
/* $OpenBSD: ifstated.c,v 1.68 2024/04/23 13:34:51 jsg Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2004 Marco Pfatschbacher <mpf@openbsd.org>
|
|
* Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* ifstated listens to link_state transitions on interfaces
|
|
* and executes predefined commands.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/route.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <paths.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <stdint.h>
|
|
#include <syslog.h>
|
|
#include <errno.h>
|
|
#include <event.h>
|
|
#include <unistd.h>
|
|
#include <ifaddrs.h>
|
|
|
|
#include "ifstated.h"
|
|
#include "log.h"
|
|
|
|
struct ifsd_config *conf, *newconf;
|
|
|
|
int opts;
|
|
int opt_inhibit;
|
|
char *configfile = "/etc/ifstated.conf";
|
|
struct event rt_msg_ev, sighup_ev, startup_ev, sigchld_ev;
|
|
|
|
void startup_handler(int, short, void *);
|
|
void sighup_handler(int, short, void *);
|
|
int load_config(void);
|
|
void sigchld_handler(int, short, void *);
|
|
void rt_msg_handler(int, short, void *);
|
|
void external_handler(int, short, void *);
|
|
void external_exec(struct ifsd_external *, int);
|
|
void check_external_status(struct ifsd_state *);
|
|
void check_ifdeparture(void);
|
|
void external_evtimer_setup(struct ifsd_state *, int);
|
|
void scan_ifstate(const char *, int, int);
|
|
int scan_ifstate_single(const char *, int, struct ifsd_state *);
|
|
void fetch_ifstate(int);
|
|
__dead void usage(void);
|
|
void adjust_expressions(struct ifsd_expression_list *, int);
|
|
void adjust_external_expressions(struct ifsd_state *);
|
|
void eval_state(struct ifsd_state *);
|
|
int state_change(void);
|
|
void do_action(struct ifsd_action *);
|
|
void remove_action(struct ifsd_action *, struct ifsd_state *);
|
|
void remove_expression(struct ifsd_expression *,
|
|
struct ifsd_state *);
|
|
|
|
__dead void
|
|
usage(void)
|
|
{
|
|
extern char *__progname;
|
|
|
|
fprintf(stderr, "usage: %s [-dhinv] [-D macro=value] [-f file]\n",
|
|
__progname);
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct timeval tv;
|
|
int ch, rt_fd;
|
|
int debug = 0;
|
|
unsigned int rtfilter;
|
|
|
|
log_init(1, LOG_DAEMON); /* log to stderr until daemonized */
|
|
log_setverbose(1);
|
|
|
|
while ((ch = getopt(argc, argv, "dD:f:hniv")) != -1) {
|
|
switch (ch) {
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'D':
|
|
if (cmdline_symset(optarg) < 0)
|
|
fatalx("could not parse macro definition %s",
|
|
optarg);
|
|
break;
|
|
case 'f':
|
|
configfile = optarg;
|
|
break;
|
|
case 'h':
|
|
usage();
|
|
break;
|
|
case 'n':
|
|
opts |= IFSD_OPT_NOACTION;
|
|
break;
|
|
case 'i':
|
|
opt_inhibit = 1;
|
|
break;
|
|
case 'v':
|
|
if (opts & IFSD_OPT_VERBOSE)
|
|
opts |= IFSD_OPT_VERBOSE2;
|
|
opts |= IFSD_OPT_VERBOSE;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
if (argc > 0)
|
|
usage();
|
|
|
|
if (opts & IFSD_OPT_NOACTION) {
|
|
if ((newconf = parse_config(configfile, opts)) == NULL)
|
|
exit(1);
|
|
fprintf(stderr, "configuration OK\n");
|
|
exit(0);
|
|
}
|
|
|
|
if (!debug)
|
|
daemon(1, 0);
|
|
|
|
event_init();
|
|
log_init(debug, LOG_DAEMON);
|
|
log_setverbose(opts & IFSD_OPT_VERBOSE);
|
|
|
|
if ((rt_fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
|
|
fatal("no routing socket");
|
|
|
|
rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE);
|
|
if (setsockopt(rt_fd, AF_ROUTE, ROUTE_MSGFILTER,
|
|
&rtfilter, sizeof(rtfilter)) == -1) /* not fatal */
|
|
log_warn("%s: setsockopt msgfilter", __func__);
|
|
|
|
rtfilter = RTABLE_ANY;
|
|
if (setsockopt(rt_fd, AF_ROUTE, ROUTE_TABLEFILTER,
|
|
&rtfilter, sizeof(rtfilter)) == -1) /* not fatal */
|
|
log_warn("%s: setsockopt tablefilter", __func__);
|
|
|
|
if (unveil(configfile, "r") == -1)
|
|
fatal("unveil %s", configfile);
|
|
if (unveil(_PATH_BSHELL, "x") == -1)
|
|
fatal("unveil %s", _PATH_BSHELL);
|
|
if (pledge("stdio rpath route proc exec", NULL) == -1)
|
|
fatal("pledge");
|
|
|
|
signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL);
|
|
signal_add(&sigchld_ev, NULL);
|
|
|
|
/* Loading the config needs to happen in the event loop */
|
|
timerclear(&tv);
|
|
evtimer_set(&startup_ev, startup_handler, (void *)(long)rt_fd);
|
|
evtimer_add(&startup_ev, &tv);
|
|
|
|
event_loop(0);
|
|
exit(0);
|
|
}
|
|
|
|
void
|
|
startup_handler(int fd, short event, void *arg)
|
|
{
|
|
int rfd = (int)(long)arg;
|
|
|
|
if (load_config() != 0) {
|
|
log_warnx("unable to load config");
|
|
exit(1);
|
|
}
|
|
|
|
event_set(&rt_msg_ev, rfd, EV_READ|EV_PERSIST, rt_msg_handler, NULL);
|
|
event_add(&rt_msg_ev, NULL);
|
|
|
|
signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL);
|
|
signal_add(&sighup_ev, NULL);
|
|
|
|
log_info("started");
|
|
}
|
|
|
|
void
|
|
sighup_handler(int fd, short event, void *arg)
|
|
{
|
|
log_info("reloading config");
|
|
if (load_config() != 0)
|
|
log_warnx("unable to reload config");
|
|
}
|
|
|
|
int
|
|
load_config(void)
|
|
{
|
|
if ((newconf = parse_config(configfile, opts)) == NULL)
|
|
return (-1);
|
|
if (conf != NULL)
|
|
clear_config(conf);
|
|
conf = newconf;
|
|
conf->initstate.entered = time(NULL);
|
|
fetch_ifstate(0);
|
|
external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_ADD);
|
|
adjust_external_expressions(&conf->initstate);
|
|
eval_state(&conf->initstate);
|
|
if (conf->curstate != NULL) {
|
|
log_info("initial state: %s", conf->curstate->name);
|
|
conf->curstate->entered = time(NULL);
|
|
conf->nextstate = conf->curstate;
|
|
conf->curstate = NULL;
|
|
while (state_change()) {
|
|
do_action(conf->curstate->init);
|
|
do_action(conf->curstate->body);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
rt_msg_handler(int fd, short event, void *arg)
|
|
{
|
|
char msg[2048];
|
|
struct rt_msghdr *rtm = (struct rt_msghdr *)&msg;
|
|
struct if_msghdr ifm;
|
|
struct if_announcemsghdr ifan;
|
|
char ifnamebuf[IFNAMSIZ];
|
|
char *ifname;
|
|
ssize_t len;
|
|
|
|
if ((len = read(fd, msg, sizeof(msg))) == -1) {
|
|
if (errno == EAGAIN || errno == EINTR)
|
|
return;
|
|
fatal("%s: routing socket read error", __func__);
|
|
}
|
|
|
|
if (len == 0)
|
|
fatal("%s: routing socket closed", __func__);
|
|
|
|
if (rtm->rtm_version != RTM_VERSION)
|
|
return;
|
|
|
|
switch (rtm->rtm_type) {
|
|
case RTM_IFINFO:
|
|
memcpy(&ifm, rtm, sizeof(ifm));
|
|
ifname = if_indextoname(ifm.ifm_index, ifnamebuf);
|
|
/* ifname is NULL on interface departure */
|
|
if (ifname != NULL)
|
|
scan_ifstate(ifname, ifm.ifm_data.ifi_link_state, 1);
|
|
break;
|
|
case RTM_IFANNOUNCE:
|
|
memcpy(&ifan, rtm, sizeof(ifan));
|
|
switch (ifan.ifan_what) {
|
|
case IFAN_DEPARTURE:
|
|
log_warnx("interface %s departed", ifan.ifan_name);
|
|
check_ifdeparture();
|
|
break;
|
|
case IFAN_ARRIVAL:
|
|
log_warnx("interface %s arrived", ifan.ifan_name);
|
|
fetch_ifstate(1);
|
|
break;
|
|
}
|
|
break;
|
|
case RTM_DESYNC:
|
|
/* we lost some routing messages so rescan interfaces */
|
|
check_ifdeparture();
|
|
fetch_ifstate(1);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void
|
|
sigchld_handler(int fd, short event, void *arg)
|
|
{
|
|
check_external_status(&conf->initstate);
|
|
if (conf->curstate != NULL)
|
|
check_external_status(conf->curstate);
|
|
}
|
|
|
|
void
|
|
external_handler(int fd, short event, void *arg)
|
|
{
|
|
struct ifsd_external *external = (struct ifsd_external *)arg;
|
|
struct timeval tv;
|
|
|
|
/* re-schedule */
|
|
timerclear(&tv);
|
|
tv.tv_sec = external->frequency;
|
|
evtimer_set(&external->ev, external_handler, external);
|
|
evtimer_add(&external->ev, &tv);
|
|
|
|
/* execute */
|
|
external_exec(external, 1);
|
|
}
|
|
|
|
void
|
|
external_exec(struct ifsd_external *external, int async)
|
|
{
|
|
char *argp[] = {"sh", "-c", NULL, NULL};
|
|
pid_t pid;
|
|
int s;
|
|
|
|
if (external->pid > 0) {
|
|
log_debug("previous command %s [%d] still running, killing it",
|
|
external->command, external->pid);
|
|
kill(external->pid, SIGKILL);
|
|
waitpid(external->pid, &s, 0);
|
|
external->pid = 0;
|
|
}
|
|
|
|
argp[2] = external->command;
|
|
log_debug("running %s", external->command);
|
|
pid = fork();
|
|
if (pid == -1) {
|
|
log_warn("fork error");
|
|
} else if (pid == 0) {
|
|
execv(_PATH_BSHELL, argp);
|
|
_exit(1);
|
|
/* NOTREACHED */
|
|
} else {
|
|
external->pid = pid;
|
|
}
|
|
if (!async) {
|
|
waitpid(external->pid, &s, 0);
|
|
external->pid = 0;
|
|
if (WIFEXITED(s))
|
|
external->prevstatus = WEXITSTATUS(s);
|
|
}
|
|
}
|
|
|
|
void
|
|
adjust_external_expressions(struct ifsd_state *state)
|
|
{
|
|
struct ifsd_external *external;
|
|
struct ifsd_expression_list expressions;
|
|
|
|
TAILQ_INIT(&expressions);
|
|
TAILQ_FOREACH(external, &state->external_tests, entries) {
|
|
struct ifsd_expression *expression;
|
|
|
|
if (external->prevstatus == -1)
|
|
continue;
|
|
|
|
TAILQ_FOREACH(expression, &external->expressions, entries) {
|
|
TAILQ_INSERT_TAIL(&expressions,
|
|
expression, eval);
|
|
expression->truth = !external->prevstatus;
|
|
}
|
|
adjust_expressions(&expressions, conf->maxdepth);
|
|
}
|
|
}
|
|
|
|
void
|
|
check_external_status(struct ifsd_state *state)
|
|
{
|
|
struct ifsd_external *external, *end = NULL;
|
|
int status, s, changed = 0;
|
|
|
|
/* Do this manually; change ordering so the oldest is first */
|
|
external = TAILQ_FIRST(&state->external_tests);
|
|
while (external != NULL && external != end) {
|
|
struct ifsd_external *newexternal;
|
|
|
|
newexternal = TAILQ_NEXT(external, entries);
|
|
|
|
if (external->pid <= 0)
|
|
goto loop;
|
|
|
|
if (wait4(external->pid, &s, WNOHANG, NULL) == 0)
|
|
goto loop;
|
|
|
|
external->pid = 0;
|
|
if (end == NULL)
|
|
end = external;
|
|
if (WIFEXITED(s))
|
|
status = WEXITSTATUS(s);
|
|
else {
|
|
log_warnx("%s exited abnormally", external->command);
|
|
goto loop;
|
|
}
|
|
|
|
if (external->prevstatus != status &&
|
|
(external->prevstatus != -1 || !opt_inhibit)) {
|
|
changed = 1;
|
|
external->prevstatus = status;
|
|
}
|
|
external->lastexec = time(NULL);
|
|
TAILQ_REMOVE(&state->external_tests, external, entries);
|
|
TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
|
|
loop:
|
|
external = newexternal;
|
|
}
|
|
|
|
if (changed) {
|
|
adjust_external_expressions(state);
|
|
eval_state(state);
|
|
}
|
|
}
|
|
|
|
void
|
|
external_evtimer_setup(struct ifsd_state *state, int action)
|
|
{
|
|
struct ifsd_external *external;
|
|
int s;
|
|
|
|
if (state != NULL) {
|
|
switch (action) {
|
|
case IFSD_EVTIMER_ADD:
|
|
TAILQ_FOREACH(external,
|
|
&state->external_tests, entries) {
|
|
struct timeval tv;
|
|
|
|
/* run it once right away */
|
|
external_exec(external, 0);
|
|
|
|
/* schedule it for later */
|
|
timerclear(&tv);
|
|
tv.tv_sec = external->frequency;
|
|
evtimer_set(&external->ev, external_handler,
|
|
external);
|
|
evtimer_add(&external->ev, &tv);
|
|
}
|
|
break;
|
|
case IFSD_EVTIMER_DEL:
|
|
TAILQ_FOREACH(external,
|
|
&state->external_tests, entries) {
|
|
if (external->pid > 0) {
|
|
kill(external->pid, SIGKILL);
|
|
waitpid(external->pid, &s, 0);
|
|
external->pid = 0;
|
|
}
|
|
evtimer_del(&external->ev);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define LINK_STATE_IS_DOWN(_s) (!LINK_STATE_IS_UP((_s)))
|
|
|
|
int
|
|
scan_ifstate_single(const char *ifname, int s, struct ifsd_state *state)
|
|
{
|
|
struct ifsd_ifstate *ifstate;
|
|
struct ifsd_expression_list expressions;
|
|
int changed = 0;
|
|
|
|
TAILQ_INIT(&expressions);
|
|
|
|
TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
|
|
if (strcmp(ifstate->ifname, ifname) == 0) {
|
|
if (ifstate->prevstate != s &&
|
|
(ifstate->prevstate != -1 || !opt_inhibit)) {
|
|
struct ifsd_expression *expression;
|
|
int truth;
|
|
|
|
truth =
|
|
(ifstate->ifstate == IFSD_LINKUNKNOWN &&
|
|
s == LINK_STATE_UNKNOWN) ||
|
|
(ifstate->ifstate == IFSD_LINKDOWN &&
|
|
LINK_STATE_IS_DOWN(s)) ||
|
|
(ifstate->ifstate == IFSD_LINKUP &&
|
|
LINK_STATE_IS_UP(s));
|
|
|
|
TAILQ_FOREACH(expression,
|
|
&ifstate->expressions, entries) {
|
|
expression->truth = truth;
|
|
TAILQ_INSERT_TAIL(&expressions,
|
|
expression, eval);
|
|
changed = 1;
|
|
}
|
|
ifstate->prevstate = s;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (changed)
|
|
adjust_expressions(&expressions, conf->maxdepth);
|
|
return (changed);
|
|
}
|
|
|
|
void
|
|
scan_ifstate(const char *ifname, int s, int do_eval)
|
|
{
|
|
struct ifsd_state *state;
|
|
int cur_eval = 0;
|
|
|
|
if (scan_ifstate_single(ifname, s, &conf->initstate) && do_eval)
|
|
eval_state(&conf->initstate);
|
|
TAILQ_FOREACH(state, &conf->states, entries) {
|
|
if (scan_ifstate_single(ifname, s, state) &&
|
|
(do_eval && state == conf->curstate))
|
|
cur_eval = 1;
|
|
}
|
|
/* execute actions _after_ all expressions have been adjusted */
|
|
if (cur_eval)
|
|
eval_state(conf->curstate);
|
|
}
|
|
|
|
/*
|
|
* Do a bottom-up adjustment of the expression tree's truth value,
|
|
* level-by-level to ensure that each expression's subexpressions have been
|
|
* evaluated.
|
|
*/
|
|
void
|
|
adjust_expressions(struct ifsd_expression_list *expressions, int depth)
|
|
{
|
|
struct ifsd_expression_list nexpressions;
|
|
struct ifsd_expression *expression;
|
|
|
|
TAILQ_INIT(&nexpressions);
|
|
while ((expression = TAILQ_FIRST(expressions)) != NULL) {
|
|
TAILQ_REMOVE(expressions, expression, eval);
|
|
if (expression->depth == depth) {
|
|
struct ifsd_expression *te;
|
|
|
|
switch (expression->type) {
|
|
case IFSD_OPER_AND:
|
|
expression->truth = expression->left->truth &&
|
|
expression->right->truth;
|
|
break;
|
|
case IFSD_OPER_OR:
|
|
expression->truth = expression->left->truth ||
|
|
expression->right->truth;
|
|
break;
|
|
case IFSD_OPER_NOT:
|
|
expression->truth = !expression->right->truth;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (expression->parent != NULL) {
|
|
if (TAILQ_EMPTY(&nexpressions))
|
|
te = NULL;
|
|
TAILQ_FOREACH(te, &nexpressions, eval)
|
|
if (expression->parent == te)
|
|
break;
|
|
if (te == NULL)
|
|
TAILQ_INSERT_TAIL(&nexpressions,
|
|
expression->parent, eval);
|
|
}
|
|
} else
|
|
TAILQ_INSERT_TAIL(&nexpressions, expression, eval);
|
|
}
|
|
if (depth > 0)
|
|
adjust_expressions(&nexpressions, depth - 1);
|
|
}
|
|
|
|
void
|
|
eval_state(struct ifsd_state *state)
|
|
{
|
|
struct ifsd_external *external;
|
|
|
|
external = TAILQ_FIRST(&state->external_tests);
|
|
if (external == NULL || external->lastexec >= state->entered ||
|
|
external->lastexec == 0) {
|
|
do_action(state->body);
|
|
while (state_change()) {
|
|
do_action(conf->curstate->init);
|
|
do_action(conf->curstate->body);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
state_change(void)
|
|
{
|
|
if (conf->nextstate != NULL && conf->curstate != conf->nextstate) {
|
|
log_info("changing state to %s", conf->nextstate->name);
|
|
if (conf->curstate != NULL) {
|
|
evtimer_del(&conf->curstate->ev);
|
|
external_evtimer_setup(conf->curstate,
|
|
IFSD_EVTIMER_DEL);
|
|
}
|
|
conf->curstate = conf->nextstate;
|
|
conf->nextstate = NULL;
|
|
conf->curstate->entered = time(NULL);
|
|
external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD);
|
|
adjust_external_expressions(conf->curstate);
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Run recursively through the tree of actions.
|
|
*/
|
|
void
|
|
do_action(struct ifsd_action *action)
|
|
{
|
|
struct ifsd_action *subaction;
|
|
|
|
switch (action->type) {
|
|
case IFSD_ACTION_COMMAND:
|
|
log_debug("running %s", action->act.command);
|
|
system(action->act.command);
|
|
break;
|
|
case IFSD_ACTION_CHANGESTATE:
|
|
conf->nextstate = action->act.nextstate;
|
|
break;
|
|
case IFSD_ACTION_CONDITION:
|
|
if ((action->act.c.expression != NULL &&
|
|
action->act.c.expression->truth) ||
|
|
action->act.c.expression == NULL) {
|
|
TAILQ_FOREACH(subaction, &action->act.c.actions,
|
|
entries)
|
|
do_action(subaction);
|
|
}
|
|
break;
|
|
default:
|
|
log_debug("%s: unknown action %d", __func__, action->type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fetch the current link states.
|
|
*/
|
|
void
|
|
fetch_ifstate(int do_eval)
|
|
{
|
|
struct ifaddrs *ifap, *ifa;
|
|
|
|
if (getifaddrs(&ifap) != 0)
|
|
fatal("getifaddrs");
|
|
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
|
if (ifa->ifa_addr != NULL &&
|
|
ifa->ifa_addr->sa_family == AF_LINK) {
|
|
struct if_data *ifdata = ifa->ifa_data;
|
|
scan_ifstate(ifa->ifa_name, ifdata->ifi_link_state,
|
|
do_eval);
|
|
}
|
|
}
|
|
|
|
freeifaddrs(ifap);
|
|
}
|
|
|
|
void
|
|
check_ifdeparture(void)
|
|
{
|
|
struct ifsd_state *state;
|
|
struct ifsd_ifstate *ifstate;
|
|
|
|
TAILQ_FOREACH(state, &conf->states, entries) {
|
|
TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
|
|
if (if_nametoindex(ifstate->ifname) == 0)
|
|
scan_ifstate(ifstate->ifname,
|
|
LINK_STATE_DOWN, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
clear_config(struct ifsd_config *oconf)
|
|
{
|
|
struct ifsd_state *state;
|
|
|
|
external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_DEL);
|
|
if (conf != NULL && conf->curstate != NULL)
|
|
external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL);
|
|
while ((state = TAILQ_FIRST(&oconf->states)) != NULL) {
|
|
TAILQ_REMOVE(&oconf->states, state, entries);
|
|
remove_action(state->init, state);
|
|
remove_action(state->body, state);
|
|
free(state->name);
|
|
free(state);
|
|
}
|
|
remove_action(oconf->initstate.init, &oconf->initstate);
|
|
remove_action(oconf->initstate.body, &oconf->initstate);
|
|
free(oconf);
|
|
}
|
|
|
|
void
|
|
remove_action(struct ifsd_action *action, struct ifsd_state *state)
|
|
{
|
|
struct ifsd_action *subaction;
|
|
|
|
if (action == NULL || state == NULL)
|
|
return;
|
|
|
|
switch (action->type) {
|
|
case IFSD_ACTION_COMMAND:
|
|
free(action->act.command);
|
|
break;
|
|
case IFSD_ACTION_CHANGESTATE:
|
|
break;
|
|
case IFSD_ACTION_CONDITION:
|
|
if (action->act.c.expression != NULL)
|
|
remove_expression(action->act.c.expression, state);
|
|
while ((subaction =
|
|
TAILQ_FIRST(&action->act.c.actions)) != NULL) {
|
|
TAILQ_REMOVE(&action->act.c.actions,
|
|
subaction, entries);
|
|
remove_action(subaction, state);
|
|
}
|
|
}
|
|
free(action);
|
|
}
|
|
|
|
void
|
|
remove_expression(struct ifsd_expression *expression,
|
|
struct ifsd_state *state)
|
|
{
|
|
switch (expression->type) {
|
|
case IFSD_OPER_IFSTATE:
|
|
TAILQ_REMOVE(&expression->u.ifstate->expressions, expression,
|
|
entries);
|
|
if (--expression->u.ifstate->refcount == 0) {
|
|
TAILQ_REMOVE(&state->interface_states,
|
|
expression->u.ifstate, entries);
|
|
free(expression->u.ifstate);
|
|
}
|
|
break;
|
|
case IFSD_OPER_EXTERNAL:
|
|
TAILQ_REMOVE(&expression->u.external->expressions, expression,
|
|
entries);
|
|
if (--expression->u.external->refcount == 0) {
|
|
TAILQ_REMOVE(&state->external_tests,
|
|
expression->u.external, entries);
|
|
free(expression->u.external->command);
|
|
event_del(&expression->u.external->ev);
|
|
free(expression->u.external);
|
|
}
|
|
break;
|
|
default:
|
|
if (expression->left != NULL)
|
|
remove_expression(expression->left, state);
|
|
if (expression->right != NULL)
|
|
remove_expression(expression->right, state);
|
|
break;
|
|
}
|
|
free(expression);
|
|
}
|