summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorRyan Thomas McBride <mcbride@cvs.openbsd.org>2004-02-04 23:47:50 +0000
committerRyan Thomas McBride <mcbride@cvs.openbsd.org>2004-02-04 23:47:50 +0000
commit76cee6cf3c1cb3ae97985e5f5dac813e1ab04017 (patch)
tree3aa753beddacbbcf8a4a708e17c379bf7770eefd /usr.sbin
parentf6b5cb8c267b47e74cac5bbb4d402b08c73e06bd (diff)
Update to ifstated; replace parser, introduce the concept of states,
external tests, and boolean logic. Allows ifstated to handle partial failures on firewalls that are CARPd to each other. ok deraadt@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/ifstated/Makefile9
-rw-r--r--usr.sbin/ifstated/ifstated.c689
-rw-r--r--usr.sbin/ifstated/ifstated.h141
-rw-r--r--usr.sbin/ifstated/parse.y869
4 files changed, 1530 insertions, 178 deletions
diff --git a/usr.sbin/ifstated/Makefile b/usr.sbin/ifstated/Makefile
index 3633d77d9e4..dcdf6a0dcff 100644
--- a/usr.sbin/ifstated/Makefile
+++ b/usr.sbin/ifstated/Makefile
@@ -1,7 +1,12 @@
-# $OpenBSD: Makefile,v 1.1 2004/01/23 21:34:30 mcbride Exp $
+# $OpenBSD: Makefile,v 1.2 2004/02/04 23:47:49 mcbride Exp $
PROG= ifstated
+SRCS= ifstated.c parse.y
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CLFAGS+= -Wmissing-declarations -Wredundant-decls
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
MAN=
-LDADD+=-lutil
+LDADD+=-lutil -levent
.include <bsd.prog.mk>
diff --git a/usr.sbin/ifstated/ifstated.c b/usr.sbin/ifstated/ifstated.c
index 0dd2fab3241..73a1307a2a6 100644
--- a/usr.sbin/ifstated/ifstated.c
+++ b/usr.sbin/ifstated/ifstated.c
@@ -1,7 +1,8 @@
-/* $OpenBSD: ifstated.c,v 1.1 2004/01/23 21:34:30 mcbride Exp $ */
+/* $OpenBSD: ifstated.c,v 1.2 2004/02/04 23:47:49 mcbride 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
@@ -25,6 +26,7 @@
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
+#include <sys/wait.h>
#include <net/if.h>
#include <net/route.h>
@@ -36,43 +38,51 @@
#include <fcntl.h>
#include <signal.h>
#include <err.h>
+#include <event.h>
#include <util.h>
#include <unistd.h>
#include <syslog.h>
#include <stdarg.h>
+#include <ifaddrs.h>
+#include "ifstated.h"
-#define CMD_LENGTH 100
-#define CONF_LINES 50
-struct conf_t {
- u_short dev; /* if_index */
- u_char state; /* if_link_state */
- char cmd[CMD_LENGTH];
-} conf_table[CONF_LINES];
+struct ifsd_config conf;
-char *state_name[] = { "UNKNOWN", "DOWN", "UP" };
-int opt_debug = 0;
-int opt_inhibit = 0; /* don't run scripts on startup */
+int opt_debug = 0;
+int opt_inhibit = 0;
char *configfile = "/etc/ifstated.conf";
-#define MAX_IFINDEX 64
-int prev_states[MAX_IFINDEX]; /* -1 to trigger init */
-
-volatile sig_atomic_t got_sighup;
-
-void loop(void);
-void eval_rtmsg(struct rt_msghdr *, int);
-void scan_table(int, int);
+void startup_handler(int, short, void *);
+void sighup_handler(int, short, void *);
+void load_config(void);
+void sigchld_handler(int, short, void *);
+void rt_msg_handler(int, short, void *);
+void external_handler(int, short, void *);
+void external_async_exec(struct ifsd_external *);
+void check_external_status(struct ifsd_state *);
+void external_evtimer_setup(struct ifsd_state *, int);
+int scan_ifstate(int, int, struct ifsd_state *);
void fetch_state(void);
void usage(void);
void doconfig(const char*);
-void sighup_handler(int);
-#define LOG(s,a) if (opt_debug) \
- printf("ifstated: " s , a ); \
- else \
- syslog(LOG_DAEMON, s , a);
-
+void adjust_expressions(struct ifsd_expression_list *, int);
+void eval_state(struct ifsd_state *);
+void state_change(void);
+void do_action(struct ifsd_action *);
+void clear_config(struct ifsd_config *);
+void remove_action(struct ifsd_action *, struct ifsd_state *);
+void remove_expression(struct ifsd_expression *, struct ifsd_state *);
+
+#define LOG(l,s,a) do { \
+ if (l <= conf.loglevel) { \
+ if (opt_debug) \
+ printf("ifstated: " s , a ); \
+ else \
+ syslog(LOG_DAEMON, s , a); \
+ } \
+} while(0)
void
usage(void)
@@ -84,240 +94,567 @@ usage(void)
int
main(int argc, char *argv[])
{
- int ch;
- struct sigaction sact;
-
- while ((ch = getopt(argc, argv, "hdif:")) != -1) {
+ struct event startup_ev, sighup_ev, sigchld_ev, rt_msg_ev;
+ int rt_fd, ch;
+ struct timeval tv;
+
+ while ((ch = getopt(argc, argv, "dD:f:hi")) != -1) {
switch (ch) {
- case 'h':
- usage();
- break;
case 'd':
opt_debug = 1;
break;
- case 'i':
- opt_inhibit = 1;
+ case 'D':
+ if (cmdline_symset(optarg) < 0)
+ errx(1, "could not parse macro definition %s",
+ optarg);
break;
case 'f':
configfile = optarg;
break;
+ case 'h':
+ usage();
+ break;
+ case 'i':
+ opt_inhibit = 1;
+ break;
+ case 'v':
+ if (conf.opts & IFSD_OPT_VERBOSE)
+ conf.opts |= IFSD_OPT_VERBOSE2;
+ conf.opts |= IFSD_OPT_VERBOSE;
+ break;
default:
usage();
}
}
- doconfig(configfile);
+ event_init();
+
+ if (parse_config(configfile, &conf) != 0)
+ errx(1, NULL);
- bzero((char *)&sact, sizeof sact);
- sigemptyset(&sact.sa_mask);
- sact.sa_flags = 0;
- /* sact.sa_flags |= SA_RESTART; */
- sact.sa_handler = sighup_handler;
- (void) sigaction(SIGHUP, &sact, NULL);
if (!opt_debug) {
daemon(0, 0);
setproctitle(NULL);
}
- LOG("%s\n", "started");
+ if ((rt_fd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0)
+ errx(1, "no routing socket");
+
+ event_set(&rt_msg_ev, rt_fd, EV_READ|EV_PERSIST,
+ rt_msg_handler, &rt_msg_ev);
+ event_add(&rt_msg_ev, NULL);
+
+ signal_set(&sighup_ev, SIGHUP, sighup_handler, &sighup_ev);
+ signal_add(&sighup_ev, NULL);
+
+ signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, &sigchld_ev);
+ signal_add(&sigchld_ev, NULL);
- loop();
+ /* Loading the config needs to happen in the event loop */
+ tv.tv_usec = 0;
+ tv.tv_sec = 0;
+ evtimer_set(&startup_ev, startup_handler, &startup_ev);
+ evtimer_add(&startup_ev, &tv);
- LOG("%s\n", "dropped out of main loop. exiting");
- return (0);
+ event_loop(0);
+ exit(0);
}
void
-doconfig(const char *conf)
+startup_handler(int fd, short event, void *arg)
{
- FILE *fconfig = NULL;
- char *line = NULL;
- size_t len, lineno = 0;
- int device = -1;
- int state = -1;
- int nrofstates = sizeof(state_name) / sizeof(char *);
- int cf_line = 0;
-
- fconfig = fopen(conf, "r");
-
- if (fconfig == NULL)
- errx(1, "could not open config: %s\n", conf);
-
- bzero(conf_table, sizeof(conf_table));
- bzero(prev_states, sizeof(prev_states));
-
- while ((line = fparseln(fconfig, &len, &lineno,
- NULL, FPARSELN_UNESCALL)) != NULL) {
-
- char statename[20];
- char devname[IF_NAMESIZE];
- int i;
- char *cp;
-
-#define WS " \t\n"
- cp = line;
-
- cp += strspn(cp, WS);
- if (cp[0] == '\0') {
- /* empty line */
- free(line);
- continue;
- }
+ LOG(IFSD_LOG_NORMAL, "%s\n", "started");
+ load_config();
+}
- if (cf_line >= CONF_LINES)
- errx(1, "too much lines in config\n");
+void
+sighup_handler(int fd, short event, void *arg)
+{
+ LOG(IFSD_LOG_NORMAL, "%s\n", "reloading config");
+ clear_config(&conf);
+ load_config();
+}
- /* commands */
- if (line[0] == ' ' || line[0] == '\t') {
- cp = line;
- while (*cp == ' ' || *cp == '\t')
- cp++;
+void
+load_config(void)
+{
+ parse_config(configfile, &conf);
+ conf.always.entered = time(NULL);
+ fetch_state();
+ eval_state(&conf.always);
+ if (conf.curstate != NULL) {
+ LOG(IFSD_LOG_NORMAL,
+ "initial state: %s\n", conf.curstate->name);
+ conf.curstate->entered = time(NULL);
+ eval_state(conf.curstate);
+ }
+ external_evtimer_setup(&conf.always, IFSD_EVTIMER_ADD);
+}
- if (state == -1)
- errx(1, "no context for config line: %lu\n",
- (u_long) lineno);
+void
+rt_msg_handler(int fd, short event, void *arg)
+{
+ struct if_msghdr *ifm;
+ char msg[2048];
+ struct rt_msghdr *rtm = (struct rt_msghdr *)&msg;
+ int len;
+
+ len = read(fd, msg, sizeof(msg));
- conf_table[cf_line].dev = device;
- conf_table[cf_line].state = state;
- snprintf(conf_table[cf_line].cmd, CMD_LENGTH, "%s", cp);
- /* read state via fetch_state() at startup */
- prev_states[device] = -1;
+ /* XXX ignore errors? */
+ if (len < sizeof(struct rt_msghdr))
+ return;
- cf_line++;
- free(line);
- continue;
- }
+ if (rtm->rtm_version != RTM_VERSION)
+ return;
- /* context */
- state = -1;
+ if (rtm->rtm_type != RTM_IFINFO)
+ return;
- if (sscanf(line, "%[^.].%19[^:]\n", devname, statename) != 2)
- errx(1, "bad state definition at: %d\n", lineno);
+ ifm = (struct if_msghdr *)rtm;
- if ((device = if_nametoindex(devname)) == 0)
- errx(1, "no such device %s at: %d\n", devname, lineno);
+ if (scan_ifstate(ifm->ifm_index, ifm->ifm_data.ifi_link_state,
+ &conf.always))
+ eval_state(&conf.always);
+ if ((conf.curstate != NULL) && scan_ifstate(ifm->ifm_index,
+ ifm->ifm_data.ifi_link_state, conf.curstate))
+ eval_state(conf.curstate);
+}
- for (i = nrofstates - 1; i >= 0; i--)
- if (strncmp(state_name[i], statename,
- strlen(state_name[i])) == 0)
- state = i;
+void
+sigchld_handler(int fd, short event, void *arg)
+{
+ check_external_status(&conf.always);
+ if (conf.curstate != NULL)
+ check_external_status(conf.curstate);
+}
- if (state == -1)
- errx(1, "bad state definition at: %d\n", lineno);
+void
+external_handler(int fd, short event, void *arg)
+{
+ struct ifsd_external *external = (struct ifsd_external *)arg;
+ struct timeval tv;
+
+ /* re-schedule */
+ tv.tv_usec = 0;
+ tv.tv_sec = external->frequency;
+ evtimer_set(&external->ev, external_handler, external);
+ evtimer_add(&external->ev, &tv);
+
+ /* execute */
+ external_async_exec(external);
+}
- if (device >= MAX_IFINDEX)
- errx(1, "ifindex exceeded at: %d\n", lineno);
+void
+external_async_exec(struct ifsd_external *external)
+{
+ pid_t pid;
+ char *argp[] = {"sh", "-c", NULL, NULL};
+
+ if (external->pid > 0) {
+ LOG(IFSD_LOG_NORMAL,
+ "previous command %s still running, killing it\n",
+ external->command);
+ kill(external->pid, SIGKILL);
+ external->pid = 0;
+ }
- free(line);
+ argp[2] = external->command;
+ LOG(IFSD_LOG_VERBOSE, "running %s\n", external->command);
+ pid = fork();
+ if (pid < 0) {
+ LOG(IFSD_LOG_QUIET, "%s", "fork error");
+ } else if (pid == 0) {
+ execv("/bin/sh", argp);
+ _exit(1);
+ /* NOTREACHED */
+ } else {
+ external->pid = pid;
}
- fclose(fconfig);
}
void
-loop(void)
+check_external_status(struct ifsd_state *state)
{
- int sockfd;
- int n;
- char msg[2048];
-
- if ((sockfd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0)
- errx(1, "no routing socket");
-
- if (!opt_inhibit)
- fetch_state();
-
- for (;;) {
- n = read(sockfd, msg, sizeof(msg));
+ struct ifsd_external *external, *end = NULL;
+ struct ifsd_expression_list expressions;
+ int status, s, changed = 0;
+
+ TAILQ_INIT(&expressions);
+
+ /* 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(IFSD_LOG_QUIET,
+ "%s exited abnormally", external->command);
+ goto loop;
+ }
- /* reread config, run commands for current states */
- if (got_sighup) {
- got_sighup = 0;
- LOG("%s\n", "restart");
- doconfig(configfile);
- if (!opt_inhibit)
- fetch_state();
+ if (external->prevstatus != status &&
+ (external->prevstatus != -1 || !opt_inhibit)) {
+ struct ifsd_expression *expression;
+
+ changed = 1;
+ TAILQ_FOREACH(expression,
+ &external->expressions, entries) {
+ TAILQ_INSERT_TAIL(&expressions,
+ expression, eval);
+ if (status == 0)
+ expression->truth = 1;
+ else
+ expression->truth = 0;
+ }
}
+ external->lastexec = time(NULL);
+ TAILQ_REMOVE(&state->external_tests, external, entries);
+ TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
+ external->prevstatus = status;
+loop:
+ external = newexternal;
+ }
- eval_rtmsg((struct rt_msghdr *) msg, n);
+ if (changed) {
+ adjust_expressions(&expressions, conf.maxdepth);
+ eval_state(state);
}
}
void
-sighup_handler(int x)
+external_evtimer_setup(struct ifsd_state *state, int action)
{
- got_sighup = 1;
+ struct ifsd_external *external;
+
+ 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_async_exec(external);
+
+ /* schedule it for later */
+ tv.tv_usec = 0;
+ 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);
+ external->pid = 0;
+ }
+ evtimer_del(&external->ev);
+ }
+ break;
+ }
+ }
}
-void
-eval_rtmsg(struct rt_msghdr *rtm, int msglen)
+int
+scan_ifstate(int ifindex, int s, struct ifsd_state *state)
{
- struct if_msghdr *ifm;
-
- /* XXX ignore errors? */
- if (msglen < sizeof(struct rt_msghdr))
- return;
-
- if (rtm->rtm_version != RTM_VERSION)
- return;
+ struct ifsd_ifstate *ifstate;
+ struct ifsd_expression_list expressions;
+ int changed = 0;
+
+ TAILQ_INIT(&expressions);
+
+ TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
+ if (ifstate->ifindex == ifindex) {
+
+ if (ifstate->prevstate != s &&
+ (ifstate->prevstate != -1 || !opt_inhibit)) {
+ struct ifsd_expression *expression;
+ int truth;
+
+ if (ifstate->ifstate == s)
+ truth = 1;
+ else
+ truth = 0;
+
+ TAILQ_FOREACH(expression,
+ &ifstate->expressions, entries) {
+ expression->truth = truth;
+ TAILQ_INSERT_TAIL(&expressions,
+ expression, eval);
+ changed = 1;
+ }
+ ifstate->prevstate = s;
+ }
+ }
+ }
- if (rtm->rtm_type != RTM_IFINFO)
- return;
+ if (changed)
+ adjust_expressions(&expressions, conf.maxdepth);
+ return (changed);
+}
- ifm = (struct if_msghdr *)rtm;
- scan_table(ifm->ifm_index, ifm->ifm_data.ifi_link_state);
+/*
+ * Do a bottom-up ajustment 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:
+ if (expression->left->truth &&
+ expression->right->truth)
+ expression->truth = 1;
+ else
+ expression->truth = 0;
+ break;
+ case IFSD_OPER_OR:
+ if (expression->left->truth ||
+ expression->right->truth)
+ expression->truth = 1;
+ else
+ expression->truth = 0;
+ break;
+ case IFSD_OPER_NOT:
+ if (expression->right->truth)
+ expression->truth = 0;
+ else
+ expression->truth = 1;
+ 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 = TAILQ_FIRST(&state->external_tests);
+ if (external == NULL || external->lastexec >= state->entered) {
+ do_action(state->always);
+ state_change();
+ }
+}
/*
- * Scan conf_table for commands to run.
- * Ignore, if there was no change to the previous state.
+ *If a previous action included a state change, process it.
*/
void
-scan_table(int if_index, int state)
+state_change(void)
{
- int i = 0;
-
- while (conf_table[i].cmd[0] != '\0') {
- if (conf_table[i].dev == if_index &&
- conf_table[i].state == state &&
- if_index <= MAX_IFINDEX &&
- prev_states[if_index] != state) {
+ if (conf.nextstate != NULL && conf.curstate != conf.nextstate) {
+ LOG(IFSD_LOG_NORMAL, "changing state to %s\n",
+ conf.nextstate->name);
+ evtimer_del(&conf.curstate->ev);
+ if (conf.curstate != NULL)
+ 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);
+ fetch_state();
+ do_action(conf.curstate->init);
+ fetch_state();
+ }
+}
- LOG("%s\n", conf_table[i].cmd);
- system(conf_table[i].cmd);
+/*
+ * 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(IFSD_LOG_NORMAL, "running %s\n", 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);
}
- i++;
+ break;
+ default:
+ LOG(IFSD_LOG_DEBUG, "do_action: unknown action %d",
+ action->type);
+ break;
}
- prev_states[if_index] = state;
}
-/* Fetch the current link states. */
+/*
+ * Fetch the current link states.
+ */
void
fetch_state(void)
{
- int i;
+ struct ifaddrs *ifap, *ifa;
+ char *oname = NULL;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
- for (i = 0; i < MAX_IFINDEX; i++) {
+ if (getifaddrs(&ifap) != 0)
+ err(1, "getifaddrs");
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
struct ifreq ifr;
struct if_data ifrdat;
- char if_name[IF_NAMESIZE];
-
- if (prev_states[i] != -1)
+ if (oname && !strcmp(oname, ifa->ifa_name))
continue;
+ oname = ifa->ifa_name;
- if_indextoname(i, if_name);
-
- strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
+ strlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
ifr.ifr_data = (caddr_t)&ifrdat;
if (ioctl(sock, SIOCGIFDATA, (caddr_t)&ifr) == -1)
continue;
- scan_table(i, ifrdat.ifi_link_state);
-
+ scan_ifstate(if_nametoindex(ifa->ifa_name),
+ ifrdat.ifi_link_state, &conf.always);
+ if (conf.curstate != NULL)
+ scan_ifstate(if_nametoindex(ifa->ifa_name),
+ ifrdat.ifi_link_state, conf.curstate);
}
close(sock);
}
+
+
+
+/*
+ * Clear the config.
+ */
+void
+clear_config(struct ifsd_config *oconf)
+{
+ struct ifsd_state *state;
+
+ external_evtimer_setup(&conf.always, IFSD_EVTIMER_DEL);
+ if (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->always, state);
+ free(state->name);
+ free(state);
+ }
+ remove_action(oconf->always.init, &oconf->always);
+ remove_action(oconf->always.always, &oconf->always);
+}
+
+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_LOG:
+ free(action->act.logmessage);
+ break;
+ 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);
+}
diff --git a/usr.sbin/ifstated/ifstated.h b/usr.sbin/ifstated/ifstated.h
new file mode 100644
index 00000000000..3998fe5afdb
--- /dev/null
+++ b/usr.sbin/ifstated/ifstated.h
@@ -0,0 +1,141 @@
+/* $OpenBSD: ifstated.h,v 1.1 2004/02/04 23:47:49 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2004 Ryan McBride
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+
+struct ifsd_expression;
+TAILQ_HEAD(ifsd_expression_list, ifsd_expression);
+
+struct ifsd_ifstate {
+ TAILQ_ENTRY(ifsd_ifstate) entries;
+ struct ifsd_expression_list expressions;
+ int ifstate;
+#define IFSD_LINKUNKNOWN 0 /* LINK_STATE_UNKNOWN */
+#define IFSD_LINKDOWN 1 /* LINK_STATE_DOWN */
+#define IFSD_LINKUP 2 /* LINK_STATE_UP */
+ int prevstate;
+ u_int32_t refcount;
+ u_short ifindex;
+};
+
+struct ifsd_external {
+ TAILQ_ENTRY(ifsd_external) entries;
+ struct event ev;
+ struct ifsd_expression_list expressions;
+ char *command;
+ int prevstatus;
+ u_int32_t frequency;
+ u_int32_t refcount;
+ u_int32_t lastexec;
+ pid_t pid;
+};
+
+struct ifsd_action;
+TAILQ_HEAD(ifsd_action_list, ifsd_action);
+
+struct ifsd_action {
+ TAILQ_ENTRY(ifsd_action) entries;
+ struct ifsd_action *parent;
+ union {
+ char *logmessage;
+ char *command;
+ struct ifsd_state *nextstate;
+ char *statename;
+ struct {
+ struct ifsd_action_list actions;
+ struct ifsd_expression *expression;
+ u_int8_t ignore_init;
+ } c;
+ } act;
+ u_int32_t type;
+#define IFSD_ACTION_LOG 0
+#define IFSD_ACTION_COMMAND 1
+#define IFSD_ACTION_CHANGESTATE 2
+#define IFSD_ACTION_CONDITION 3
+};
+
+
+struct ifsd_expression {
+ TAILQ_ENTRY(ifsd_expression) entries;
+ TAILQ_ENTRY(ifsd_expression) eval;
+ struct ifsd_expression *parent;
+ struct ifsd_action *action;
+ struct ifsd_expression *left;
+ struct ifsd_expression *right;
+ union {
+ struct ifsd_ifstate *ifstate;
+ struct ifsd_external *external;
+ } u;
+ int depth;
+ u_int32_t type;
+#define IFSD_OPER_AND 1
+#define IFSD_OPER_OR 2
+#define IFSD_OPER_NOT 3
+#define IFSD_OPER_EXTERNAL 4
+#define IFSD_OPER_IFSTATE 5
+ u_int8_t truth;
+};
+
+TAILQ_HEAD(ifsd_ifstate_list, ifsd_ifstate);
+TAILQ_HEAD(ifsd_external_list, ifsd_external);
+
+struct ifsd_state {
+ struct event ev;
+ struct ifsd_ifstate_list interface_states;
+ struct ifsd_external_list external_tests;
+ TAILQ_ENTRY(ifsd_state) entries;
+ struct ifsd_action *init;
+ struct ifsd_action *always;
+ u_int32_t entered;
+ char *name;
+};
+
+TAILQ_HEAD(ifsd_state_list, ifsd_state);
+
+struct ifsd_config {
+ struct ifsd_state always;
+ struct ifsd_state_list states;
+ struct ifsd_state *curstate;
+ struct ifsd_state *nextstate;
+ u_int32_t opts;
+#define IFSD_OPT_VERBOSE 0x00000001
+#define IFSD_OPT_VERBOSE2 0x00000002
+ int maxdepth;
+ u_int8_t loglevel;
+#define IFSD_LOG_NONE 0
+#define IFSD_LOG_QUIET 1
+#define IFSD_LOG_NORMAL 2
+#define IFSD_LOG_VERBOSE 3
+#define IFSD_LOG_DEBUG 4
+};
+
+enum { IFSD_EVTIMER_ADD, IFSD_EVTIMER_DEL };
+int parse_config(char *, struct ifsd_config *);
+int cmdline_symset(char *);
diff --git a/usr.sbin/ifstated/parse.y b/usr.sbin/ifstated/parse.y
new file mode 100644
index 00000000000..918491bf94a
--- /dev/null
+++ b/usr.sbin/ifstated/parse.y
@@ -0,0 +1,869 @@
+/* $OpenBSD: parse.y,v 1.1 2004/02/04 23:47:49 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * 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.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <event.h>
+
+#include "ifstated.h"
+
+static struct ifsd_config *conf;
+static FILE *fin = NULL;
+static int lineno = 1;
+static int errors = 0;
+static int pdebug = 1;
+char *infile;
+char *start_state;
+
+struct ifsd_action *curaction;
+struct ifsd_state *curstate = NULL;
+
+int yyerror(const char *, ...);
+int yyparse(void);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(FILE *);
+int lungetc(int);
+int findeol(void);
+int yylex(void);
+
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entries;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+
+void link_states(struct ifsd_action *);
+int symset(const char *, const char *, int);
+char *symget(const char *);
+int atoul(char *, u_long *);
+void set_expression_depth(struct ifsd_expression *, int);
+void init_state(struct ifsd_state *);
+struct ifsd_ifstate *new_ifstate(u_short, int);
+struct ifsd_external *new_external(char *, u_int32_t);
+
+typedef struct {
+ union {
+ u_int32_t number;
+ char *string;
+ struct in_addr addr;
+ u_short interface;
+
+ struct ifsd_expression *expression;
+ struct ifsd_ifstate *ifstate;
+ struct ifsd_external *external;
+
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token STATE INITSTATE
+%token LINK UP DOWN UNKNOWN ADDED REMOVED
+%token IF RUN SETSTATE EVERY INIT LOGLEVEL
+%left AND OR
+%left UNARY
+%token ERROR
+%token <v.string> STRING
+%type <v.number> number
+%type <v.string> string
+%type <v.interface> interface
+%type <v.ifstate> if_test
+%type <v.external> ext_test
+%type <v.expression> expr term
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar conf_main '\n'
+ | grammar varset '\n'
+ | grammar action '\n'
+ | grammar state '\n'
+ | grammar error '\n' { errors++; }
+ ;
+
+number : STRING {
+ u_long ulval;
+
+ if (atoul($1, &ulval) == -1) {
+ yyerror("%s is not a number", $1);
+ YYERROR;
+ } else
+ $$ = ulval;
+ }
+ ;
+
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1) {
+ yyerror("string: asprintf");
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+varset : STRING '=' string {
+ if (conf->opts & IFSD_OPT_VERBOSE)
+ printf("%s = \"%s\"\n", $1, $3);
+ if (symset($1, $3, 0) == -1) {
+ yyerror("cannot store variable");
+ YYERROR;
+ }
+ }
+ ;
+
+conf_main : INITSTATE STRING {
+ if ((start_state = strdup($2)) == NULL) {
+ yyerror("conf_main: strdup");
+ YYERROR;
+ }
+ }
+ | LOGLEVEL STRING {
+ if (!strcmp($2, "none"))
+ conf->loglevel = IFSD_LOG_NONE;
+ else if (!strcmp($2, "quiet"))
+ conf->loglevel = IFSD_LOG_QUIET;
+ else if (!strcmp($2, "normal"))
+ conf->loglevel = IFSD_LOG_NORMAL;
+ else if (!strcmp($2, "verbose"))
+ conf->loglevel = IFSD_LOG_VERBOSE;
+ else if (!strcmp($2, "debug"))
+ conf->loglevel = IFSD_LOG_DEBUG;
+ }
+ ;
+
+interface : STRING {
+ if (($$ = if_nametoindex($1)) == 0) {
+ yyerror("unknown interface %s", $1);
+ YYERROR;
+ }
+ }
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+nl : '\n' optnl /* one newline or more */
+ ;
+
+action : RUN STRING {
+ struct ifsd_action *action;
+
+ if ((action = calloc(1, sizeof(*action))) == NULL)
+ err(1, "action: calloc");
+ action->type = IFSD_ACTION_COMMAND;
+ action->act.command = strdup($2);
+ if (action->act.command == NULL)
+ err(1, "action: strdup");
+ TAILQ_INSERT_TAIL(&curaction->act.c.actions,
+ action, entries);
+ }
+ | SETSTATE STRING {
+ struct ifsd_action *action;
+
+ if (curstate == NULL) {
+ yyerror("setstate must be used inside 'if'");
+ YYERROR;
+ }
+ if ((action = calloc(1, sizeof(*action))) == NULL)
+ err(1, "action: calloc");
+ action->type = IFSD_ACTION_CHANGESTATE;
+ if ((action->act.statename = strdup($2)) == NULL)
+ err(1, "action: strdup");
+ TAILQ_INSERT_TAIL(&curaction->act.c.actions,
+ action, entries);
+ }
+ | IF {
+ struct ifsd_action *action;
+
+ if ((action = calloc(1, sizeof(*action))) == NULL)
+ err(1, "action: calloc");
+ action->type = IFSD_ACTION_CONDITION;
+ TAILQ_INIT(&action->act.c.actions);
+ TAILQ_INSERT_TAIL(&curaction->act.c.actions,
+ action, entries);
+ action->parent = curaction;
+ curaction = action;
+ } expr optnl '{' optnl action_l '}' {
+ set_expression_depth(curaction->act.c.expression, 0);
+ curaction = curaction->parent;
+ }
+ ;
+
+action_l : action_l action nl
+ | action nl
+ ;
+
+init : INIT {
+ if (curstate != NULL)
+ curaction = curstate->init;
+ else
+ curaction = conf->always.init;
+ if (curaction == NULL) errx(1, "curaction == NULL");
+
+ } optnl '{' optnl action_l '}' {
+ if (curstate != NULL)
+ curaction = curstate->always;
+ else
+ curaction = conf->always.always;
+ if (curaction == NULL) errx(1, "curaction == NULL");
+ }
+
+if_test : interface LINK UP {
+ $$ = new_ifstate($1, IFSD_LINKUP);
+ }
+ | interface LINK DOWN {
+ $$ = new_ifstate($1, IFSD_LINKDOWN);
+ }
+ | interface LINK UNKNOWN {
+ $$ = new_ifstate($1, IFSD_LINKUNKNOWN);
+ }
+ ;
+
+ext_test : STRING EVERY number {
+ $$ = new_external($1, $3);
+ }
+ ;
+
+term : if_test {
+ if (($$ = calloc(1, sizeof(*$$))) == NULL)
+ errx(1, "term: calloc");
+ curaction->act.c.expression = $$;
+ $$->type = IFSD_OPER_IFSTATE;
+ $$->u.ifstate = $1;
+ TAILQ_INSERT_TAIL(&$1->expressions, $$, entries);
+ }
+ | ext_test {
+ if (($$ = calloc(1, sizeof(*$$))) == NULL)
+ errx(1, "term: calloc");
+ curaction->act.c.expression = $$;
+ $$->type = IFSD_OPER_EXTERNAL;
+ $$->u.external = $1;
+ TAILQ_INSERT_TAIL(&$1->expressions, $$, entries);
+ }
+ | '(' expr ')' {
+ $$ = $2;
+ }
+ ;
+
+expr : '!' expr %prec UNARY {
+ if (($$ = calloc(1, sizeof(*$$))) == NULL)
+ errx(1, "expr: calloc");
+ curaction->act.c.expression = $$;
+ $$->type = IFSD_OPER_NOT;
+ $2->parent = $$;
+ $$->right = $2;
+ }
+ | expr AND expr {
+ if (($$ = calloc(1, sizeof(*$$))) == NULL)
+ errx(1, "expr: calloc");
+ curaction->act.c.expression = $$;
+ $$->type = IFSD_OPER_AND;
+ $1->parent = $$;
+ $3->parent = $$;
+ $$->left = $1;
+ $$->right = $3;
+ }
+ | expr OR expr {
+ if (($$ = calloc(1, sizeof(*$$))) == NULL)
+ errx(1, "expr: calloc");
+ curaction->act.c.expression = $$;
+ $$->type = IFSD_OPER_OR;
+ $1->parent = $$;
+ $3->parent = $$;
+ $$->left = $1;
+ $$->right = $3;
+ }
+ | term
+ ;
+
+state : STATE string {
+ struct ifsd_state *state = NULL;
+
+ TAILQ_FOREACH(state, &conf->states, entries)
+ if (!strcmp(state->name, $2)) {
+ yyerror("state %s already exists", $2);
+ YYERROR;
+ }
+ if ((state = calloc(1, sizeof(*curstate))) == NULL)
+ errx(1, "state: calloc");
+ init_state(state);
+ if ((state->name = strdup($2)) == NULL)
+ errx(1, "state: strdup");
+ curstate = state;
+ curaction = state->always;
+ } optnl '{' optnl stateopts_l '}' {
+ TAILQ_INSERT_TAIL(&conf->states, curstate, entries);
+ curstate = NULL;
+ curaction = conf->always.always;
+ }
+ ;
+
+stateopts_l : stateopts_l stateoptsl
+ | stateoptsl
+ ;
+
+stateoptsl : init nl
+ | action nl
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+
+ errors = 1;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", infile, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "added", ADDED},
+ { "and", AND},
+ { "down", DOWN},
+ { "every", EVERY},
+ { "if", IF},
+ { "init", INIT},
+ { "init-state", INITSTATE},
+ { "link", LINK},
+ { "loglevel", LOGLEVEL},
+ { "or", OR},
+ { "removed", REMOVED},
+ { "run", RUN},
+ { "set-state", SETSTATE},
+ { "state", STATE},
+ { "unknown", UNKNOWN},
+ { "up", UP}
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p) {
+ if (pdebug > 1)
+ fprintf(stderr, "%s: %d\n", s, p->k_val);
+ return (p->k_val);
+ } else {
+ if (pdebug > 1)
+ fprintf(stderr, "string: %s\n", s);
+ return (STRING);
+ }
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(FILE *f)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ while ((c = getc(f)) == '\\') {
+ next = getc(f);
+ if (next != '\n') {
+ if (isspace(next))
+ yyerror("whitespace after \\");
+ ungetc(next, f);
+ break;
+ }
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == '\t' || c == ' ') {
+ /* Compress blanks to a single space. */
+ do {
+ c = getc(f);
+ } while (c == '\t' || c == ' ');
+ ungetc(c, f);
+ c = ' ';
+ }
+
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(fin);
+ if (c == '\n') {
+ lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p, *val;
+ int endc, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(fin)) == ' ')
+ ; /* nothing */
+
+ yylval.lineno = lineno;
+ if (c == '#')
+ while ((c = lgetc(fin)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = (char)c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ endc = c;
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+ if (c == endc) {
+ *p = '\0';
+ break;
+ }
+ if (c == '\n') {
+ lineno++;
+ continue;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ errx(1, "yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ token = lookup(buf);
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ errx(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+int
+parse_config(char *filename, struct ifsd_config *xconf)
+{
+ struct sym *sym, *next;
+ struct ifsd_state *state;
+
+ if ((conf = calloc(1, sizeof(struct ifsd_config))) == NULL)
+ errx(1, "parse_config calloc");
+
+ TAILQ_INIT(&conf->states);
+
+ init_state(&conf->always);
+ curaction = conf->always.always;
+ conf->loglevel = IFSD_LOG_NORMAL;
+
+ if ((fin = fopen(filename, "r")) == NULL) {
+ warn("%s", filename);
+ free(conf);
+ return (-1);
+ }
+ infile = filename;
+
+ yyparse();
+
+ fclose(fin);
+
+ /* Link states */
+ TAILQ_FOREACH(state, &conf->states, entries) {
+ link_states(state->init);
+ link_states(state->always);
+ }
+
+ if (start_state != NULL) {
+ TAILQ_FOREACH(state, &conf->states, entries) {
+ if (strcmp(start_state, state->name) == 0) {
+ conf->curstate = state;
+ break;
+ }
+ }
+ if (conf->curstate == NULL)
+ errx(1, "invalid start state %s", start_state);
+ } else {
+ conf->curstate = TAILQ_FIRST(&conf->states);
+ }
+
+ /* Free macros and check which have not been used. */
+ for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
+ next = TAILQ_NEXT(sym, entries);
+ if ((conf->opts & IFSD_OPT_VERBOSE2) && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not "
+ "used\n", sym->nam);
+ if (!sym->persist) {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entries);
+ free(sym);
+ }
+ }
+
+ bcopy(conf, xconf, sizeof(*conf));
+ free(conf);
+
+ return (errors ? -1 : 0);
+}
+
+void
+link_states(struct ifsd_action *action)
+{
+ struct ifsd_action *subaction;
+
+ switch (action->type) {
+ default:
+ case IFSD_ACTION_COMMAND:
+ break;
+ case IFSD_ACTION_CHANGESTATE: {
+ struct ifsd_state *state;
+
+ TAILQ_FOREACH(state, &conf->states, entries) {
+ if (strcmp(action->act.statename,
+ state->name) == 0) {
+ action->act.nextstate = state;
+ break;
+ }
+ }
+ break;
+ }
+ case IFSD_ACTION_CONDITION:
+ TAILQ_FOREACH(subaction, &action->act.c.actions, entries)
+ link_states(subaction);
+ break;
+ }
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entries))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entries);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entries);
+ return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = strlen(s) - strlen(val) + 1;
+ if ((sym = malloc(len)) == NULL)
+ errx(1, "cmdline_symset: malloc");
+
+ strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entries)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+int
+atoul(char *s, u_long *ulvalp)
+{
+ u_long ulval;
+ char *ep;
+
+ errno = 0;
+ ulval = strtoul(s, &ep, 0);
+ if (s[0] == '\0' || *ep != '\0')
+ return (-1);
+ if (errno == ERANGE && ulval == ULONG_MAX)
+ return (-1);
+ *ulvalp = ulval;
+ return (0);
+}
+
+void
+set_expression_depth(struct ifsd_expression *expression, int depth)
+{
+ expression->depth = depth;
+ if (conf->maxdepth < depth)
+ conf->maxdepth = depth;
+ if (expression->left != NULL)
+ set_expression_depth(expression->left, depth + 1);
+ if (expression->right != NULL)
+ set_expression_depth(expression->right, depth + 1);
+}
+
+void
+init_state(struct ifsd_state *state)
+{
+ TAILQ_INIT(&state->interface_states);
+ TAILQ_INIT(&state->external_tests);
+
+ if ((state->init = calloc(1, sizeof(*state->init))) == NULL)
+ err(1, "init_state: calloc");
+ state->init->type = IFSD_ACTION_CONDITION;
+ TAILQ_INIT(&state->init->act.c.actions);
+
+ if ((state->always = calloc(1, sizeof(*state->always))) == NULL)
+ err(1, "init_state: calloc");
+ state->always->type = IFSD_ACTION_CONDITION;
+ TAILQ_INIT(&state->always->act.c.actions);
+}
+
+struct ifsd_ifstate *
+new_ifstate(u_short ifindex, int s)
+{
+ struct ifsd_ifstate *ifstate = NULL;
+ struct ifsd_state *state;
+
+ if (curstate != NULL)
+ state = curstate;
+ else
+ state = &conf->always;
+
+ TAILQ_FOREACH(ifstate, &state->interface_states, entries)
+ if (ifstate->ifindex == ifindex && ifstate->ifstate == s)
+ break;
+ if (ifstate == NULL) {
+ if ((ifstate = calloc(1, sizeof(*ifstate))) == NULL)
+ errx(1, "new_ifstate: calloc");
+ ifstate->ifindex = ifindex;
+ ifstate->ifstate = s;
+ TAILQ_INIT(&ifstate->expressions);
+ TAILQ_INSERT_TAIL(&state->interface_states, ifstate, entries);
+ }
+ ifstate->prevstate = -1;
+ ifstate->refcount++;
+ return (ifstate);
+}
+
+struct ifsd_external *
+new_external(char *command, u_int32_t frequency)
+{
+ struct ifsd_external *external = NULL;
+ struct ifsd_state *state;
+
+ if (curstate != NULL)
+ state = curstate;
+ else
+ state = &conf->always;
+
+ TAILQ_FOREACH(external, &state->external_tests, entries)
+ if (strcmp(external->command, command) == 0 &&
+ external->frequency == frequency)
+ break;
+ if (external == NULL) {
+ if ((external = calloc(1, sizeof(*external))) == NULL)
+ errx(1, "new_external: calloc");
+ if ((external->command = strdup(command)) == NULL)
+ errx(1, "new_external: strdup");
+ external->frequency = frequency;
+ TAILQ_INIT(&external->expressions);
+ TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
+ }
+ external->prevstatus = -1;
+ external->refcount++;
+ return (external);
+}