1 /* $OpenBSD: ifstated.c,v 1.35 2009/06/25 17:14:57 sthen 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/ioctl.h> 28 #include <sys/socket.h> 29 #include <sys/wait.h> 30 31 #include <net/if.h> 32 #include <net/route.h> 33 #include <netinet/in.h> 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <signal.h> 39 #include <err.h> 40 #include <event.h> 41 #include <unistd.h> 42 #include <ifaddrs.h> 43 44 #include "ifstated.h" 45 46 struct ifsd_config *conf = NULL, *newconf = NULL; 47 48 int opts = 0; 49 int opt_inhibit = 0; 50 char *configfile = "/etc/ifstated.conf"; 51 struct event rt_msg_ev, sighup_ev, startup_ev, sigchld_ev; 52 53 void startup_handler(int, short, void *); 54 void sighup_handler(int, short, void *); 55 int load_config(void); 56 void sigchld_handler(int, short, void *); 57 void rt_msg_handler(int, short, void *); 58 void external_handler(int, short, void *); 59 void external_exec(struct ifsd_external *, int); 60 void check_external_status(struct ifsd_state *); 61 void external_evtimer_setup(struct ifsd_state *, int); 62 void scan_ifstate(int, int, int); 63 int scan_ifstate_single(int, int, struct ifsd_state *); 64 void fetch_state(void); 65 void usage(void); 66 void adjust_expressions(struct ifsd_expression_list *, int); 67 void adjust_external_expressions(struct ifsd_state *); 68 void eval_state(struct ifsd_state *); 69 int state_change(void); 70 void do_action(struct ifsd_action *); 71 void remove_action(struct ifsd_action *, struct ifsd_state *); 72 void remove_expression(struct ifsd_expression *, struct ifsd_state *); 73 74 void 75 usage(void) 76 { 77 extern char *__progname; 78 79 fprintf(stderr, "usage: %s [-dhinv] [-D macro=value] [-f file]\n", 80 __progname); 81 exit(1); 82 } 83 84 int 85 main(int argc, char *argv[]) 86 { 87 struct timeval tv; 88 int ch; 89 int debug = 0; 90 91 log_init(1); 92 93 while ((ch = getopt(argc, argv, "dD:f:hniv")) != -1) { 94 switch (ch) { 95 case 'd': 96 debug = 1; 97 break; 98 case 'D': 99 if (cmdline_symset(optarg) < 0) 100 errx(1, "could not parse macro definition %s", 101 optarg); 102 break; 103 case 'f': 104 configfile = optarg; 105 break; 106 case 'h': 107 usage(); 108 break; 109 case 'n': 110 opts |= IFSD_OPT_NOACTION; 111 break; 112 case 'i': 113 opt_inhibit = 1; 114 break; 115 case 'v': 116 if (opts & IFSD_OPT_VERBOSE) 117 opts |= IFSD_OPT_VERBOSE2; 118 opts |= IFSD_OPT_VERBOSE; 119 break; 120 default: 121 usage(); 122 } 123 } 124 125 argc -= optind; 126 argv += optind; 127 if (argc > 0) 128 usage(); 129 130 if (opts & IFSD_OPT_NOACTION) { 131 if ((newconf = parse_config(configfile, opts)) == NULL) 132 exit(1); 133 warnx("configuration OK"); 134 exit(0); 135 } 136 137 if (!debug) { 138 daemon(1, 0); 139 setproctitle(NULL); 140 } 141 142 event_init(); 143 log_init(debug); 144 145 signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL); 146 signal_add(&sigchld_ev, NULL); 147 148 /* Loading the config needs to happen in the event loop */ 149 tv.tv_usec = 0; 150 tv.tv_sec = 0; 151 evtimer_set(&startup_ev, startup_handler, NULL); 152 evtimer_add(&startup_ev, &tv); 153 154 event_loop(0); 155 exit(0); 156 } 157 158 void 159 startup_handler(int fd, short event, void *arg) 160 { 161 int rt_fd; 162 unsigned int rtfilter; 163 164 if ((rt_fd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) 165 err(1, "no routing socket"); 166 167 if (load_config() != 0) { 168 log_warnx("unable to load config"); 169 exit(1); 170 } 171 172 rtfilter = ROUTE_FILTER(RTM_IFINFO); 173 if (setsockopt(rt_fd, PF_ROUTE, ROUTE_MSGFILTER, 174 &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */ 175 log_warn("startup_handler: setsockopt"); 176 177 event_set(&rt_msg_ev, rt_fd, EV_READ|EV_PERSIST, rt_msg_handler, NULL); 178 event_add(&rt_msg_ev, NULL); 179 180 signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL); 181 signal_add(&sighup_ev, NULL); 182 183 log_info("started"); 184 } 185 186 void 187 sighup_handler(int fd, short event, void *arg) 188 { 189 log_info("reloading config"); 190 if (load_config() != 0) 191 log_warnx("unable to reload config"); 192 } 193 194 int 195 load_config(void) 196 { 197 if ((newconf = parse_config(configfile, opts)) == NULL) 198 return (-1); 199 if (conf != NULL) 200 clear_config(conf); 201 conf = newconf; 202 conf->always.entered = time(NULL); 203 fetch_state(); 204 external_evtimer_setup(&conf->always, IFSD_EVTIMER_ADD); 205 adjust_external_expressions(&conf->always); 206 eval_state(&conf->always); 207 if (conf->curstate != NULL) { 208 log_info("initial state: %s", conf->curstate->name); 209 conf->curstate->entered = time(NULL); 210 conf->nextstate = conf->curstate; 211 conf->curstate = NULL; 212 while (state_change()) 213 do_action(conf->curstate->always); 214 } 215 return (0); 216 } 217 218 void 219 rt_msg_handler(int fd, short event, void *arg) 220 { 221 char msg[2048]; 222 struct rt_msghdr *rtm = (struct rt_msghdr *)&msg; 223 struct if_msghdr ifm; 224 int len; 225 226 len = read(fd, msg, sizeof(msg)); 227 228 /* XXX ignore errors? */ 229 if (len < sizeof(struct rt_msghdr)) 230 return; 231 232 if (rtm->rtm_version != RTM_VERSION) 233 return; 234 235 if (rtm->rtm_type != RTM_IFINFO) 236 return; 237 238 memcpy(&ifm, rtm, sizeof(ifm)); 239 scan_ifstate(ifm.ifm_index, ifm.ifm_data.ifi_link_state, 1); 240 } 241 242 void 243 sigchld_handler(int fd, short event, void *arg) 244 { 245 check_external_status(&conf->always); 246 if (conf->curstate != NULL) 247 check_external_status(conf->curstate); 248 } 249 250 void 251 external_handler(int fd, short event, void *arg) 252 { 253 struct ifsd_external *external = (struct ifsd_external *)arg; 254 struct timeval tv; 255 256 /* re-schedule */ 257 tv.tv_usec = 0; 258 tv.tv_sec = external->frequency; 259 evtimer_set(&external->ev, external_handler, external); 260 evtimer_add(&external->ev, &tv); 261 262 /* execute */ 263 external_exec(external, 1); 264 } 265 266 void 267 external_exec(struct ifsd_external *external, int async) 268 { 269 char *argp[] = {"sh", "-c", NULL, NULL}; 270 pid_t pid; 271 int s; 272 273 if (external->pid > 0) { 274 log_info("previous command %s [%d] still running, killing it", 275 external->command, external->pid); 276 kill(external->pid, SIGKILL); 277 waitpid(external->pid, &s, 0); 278 external->pid = 0; 279 } 280 281 argp[2] = external->command; 282 log_debug("running %s", external->command); 283 pid = fork(); 284 if (pid < 0) { 285 log_warn("fork error"); 286 } else if (pid == 0) { 287 execv("/bin/sh", argp); 288 _exit(1); 289 /* NOTREACHED */ 290 } else { 291 external->pid = pid; 292 } 293 if (!async) { 294 waitpid(external->pid, &s, 0); 295 external->pid = 0; 296 if (WIFEXITED(s)) 297 external->prevstatus = WEXITSTATUS(s); 298 } 299 } 300 301 void 302 adjust_external_expressions(struct ifsd_state *state) 303 { 304 struct ifsd_external *external; 305 struct ifsd_expression_list expressions; 306 307 TAILQ_INIT(&expressions); 308 TAILQ_FOREACH(external, &state->external_tests, entries) { 309 struct ifsd_expression *expression; 310 311 if (external->prevstatus == -1) 312 continue; 313 314 TAILQ_FOREACH(expression, &external->expressions, entries) { 315 TAILQ_INSERT_TAIL(&expressions, 316 expression, eval); 317 expression->truth = !external->prevstatus; 318 } 319 adjust_expressions(&expressions, conf->maxdepth); 320 } 321 } 322 323 void 324 check_external_status(struct ifsd_state *state) 325 { 326 struct ifsd_external *external, *end = NULL; 327 int status, s, changed = 0; 328 329 /* Do this manually; change ordering so the oldest is first */ 330 external = TAILQ_FIRST(&state->external_tests); 331 while (external != NULL && external != end) { 332 struct ifsd_external *newexternal; 333 334 newexternal = TAILQ_NEXT(external, entries); 335 336 if (external->pid <= 0) 337 goto loop; 338 339 if (wait4(external->pid, &s, WNOHANG, NULL) == 0) 340 goto loop; 341 342 external->pid = 0; 343 if (end == NULL) 344 end = external; 345 if (WIFEXITED(s)) 346 status = WEXITSTATUS(s); 347 else { 348 log_warnx("%s exited abnormally", external->command); 349 goto loop; 350 } 351 352 if (external->prevstatus != status && 353 (external->prevstatus != -1 || !opt_inhibit)) { 354 changed = 1; 355 external->prevstatus = status; 356 } 357 external->lastexec = time(NULL); 358 TAILQ_REMOVE(&state->external_tests, external, entries); 359 TAILQ_INSERT_TAIL(&state->external_tests, external, entries); 360 loop: 361 external = newexternal; 362 } 363 364 if (changed) { 365 adjust_external_expressions(state); 366 eval_state(state); 367 } 368 } 369 370 void 371 external_evtimer_setup(struct ifsd_state *state, int action) 372 { 373 struct ifsd_external *external; 374 int s; 375 376 if (state != NULL) { 377 switch (action) { 378 case IFSD_EVTIMER_ADD: 379 TAILQ_FOREACH(external, 380 &state->external_tests, entries) { 381 struct timeval tv; 382 383 /* run it once right away */ 384 external_exec(external, 0); 385 386 /* schedule it for later */ 387 tv.tv_usec = 0; 388 tv.tv_sec = external->frequency; 389 evtimer_set(&external->ev, external_handler, 390 external); 391 evtimer_add(&external->ev, &tv); 392 } 393 break; 394 case IFSD_EVTIMER_DEL: 395 TAILQ_FOREACH(external, 396 &state->external_tests, entries) { 397 if (external->pid > 0) { 398 kill(external->pid, SIGKILL); 399 waitpid(external->pid, &s, 0); 400 external->pid = 0; 401 } 402 evtimer_del(&external->ev); 403 } 404 break; 405 } 406 } 407 } 408 409 int 410 scan_ifstate_single(int ifindex, int s, struct ifsd_state *state) 411 { 412 struct ifsd_ifstate *ifstate; 413 struct ifsd_expression_list expressions; 414 int changed = 0; 415 416 TAILQ_INIT(&expressions); 417 418 TAILQ_FOREACH(ifstate, &state->interface_states, entries) { 419 if (ifstate->ifindex == ifindex) { 420 if (ifstate->prevstate != s && 421 (ifstate->prevstate != -1 || !opt_inhibit)) { 422 struct ifsd_expression *expression; 423 int truth; 424 425 truth = (ifstate->ifstate == s) || 426 (ifstate->ifstate == IFSD_LINKUP && 427 LINK_STATE_IS_UP(s)); 428 429 TAILQ_FOREACH(expression, 430 &ifstate->expressions, entries) { 431 expression->truth = truth; 432 TAILQ_INSERT_TAIL(&expressions, 433 expression, eval); 434 changed = 1; 435 } 436 ifstate->prevstate = s; 437 } 438 } 439 } 440 441 if (changed) 442 adjust_expressions(&expressions, conf->maxdepth); 443 return (changed); 444 } 445 446 void 447 scan_ifstate(int ifindex, int s, int do_eval) 448 { 449 struct ifsd_state *state; 450 int cur_eval = 0; 451 452 if (scan_ifstate_single(ifindex, s, &conf->always) && do_eval) 453 eval_state(&conf->always); 454 TAILQ_FOREACH(state, &conf->states, entries) { 455 if (scan_ifstate_single(ifindex, s, state) && 456 (do_eval && state == conf->curstate)) 457 cur_eval = 1; 458 } 459 /* execute actions _after_ all expressions have been adjusted */ 460 if (cur_eval) 461 eval_state(conf->curstate); 462 } 463 464 /* 465 * Do a bottom-up ajustment of the expression tree's truth value, 466 * level-by-level to ensure that each expression's subexpressions have been 467 * evaluated. 468 */ 469 void 470 adjust_expressions(struct ifsd_expression_list *expressions, int depth) 471 { 472 struct ifsd_expression_list nexpressions; 473 struct ifsd_expression *expression; 474 475 TAILQ_INIT(&nexpressions); 476 while ((expression = TAILQ_FIRST(expressions)) != NULL) { 477 TAILQ_REMOVE(expressions, expression, eval); 478 if (expression->depth == depth) { 479 struct ifsd_expression *te; 480 481 switch (expression->type) { 482 case IFSD_OPER_AND: 483 expression->truth = expression->left->truth && 484 expression->right->truth; 485 break; 486 case IFSD_OPER_OR: 487 expression->truth = expression->left->truth || 488 expression->right->truth; 489 break; 490 case IFSD_OPER_NOT: 491 expression->truth = !expression->right->truth; 492 break; 493 default: 494 break; 495 } 496 if (expression->parent != NULL) { 497 if (TAILQ_EMPTY(&nexpressions)) 498 te = NULL; 499 TAILQ_FOREACH(te, &nexpressions, eval) 500 if (expression->parent == te) 501 break; 502 if (te == NULL) 503 TAILQ_INSERT_TAIL(&nexpressions, 504 expression->parent, eval); 505 } 506 } else 507 TAILQ_INSERT_TAIL(&nexpressions, expression, eval); 508 } 509 if (depth > 0) 510 adjust_expressions(&nexpressions, depth - 1); 511 } 512 513 void 514 eval_state(struct ifsd_state *state) 515 { 516 struct ifsd_external *external = TAILQ_FIRST(&state->external_tests); 517 if (external == NULL || external->lastexec >= state->entered || 518 external->lastexec == 0) { 519 do_action(state->always); 520 while (state_change()) 521 do_action(conf->curstate->always); 522 } 523 } 524 525 /* 526 *If a previous action included a state change, process it. 527 */ 528 int 529 state_change(void) 530 { 531 if (conf->nextstate != NULL && conf->curstate != conf->nextstate) { 532 log_info("changing state to %s", conf->nextstate->name); 533 if (conf->curstate != NULL) { 534 evtimer_del(&conf->curstate->ev); 535 external_evtimer_setup(conf->curstate, 536 IFSD_EVTIMER_DEL); 537 } 538 conf->curstate = conf->nextstate; 539 conf->nextstate = NULL; 540 conf->curstate->entered = time(NULL); 541 external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD); 542 adjust_external_expressions(conf->curstate); 543 do_action(conf->curstate->init); 544 return (1); 545 } 546 return (0); 547 } 548 549 /* 550 * Run recursively through the tree of actions. 551 */ 552 void 553 do_action(struct ifsd_action *action) 554 { 555 struct ifsd_action *subaction; 556 557 switch (action->type) { 558 case IFSD_ACTION_COMMAND: 559 log_info("running %s", action->act.command); 560 system(action->act.command); 561 break; 562 case IFSD_ACTION_CHANGESTATE: 563 conf->nextstate = action->act.nextstate; 564 break; 565 case IFSD_ACTION_CONDITION: 566 if ((action->act.c.expression != NULL && 567 action->act.c.expression->truth) || 568 action->act.c.expression == NULL) { 569 TAILQ_FOREACH(subaction, &action->act.c.actions, 570 entries) 571 do_action(subaction); 572 } 573 break; 574 default: 575 log_debug("do_action: unknown action %d", action->type); 576 break; 577 } 578 } 579 580 /* 581 * Fetch the current link states. 582 */ 583 void 584 fetch_state(void) 585 { 586 struct ifaddrs *ifap, *ifa; 587 char *oname = NULL; 588 int sock = socket(AF_INET, SOCK_DGRAM, 0); 589 590 if (getifaddrs(&ifap) != 0) 591 err(1, "getifaddrs"); 592 593 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 594 struct ifreq ifr; 595 struct if_data ifrdat; 596 597 if (oname && !strcmp(oname, ifa->ifa_name)) 598 continue; 599 oname = ifa->ifa_name; 600 601 strlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); 602 ifr.ifr_data = (caddr_t)&ifrdat; 603 604 if (ioctl(sock, SIOCGIFDATA, (caddr_t)&ifr) == -1) 605 continue; 606 607 scan_ifstate(if_nametoindex(ifa->ifa_name), 608 ifrdat.ifi_link_state, 0); 609 } 610 freeifaddrs(ifap); 611 close(sock); 612 } 613 614 615 616 /* 617 * Clear the config. 618 */ 619 void 620 clear_config(struct ifsd_config *oconf) 621 { 622 struct ifsd_state *state; 623 624 external_evtimer_setup(&conf->always, IFSD_EVTIMER_DEL); 625 if (conf != NULL && conf->curstate != NULL) 626 external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL); 627 while ((state = TAILQ_FIRST(&oconf->states)) != NULL) { 628 TAILQ_REMOVE(&oconf->states, state, entries); 629 remove_action(state->init, state); 630 remove_action(state->always, state); 631 free(state->name); 632 free(state); 633 } 634 remove_action(oconf->always.init, &oconf->always); 635 remove_action(oconf->always.always, &oconf->always); 636 free(oconf); 637 } 638 639 void 640 remove_action(struct ifsd_action *action, struct ifsd_state *state) 641 { 642 struct ifsd_action *subaction; 643 644 if (action == NULL || state == NULL) 645 return; 646 647 switch (action->type) { 648 case IFSD_ACTION_LOG: 649 free(action->act.logmessage); 650 break; 651 case IFSD_ACTION_COMMAND: 652 free(action->act.command); 653 break; 654 case IFSD_ACTION_CHANGESTATE: 655 break; 656 case IFSD_ACTION_CONDITION: 657 if (action->act.c.expression != NULL) 658 remove_expression(action->act.c.expression, state); 659 while ((subaction = 660 TAILQ_FIRST(&action->act.c.actions)) != NULL) { 661 TAILQ_REMOVE(&action->act.c.actions, 662 subaction, entries); 663 remove_action(subaction, state); 664 } 665 } 666 free(action); 667 } 668 669 void 670 remove_expression(struct ifsd_expression *expression, 671 struct ifsd_state *state) 672 { 673 switch (expression->type) { 674 case IFSD_OPER_IFSTATE: 675 TAILQ_REMOVE(&expression->u.ifstate->expressions, expression, 676 entries); 677 if (--expression->u.ifstate->refcount == 0) { 678 TAILQ_REMOVE(&state->interface_states, 679 expression->u.ifstate, entries); 680 free(expression->u.ifstate); 681 } 682 break; 683 case IFSD_OPER_EXTERNAL: 684 TAILQ_REMOVE(&expression->u.external->expressions, expression, 685 entries); 686 if (--expression->u.external->refcount == 0) { 687 TAILQ_REMOVE(&state->external_tests, 688 expression->u.external, entries); 689 free(expression->u.external->command); 690 event_del(&expression->u.external->ev); 691 free(expression->u.external); 692 } 693 break; 694 default: 695 if (expression->left != NULL) 696 remove_expression(expression->left, state); 697 if (expression->right != NULL) 698 remove_expression(expression->right, state); 699 break; 700 } 701 free(expression); 702 } 703