1*e6c7c102Sjsg /* $OpenBSD: ifstated.c,v 1.68 2024/04/23 13:34:51 jsg Exp $ */
26dbcce3fSmcbride
36dbcce3fSmcbride /*
46dbcce3fSmcbride * Copyright (c) 2004 Marco Pfatschbacher <mpf@openbsd.org>
5b73b88efSmcbride * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
66dbcce3fSmcbride *
76dbcce3fSmcbride * Permission to use, copy, modify, and distribute this software for any
86dbcce3fSmcbride * purpose with or without fee is hereby granted, provided that the above
96dbcce3fSmcbride * copyright notice and this permission notice appear in all copies.
106dbcce3fSmcbride *
116dbcce3fSmcbride * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
126dbcce3fSmcbride * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
136dbcce3fSmcbride * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
146dbcce3fSmcbride * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
156dbcce3fSmcbride * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
166dbcce3fSmcbride * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
176dbcce3fSmcbride * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
186dbcce3fSmcbride */
196dbcce3fSmcbride
206dbcce3fSmcbride /*
216dbcce3fSmcbride * ifstated listens to link_state transitions on interfaces
226dbcce3fSmcbride * and executes predefined commands.
236dbcce3fSmcbride */
246dbcce3fSmcbride
256dbcce3fSmcbride #include <sys/types.h>
266dbcce3fSmcbride #include <sys/time.h>
276dbcce3fSmcbride #include <sys/socket.h>
28b73b88efSmcbride #include <sys/wait.h>
296dbcce3fSmcbride
306dbcce3fSmcbride #include <net/if.h>
316dbcce3fSmcbride #include <net/route.h>
326dbcce3fSmcbride #include <netinet/in.h>
336dbcce3fSmcbride
34a4459108Smestre #include <paths.h>
356dbcce3fSmcbride #include <stdio.h>
366dbcce3fSmcbride #include <stdlib.h>
376dbcce3fSmcbride #include <string.h>
386dbcce3fSmcbride #include <signal.h>
393c0535b0Sbenno #include <stdint.h>
40bfdb9ad4Sbenno #include <syslog.h>
41089b0f81Srob #include <errno.h>
42b73b88efSmcbride #include <event.h>
436dbcce3fSmcbride #include <unistd.h>
44b73b88efSmcbride #include <ifaddrs.h>
45b73b88efSmcbride
46b73b88efSmcbride #include "ifstated.h"
47bfdb9ad4Sbenno #include "log.h"
486dbcce3fSmcbride
49d64c7ac9Sderaadt struct ifsd_config *conf, *newconf;
506dbcce3fSmcbride
51d64c7ac9Sderaadt int opts;
52d64c7ac9Sderaadt int opt_inhibit;
536dbcce3fSmcbride char *configfile = "/etc/ifstated.conf";
548bb56c35Smcbride struct event rt_msg_ev, sighup_ev, startup_ev, sigchld_ev;
556dbcce3fSmcbride
56b73b88efSmcbride void startup_handler(int, short, void *);
57b73b88efSmcbride void sighup_handler(int, short, void *);
588bb56c35Smcbride int load_config(void);
59b73b88efSmcbride void sigchld_handler(int, short, void *);
60b73b88efSmcbride void rt_msg_handler(int, short, void *);
61b73b88efSmcbride void external_handler(int, short, void *);
62b688c394Smpf void external_exec(struct ifsd_external *, int);
63b73b88efSmcbride void check_external_status(struct ifsd_state *);
6440699dccSrob void check_ifdeparture(void);
65b73b88efSmcbride void external_evtimer_setup(struct ifsd_state *, int);
6618e4260cSrob void scan_ifstate(const char *, int, int);
6718e4260cSrob int scan_ifstate_single(const char *, int, struct ifsd_state *);
68089b0f81Srob void fetch_ifstate(int);
69bce9047dSbenno __dead void usage(void);
70b73b88efSmcbride void adjust_expressions(struct ifsd_expression_list *, int);
71b688c394Smpf void adjust_external_expressions(struct ifsd_state *);
72b73b88efSmcbride void eval_state(struct ifsd_state *);
73b688c394Smpf int state_change(void);
74b73b88efSmcbride void do_action(struct ifsd_action *);
75b73b88efSmcbride void remove_action(struct ifsd_action *, struct ifsd_state *);
767c875bb8Sbenno void remove_expression(struct ifsd_expression *,
777c875bb8Sbenno struct ifsd_state *);
786dbcce3fSmcbride
79bce9047dSbenno __dead void
usage(void)806dbcce3fSmcbride usage(void)
816dbcce3fSmcbride {
82fb210867Smcbride extern char *__progname;
83fb210867Smcbride
8497bef895Sjmc fprintf(stderr, "usage: %s [-dhinv] [-D macro=value] [-f file]\n",
85fb210867Smcbride __progname);
866dbcce3fSmcbride exit(1);
876dbcce3fSmcbride }
886dbcce3fSmcbride
896dbcce3fSmcbride int
main(int argc,char * argv[])906dbcce3fSmcbride main(int argc, char *argv[])
916dbcce3fSmcbride {
92b73b88efSmcbride struct timeval tv;
933c0535b0Sbenno int ch, rt_fd;
9435360db1Spyr int debug = 0;
953c0535b0Sbenno unsigned int rtfilter;
9635360db1Spyr
97bfdb9ad4Sbenno log_init(1, LOG_DAEMON); /* log to stderr until daemonized */
98bfdb9ad4Sbenno log_setverbose(1);
996dbcce3fSmcbride
10084f5df3aSmcbride while ((ch = getopt(argc, argv, "dD:f:hniv")) != -1) {
1016dbcce3fSmcbride switch (ch) {
1026dbcce3fSmcbride case 'd':
10335360db1Spyr debug = 1;
1046dbcce3fSmcbride break;
105b73b88efSmcbride case 'D':
106b73b88efSmcbride if (cmdline_symset(optarg) < 0)
10795b4ea67Srob fatalx("could not parse macro definition %s",
108b73b88efSmcbride optarg);
109b73b88efSmcbride break;
110b73b88efSmcbride case 'f':
111b73b88efSmcbride configfile = optarg;
112b73b88efSmcbride break;
113b73b88efSmcbride case 'h':
114b73b88efSmcbride usage();
115b73b88efSmcbride break;
11684f5df3aSmcbride case 'n':
1178bb56c35Smcbride opts |= IFSD_OPT_NOACTION;
11884f5df3aSmcbride break;
1196dbcce3fSmcbride case 'i':
1206dbcce3fSmcbride opt_inhibit = 1;
1216dbcce3fSmcbride break;
122b73b88efSmcbride case 'v':
1238bb56c35Smcbride if (opts & IFSD_OPT_VERBOSE)
1248bb56c35Smcbride opts |= IFSD_OPT_VERBOSE2;
1258bb56c35Smcbride opts |= IFSD_OPT_VERBOSE;
1266dbcce3fSmcbride break;
1276dbcce3fSmcbride default:
1286dbcce3fSmcbride usage();
1296dbcce3fSmcbride }
1306dbcce3fSmcbride }
1316dbcce3fSmcbride
132fde55bc4Spyr argc -= optind;
133fde55bc4Spyr argv += optind;
134fde55bc4Spyr if (argc > 0)
135fde55bc4Spyr usage();
136fde55bc4Spyr
1378bb56c35Smcbride if (opts & IFSD_OPT_NOACTION) {
1388bb56c35Smcbride if ((newconf = parse_config(configfile, opts)) == NULL)
13984f5df3aSmcbride exit(1);
14095b4ea67Srob fprintf(stderr, "configuration OK\n");
14184f5df3aSmcbride exit(0);
14284f5df3aSmcbride }
1436dbcce3fSmcbride
14482e24455Sderaadt if (!debug)
1452f505238Scamield daemon(1, 0);
1466dbcce3fSmcbride
1473d2bb036Smarkus event_init();
148bfdb9ad4Sbenno log_init(debug, LOG_DAEMON);
149bfdb9ad4Sbenno log_setverbose(opts & IFSD_OPT_VERBOSE);
1503d2bb036Smarkus
151df69c215Sderaadt if ((rt_fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
15295b4ea67Srob fatal("no routing socket");
153b2017614Smpf
15440699dccSrob rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE);
155b47fcd70Skrw if (setsockopt(rt_fd, AF_ROUTE, ROUTE_MSGFILTER,
156aad308e5Ssthen &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */
157bfdb9ad4Sbenno log_warn("%s: setsockopt msgfilter", __func__);
15887df0332Shenning
15987df0332Shenning rtfilter = RTABLE_ANY;
160b47fcd70Skrw if (setsockopt(rt_fd, AF_ROUTE, ROUTE_TABLEFILTER,
16187df0332Shenning &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */
162bfdb9ad4Sbenno log_warn("%s: setsockopt tablefilter", __func__);
163aad308e5Ssthen
164a4459108Smestre if (unveil(configfile, "r") == -1)
165bc5a8259Sbeck fatal("unveil %s", configfile);
166a4459108Smestre if (unveil(_PATH_BSHELL, "x") == -1)
167bc5a8259Sbeck fatal("unveil %s", _PATH_BSHELL);
1680eb8ec17Sjca if (pledge("stdio rpath route proc exec", NULL) == -1)
1690eb8ec17Sjca fatal("pledge");
1700eb8ec17Sjca
1713c0535b0Sbenno signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL);
1723c0535b0Sbenno signal_add(&sigchld_ev, NULL);
1733c0535b0Sbenno
1743c0535b0Sbenno /* Loading the config needs to happen in the event loop */
1753c0535b0Sbenno timerclear(&tv);
1763c0535b0Sbenno evtimer_set(&startup_ev, startup_handler, (void *)(long)rt_fd);
1773c0535b0Sbenno evtimer_add(&startup_ev, &tv);
1783c0535b0Sbenno
1793c0535b0Sbenno event_loop(0);
1803c0535b0Sbenno exit(0);
1813c0535b0Sbenno }
1823c0535b0Sbenno
1833c0535b0Sbenno void
startup_handler(int fd,short event,void * arg)1843c0535b0Sbenno startup_handler(int fd, short event, void *arg)
1853c0535b0Sbenno {
1863c0535b0Sbenno int rfd = (int)(long)arg;
1873c0535b0Sbenno
1883c0535b0Sbenno if (load_config() != 0) {
1893c0535b0Sbenno log_warnx("unable to load config");
1903c0535b0Sbenno exit(1);
1913c0535b0Sbenno }
1923c0535b0Sbenno
1933c0535b0Sbenno event_set(&rt_msg_ev, rfd, EV_READ|EV_PERSIST, rt_msg_handler, NULL);
1948bb56c35Smcbride event_add(&rt_msg_ev, NULL);
1958bb56c35Smcbride
1964dab0c72Smcbride signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL);
1978bb56c35Smcbride signal_add(&sighup_ev, NULL);
1988bb56c35Smcbride
19935360db1Spyr log_info("started");
2006dbcce3fSmcbride }
2016dbcce3fSmcbride
2026dbcce3fSmcbride void
sighup_handler(int fd,short event,void * arg)203b73b88efSmcbride sighup_handler(int fd, short event, void *arg)
204b73b88efSmcbride {
20535360db1Spyr log_info("reloading config");
2068bb56c35Smcbride if (load_config() != 0)
20735360db1Spyr log_warnx("unable to reload config");
208b73b88efSmcbride }
209b73b88efSmcbride
2108bb56c35Smcbride int
load_config(void)211b73b88efSmcbride load_config(void)
212b73b88efSmcbride {
2138bb56c35Smcbride if ((newconf = parse_config(configfile, opts)) == NULL)
2148bb56c35Smcbride return (-1);
2158bb56c35Smcbride if (conf != NULL)
2168bb56c35Smcbride clear_config(conf);
2178bb56c35Smcbride conf = newconf;
21820863243Sbenno conf->initstate.entered = time(NULL);
219089b0f81Srob fetch_ifstate(0);
22020863243Sbenno external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_ADD);
22120863243Sbenno adjust_external_expressions(&conf->initstate);
22220863243Sbenno eval_state(&conf->initstate);
2238bb56c35Smcbride if (conf->curstate != NULL) {
22435360db1Spyr log_info("initial state: %s", conf->curstate->name);
2258bb56c35Smcbride conf->curstate->entered = time(NULL);
22622794b72Smpf conf->nextstate = conf->curstate;
22722794b72Smpf conf->curstate = NULL;
228988c4ffaSbenno while (state_change()) {
229988c4ffaSbenno do_action(conf->curstate->init);
230138e089fSbenno do_action(conf->curstate->body);
231b73b88efSmcbride }
232988c4ffaSbenno }
2338bb56c35Smcbride return (0);
234b73b88efSmcbride }
235b73b88efSmcbride
236b73b88efSmcbride void
rt_msg_handler(int fd,short event,void * arg)237b73b88efSmcbride rt_msg_handler(int fd, short event, void *arg)
2386dbcce3fSmcbride {
239b73b88efSmcbride char msg[2048];
240b73b88efSmcbride struct rt_msghdr *rtm = (struct rt_msghdr *)&msg;
241aba3295aSderaadt struct if_msghdr ifm;
24240699dccSrob struct if_announcemsghdr ifan;
24318e4260cSrob char ifnamebuf[IFNAMSIZ];
24418e4260cSrob char *ifname;
24577c6c732Sderaadt ssize_t len;
246b73b88efSmcbride
247089b0f81Srob if ((len = read(fd, msg, sizeof(msg))) == -1) {
248089b0f81Srob if (errno == EAGAIN || errno == EINTR)
2496dbcce3fSmcbride return;
250089b0f81Srob fatal("%s: routing socket read error", __func__);
251089b0f81Srob }
252089b0f81Srob
253089b0f81Srob if (len == 0)
254089b0f81Srob fatal("%s: routing socket closed", __func__);
2556dbcce3fSmcbride
2566dbcce3fSmcbride if (rtm->rtm_version != RTM_VERSION)
2576dbcce3fSmcbride return;
2586dbcce3fSmcbride
259089b0f81Srob switch (rtm->rtm_type) {
260089b0f81Srob case RTM_IFINFO:
261f442093cShenning memcpy(&ifm, rtm, sizeof(ifm));
26218e4260cSrob ifname = if_indextoname(ifm.ifm_index, ifnamebuf);
26318e4260cSrob /* ifname is NULL on interface departure */
26418e4260cSrob if (ifname != NULL)
26518e4260cSrob scan_ifstate(ifname, ifm.ifm_data.ifi_link_state, 1);
266089b0f81Srob break;
26740699dccSrob case RTM_IFANNOUNCE:
26840699dccSrob memcpy(&ifan, rtm, sizeof(ifan));
26940699dccSrob switch (ifan.ifan_what) {
27040699dccSrob case IFAN_DEPARTURE:
27140699dccSrob log_warnx("interface %s departed", ifan.ifan_name);
27240699dccSrob check_ifdeparture();
27340699dccSrob break;
27440699dccSrob case IFAN_ARRIVAL:
27540699dccSrob log_warnx("interface %s arrived", ifan.ifan_name);
276089b0f81Srob fetch_ifstate(1);
277089b0f81Srob break;
27840699dccSrob }
27940699dccSrob break;
28040699dccSrob case RTM_DESYNC:
28140699dccSrob /* we lost some routing messages so rescan interfaces */
28240699dccSrob check_ifdeparture();
28340699dccSrob fetch_ifstate(1);
284089b0f81Srob break;
285089b0f81Srob }
286089b0f81Srob return;
2876dbcce3fSmcbride }
2886dbcce3fSmcbride
289b73b88efSmcbride void
sigchld_handler(int fd,short event,void * arg)290b73b88efSmcbride sigchld_handler(int fd, short event, void *arg)
291b73b88efSmcbride {
29220863243Sbenno check_external_status(&conf->initstate);
2938bb56c35Smcbride if (conf->curstate != NULL)
2948bb56c35Smcbride check_external_status(conf->curstate);
295b73b88efSmcbride }
296b73b88efSmcbride
297b73b88efSmcbride void
external_handler(int fd,short event,void * arg)298b73b88efSmcbride external_handler(int fd, short event, void *arg)
299b73b88efSmcbride {
300b73b88efSmcbride struct ifsd_external *external = (struct ifsd_external *)arg;
301b73b88efSmcbride struct timeval tv;
302b73b88efSmcbride
303b73b88efSmcbride /* re-schedule */
30432a8a117Sokan timerclear(&tv);
305b73b88efSmcbride tv.tv_sec = external->frequency;
306b73b88efSmcbride evtimer_set(&external->ev, external_handler, external);
307b73b88efSmcbride evtimer_add(&external->ev, &tv);
308b73b88efSmcbride
309b73b88efSmcbride /* execute */
310b688c394Smpf external_exec(external, 1);
311b73b88efSmcbride }
312b73b88efSmcbride
313b73b88efSmcbride void
external_exec(struct ifsd_external * external,int async)314b688c394Smpf external_exec(struct ifsd_external *external, int async)
315b73b88efSmcbride {
316b73b88efSmcbride char *argp[] = {"sh", "-c", NULL, NULL};
317aba3295aSderaadt pid_t pid;
318172fe27aSmpf int s;
319b73b88efSmcbride
320b73b88efSmcbride if (external->pid > 0) {
3218c3698cfSstsp log_debug("previous command %s [%d] still running, killing it",
32222794b72Smpf external->command, external->pid);
323b73b88efSmcbride kill(external->pid, SIGKILL);
324172fe27aSmpf waitpid(external->pid, &s, 0);
325b73b88efSmcbride external->pid = 0;
326b73b88efSmcbride }
327b73b88efSmcbride
328b73b88efSmcbride argp[2] = external->command;
32935360db1Spyr log_debug("running %s", external->command);
330b73b88efSmcbride pid = fork();
331df69c215Sderaadt if (pid == -1) {
33235360db1Spyr log_warn("fork error");
333b73b88efSmcbride } else if (pid == 0) {
334a4459108Smestre execv(_PATH_BSHELL, argp);
335b73b88efSmcbride _exit(1);
336b73b88efSmcbride /* NOTREACHED */
337b73b88efSmcbride } else {
338b73b88efSmcbride external->pid = pid;
339b73b88efSmcbride }
340b688c394Smpf if (!async) {
341b688c394Smpf waitpid(external->pid, &s, 0);
342b688c394Smpf external->pid = 0;
343b688c394Smpf if (WIFEXITED(s))
344b688c394Smpf external->prevstatus = WEXITSTATUS(s);
345b688c394Smpf }
346b688c394Smpf }
347b688c394Smpf
348b688c394Smpf void
adjust_external_expressions(struct ifsd_state * state)349b688c394Smpf adjust_external_expressions(struct ifsd_state *state)
350b688c394Smpf {
351b688c394Smpf struct ifsd_external *external;
352b688c394Smpf struct ifsd_expression_list expressions;
353b688c394Smpf
354b688c394Smpf TAILQ_INIT(&expressions);
355b688c394Smpf TAILQ_FOREACH(external, &state->external_tests, entries) {
356b688c394Smpf struct ifsd_expression *expression;
357b688c394Smpf
358b688c394Smpf if (external->prevstatus == -1)
359b688c394Smpf continue;
360b688c394Smpf
361b688c394Smpf TAILQ_FOREACH(expression, &external->expressions, entries) {
362b688c394Smpf TAILQ_INSERT_TAIL(&expressions,
363b688c394Smpf expression, eval);
3646ee78c37Smpf expression->truth = !external->prevstatus;
365b688c394Smpf }
366b688c394Smpf adjust_expressions(&expressions, conf->maxdepth);
367b688c394Smpf }
368b73b88efSmcbride }
369b73b88efSmcbride
370b73b88efSmcbride void
check_external_status(struct ifsd_state * state)371b73b88efSmcbride check_external_status(struct ifsd_state *state)
372b73b88efSmcbride {
373b73b88efSmcbride struct ifsd_external *external, *end = NULL;
374b73b88efSmcbride int status, s, changed = 0;
375b73b88efSmcbride
376b73b88efSmcbride /* Do this manually; change ordering so the oldest is first */
377b73b88efSmcbride external = TAILQ_FIRST(&state->external_tests);
378b73b88efSmcbride while (external != NULL && external != end) {
379b73b88efSmcbride struct ifsd_external *newexternal;
380b73b88efSmcbride
381b73b88efSmcbride newexternal = TAILQ_NEXT(external, entries);
382b73b88efSmcbride
383b73b88efSmcbride if (external->pid <= 0)
384b73b88efSmcbride goto loop;
385b73b88efSmcbride
386b73b88efSmcbride if (wait4(external->pid, &s, WNOHANG, NULL) == 0)
387b73b88efSmcbride goto loop;
388b73b88efSmcbride
389b73b88efSmcbride external->pid = 0;
390b73b88efSmcbride if (end == NULL)
391b73b88efSmcbride end = external;
392b73b88efSmcbride if (WIFEXITED(s))
393b73b88efSmcbride status = WEXITSTATUS(s);
394b73b88efSmcbride else {
39535360db1Spyr log_warnx("%s exited abnormally", external->command);
396b73b88efSmcbride goto loop;
397b73b88efSmcbride }
398b73b88efSmcbride
399b73b88efSmcbride if (external->prevstatus != status &&
400b73b88efSmcbride (external->prevstatus != -1 || !opt_inhibit)) {
401b73b88efSmcbride changed = 1;
402b688c394Smpf external->prevstatus = status;
403b73b88efSmcbride }
404b73b88efSmcbride external->lastexec = time(NULL);
405b73b88efSmcbride TAILQ_REMOVE(&state->external_tests, external, entries);
406b73b88efSmcbride TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
407b73b88efSmcbride loop:
408b73b88efSmcbride external = newexternal;
409b73b88efSmcbride }
410b73b88efSmcbride
411b73b88efSmcbride if (changed) {
412b688c394Smpf adjust_external_expressions(state);
413b73b88efSmcbride eval_state(state);
414b73b88efSmcbride }
415b73b88efSmcbride }
416b73b88efSmcbride
417b73b88efSmcbride void
external_evtimer_setup(struct ifsd_state * state,int action)418b73b88efSmcbride external_evtimer_setup(struct ifsd_state *state, int action)
419b73b88efSmcbride {
420b73b88efSmcbride struct ifsd_external *external;
421172fe27aSmpf int s;
422b73b88efSmcbride
423b73b88efSmcbride if (state != NULL) {
424b73b88efSmcbride switch (action) {
425b73b88efSmcbride case IFSD_EVTIMER_ADD:
426b73b88efSmcbride TAILQ_FOREACH(external,
427b73b88efSmcbride &state->external_tests, entries) {
428b73b88efSmcbride struct timeval tv;
429b73b88efSmcbride
430b73b88efSmcbride /* run it once right away */
431b688c394Smpf external_exec(external, 0);
432b73b88efSmcbride
433b73b88efSmcbride /* schedule it for later */
43432a8a117Sokan timerclear(&tv);
435b73b88efSmcbride tv.tv_sec = external->frequency;
436b73b88efSmcbride evtimer_set(&external->ev, external_handler,
437b73b88efSmcbride external);
438b73b88efSmcbride evtimer_add(&external->ev, &tv);
439b73b88efSmcbride }
440b73b88efSmcbride break;
441b73b88efSmcbride case IFSD_EVTIMER_DEL:
442b73b88efSmcbride TAILQ_FOREACH(external,
443b73b88efSmcbride &state->external_tests, entries) {
444b73b88efSmcbride if (external->pid > 0) {
445b73b88efSmcbride kill(external->pid, SIGKILL);
446172fe27aSmpf waitpid(external->pid, &s, 0);
447b73b88efSmcbride external->pid = 0;
448b73b88efSmcbride }
449b73b88efSmcbride evtimer_del(&external->ev);
450b73b88efSmcbride }
451b73b88efSmcbride break;
452b73b88efSmcbride }
453b73b88efSmcbride }
454b73b88efSmcbride }
455b73b88efSmcbride
4569a2e0324Sclaudio #define LINK_STATE_IS_DOWN(_s) (!LINK_STATE_IS_UP((_s)))
457a3630be1Sstevesk
458b73b88efSmcbride int
scan_ifstate_single(const char * ifname,int s,struct ifsd_state * state)45918e4260cSrob scan_ifstate_single(const char *ifname, int s, struct ifsd_state *state)
460b73b88efSmcbride {
461b73b88efSmcbride struct ifsd_ifstate *ifstate;
462b73b88efSmcbride struct ifsd_expression_list expressions;
463b73b88efSmcbride int changed = 0;
464b73b88efSmcbride
465b73b88efSmcbride TAILQ_INIT(&expressions);
466b73b88efSmcbride
467b73b88efSmcbride TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
46818e4260cSrob if (strcmp(ifstate->ifname, ifname) == 0) {
469b73b88efSmcbride if (ifstate->prevstate != s &&
470b73b88efSmcbride (ifstate->prevstate != -1 || !opt_inhibit)) {
471b73b88efSmcbride struct ifsd_expression *expression;
472b73b88efSmcbride int truth;
473b73b88efSmcbride
474a3630be1Sstevesk truth =
475a3630be1Sstevesk (ifstate->ifstate == IFSD_LINKUNKNOWN &&
476a3630be1Sstevesk s == LINK_STATE_UNKNOWN) ||
477a3630be1Sstevesk (ifstate->ifstate == IFSD_LINKDOWN &&
478a3630be1Sstevesk LINK_STATE_IS_DOWN(s)) ||
479cda33248Sreyk (ifstate->ifstate == IFSD_LINKUP &&
480cda33248Sreyk LINK_STATE_IS_UP(s));
481b73b88efSmcbride
482b73b88efSmcbride TAILQ_FOREACH(expression,
483b73b88efSmcbride &ifstate->expressions, entries) {
484b73b88efSmcbride expression->truth = truth;
485b73b88efSmcbride TAILQ_INSERT_TAIL(&expressions,
486b73b88efSmcbride expression, eval);
487b73b88efSmcbride changed = 1;
488b73b88efSmcbride }
489b73b88efSmcbride ifstate->prevstate = s;
490b73b88efSmcbride }
491b73b88efSmcbride }
492b73b88efSmcbride }
493b73b88efSmcbride
494b73b88efSmcbride if (changed)
4958bb56c35Smcbride adjust_expressions(&expressions, conf->maxdepth);
496b73b88efSmcbride return (changed);
497b73b88efSmcbride }
4986dbcce3fSmcbride
499616cc397Smpf void
scan_ifstate(const char * ifname,int s,int do_eval)50018e4260cSrob scan_ifstate(const char *ifname, int s, int do_eval)
501616cc397Smpf {
502616cc397Smpf struct ifsd_state *state;
503616cc397Smpf int cur_eval = 0;
504616cc397Smpf
50518e4260cSrob if (scan_ifstate_single(ifname, s, &conf->initstate) && do_eval)
50620863243Sbenno eval_state(&conf->initstate);
507616cc397Smpf TAILQ_FOREACH(state, &conf->states, entries) {
50818e4260cSrob if (scan_ifstate_single(ifname, s, state) &&
509616cc397Smpf (do_eval && state == conf->curstate))
510616cc397Smpf cur_eval = 1;
511616cc397Smpf }
512616cc397Smpf /* execute actions _after_ all expressions have been adjusted */
513616cc397Smpf if (cur_eval)
514616cc397Smpf eval_state(conf->curstate);
515616cc397Smpf }
516616cc397Smpf
5176dbcce3fSmcbride /*
5183a50f0a9Sjmc * Do a bottom-up adjustment of the expression tree's truth value,
519b73b88efSmcbride * level-by-level to ensure that each expression's subexpressions have been
520b73b88efSmcbride * evaluated.
5216dbcce3fSmcbride */
5226dbcce3fSmcbride void
adjust_expressions(struct ifsd_expression_list * expressions,int depth)523b73b88efSmcbride adjust_expressions(struct ifsd_expression_list *expressions, int depth)
5246dbcce3fSmcbride {
525b73b88efSmcbride struct ifsd_expression_list nexpressions;
526b73b88efSmcbride struct ifsd_expression *expression;
5276dbcce3fSmcbride
528b73b88efSmcbride TAILQ_INIT(&nexpressions);
529b73b88efSmcbride while ((expression = TAILQ_FIRST(expressions)) != NULL) {
530b73b88efSmcbride TAILQ_REMOVE(expressions, expression, eval);
531b73b88efSmcbride if (expression->depth == depth) {
532b73b88efSmcbride struct ifsd_expression *te;
5336dbcce3fSmcbride
534b73b88efSmcbride switch (expression->type) {
535b73b88efSmcbride case IFSD_OPER_AND:
5366ee78c37Smpf expression->truth = expression->left->truth &&
5376ee78c37Smpf expression->right->truth;
538b73b88efSmcbride break;
539b73b88efSmcbride case IFSD_OPER_OR:
5406ee78c37Smpf expression->truth = expression->left->truth ||
5416ee78c37Smpf expression->right->truth;
542b73b88efSmcbride break;
543b73b88efSmcbride case IFSD_OPER_NOT:
5446ee78c37Smpf expression->truth = !expression->right->truth;
545b73b88efSmcbride break;
546b73b88efSmcbride default:
547b73b88efSmcbride break;
5486dbcce3fSmcbride }
549b73b88efSmcbride if (expression->parent != NULL) {
550b73b88efSmcbride if (TAILQ_EMPTY(&nexpressions))
551b73b88efSmcbride te = NULL;
552b73b88efSmcbride TAILQ_FOREACH(te, &nexpressions, eval)
553b73b88efSmcbride if (expression->parent == te)
554b73b88efSmcbride break;
555b73b88efSmcbride if (te == NULL)
556b73b88efSmcbride TAILQ_INSERT_TAIL(&nexpressions,
557b73b88efSmcbride expression->parent, eval);
5586dbcce3fSmcbride }
559b73b88efSmcbride } else
560b73b88efSmcbride TAILQ_INSERT_TAIL(&nexpressions, expression, eval);
561b73b88efSmcbride }
562b73b88efSmcbride if (depth > 0)
563b73b88efSmcbride adjust_expressions(&nexpressions, depth - 1);
5646dbcce3fSmcbride }
5656dbcce3fSmcbride
566b73b88efSmcbride void
eval_state(struct ifsd_state * state)567b73b88efSmcbride eval_state(struct ifsd_state *state)
568b73b88efSmcbride {
5692f67262dSbenno struct ifsd_external *external;
5702f67262dSbenno
5712f67262dSbenno external = TAILQ_FIRST(&state->external_tests);
57222794b72Smpf if (external == NULL || external->lastexec >= state->entered ||
57322794b72Smpf external->lastexec == 0) {
574138e089fSbenno do_action(state->body);
575988c4ffaSbenno while (state_change()) {
576988c4ffaSbenno do_action(conf->curstate->init);
577138e089fSbenno do_action(conf->curstate->body);
578b73b88efSmcbride }
579b73b88efSmcbride }
580988c4ffaSbenno }
581b73b88efSmcbride
582b688c394Smpf int
state_change(void)583b73b88efSmcbride state_change(void)
584b73b88efSmcbride {
5858bb56c35Smcbride if (conf->nextstate != NULL && conf->curstate != conf->nextstate) {
58635360db1Spyr log_info("changing state to %s", conf->nextstate->name);
58722794b72Smpf if (conf->curstate != NULL) {
5888bb56c35Smcbride evtimer_del(&conf->curstate->ev);
5898bb56c35Smcbride external_evtimer_setup(conf->curstate,
5908bb56c35Smcbride IFSD_EVTIMER_DEL);
59122794b72Smpf }
5928bb56c35Smcbride conf->curstate = conf->nextstate;
5938bb56c35Smcbride conf->nextstate = NULL;
5948bb56c35Smcbride conf->curstate->entered = time(NULL);
5958bb56c35Smcbride external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD);
596b688c394Smpf adjust_external_expressions(conf->curstate);
597b688c394Smpf return (1);
598b73b88efSmcbride }
599b688c394Smpf return (0);
600b73b88efSmcbride }
601b73b88efSmcbride
602b73b88efSmcbride /*
603b73b88efSmcbride * Run recursively through the tree of actions.
604b73b88efSmcbride */
605b73b88efSmcbride void
do_action(struct ifsd_action * action)606b73b88efSmcbride do_action(struct ifsd_action *action)
607b73b88efSmcbride {
608b73b88efSmcbride struct ifsd_action *subaction;
609b73b88efSmcbride
610b73b88efSmcbride switch (action->type) {
611b73b88efSmcbride case IFSD_ACTION_COMMAND:
6128c3698cfSstsp log_debug("running %s", action->act.command);
613b73b88efSmcbride system(action->act.command);
614b73b88efSmcbride break;
615b73b88efSmcbride case IFSD_ACTION_CHANGESTATE:
6168bb56c35Smcbride conf->nextstate = action->act.nextstate;
617b73b88efSmcbride break;
618b73b88efSmcbride case IFSD_ACTION_CONDITION:
619b73b88efSmcbride if ((action->act.c.expression != NULL &&
620b73b88efSmcbride action->act.c.expression->truth) ||
621b73b88efSmcbride action->act.c.expression == NULL) {
622b73b88efSmcbride TAILQ_FOREACH(subaction, &action->act.c.actions,
623b73b88efSmcbride entries)
624b73b88efSmcbride do_action(subaction);
625b73b88efSmcbride }
626b73b88efSmcbride break;
627b73b88efSmcbride default:
628bfdb9ad4Sbenno log_debug("%s: unknown action %d", __func__, action->type);
629b73b88efSmcbride break;
630b73b88efSmcbride }
631b73b88efSmcbride }
632b73b88efSmcbride
633b73b88efSmcbride /*
634b73b88efSmcbride * Fetch the current link states.
635b73b88efSmcbride */
6366dbcce3fSmcbride void
fetch_ifstate(int do_eval)637089b0f81Srob fetch_ifstate(int do_eval)
6386dbcce3fSmcbride {
639b73b88efSmcbride struct ifaddrs *ifap, *ifa;
6406dbcce3fSmcbride
641b73b88efSmcbride if (getifaddrs(&ifap) != 0)
64295b4ea67Srob fatal("getifaddrs");
643b73b88efSmcbride
644b73b88efSmcbride for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
6450aada395Sbenno if (ifa->ifa_addr != NULL &&
6460aada395Sbenno ifa->ifa_addr->sa_family == AF_LINK) {
6476b38c177Sjca struct if_data *ifdata = ifa->ifa_data;
64818e4260cSrob scan_ifstate(ifa->ifa_name, ifdata->ifi_link_state,
64918e4260cSrob do_eval);
6506b38c177Sjca }
6516dbcce3fSmcbride }
6524baea900Sjca
6539b890fceSmcbride freeifaddrs(ifap);
6546dbcce3fSmcbride }
655b73b88efSmcbride
656b73b88efSmcbride void
check_ifdeparture(void)65740699dccSrob check_ifdeparture(void)
65840699dccSrob {
65940699dccSrob struct ifsd_state *state;
66040699dccSrob struct ifsd_ifstate *ifstate;
66140699dccSrob
66240699dccSrob TAILQ_FOREACH(state, &conf->states, entries) {
66340699dccSrob TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
66440699dccSrob if (if_nametoindex(ifstate->ifname) == 0)
66540699dccSrob scan_ifstate(ifstate->ifname,
66640699dccSrob LINK_STATE_DOWN, 1);
66740699dccSrob }
66840699dccSrob }
66940699dccSrob }
67040699dccSrob
67140699dccSrob void
clear_config(struct ifsd_config * oconf)672b73b88efSmcbride clear_config(struct ifsd_config *oconf)
673b73b88efSmcbride {
674b73b88efSmcbride struct ifsd_state *state;
675b73b88efSmcbride
67620863243Sbenno external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_DEL);
6771d74c151Smcbride if (conf != NULL && conf->curstate != NULL)
6788bb56c35Smcbride external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL);
679b73b88efSmcbride while ((state = TAILQ_FIRST(&oconf->states)) != NULL) {
680b73b88efSmcbride TAILQ_REMOVE(&oconf->states, state, entries);
681b73b88efSmcbride remove_action(state->init, state);
682138e089fSbenno remove_action(state->body, state);
683b73b88efSmcbride free(state->name);
684b73b88efSmcbride free(state);
685b73b88efSmcbride }
68620863243Sbenno remove_action(oconf->initstate.init, &oconf->initstate);
68720863243Sbenno remove_action(oconf->initstate.body, &oconf->initstate);
688346e1d62Smcbride free(oconf);
689b73b88efSmcbride }
690b73b88efSmcbride
691b73b88efSmcbride void
remove_action(struct ifsd_action * action,struct ifsd_state * state)692b73b88efSmcbride remove_action(struct ifsd_action *action, struct ifsd_state *state)
693b73b88efSmcbride {
694b73b88efSmcbride struct ifsd_action *subaction;
695b73b88efSmcbride
696b73b88efSmcbride if (action == NULL || state == NULL)
697b73b88efSmcbride return;
698b73b88efSmcbride
699b73b88efSmcbride switch (action->type) {
700b73b88efSmcbride case IFSD_ACTION_COMMAND:
701b73b88efSmcbride free(action->act.command);
702b73b88efSmcbride break;
703b73b88efSmcbride case IFSD_ACTION_CHANGESTATE:
704b73b88efSmcbride break;
705b73b88efSmcbride case IFSD_ACTION_CONDITION:
706b73b88efSmcbride if (action->act.c.expression != NULL)
707b73b88efSmcbride remove_expression(action->act.c.expression, state);
708b73b88efSmcbride while ((subaction =
709b73b88efSmcbride TAILQ_FIRST(&action->act.c.actions)) != NULL) {
710b73b88efSmcbride TAILQ_REMOVE(&action->act.c.actions,
711b73b88efSmcbride subaction, entries);
712b73b88efSmcbride remove_action(subaction, state);
713b73b88efSmcbride }
714b73b88efSmcbride }
715b73b88efSmcbride free(action);
716b73b88efSmcbride }
717b73b88efSmcbride
718b73b88efSmcbride void
remove_expression(struct ifsd_expression * expression,struct ifsd_state * state)719b73b88efSmcbride remove_expression(struct ifsd_expression *expression,
720b73b88efSmcbride struct ifsd_state *state)
721b73b88efSmcbride {
722b73b88efSmcbride switch (expression->type) {
723b73b88efSmcbride case IFSD_OPER_IFSTATE:
724b73b88efSmcbride TAILQ_REMOVE(&expression->u.ifstate->expressions, expression,
725b73b88efSmcbride entries);
726b73b88efSmcbride if (--expression->u.ifstate->refcount == 0) {
727b73b88efSmcbride TAILQ_REMOVE(&state->interface_states,
728b73b88efSmcbride expression->u.ifstate, entries);
729b73b88efSmcbride free(expression->u.ifstate);
730b73b88efSmcbride }
731b73b88efSmcbride break;
732b73b88efSmcbride case IFSD_OPER_EXTERNAL:
733b73b88efSmcbride TAILQ_REMOVE(&expression->u.external->expressions, expression,
734b73b88efSmcbride entries);
735b73b88efSmcbride if (--expression->u.external->refcount == 0) {
736b73b88efSmcbride TAILQ_REMOVE(&state->external_tests,
737b73b88efSmcbride expression->u.external, entries);
738b73b88efSmcbride free(expression->u.external->command);
739b73b88efSmcbride event_del(&expression->u.external->ev);
740b73b88efSmcbride free(expression->u.external);
741b73b88efSmcbride }
742b73b88efSmcbride break;
743b73b88efSmcbride default:
744b73b88efSmcbride if (expression->left != NULL)
745b73b88efSmcbride remove_expression(expression->left, state);
746b73b88efSmcbride if (expression->right != NULL)
747b73b88efSmcbride remove_expression(expression->right, state);
748b73b88efSmcbride break;
749b73b88efSmcbride }
750b73b88efSmcbride free(expression);
751b73b88efSmcbride }
752