| File: | src/usr.sbin/ifstated/ifstated.c |
| Warning: | line 133, column 2 Value stored to 'argv' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: ifstated.c,v 1.66 2021/07/12 15:09:21 beck Exp $ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (c) 2004 Marco Pfatschbacher <mpf@openbsd.org> |
| 5 | * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> |
| 6 | * |
| 7 | * Permission to use, copy, modify, and distribute this software for any |
| 8 | * purpose with or without fee is hereby granted, provided that the above |
| 9 | * copyright notice and this permission notice appear in all copies. |
| 10 | * |
| 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 18 | */ |
| 19 | |
| 20 | /* |
| 21 | * ifstated listens to link_state transitions on interfaces |
| 22 | * and executes predefined commands. |
| 23 | */ |
| 24 | |
| 25 | #include <sys/types.h> |
| 26 | #include <sys/time.h> |
| 27 | #include <sys/socket.h> |
| 28 | #include <sys/wait.h> |
| 29 | |
| 30 | #include <net/if.h> |
| 31 | #include <net/route.h> |
| 32 | #include <netinet/in.h> |
| 33 | |
| 34 | #include <paths.h> |
| 35 | #include <stdio.h> |
| 36 | #include <stdlib.h> |
| 37 | #include <string.h> |
| 38 | #include <signal.h> |
| 39 | #include <stdint.h> |
| 40 | #include <syslog.h> |
| 41 | #include <errno(*__errno()).h> |
| 42 | #include <event.h> |
| 43 | #include <unistd.h> |
| 44 | #include <ifaddrs.h> |
| 45 | |
| 46 | #include "ifstated.h" |
| 47 | #include "log.h" |
| 48 | |
| 49 | struct ifsd_config *conf, *newconf; |
| 50 | |
| 51 | int opts; |
| 52 | int opt_inhibit; |
| 53 | char *configfile = "/etc/ifstated.conf"; |
| 54 | struct event rt_msg_ev, sighup_ev, startup_ev, sigchld_ev; |
| 55 | |
| 56 | void startup_handler(int, short, void *); |
| 57 | void sighup_handler(int, short, void *); |
| 58 | int load_config(void); |
| 59 | void sigchld_handler(int, short, void *); |
| 60 | void rt_msg_handler(int, short, void *); |
| 61 | void external_handler(int, short, void *); |
| 62 | void external_exec(struct ifsd_external *, int); |
| 63 | void check_external_status(struct ifsd_state *); |
| 64 | void check_ifdeparture(void); |
| 65 | void external_evtimer_setup(struct ifsd_state *, int); |
| 66 | void scan_ifstate(const char *, int, int); |
| 67 | int scan_ifstate_single(const char *, int, struct ifsd_state *); |
| 68 | void fetch_ifstate(int); |
| 69 | __dead__attribute__((__noreturn__)) void usage(void); |
| 70 | void adjust_expressions(struct ifsd_expression_list *, int); |
| 71 | void adjust_external_expressions(struct ifsd_state *); |
| 72 | void eval_state(struct ifsd_state *); |
| 73 | int state_change(void); |
| 74 | void do_action(struct ifsd_action *); |
| 75 | void remove_action(struct ifsd_action *, struct ifsd_state *); |
| 76 | void remove_expression(struct ifsd_expression *, |
| 77 | struct ifsd_state *); |
| 78 | |
| 79 | __dead__attribute__((__noreturn__)) void |
| 80 | usage(void) |
| 81 | { |
| 82 | extern char *__progname; |
| 83 | |
| 84 | fprintf(stderr(&__sF[2]), "usage: %s [-dhinv] [-D macro=value] [-f file]\n", |
| 85 | __progname); |
| 86 | exit(1); |
| 87 | } |
| 88 | |
| 89 | int |
| 90 | main(int argc, char *argv[]) |
| 91 | { |
| 92 | struct timeval tv; |
| 93 | int ch, rt_fd; |
| 94 | int debug = 0; |
| 95 | unsigned int rtfilter; |
| 96 | |
| 97 | log_init(1, LOG_DAEMON(3<<3)); /* log to stderr until daemonized */ |
| 98 | log_setverbose(1); |
| 99 | |
| 100 | while ((ch = getopt(argc, argv, "dD:f:hniv")) != -1) { |
| 101 | switch (ch) { |
| 102 | case 'd': |
| 103 | debug = 1; |
| 104 | break; |
| 105 | case 'D': |
| 106 | if (cmdline_symset(optarg) < 0) |
| 107 | fatalx("could not parse macro definition %s", |
| 108 | optarg); |
| 109 | break; |
| 110 | case 'f': |
| 111 | configfile = optarg; |
| 112 | break; |
| 113 | case 'h': |
| 114 | usage(); |
| 115 | break; |
| 116 | case 'n': |
| 117 | opts |= IFSD_OPT_NOACTION0x00000004; |
| 118 | break; |
| 119 | case 'i': |
| 120 | opt_inhibit = 1; |
| 121 | break; |
| 122 | case 'v': |
| 123 | if (opts & IFSD_OPT_VERBOSE0x00000001) |
| 124 | opts |= IFSD_OPT_VERBOSE20x00000002; |
| 125 | opts |= IFSD_OPT_VERBOSE0x00000001; |
| 126 | break; |
| 127 | default: |
| 128 | usage(); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | argc -= optind; |
| 133 | argv += optind; |
Value stored to 'argv' is never read | |
| 134 | if (argc > 0) |
| 135 | usage(); |
| 136 | |
| 137 | if (opts & IFSD_OPT_NOACTION0x00000004) { |
| 138 | if ((newconf = parse_config(configfile, opts)) == NULL((void *)0)) |
| 139 | exit(1); |
| 140 | fprintf(stderr(&__sF[2]), "configuration OK\n"); |
| 141 | exit(0); |
| 142 | } |
| 143 | |
| 144 | if (!debug) |
| 145 | daemon(1, 0); |
| 146 | |
| 147 | event_init(); |
| 148 | log_init(debug, LOG_DAEMON(3<<3)); |
| 149 | log_setverbose(opts & IFSD_OPT_VERBOSE0x00000001); |
| 150 | |
| 151 | if ((rt_fd = socket(AF_ROUTE17, SOCK_RAW3, 0)) == -1) |
| 152 | fatal("no routing socket"); |
| 153 | |
| 154 | rtfilter = ROUTE_FILTER(RTM_IFINFO)(1 << (0xe)) | ROUTE_FILTER(RTM_IFANNOUNCE)(1 << (0xf)); |
| 155 | if (setsockopt(rt_fd, AF_ROUTE17, ROUTE_MSGFILTER1, |
| 156 | &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */ |
| 157 | log_warn("%s: setsockopt msgfilter", __func__); |
| 158 | |
| 159 | rtfilter = RTABLE_ANY0xffffffff; |
| 160 | if (setsockopt(rt_fd, AF_ROUTE17, ROUTE_TABLEFILTER2, |
| 161 | &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */ |
| 162 | log_warn("%s: setsockopt tablefilter", __func__); |
| 163 | |
| 164 | if (unveil(configfile, "r") == -1) |
| 165 | fatal("unveil %s", configfile); |
| 166 | if (unveil(_PATH_BSHELL"/bin/sh", "x") == -1) |
| 167 | fatal("unveil %s", _PATH_BSHELL"/bin/sh"); |
| 168 | if (pledge("stdio rpath route proc exec", NULL((void *)0)) == -1) |
| 169 | fatal("pledge"); |
| 170 | |
| 171 | signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL)event_set(&sigchld_ev, 20, 0x08|0x10, sigchld_handler, (( void *)0)); |
| 172 | signal_add(&sigchld_ev, NULL)event_add(&sigchld_ev, ((void *)0)); |
| 173 | |
| 174 | /* Loading the config needs to happen in the event loop */ |
| 175 | timerclear(&tv)(&tv)->tv_sec = (&tv)->tv_usec = 0; |
| 176 | evtimer_set(&startup_ev, startup_handler, (void *)(long)rt_fd)event_set(&startup_ev, -1, 0, startup_handler, (void *)(long )rt_fd); |
| 177 | evtimer_add(&startup_ev, &tv)event_add(&startup_ev, &tv); |
| 178 | |
| 179 | event_loop(0); |
| 180 | exit(0); |
| 181 | } |
| 182 | |
| 183 | void |
| 184 | startup_handler(int fd, short event, void *arg) |
| 185 | { |
| 186 | int rfd = (int)(long)arg; |
| 187 | |
| 188 | if (load_config() != 0) { |
| 189 | log_warnx("unable to load config"); |
| 190 | exit(1); |
| 191 | } |
| 192 | |
| 193 | event_set(&rt_msg_ev, rfd, EV_READ0x02|EV_PERSIST0x10, rt_msg_handler, NULL((void *)0)); |
| 194 | event_add(&rt_msg_ev, NULL((void *)0)); |
| 195 | |
| 196 | signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL)event_set(&sighup_ev, 1, 0x08|0x10, sighup_handler, ((void *)0)); |
| 197 | signal_add(&sighup_ev, NULL)event_add(&sighup_ev, ((void *)0)); |
| 198 | |
| 199 | log_info("started"); |
| 200 | } |
| 201 | |
| 202 | void |
| 203 | sighup_handler(int fd, short event, void *arg) |
| 204 | { |
| 205 | log_info("reloading config"); |
| 206 | if (load_config() != 0) |
| 207 | log_warnx("unable to reload config"); |
| 208 | } |
| 209 | |
| 210 | int |
| 211 | load_config(void) |
| 212 | { |
| 213 | if ((newconf = parse_config(configfile, opts)) == NULL((void *)0)) |
| 214 | return (-1); |
| 215 | if (conf != NULL((void *)0)) |
| 216 | clear_config(conf); |
| 217 | conf = newconf; |
| 218 | conf->initstate.entered = time(NULL((void *)0)); |
| 219 | fetch_ifstate(0); |
| 220 | external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_ADD); |
| 221 | adjust_external_expressions(&conf->initstate); |
| 222 | eval_state(&conf->initstate); |
| 223 | if (conf->curstate != NULL((void *)0)) { |
| 224 | log_info("initial state: %s", conf->curstate->name); |
| 225 | conf->curstate->entered = time(NULL((void *)0)); |
| 226 | conf->nextstate = conf->curstate; |
| 227 | conf->curstate = NULL((void *)0); |
| 228 | while (state_change()) { |
| 229 | do_action(conf->curstate->init); |
| 230 | do_action(conf->curstate->body); |
| 231 | } |
| 232 | } |
| 233 | return (0); |
| 234 | } |
| 235 | |
| 236 | void |
| 237 | rt_msg_handler(int fd, short event, void *arg) |
| 238 | { |
| 239 | char msg[2048]; |
| 240 | struct rt_msghdr *rtm = (struct rt_msghdr *)&msg; |
| 241 | struct if_msghdr ifm; |
| 242 | struct if_announcemsghdr ifan; |
| 243 | char ifnamebuf[IFNAMSIZ16]; |
| 244 | char *ifname; |
| 245 | ssize_t len; |
| 246 | |
| 247 | if ((len = read(fd, msg, sizeof(msg))) == -1) { |
| 248 | if (errno(*__errno()) == EAGAIN35 || errno(*__errno()) == EINTR4) |
| 249 | return; |
| 250 | fatal("%s: routing socket read error", __func__); |
| 251 | } |
| 252 | |
| 253 | if (len == 0) |
| 254 | fatal("%s: routing socket closed", __func__); |
| 255 | |
| 256 | if (rtm->rtm_version != RTM_VERSION5) |
| 257 | return; |
| 258 | |
| 259 | switch (rtm->rtm_type) { |
| 260 | case RTM_IFINFO0xe: |
| 261 | memcpy(&ifm, rtm, sizeof(ifm)); |
| 262 | ifname = if_indextoname(ifm.ifm_index, ifnamebuf); |
| 263 | /* ifname is NULL on interface departure */ |
| 264 | if (ifname != NULL((void *)0)) |
| 265 | scan_ifstate(ifname, ifm.ifm_data.ifi_link_state, 1); |
| 266 | break; |
| 267 | case RTM_IFANNOUNCE0xf: |
| 268 | memcpy(&ifan, rtm, sizeof(ifan)); |
| 269 | switch (ifan.ifan_what) { |
| 270 | case IFAN_DEPARTURE1: |
| 271 | log_warnx("interface %s departed", ifan.ifan_name); |
| 272 | check_ifdeparture(); |
| 273 | break; |
| 274 | case IFAN_ARRIVAL0: |
| 275 | log_warnx("interface %s arrived", ifan.ifan_name); |
| 276 | fetch_ifstate(1); |
| 277 | break; |
| 278 | } |
| 279 | break; |
| 280 | case RTM_DESYNC0x10: |
| 281 | /* we lost some routing messages so rescan interfaces */ |
| 282 | check_ifdeparture(); |
| 283 | fetch_ifstate(1); |
| 284 | break; |
| 285 | } |
| 286 | return; |
| 287 | } |
| 288 | |
| 289 | void |
| 290 | sigchld_handler(int fd, short event, void *arg) |
| 291 | { |
| 292 | check_external_status(&conf->initstate); |
| 293 | if (conf->curstate != NULL((void *)0)) |
| 294 | check_external_status(conf->curstate); |
| 295 | } |
| 296 | |
| 297 | void |
| 298 | external_handler(int fd, short event, void *arg) |
| 299 | { |
| 300 | struct ifsd_external *external = (struct ifsd_external *)arg; |
| 301 | struct timeval tv; |
| 302 | |
| 303 | /* re-schedule */ |
| 304 | timerclear(&tv)(&tv)->tv_sec = (&tv)->tv_usec = 0; |
| 305 | tv.tv_sec = external->frequency; |
| 306 | evtimer_set(&external->ev, external_handler, external)event_set(&external->ev, -1, 0, external_handler, external ); |
| 307 | evtimer_add(&external->ev, &tv)event_add(&external->ev, &tv); |
| 308 | |
| 309 | /* execute */ |
| 310 | external_exec(external, 1); |
| 311 | } |
| 312 | |
| 313 | void |
| 314 | external_exec(struct ifsd_external *external, int async) |
| 315 | { |
| 316 | char *argp[] = {"sh", "-c", NULL((void *)0), NULL((void *)0)}; |
| 317 | pid_t pid; |
| 318 | int s; |
| 319 | |
| 320 | if (external->pid > 0) { |
| 321 | log_debug("previous command %s [%d] still running, killing it", |
| 322 | external->command, external->pid); |
| 323 | kill(external->pid, SIGKILL9); |
| 324 | waitpid(external->pid, &s, 0); |
| 325 | external->pid = 0; |
| 326 | } |
| 327 | |
| 328 | argp[2] = external->command; |
| 329 | log_debug("running %s", external->command); |
| 330 | pid = fork(); |
| 331 | if (pid == -1) { |
| 332 | log_warn("fork error"); |
| 333 | } else if (pid == 0) { |
| 334 | execv(_PATH_BSHELL"/bin/sh", argp); |
| 335 | _exit(1); |
| 336 | /* NOTREACHED */ |
| 337 | } else { |
| 338 | external->pid = pid; |
| 339 | } |
| 340 | if (!async) { |
| 341 | waitpid(external->pid, &s, 0); |
| 342 | external->pid = 0; |
| 343 | if (WIFEXITED(s)(((s) & 0177) == 0)) |
| 344 | external->prevstatus = WEXITSTATUS(s)(int)(((unsigned)(s) >> 8) & 0xff); |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | void |
| 349 | adjust_external_expressions(struct ifsd_state *state) |
| 350 | { |
| 351 | struct ifsd_external *external; |
| 352 | struct ifsd_expression_list expressions; |
| 353 | |
| 354 | TAILQ_INIT(&expressions)do { (&expressions)->tqh_first = ((void *)0); (&expressions )->tqh_last = &(&expressions)->tqh_first; } while (0); |
| 355 | TAILQ_FOREACH(external, &state->external_tests, entries)for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) { |
| 356 | struct ifsd_expression *expression; |
| 357 | |
| 358 | if (external->prevstatus == -1) |
| 359 | continue; |
| 360 | |
| 361 | TAILQ_FOREACH(expression, &external->expressions, entries)for((expression) = ((&external->expressions)->tqh_first ); (expression) != ((void *)0); (expression) = ((expression)-> entries.tqe_next)) { |
| 362 | TAILQ_INSERT_TAIL(&expressions,do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&expressions)->tqh_last; *(& expressions)->tqh_last = (expression); (&expressions)-> tqh_last = &(expression)->eval.tqe_next; } while (0) |
| 363 | expression, eval)do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&expressions)->tqh_last; *(& expressions)->tqh_last = (expression); (&expressions)-> tqh_last = &(expression)->eval.tqe_next; } while (0); |
| 364 | expression->truth = !external->prevstatus; |
| 365 | } |
| 366 | adjust_expressions(&expressions, conf->maxdepth); |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | void |
| 371 | check_external_status(struct ifsd_state *state) |
| 372 | { |
| 373 | struct ifsd_external *external, *end = NULL((void *)0); |
| 374 | int status, s, changed = 0; |
| 375 | |
| 376 | /* Do this manually; change ordering so the oldest is first */ |
| 377 | external = TAILQ_FIRST(&state->external_tests)((&state->external_tests)->tqh_first); |
| 378 | while (external != NULL((void *)0) && external != end) { |
| 379 | struct ifsd_external *newexternal; |
| 380 | |
| 381 | newexternal = TAILQ_NEXT(external, entries)((external)->entries.tqe_next); |
| 382 | |
| 383 | if (external->pid <= 0) |
| 384 | goto loop; |
| 385 | |
| 386 | if (wait4(external->pid, &s, WNOHANG1, NULL((void *)0)) == 0) |
| 387 | goto loop; |
| 388 | |
| 389 | external->pid = 0; |
| 390 | if (end == NULL((void *)0)) |
| 391 | end = external; |
| 392 | if (WIFEXITED(s)(((s) & 0177) == 0)) |
| 393 | status = WEXITSTATUS(s)(int)(((unsigned)(s) >> 8) & 0xff); |
| 394 | else { |
| 395 | log_warnx("%s exited abnormally", external->command); |
| 396 | goto loop; |
| 397 | } |
| 398 | |
| 399 | if (external->prevstatus != status && |
| 400 | (external->prevstatus != -1 || !opt_inhibit)) { |
| 401 | changed = 1; |
| 402 | external->prevstatus = status; |
| 403 | } |
| 404 | external->lastexec = time(NULL((void *)0)); |
| 405 | TAILQ_REMOVE(&state->external_tests, external, entries)do { if (((external)->entries.tqe_next) != ((void *)0)) (external )->entries.tqe_next->entries.tqe_prev = (external)-> entries.tqe_prev; else (&state->external_tests)->tqh_last = (external)->entries.tqe_prev; *(external)->entries.tqe_prev = (external)->entries.tqe_next; ; ; } while (0); |
| 406 | TAILQ_INSERT_TAIL(&state->external_tests, external, entries)do { (external)->entries.tqe_next = ((void *)0); (external )->entries.tqe_prev = (&state->external_tests)-> tqh_last; *(&state->external_tests)->tqh_last = (external ); (&state->external_tests)->tqh_last = &(external )->entries.tqe_next; } while (0); |
| 407 | loop: |
| 408 | external = newexternal; |
| 409 | } |
| 410 | |
| 411 | if (changed) { |
| 412 | adjust_external_expressions(state); |
| 413 | eval_state(state); |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | void |
| 418 | external_evtimer_setup(struct ifsd_state *state, int action) |
| 419 | { |
| 420 | struct ifsd_external *external; |
| 421 | int s; |
| 422 | |
| 423 | if (state != NULL((void *)0)) { |
| 424 | switch (action) { |
| 425 | case IFSD_EVTIMER_ADD: |
| 426 | TAILQ_FOREACH(external,for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) |
| 427 | &state->external_tests, entries)for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) { |
| 428 | struct timeval tv; |
| 429 | |
| 430 | /* run it once right away */ |
| 431 | external_exec(external, 0); |
| 432 | |
| 433 | /* schedule it for later */ |
| 434 | timerclear(&tv)(&tv)->tv_sec = (&tv)->tv_usec = 0; |
| 435 | tv.tv_sec = external->frequency; |
| 436 | evtimer_set(&external->ev, external_handler,event_set(&external->ev, -1, 0, external_handler, external ) |
| 437 | external)event_set(&external->ev, -1, 0, external_handler, external ); |
| 438 | evtimer_add(&external->ev, &tv)event_add(&external->ev, &tv); |
| 439 | } |
| 440 | break; |
| 441 | case IFSD_EVTIMER_DEL: |
| 442 | TAILQ_FOREACH(external,for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) |
| 443 | &state->external_tests, entries)for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) { |
| 444 | if (external->pid > 0) { |
| 445 | kill(external->pid, SIGKILL9); |
| 446 | waitpid(external->pid, &s, 0); |
| 447 | external->pid = 0; |
| 448 | } |
| 449 | evtimer_del(&external->ev)event_del(&external->ev); |
| 450 | } |
| 451 | break; |
| 452 | } |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | #define LINK_STATE_IS_DOWN(_s)(!(((_s)) >= 4 || ((_s)) == 0)) (!LINK_STATE_IS_UP((_s))(((_s)) >= 4 || ((_s)) == 0)) |
| 457 | |
| 458 | int |
| 459 | scan_ifstate_single(const char *ifname, int s, struct ifsd_state *state) |
| 460 | { |
| 461 | struct ifsd_ifstate *ifstate; |
| 462 | struct ifsd_expression_list expressions; |
| 463 | int changed = 0; |
| 464 | |
| 465 | TAILQ_INIT(&expressions)do { (&expressions)->tqh_first = ((void *)0); (&expressions )->tqh_last = &(&expressions)->tqh_first; } while (0); |
| 466 | |
| 467 | TAILQ_FOREACH(ifstate, &state->interface_states, entries)for((ifstate) = ((&state->interface_states)->tqh_first ); (ifstate) != ((void *)0); (ifstate) = ((ifstate)->entries .tqe_next)) { |
| 468 | if (strcmp(ifstate->ifname, ifname) == 0) { |
| 469 | if (ifstate->prevstate != s && |
| 470 | (ifstate->prevstate != -1 || !opt_inhibit)) { |
| 471 | struct ifsd_expression *expression; |
| 472 | int truth; |
| 473 | |
| 474 | truth = |
| 475 | (ifstate->ifstate == IFSD_LINKUNKNOWN0 && |
| 476 | s == LINK_STATE_UNKNOWN0) || |
| 477 | (ifstate->ifstate == IFSD_LINKDOWN1 && |
| 478 | LINK_STATE_IS_DOWN(s)(!(((s)) >= 4 || ((s)) == 0))) || |
| 479 | (ifstate->ifstate == IFSD_LINKUP2 && |
| 480 | LINK_STATE_IS_UP(s)((s) >= 4 || (s) == 0)); |
| 481 | |
| 482 | TAILQ_FOREACH(expression,for((expression) = ((&ifstate->expressions)->tqh_first ); (expression) != ((void *)0); (expression) = ((expression)-> entries.tqe_next)) |
| 483 | &ifstate->expressions, entries)for((expression) = ((&ifstate->expressions)->tqh_first ); (expression) != ((void *)0); (expression) = ((expression)-> entries.tqe_next)) { |
| 484 | expression->truth = truth; |
| 485 | TAILQ_INSERT_TAIL(&expressions,do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&expressions)->tqh_last; *(& expressions)->tqh_last = (expression); (&expressions)-> tqh_last = &(expression)->eval.tqe_next; } while (0) |
| 486 | expression, eval)do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&expressions)->tqh_last; *(& expressions)->tqh_last = (expression); (&expressions)-> tqh_last = &(expression)->eval.tqe_next; } while (0); |
| 487 | changed = 1; |
| 488 | } |
| 489 | ifstate->prevstate = s; |
| 490 | } |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | if (changed) |
| 495 | adjust_expressions(&expressions, conf->maxdepth); |
| 496 | return (changed); |
| 497 | } |
| 498 | |
| 499 | void |
| 500 | scan_ifstate(const char *ifname, int s, int do_eval) |
| 501 | { |
| 502 | struct ifsd_state *state; |
| 503 | int cur_eval = 0; |
| 504 | |
| 505 | if (scan_ifstate_single(ifname, s, &conf->initstate) && do_eval) |
| 506 | eval_state(&conf->initstate); |
| 507 | TAILQ_FOREACH(state, &conf->states, entries)for((state) = ((&conf->states)->tqh_first); (state) != ((void *)0); (state) = ((state)->entries.tqe_next)) { |
| 508 | if (scan_ifstate_single(ifname, s, state) && |
| 509 | (do_eval && state == conf->curstate)) |
| 510 | cur_eval = 1; |
| 511 | } |
| 512 | /* execute actions _after_ all expressions have been adjusted */ |
| 513 | if (cur_eval) |
| 514 | eval_state(conf->curstate); |
| 515 | } |
| 516 | |
| 517 | /* |
| 518 | * Do a bottom-up ajustment of the expression tree's truth value, |
| 519 | * level-by-level to ensure that each expression's subexpressions have been |
| 520 | * evaluated. |
| 521 | */ |
| 522 | void |
| 523 | adjust_expressions(struct ifsd_expression_list *expressions, int depth) |
| 524 | { |
| 525 | struct ifsd_expression_list nexpressions; |
| 526 | struct ifsd_expression *expression; |
| 527 | |
| 528 | TAILQ_INIT(&nexpressions)do { (&nexpressions)->tqh_first = ((void *)0); (&nexpressions )->tqh_last = &(&nexpressions)->tqh_first; } while (0); |
| 529 | while ((expression = TAILQ_FIRST(expressions)((expressions)->tqh_first)) != NULL((void *)0)) { |
| 530 | TAILQ_REMOVE(expressions, expression, eval)do { if (((expression)->eval.tqe_next) != ((void *)0)) (expression )->eval.tqe_next->eval.tqe_prev = (expression)->eval .tqe_prev; else (expressions)->tqh_last = (expression)-> eval.tqe_prev; *(expression)->eval.tqe_prev = (expression) ->eval.tqe_next; ; ; } while (0); |
| 531 | if (expression->depth == depth) { |
| 532 | struct ifsd_expression *te; |
| 533 | |
| 534 | switch (expression->type) { |
| 535 | case IFSD_OPER_AND1: |
| 536 | expression->truth = expression->left->truth && |
| 537 | expression->right->truth; |
| 538 | break; |
| 539 | case IFSD_OPER_OR2: |
| 540 | expression->truth = expression->left->truth || |
| 541 | expression->right->truth; |
| 542 | break; |
| 543 | case IFSD_OPER_NOT3: |
| 544 | expression->truth = !expression->right->truth; |
| 545 | break; |
| 546 | default: |
| 547 | break; |
| 548 | } |
| 549 | if (expression->parent != NULL((void *)0)) { |
| 550 | if (TAILQ_EMPTY(&nexpressions)(((&nexpressions)->tqh_first) == ((void *)0))) |
| 551 | te = NULL((void *)0); |
| 552 | TAILQ_FOREACH(te, &nexpressions, eval)for((te) = ((&nexpressions)->tqh_first); (te) != ((void *)0); (te) = ((te)->eval.tqe_next)) |
| 553 | if (expression->parent == te) |
| 554 | break; |
| 555 | if (te == NULL((void *)0)) |
| 556 | TAILQ_INSERT_TAIL(&nexpressions,do { (expression->parent)->eval.tqe_next = ((void *)0); (expression->parent)->eval.tqe_prev = (&nexpressions )->tqh_last; *(&nexpressions)->tqh_last = (expression ->parent); (&nexpressions)->tqh_last = &(expression ->parent)->eval.tqe_next; } while (0) |
| 557 | expression->parent, eval)do { (expression->parent)->eval.tqe_next = ((void *)0); (expression->parent)->eval.tqe_prev = (&nexpressions )->tqh_last; *(&nexpressions)->tqh_last = (expression ->parent); (&nexpressions)->tqh_last = &(expression ->parent)->eval.tqe_next; } while (0); |
| 558 | } |
| 559 | } else |
| 560 | TAILQ_INSERT_TAIL(&nexpressions, expression, eval)do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&nexpressions)->tqh_last; *(& nexpressions)->tqh_last = (expression); (&nexpressions )->tqh_last = &(expression)->eval.tqe_next; } while (0); |
| 561 | } |
| 562 | if (depth > 0) |
| 563 | adjust_expressions(&nexpressions, depth - 1); |
| 564 | } |
| 565 | |
| 566 | void |
| 567 | eval_state(struct ifsd_state *state) |
| 568 | { |
| 569 | struct ifsd_external *external; |
| 570 | |
| 571 | external = TAILQ_FIRST(&state->external_tests)((&state->external_tests)->tqh_first); |
| 572 | if (external == NULL((void *)0) || external->lastexec >= state->entered || |
| 573 | external->lastexec == 0) { |
| 574 | do_action(state->body); |
| 575 | while (state_change()) { |
| 576 | do_action(conf->curstate->init); |
| 577 | do_action(conf->curstate->body); |
| 578 | } |
| 579 | } |
| 580 | } |
| 581 | |
| 582 | int |
| 583 | state_change(void) |
| 584 | { |
| 585 | if (conf->nextstate != NULL((void *)0) && conf->curstate != conf->nextstate) { |
| 586 | log_info("changing state to %s", conf->nextstate->name); |
| 587 | if (conf->curstate != NULL((void *)0)) { |
| 588 | evtimer_del(&conf->curstate->ev)event_del(&conf->curstate->ev); |
| 589 | external_evtimer_setup(conf->curstate, |
| 590 | IFSD_EVTIMER_DEL); |
| 591 | } |
| 592 | conf->curstate = conf->nextstate; |
| 593 | conf->nextstate = NULL((void *)0); |
| 594 | conf->curstate->entered = time(NULL((void *)0)); |
| 595 | external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD); |
| 596 | adjust_external_expressions(conf->curstate); |
| 597 | return (1); |
| 598 | } |
| 599 | return (0); |
| 600 | } |
| 601 | |
| 602 | /* |
| 603 | * Run recursively through the tree of actions. |
| 604 | */ |
| 605 | void |
| 606 | do_action(struct ifsd_action *action) |
| 607 | { |
| 608 | struct ifsd_action *subaction; |
| 609 | |
| 610 | switch (action->type) { |
| 611 | case IFSD_ACTION_COMMAND1: |
| 612 | log_debug("running %s", action->act.command); |
| 613 | system(action->act.command); |
| 614 | break; |
| 615 | case IFSD_ACTION_CHANGESTATE2: |
| 616 | conf->nextstate = action->act.nextstate; |
| 617 | break; |
| 618 | case IFSD_ACTION_CONDITION3: |
| 619 | if ((action->act.c.expression != NULL((void *)0) && |
| 620 | action->act.c.expression->truth) || |
| 621 | action->act.c.expression == NULL((void *)0)) { |
| 622 | TAILQ_FOREACH(subaction, &action->act.c.actions,for((subaction) = ((&action->act.c.actions)->tqh_first ); (subaction) != ((void *)0); (subaction) = ((subaction)-> entries.tqe_next)) |
| 623 | entries)for((subaction) = ((&action->act.c.actions)->tqh_first ); (subaction) != ((void *)0); (subaction) = ((subaction)-> entries.tqe_next)) |
| 624 | do_action(subaction); |
| 625 | } |
| 626 | break; |
| 627 | default: |
| 628 | log_debug("%s: unknown action %d", __func__, action->type); |
| 629 | break; |
| 630 | } |
| 631 | } |
| 632 | |
| 633 | /* |
| 634 | * Fetch the current link states. |
| 635 | */ |
| 636 | void |
| 637 | fetch_ifstate(int do_eval) |
| 638 | { |
| 639 | struct ifaddrs *ifap, *ifa; |
| 640 | |
| 641 | if (getifaddrs(&ifap) != 0) |
| 642 | fatal("getifaddrs"); |
| 643 | |
| 644 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { |
| 645 | if (ifa->ifa_addr != NULL((void *)0) && |
| 646 | ifa->ifa_addr->sa_family == AF_LINK18) { |
| 647 | struct if_data *ifdata = ifa->ifa_data; |
| 648 | scan_ifstate(ifa->ifa_name, ifdata->ifi_link_state, |
| 649 | do_eval); |
| 650 | } |
| 651 | } |
| 652 | |
| 653 | freeifaddrs(ifap); |
| 654 | } |
| 655 | |
| 656 | void |
| 657 | check_ifdeparture(void) |
| 658 | { |
| 659 | struct ifsd_state *state; |
| 660 | struct ifsd_ifstate *ifstate; |
| 661 | |
| 662 | TAILQ_FOREACH(state, &conf->states, entries)for((state) = ((&conf->states)->tqh_first); (state) != ((void *)0); (state) = ((state)->entries.tqe_next)) { |
| 663 | TAILQ_FOREACH(ifstate, &state->interface_states, entries)for((ifstate) = ((&state->interface_states)->tqh_first ); (ifstate) != ((void *)0); (ifstate) = ((ifstate)->entries .tqe_next)) { |
| 664 | if (if_nametoindex(ifstate->ifname) == 0) |
| 665 | scan_ifstate(ifstate->ifname, |
| 666 | LINK_STATE_DOWN2, 1); |
| 667 | } |
| 668 | } |
| 669 | } |
| 670 | |
| 671 | void |
| 672 | clear_config(struct ifsd_config *oconf) |
| 673 | { |
| 674 | struct ifsd_state *state; |
| 675 | |
| 676 | external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_DEL); |
| 677 | if (conf != NULL((void *)0) && conf->curstate != NULL((void *)0)) |
| 678 | external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL); |
| 679 | while ((state = TAILQ_FIRST(&oconf->states)((&oconf->states)->tqh_first)) != NULL((void *)0)) { |
| 680 | TAILQ_REMOVE(&oconf->states, state, entries)do { if (((state)->entries.tqe_next) != ((void *)0)) (state )->entries.tqe_next->entries.tqe_prev = (state)->entries .tqe_prev; else (&oconf->states)->tqh_last = (state )->entries.tqe_prev; *(state)->entries.tqe_prev = (state )->entries.tqe_next; ; ; } while (0); |
| 681 | remove_action(state->init, state); |
| 682 | remove_action(state->body, state); |
| 683 | free(state->name); |
| 684 | free(state); |
| 685 | } |
| 686 | remove_action(oconf->initstate.init, &oconf->initstate); |
| 687 | remove_action(oconf->initstate.body, &oconf->initstate); |
| 688 | free(oconf); |
| 689 | } |
| 690 | |
| 691 | void |
| 692 | remove_action(struct ifsd_action *action, struct ifsd_state *state) |
| 693 | { |
| 694 | struct ifsd_action *subaction; |
| 695 | |
| 696 | if (action == NULL((void *)0) || state == NULL((void *)0)) |
| 697 | return; |
| 698 | |
| 699 | switch (action->type) { |
| 700 | case IFSD_ACTION_COMMAND1: |
| 701 | free(action->act.command); |
| 702 | break; |
| 703 | case IFSD_ACTION_CHANGESTATE2: |
| 704 | break; |
| 705 | case IFSD_ACTION_CONDITION3: |
| 706 | if (action->act.c.expression != NULL((void *)0)) |
| 707 | remove_expression(action->act.c.expression, state); |
| 708 | while ((subaction = |
| 709 | TAILQ_FIRST(&action->act.c.actions)((&action->act.c.actions)->tqh_first)) != NULL((void *)0)) { |
| 710 | TAILQ_REMOVE(&action->act.c.actions,do { if (((subaction)->entries.tqe_next) != ((void *)0)) ( subaction)->entries.tqe_next->entries.tqe_prev = (subaction )->entries.tqe_prev; else (&action->act.c.actions)-> tqh_last = (subaction)->entries.tqe_prev; *(subaction)-> entries.tqe_prev = (subaction)->entries.tqe_next; ; ; } while (0) |
| 711 | subaction, entries)do { if (((subaction)->entries.tqe_next) != ((void *)0)) ( subaction)->entries.tqe_next->entries.tqe_prev = (subaction )->entries.tqe_prev; else (&action->act.c.actions)-> tqh_last = (subaction)->entries.tqe_prev; *(subaction)-> entries.tqe_prev = (subaction)->entries.tqe_next; ; ; } while (0); |
| 712 | remove_action(subaction, state); |
| 713 | } |
| 714 | } |
| 715 | free(action); |
| 716 | } |
| 717 | |
| 718 | void |
| 719 | remove_expression(struct ifsd_expression *expression, |
| 720 | struct ifsd_state *state) |
| 721 | { |
| 722 | switch (expression->type) { |
| 723 | case IFSD_OPER_IFSTATE5: |
| 724 | TAILQ_REMOVE(&expression->u.ifstate->expressions, expression,do { if (((expression)->entries.tqe_next) != ((void *)0)) ( expression)->entries.tqe_next->entries.tqe_prev = (expression )->entries.tqe_prev; else (&expression->u.ifstate-> expressions)->tqh_last = (expression)->entries.tqe_prev ; *(expression)->entries.tqe_prev = (expression)->entries .tqe_next; ; ; } while (0) |
| 725 | entries)do { if (((expression)->entries.tqe_next) != ((void *)0)) ( expression)->entries.tqe_next->entries.tqe_prev = (expression )->entries.tqe_prev; else (&expression->u.ifstate-> expressions)->tqh_last = (expression)->entries.tqe_prev ; *(expression)->entries.tqe_prev = (expression)->entries .tqe_next; ; ; } while (0); |
| 726 | if (--expression->u.ifstate->refcount == 0) { |
| 727 | TAILQ_REMOVE(&state->interface_states,do { if (((expression->u.ifstate)->entries.tqe_next) != ((void *)0)) (expression->u.ifstate)->entries.tqe_next ->entries.tqe_prev = (expression->u.ifstate)->entries .tqe_prev; else (&state->interface_states)->tqh_last = (expression->u.ifstate)->entries.tqe_prev; *(expression ->u.ifstate)->entries.tqe_prev = (expression->u.ifstate )->entries.tqe_next; ; ; } while (0) |
| 728 | expression->u.ifstate, entries)do { if (((expression->u.ifstate)->entries.tqe_next) != ((void *)0)) (expression->u.ifstate)->entries.tqe_next ->entries.tqe_prev = (expression->u.ifstate)->entries .tqe_prev; else (&state->interface_states)->tqh_last = (expression->u.ifstate)->entries.tqe_prev; *(expression ->u.ifstate)->entries.tqe_prev = (expression->u.ifstate )->entries.tqe_next; ; ; } while (0); |
| 729 | free(expression->u.ifstate); |
| 730 | } |
| 731 | break; |
| 732 | case IFSD_OPER_EXTERNAL4: |
| 733 | TAILQ_REMOVE(&expression->u.external->expressions, expression,do { if (((expression)->entries.tqe_next) != ((void *)0)) ( expression)->entries.tqe_next->entries.tqe_prev = (expression )->entries.tqe_prev; else (&expression->u.external-> expressions)->tqh_last = (expression)->entries.tqe_prev ; *(expression)->entries.tqe_prev = (expression)->entries .tqe_next; ; ; } while (0) |
| 734 | entries)do { if (((expression)->entries.tqe_next) != ((void *)0)) ( expression)->entries.tqe_next->entries.tqe_prev = (expression )->entries.tqe_prev; else (&expression->u.external-> expressions)->tqh_last = (expression)->entries.tqe_prev ; *(expression)->entries.tqe_prev = (expression)->entries .tqe_next; ; ; } while (0); |
| 735 | if (--expression->u.external->refcount == 0) { |
| 736 | TAILQ_REMOVE(&state->external_tests,do { if (((expression->u.external)->entries.tqe_next) != ((void *)0)) (expression->u.external)->entries.tqe_next ->entries.tqe_prev = (expression->u.external)->entries .tqe_prev; else (&state->external_tests)->tqh_last = (expression->u.external)->entries.tqe_prev; *(expression ->u.external)->entries.tqe_prev = (expression->u.external )->entries.tqe_next; ; ; } while (0) |
| 737 | expression->u.external, entries)do { if (((expression->u.external)->entries.tqe_next) != ((void *)0)) (expression->u.external)->entries.tqe_next ->entries.tqe_prev = (expression->u.external)->entries .tqe_prev; else (&state->external_tests)->tqh_last = (expression->u.external)->entries.tqe_prev; *(expression ->u.external)->entries.tqe_prev = (expression->u.external )->entries.tqe_next; ; ; } while (0); |
| 738 | free(expression->u.external->command); |
| 739 | event_del(&expression->u.external->ev); |
| 740 | free(expression->u.external); |
| 741 | } |
| 742 | break; |
| 743 | default: |
| 744 | if (expression->left != NULL((void *)0)) |
| 745 | remove_expression(expression->left, state); |
| 746 | if (expression->right != NULL((void *)0)) |
| 747 | remove_expression(expression->right, state); |
| 748 | break; |
| 749 | } |
| 750 | free(expression); |
| 751 | } |