1 /* $OpenBSD: ifstated.c,v 1.33 2008/05/12 19:15:02 pyr 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 163 if ((rt_fd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) 164 err(1, "no routing socket"); 165 166 if (load_config() != 0) { 167 log_warnx("unable to load config"); 168 exit(1); 169 } 170 171 event_set(&rt_msg_ev, rt_fd, EV_READ|EV_PERSIST, rt_msg_handler, NULL); 172 event_add(&rt_msg_ev, NULL); 173 174 signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL); 175 signal_add(&sighup_ev, NULL); 176 177 log_info("started"); 178 } 179 180 void 181 sighup_handler(int fd, short event, void *arg) 182 { 183 log_info("reloading config"); 184 if (load_config() != 0) 185 log_warnx("unable to reload config"); 186 } 187 188 int 189 load_config(void) 190 { 191 if ((newconf = parse_config(configfile, opts)) == NULL) 192 return (-1); 193 if (conf != NULL) 194 clear_config(conf); 195 conf = newconf; 196 conf->always.entered = time(NULL); 197 fetch_state(); 198 external_evtimer_setup(&conf->always, IFSD_EVTIMER_ADD); 199 adjust_external_expressions(&conf->always); 200 eval_state(&conf->always); 201 if (conf->curstate != NULL) { 202 log_info("initial state: %s", conf->curstate->name); 203 conf->curstate->entered = time(NULL); 204 conf->nextstate = conf->curstate; 205 conf->curstate = NULL; 206 while (state_change()) 207 do_action(conf->curstate->always); 208 } 209 return (0); 210 } 211 212 void 213 rt_msg_handler(int fd, short event, void *arg) 214 { 215 char msg[2048]; 216 struct rt_msghdr *rtm = (struct rt_msghdr *)&msg; 217 struct if_msghdr ifm; 218 int len; 219 220 len = read(fd, msg, sizeof(msg)); 221 222 /* XXX ignore errors? */ 223 if (len < sizeof(struct rt_msghdr)) 224 return; 225 226 if (rtm->rtm_version != RTM_VERSION) 227 return; 228 229 if (rtm->rtm_type != RTM_IFINFO) 230 return; 231 232 memcpy(&ifm, rtm, sizeof(ifm)); 233 scan_ifstate(ifm.ifm_index, ifm.ifm_data.ifi_link_state, 1); 234 } 235 236 void 237 sigchld_handler(int fd, short event, void *arg) 238 { 239 check_external_status(&conf->always); 240 if (conf->curstate != NULL) 241 check_external_status(conf->curstate); 242 } 243 244 void 245 external_handler(int fd, short event, void *arg) 246 { 247 struct ifsd_external *external = (struct ifsd_external *)arg; 248 struct timeval tv; 249 250 /* re-schedule */ 251 tv.tv_usec = 0; 252 tv.tv_sec = external->frequency; 253 evtimer_set(&external->ev, external_handler, external); 254 evtimer_add(&external->ev, &tv); 255 256 /* execute */ 257 external_exec(external, 1); 258 } 259 260 void 261 external_exec(struct ifsd_external *external, int async) 262 { 263 char *argp[] = {"sh", "-c", NULL, NULL}; 264 pid_t pid; 265 int s; 266 267 if (external->pid > 0) { 268 log_info("previous command %s [%d] still running, killing it", 269 external->command, external->pid); 270 kill(external->pid, SIGKILL); 271 waitpid(external->pid, &s, 0); 272 external->pid = 0; 273 } 274 275 argp[2] = external->command; 276 log_debug("running %s", external->command); 277 pid = fork(); 278 if (pid < 0) { 279 log_warn("fork error"); 280 } else if (pid == 0) { 281 execv("/bin/sh", argp); 282 _exit(1); 283 /* NOTREACHED */ 284 } else { 285 external->pid = pid; 286 } 287 if (!async) { 288 waitpid(external->pid, &s, 0); 289 external->pid = 0; 290 if (WIFEXITED(s)) 291 external->prevstatus = WEXITSTATUS(s); 292 } 293 } 294 295 void 296 adjust_external_expressions(struct ifsd_state *state) 297 { 298 struct ifsd_external *external; 299 struct ifsd_expression_list expressions; 300 301 TAILQ_INIT(&expressions); 302 TAILQ_FOREACH(external, &state->external_tests, entries) { 303 struct ifsd_expression *expression; 304 305 if (external->prevstatus == -1) 306 continue; 307 308 TAILQ_FOREACH(expression, &external->expressions, entries) { 309 TAILQ_INSERT_TAIL(&expressions, 310 expression, eval); 311 expression->truth = !external->prevstatus; 312 } 313 adjust_expressions(&expressions, conf->maxdepth); 314 } 315 } 316 317 void 318 check_external_status(struct ifsd_state *state) 319 { 320 struct ifsd_external *external, *end = NULL; 321 int status, s, changed = 0; 322 323 /* Do this manually; change ordering so the oldest is first */ 324 external = TAILQ_FIRST(&state->external_tests); 325 while (external != NULL && external != end) { 326 struct ifsd_external *newexternal; 327 328 newexternal = TAILQ_NEXT(external, entries); 329 330 if (external->pid <= 0) 331 goto loop; 332 333 if (wait4(external->pid, &s, WNOHANG, NULL) == 0) 334 goto loop; 335 336 external->pid = 0; 337 if (end == NULL) 338 end = external; 339 if (WIFEXITED(s)) 340 status = WEXITSTATUS(s); 341 else { 342 log_warnx("%s exited abnormally", external->command); 343 goto loop; 344 } 345 346 if (external->prevstatus != status && 347 (external->prevstatus != -1 || !opt_inhibit)) { 348 changed = 1; 349 external->prevstatus = status; 350 } 351 external->lastexec = time(NULL); 352 TAILQ_REMOVE(&state->external_tests, external, entries); 353 TAILQ_INSERT_TAIL(&state->external_tests, external, entries); 354 loop: 355 external = newexternal; 356 } 357 358 if (changed) { 359 adjust_external_expressions(state); 360 eval_state(state); 361 } 362 } 363 364 void 365 external_evtimer_setup(struct ifsd_state *state, int action) 366 { 367 struct ifsd_external *external; 368 int s; 369 370 if (state != NULL) { 371 switch (action) { 372 case IFSD_EVTIMER_ADD: 373 TAILQ_FOREACH(external, 374 &state->external_tests, entries) { 375 struct timeval tv; 376 377 /* run it once right away */ 378 external_exec(external, 0); 379 380 /* schedule it for later */ 381 tv.tv_usec = 0; 382 tv.tv_sec = external->frequency; 383 evtimer_set(&external->ev, external_handler, 384 external); 385 evtimer_add(&external->ev, &tv); 386 } 387 break; 388 case IFSD_EVTIMER_DEL: 389 TAILQ_FOREACH(external, 390 &state->external_tests, entries) { 391 if (external->pid > 0) { 392 kill(external->pid, SIGKILL); 393 waitpid(external->pid, &s, 0); 394 external->pid = 0; 395 } 396 evtimer_del(&external->ev); 397 } 398 break; 399 } 400 } 401 } 402 403 int 404 scan_ifstate_single(int ifindex, int s, struct ifsd_state *state) 405 { 406 struct ifsd_ifstate *ifstate; 407 struct ifsd_expression_list expressions; 408 int changed = 0; 409 410 TAILQ_INIT(&expressions); 411 412 TAILQ_FOREACH(ifstate, &state->interface_states, entries) { 413 if (ifstate->ifindex == ifindex) { 414 if (ifstate->prevstate != s && 415 (ifstate->prevstate != -1 || !opt_inhibit)) { 416 struct ifsd_expression *expression; 417 int truth; 418 419 truth = (ifstate->ifstate == s) || 420 (ifstate->ifstate == IFSD_LINKUP && 421 LINK_STATE_IS_UP(s)); 422 423 TAILQ_FOREACH(expression, 424 &ifstate->expressions, entries) { 425 expression->truth = truth; 426 TAILQ_INSERT_TAIL(&expressions, 427 expression, eval); 428 changed = 1; 429 } 430 ifstate->prevstate = s; 431 } 432 } 433 } 434 435 if (changed) 436 adjust_expressions(&expressions, conf->maxdepth); 437 return (changed); 438 } 439 440 void 441 scan_ifstate(int ifindex, int s, int do_eval) 442 { 443 struct ifsd_state *state; 444 int cur_eval = 0; 445 446 if (scan_ifstate_single(ifindex, s, &conf->always) && do_eval) 447 eval_state(&conf->always); 448 TAILQ_FOREACH(state, &conf->states, entries) { 449 if (scan_ifstate_single(ifindex, s, state) && 450 (do_eval && state == conf->curstate)) 451 cur_eval = 1; 452 } 453 /* execute actions _after_ all expressions have been adjusted */ 454 if (cur_eval) 455 eval_state(conf->curstate); 456 } 457 458 /* 459 * Do a bottom-up ajustment of the expression tree's truth value, 460 * level-by-level to ensure that each expression's subexpressions have been 461 * evaluated. 462 */ 463 void 464 adjust_expressions(struct ifsd_expression_list *expressions, int depth) 465 { 466 struct ifsd_expression_list nexpressions; 467 struct ifsd_expression *expression; 468 469 TAILQ_INIT(&nexpressions); 470 while ((expression = TAILQ_FIRST(expressions)) != NULL) { 471 TAILQ_REMOVE(expressions, expression, eval); 472 if (expression->depth == depth) { 473 struct ifsd_expression *te; 474 475 switch (expression->type) { 476 case IFSD_OPER_AND: 477 expression->truth = expression->left->truth && 478 expression->right->truth; 479 break; 480 case IFSD_OPER_OR: 481 expression->truth = expression->left->truth || 482 expression->right->truth; 483 break; 484 case IFSD_OPER_NOT: 485 expression->truth = !expression->right->truth; 486 break; 487 default: 488 break; 489 } 490 if (expression->parent != NULL) { 491 if (TAILQ_EMPTY(&nexpressions)) 492 te = NULL; 493 TAILQ_FOREACH(te, &nexpressions, eval) 494 if (expression->parent == te) 495 break; 496 if (te == NULL) 497 TAILQ_INSERT_TAIL(&nexpressions, 498 expression->parent, eval); 499 } 500 } else 501 TAILQ_INSERT_TAIL(&nexpressions, expression, eval); 502 } 503 if (depth > 0) 504 adjust_expressions(&nexpressions, depth - 1); 505 } 506 507 void 508 eval_state(struct ifsd_state *state) 509 { 510 struct ifsd_external *external = TAILQ_FIRST(&state->external_tests); 511 if (external == NULL || external->lastexec >= state->entered || 512 external->lastexec == 0) { 513 do_action(state->always); 514 while (state_change()) 515 do_action(conf->curstate->always); 516 } 517 } 518 519 /* 520 *If a previous action included a state change, process it. 521 */ 522 int 523 state_change(void) 524 { 525 if (conf->nextstate != NULL && conf->curstate != conf->nextstate) { 526 log_info("changing state to %s", conf->nextstate->name); 527 if (conf->curstate != NULL) { 528 evtimer_del(&conf->curstate->ev); 529 external_evtimer_setup(conf->curstate, 530 IFSD_EVTIMER_DEL); 531 } 532 conf->curstate = conf->nextstate; 533 conf->nextstate = NULL; 534 conf->curstate->entered = time(NULL); 535 external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD); 536 adjust_external_expressions(conf->curstate); 537 do_action(conf->curstate->init); 538 return (1); 539 } 540 return (0); 541 } 542 543 /* 544 * Run recursively through the tree of actions. 545 */ 546 void 547 do_action(struct ifsd_action *action) 548 { 549 struct ifsd_action *subaction; 550 551 switch (action->type) { 552 case IFSD_ACTION_COMMAND: 553 log_info("running %s", action->act.command); 554 system(action->act.command); 555 break; 556 case IFSD_ACTION_CHANGESTATE: 557 conf->nextstate = action->act.nextstate; 558 break; 559 case IFSD_ACTION_CONDITION: 560 if ((action->act.c.expression != NULL && 561 action->act.c.expression->truth) || 562 action->act.c.expression == NULL) { 563 TAILQ_FOREACH(subaction, &action->act.c.actions, 564 entries) 565 do_action(subaction); 566 } 567 break; 568 default: 569 log_debug("do_action: unknown action %d", action->type); 570 break; 571 } 572 } 573 574 /* 575 * Fetch the current link states. 576 */ 577 void 578 fetch_state(void) 579 { 580 struct ifaddrs *ifap, *ifa; 581 char *oname = NULL; 582 int sock = socket(AF_INET, SOCK_DGRAM, 0); 583 584 if (getifaddrs(&ifap) != 0) 585 err(1, "getifaddrs"); 586 587 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 588 struct ifreq ifr; 589 struct if_data ifrdat; 590 591 if (oname && !strcmp(oname, ifa->ifa_name)) 592 continue; 593 oname = ifa->ifa_name; 594 595 strlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); 596 ifr.ifr_data = (caddr_t)&ifrdat; 597 598 if (ioctl(sock, SIOCGIFDATA, (caddr_t)&ifr) == -1) 599 continue; 600 601 scan_ifstate(if_nametoindex(ifa->ifa_name), 602 ifrdat.ifi_link_state, 0); 603 } 604 freeifaddrs(ifap); 605 close(sock); 606 } 607 608 609 610 /* 611 * Clear the config. 612 */ 613 void 614 clear_config(struct ifsd_config *oconf) 615 { 616 struct ifsd_state *state; 617 618 external_evtimer_setup(&conf->always, IFSD_EVTIMER_DEL); 619 if (conf != NULL && conf->curstate != NULL) 620 external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL); 621 while ((state = TAILQ_FIRST(&oconf->states)) != NULL) { 622 TAILQ_REMOVE(&oconf->states, state, entries); 623 remove_action(state->init, state); 624 remove_action(state->always, state); 625 free(state->name); 626 free(state); 627 } 628 remove_action(oconf->always.init, &oconf->always); 629 remove_action(oconf->always.always, &oconf->always); 630 free(oconf); 631 } 632 633 void 634 remove_action(struct ifsd_action *action, struct ifsd_state *state) 635 { 636 struct ifsd_action *subaction; 637 638 if (action == NULL || state == NULL) 639 return; 640 641 switch (action->type) { 642 case IFSD_ACTION_LOG: 643 free(action->act.logmessage); 644 break; 645 case IFSD_ACTION_COMMAND: 646 free(action->act.command); 647 break; 648 case IFSD_ACTION_CHANGESTATE: 649 break; 650 case IFSD_ACTION_CONDITION: 651 if (action->act.c.expression != NULL) 652 remove_expression(action->act.c.expression, state); 653 while ((subaction = 654 TAILQ_FIRST(&action->act.c.actions)) != NULL) { 655 TAILQ_REMOVE(&action->act.c.actions, 656 subaction, entries); 657 remove_action(subaction, state); 658 } 659 } 660 free(action); 661 } 662 663 void 664 remove_expression(struct ifsd_expression *expression, 665 struct ifsd_state *state) 666 { 667 switch (expression->type) { 668 case IFSD_OPER_IFSTATE: 669 TAILQ_REMOVE(&expression->u.ifstate->expressions, expression, 670 entries); 671 if (--expression->u.ifstate->refcount == 0) { 672 TAILQ_REMOVE(&state->interface_states, 673 expression->u.ifstate, entries); 674 free(expression->u.ifstate); 675 } 676 break; 677 case IFSD_OPER_EXTERNAL: 678 TAILQ_REMOVE(&expression->u.external->expressions, expression, 679 entries); 680 if (--expression->u.external->refcount == 0) { 681 TAILQ_REMOVE(&state->external_tests, 682 expression->u.external, entries); 683 free(expression->u.external->command); 684 event_del(&expression->u.external->ev); 685 free(expression->u.external); 686 } 687 break; 688 default: 689 if (expression->left != NULL) 690 remove_expression(expression->left, state); 691 if (expression->right != NULL) 692 remove_expression(expression->right, state); 693 break; 694 } 695 free(expression); 696 } 697