diff options
author | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2004-02-04 23:47:50 +0000 |
---|---|---|
committer | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2004-02-04 23:47:50 +0000 |
commit | 76cee6cf3c1cb3ae97985e5f5dac813e1ab04017 (patch) | |
tree | 3aa753beddacbbcf8a4a708e17c379bf7770eefd /usr.sbin | |
parent | f6b5cb8c267b47e74cac5bbb4d402b08c73e06bd (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/Makefile | 9 | ||||
-rw-r--r-- | usr.sbin/ifstated/ifstated.c | 689 | ||||
-rw-r--r-- | usr.sbin/ifstated/ifstated.h | 141 | ||||
-rw-r--r-- | usr.sbin/ifstated/parse.y | 869 |
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); +} |