1 /* $OpenBSD: parse.y,v 1.29 2009/03/31 21:03:48 tobias Exp $ */ 2 3 /* 4 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 5 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 6 * Copyright (c) 2001 Markus Friedl. All rights reserved. 7 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 8 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 */ 22 23 %{ 24 #include <sys/types.h> 25 #include <sys/time.h> 26 #include <sys/socket.h> 27 #include <sys/stat.h> 28 #include <netinet/in.h> 29 #include <arpa/inet.h> 30 #include <net/if.h> 31 32 #include <ctype.h> 33 #include <unistd.h> 34 #include <err.h> 35 #include <errno.h> 36 #include <limits.h> 37 #include <stdarg.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <syslog.h> 41 #include <event.h> 42 43 #include "ifstated.h" 44 45 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 46 static struct file { 47 TAILQ_ENTRY(file) entry; 48 FILE *stream; 49 char *name; 50 int lineno; 51 int errors; 52 } *file, *topfile; 53 struct file *pushfile(const char *, int); 54 int popfile(void); 55 int check_file_secrecy(int, const char *); 56 int yyparse(void); 57 int yylex(void); 58 int yyerror(const char *, ...); 59 int kw_cmp(const void *, const void *); 60 int lookup(char *); 61 int lgetc(int); 62 int lungetc(int); 63 int findeol(void); 64 65 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 66 struct sym { 67 TAILQ_ENTRY(sym) entry; 68 int used; 69 int persist; 70 char *nam; 71 char *val; 72 }; 73 int symset(const char *, const char *, int); 74 char *symget(const char *); 75 76 static struct ifsd_config *conf; 77 char *start_state; 78 79 struct ifsd_action *curaction; 80 struct ifsd_state *curstate = NULL; 81 82 void link_states(struct ifsd_action *); 83 void set_expression_depth(struct ifsd_expression *, int); 84 void init_state(struct ifsd_state *); 85 struct ifsd_ifstate *new_ifstate(u_short, int); 86 struct ifsd_external *new_external(char *, u_int32_t); 87 88 typedef struct { 89 union { 90 int64_t number; 91 char *string; 92 struct in_addr addr; 93 u_short interface; 94 95 struct ifsd_expression *expression; 96 struct ifsd_ifstate *ifstate; 97 struct ifsd_external *external; 98 99 } v; 100 int lineno; 101 } YYSTYPE; 102 103 %} 104 105 %token STATE INITSTATE 106 %token LINK UP DOWN UNKNOWN ADDED REMOVED 107 %token IF RUN SETSTATE EVERY INIT 108 %left AND OR 109 %left UNARY 110 %token ERROR 111 %token <v.string> STRING 112 %token <v.number> NUMBER 113 %type <v.string> string 114 %type <v.interface> interface 115 %type <v.ifstate> if_test 116 %type <v.external> ext_test 117 %type <v.expression> expr term 118 %% 119 120 grammar : /* empty */ 121 | grammar '\n' 122 | grammar conf_main '\n' 123 | grammar varset '\n' 124 | grammar action '\n' 125 | grammar state '\n' 126 | grammar error '\n' { file->errors++; } 127 ; 128 129 string : string STRING { 130 if (asprintf(&$$, "%s %s", $1, $2) == -1) { 131 free($1); 132 free($2); 133 yyerror("string: asprintf"); 134 YYERROR; 135 } 136 free($1); 137 free($2); 138 } 139 | STRING 140 ; 141 142 varset : STRING '=' string { 143 if (conf->opts & IFSD_OPT_VERBOSE) 144 printf("%s = \"%s\"\n", $1, $3); 145 if (symset($1, $3, 0) == -1) { 146 free($1); 147 free($3); 148 yyerror("cannot store variable"); 149 YYERROR; 150 } 151 free($1); 152 free($3); 153 } 154 ; 155 156 conf_main : INITSTATE STRING { 157 start_state = $2; 158 } 159 ; 160 161 interface : STRING { 162 if (($$ = if_nametoindex($1)) == 0) { 163 yyerror("unknown interface %s", $1); 164 free($1); 165 YYERROR; 166 } 167 free($1); 168 } 169 ; 170 171 optnl : '\n' optnl 172 | 173 ; 174 175 nl : '\n' optnl /* one newline or more */ 176 ; 177 178 action : RUN STRING { 179 struct ifsd_action *action; 180 181 if ((action = calloc(1, sizeof(*action))) == NULL) 182 err(1, "action: calloc"); 183 action->type = IFSD_ACTION_COMMAND; 184 action->act.command = $2; 185 if (action->act.command == NULL) 186 err(1, "action: strdup"); 187 TAILQ_INSERT_TAIL(&curaction->act.c.actions, 188 action, entries); 189 } 190 | SETSTATE STRING { 191 struct ifsd_action *action; 192 193 if (curstate == NULL) { 194 free($2); 195 yyerror("set-state must be used inside 'if'"); 196 YYERROR; 197 } 198 if ((action = calloc(1, sizeof(*action))) == NULL) 199 err(1, "action: calloc"); 200 action->type = IFSD_ACTION_CHANGESTATE; 201 action->act.statename = $2; 202 TAILQ_INSERT_TAIL(&curaction->act.c.actions, 203 action, entries); 204 } 205 | IF { 206 struct ifsd_action *action; 207 208 if ((action = calloc(1, sizeof(*action))) == NULL) 209 err(1, "action: calloc"); 210 action->type = IFSD_ACTION_CONDITION; 211 TAILQ_INIT(&action->act.c.actions); 212 TAILQ_INSERT_TAIL(&curaction->act.c.actions, 213 action, entries); 214 action->parent = curaction; 215 curaction = action; 216 } expr action_block { 217 set_expression_depth(curaction->act.c.expression, 0); 218 curaction = curaction->parent; 219 } 220 ; 221 222 action_block : optnl '{' optnl action_l '}' 223 | optnl action 224 ; 225 226 action_l : action_l action nl 227 | action nl 228 ; 229 230 init : INIT { 231 if (curstate != NULL) 232 curaction = curstate->init; 233 else 234 curaction = conf->always.init; 235 } action_block { 236 if (curstate != NULL) 237 curaction = curstate->always; 238 else 239 curaction = conf->always.always; 240 } 241 ; 242 243 if_test : interface '.' LINK '.' UP { 244 $$ = new_ifstate($1, IFSD_LINKUP); 245 } 246 | interface '.' LINK '.' DOWN { 247 $$ = new_ifstate($1, IFSD_LINKDOWN); 248 } 249 | interface '.' LINK '.' UNKNOWN { 250 $$ = new_ifstate($1, IFSD_LINKUNKNOWN); 251 } 252 ; 253 254 ext_test : STRING EVERY NUMBER { 255 if ($3 <= 0 || $3 > UINT_MAX) { 256 yyerror("invalid interval: %d", $3); 257 free($1); 258 YYERROR; 259 } 260 $$ = new_external($1, $3); 261 free($1); 262 } 263 ; 264 265 term : if_test { 266 if (($$ = calloc(1, sizeof(*$$))) == NULL) 267 errx(1, "term: calloc"); 268 curaction->act.c.expression = $$; 269 $$->type = IFSD_OPER_IFSTATE; 270 $$->u.ifstate = $1; 271 TAILQ_INSERT_TAIL(&$1->expressions, $$, entries); 272 } 273 | ext_test { 274 if (($$ = calloc(1, sizeof(*$$))) == NULL) 275 errx(1, "term: calloc"); 276 curaction->act.c.expression = $$; 277 $$->type = IFSD_OPER_EXTERNAL; 278 $$->u.external = $1; 279 TAILQ_INSERT_TAIL(&$1->expressions, $$, entries); 280 } 281 | '(' expr ')' { 282 $$ = $2; 283 } 284 ; 285 286 expr : '!' expr %prec UNARY { 287 if (($$ = calloc(1, sizeof(*$$))) == NULL) 288 errx(1, "expr: calloc"); 289 curaction->act.c.expression = $$; 290 $$->type = IFSD_OPER_NOT; 291 $2->parent = $$; 292 $$->right = $2; 293 } 294 | expr AND expr { 295 if (($$ = calloc(1, sizeof(*$$))) == NULL) 296 errx(1, "expr: calloc"); 297 curaction->act.c.expression = $$; 298 $$->type = IFSD_OPER_AND; 299 $1->parent = $$; 300 $3->parent = $$; 301 $$->left = $1; 302 $$->right = $3; 303 } 304 | expr OR expr { 305 if (($$ = calloc(1, sizeof(*$$))) == NULL) 306 errx(1, "expr: calloc"); 307 curaction->act.c.expression = $$; 308 $$->type = IFSD_OPER_OR; 309 $1->parent = $$; 310 $3->parent = $$; 311 $$->left = $1; 312 $$->right = $3; 313 } 314 | term 315 ; 316 317 state : STATE string { 318 struct ifsd_state *state = NULL; 319 320 TAILQ_FOREACH(state, &conf->states, entries) 321 if (!strcmp(state->name, $2)) { 322 yyerror("state %s already exists", $2); 323 free($2); 324 YYERROR; 325 } 326 if ((state = calloc(1, sizeof(*curstate))) == NULL) 327 errx(1, "state: calloc"); 328 init_state(state); 329 state->name = $2; 330 curstate = state; 331 curaction = state->always; 332 } optnl '{' optnl stateopts_l '}' { 333 TAILQ_INSERT_TAIL(&conf->states, curstate, entries); 334 curstate = NULL; 335 curaction = conf->always.always; 336 } 337 ; 338 339 stateopts_l : stateopts_l stateoptsl 340 | stateoptsl 341 ; 342 343 stateoptsl : init nl 344 | action nl 345 ; 346 347 %% 348 349 struct keywords { 350 const char *k_name; 351 int k_val; 352 }; 353 354 int 355 yyerror(const char *fmt, ...) 356 { 357 va_list ap; 358 359 file->errors++; 360 va_start(ap, fmt); 361 fprintf(stderr, "%s:%d: ", file->name, yylval.lineno); 362 vfprintf(stderr, fmt, ap); 363 fprintf(stderr, "\n"); 364 va_end(ap); 365 return (0); 366 } 367 368 int 369 kw_cmp(const void *k, const void *e) 370 { 371 return (strcmp(k, ((const struct keywords *)e)->k_name)); 372 } 373 374 int 375 lookup(char *s) 376 { 377 /* this has to be sorted always */ 378 static const struct keywords keywords[] = { 379 { "&&", AND}, 380 { "added", ADDED}, 381 { "down", DOWN}, 382 { "every", EVERY}, 383 { "if", IF}, 384 { "init", INIT}, 385 { "init-state", INITSTATE}, 386 { "link", LINK}, 387 { "removed", REMOVED}, 388 { "run", RUN}, 389 { "set-state", SETSTATE}, 390 { "state", STATE}, 391 { "unknown", UNKNOWN}, 392 { "up", UP}, 393 { "||", OR} 394 }; 395 const struct keywords *p; 396 397 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 398 sizeof(keywords[0]), kw_cmp); 399 400 if (p) 401 return (p->k_val); 402 else 403 return (STRING); 404 } 405 406 #define MAXPUSHBACK 128 407 408 char *parsebuf; 409 int parseindex; 410 char pushback_buffer[MAXPUSHBACK]; 411 int pushback_index = 0; 412 413 int 414 lgetc(int quotec) 415 { 416 int c, next; 417 418 if (parsebuf) { 419 /* Read character from the parsebuffer instead of input. */ 420 if (parseindex >= 0) { 421 c = parsebuf[parseindex++]; 422 if (c != '\0') 423 return (c); 424 parsebuf = NULL; 425 } else 426 parseindex++; 427 } 428 429 if (pushback_index) 430 return (pushback_buffer[--pushback_index]); 431 432 if (quotec) { 433 if ((c = getc(file->stream)) == EOF) { 434 yyerror("reached end of file while parsing " 435 "quoted string"); 436 if (file == topfile || popfile() == EOF) 437 return (EOF); 438 return (quotec); 439 } 440 return (c); 441 } 442 443 while ((c = getc(file->stream)) == '\\') { 444 next = getc(file->stream); 445 if (next != '\n') { 446 c = next; 447 break; 448 } 449 yylval.lineno = file->lineno; 450 file->lineno++; 451 } 452 453 while (c == EOF) { 454 if (file == topfile || popfile() == EOF) 455 return (EOF); 456 c = getc(file->stream); 457 } 458 return (c); 459 } 460 461 int 462 lungetc(int c) 463 { 464 if (c == EOF) 465 return (EOF); 466 if (parsebuf) { 467 parseindex--; 468 if (parseindex >= 0) 469 return (c); 470 } 471 if (pushback_index < MAXPUSHBACK-1) 472 return (pushback_buffer[pushback_index++] = c); 473 else 474 return (EOF); 475 } 476 477 int 478 findeol(void) 479 { 480 int c; 481 482 parsebuf = NULL; 483 484 /* skip to either EOF or the first real EOL */ 485 while (1) { 486 if (pushback_index) 487 c = pushback_buffer[--pushback_index]; 488 else 489 c = lgetc(0); 490 if (c == '\n') { 491 file->lineno++; 492 break; 493 } 494 if (c == EOF) 495 break; 496 } 497 return (ERROR); 498 } 499 500 int 501 yylex(void) 502 { 503 char buf[8096]; 504 char *p, *val; 505 int quotec, next, c; 506 int token; 507 508 top: 509 p = buf; 510 while ((c = lgetc(0)) == ' ' || c == '\t') 511 ; /* nothing */ 512 513 yylval.lineno = file->lineno; 514 if (c == '#') 515 while ((c = lgetc(0)) != '\n' && c != EOF) 516 ; /* nothing */ 517 if (c == '$' && parsebuf == NULL) { 518 while (1) { 519 if ((c = lgetc(0)) == EOF) 520 return (0); 521 522 if (p + 1 >= buf + sizeof(buf) - 1) { 523 yyerror("string too long"); 524 return (findeol()); 525 } 526 if (isalnum(c) || c == '_') { 527 *p++ = (char)c; 528 continue; 529 } 530 *p = '\0'; 531 lungetc(c); 532 break; 533 } 534 val = symget(buf); 535 if (val == NULL) { 536 yyerror("macro '%s' not defined", buf); 537 return (findeol()); 538 } 539 parsebuf = val; 540 parseindex = 0; 541 goto top; 542 } 543 544 switch (c) { 545 case '\'': 546 case '"': 547 quotec = c; 548 while (1) { 549 if ((c = lgetc(quotec)) == EOF) 550 return (0); 551 if (c == '\n') { 552 file->lineno++; 553 continue; 554 } else if (c == '\\') { 555 if ((next = lgetc(quotec)) == EOF) 556 return (0); 557 if (next == quotec || c == ' ' || c == '\t') 558 c = next; 559 else if (next == '\n') 560 continue; 561 else 562 lungetc(next); 563 } else if (c == quotec) { 564 *p = '\0'; 565 break; 566 } 567 if (p + 1 >= buf + sizeof(buf) - 1) { 568 yyerror("string too long"); 569 return (findeol()); 570 } 571 *p++ = (char)c; 572 } 573 yylval.v.string = strdup(buf); 574 if (yylval.v.string == NULL) 575 err(1, "yylex: strdup"); 576 return (STRING); 577 } 578 579 #define allowed_to_end_number(x) \ 580 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 581 582 if (c == '-' || isdigit(c)) { 583 do { 584 *p++ = c; 585 if ((unsigned)(p-buf) >= sizeof(buf)) { 586 yyerror("string too long"); 587 return (findeol()); 588 } 589 } while ((c = lgetc(0)) != EOF && isdigit(c)); 590 lungetc(c); 591 if (p == buf + 1 && buf[0] == '-') 592 goto nodigits; 593 if (c == EOF || allowed_to_end_number(c)) { 594 const char *errstr = NULL; 595 596 *p = '\0'; 597 yylval.v.number = strtonum(buf, LLONG_MIN, 598 LLONG_MAX, &errstr); 599 if (errstr) { 600 yyerror("\"%s\" invalid number: %s", 601 buf, errstr); 602 return (findeol()); 603 } 604 return (NUMBER); 605 } else { 606 nodigits: 607 while (p > buf + 1) 608 lungetc(*--p); 609 c = *--p; 610 if (c == '-') 611 return (c); 612 } 613 } 614 615 #define allowed_in_string(x) \ 616 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 617 x != '{' && x != '}' && \ 618 x != '!' && x != '=' && x != '#' && \ 619 x != ',' && x != '.')) 620 621 if (isalnum(c) || c == ':' || c == '_' || c == '&' || c == '|') { 622 do { 623 *p++ = c; 624 if ((unsigned)(p-buf) >= sizeof(buf)) { 625 yyerror("string too long"); 626 return (findeol()); 627 } 628 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 629 lungetc(c); 630 *p = '\0'; 631 if ((token = lookup(buf)) == STRING) 632 if ((yylval.v.string = strdup(buf)) == NULL) 633 err(1, "yylex: strdup"); 634 return (token); 635 } 636 if (c == '\n') { 637 yylval.lineno = file->lineno; 638 file->lineno++; 639 } 640 if (c == EOF) 641 return (0); 642 return (c); 643 } 644 645 int 646 check_file_secrecy(int fd, const char *fname) 647 { 648 struct stat st; 649 650 if (fstat(fd, &st)) { 651 warn("cannot stat %s", fname); 652 return (-1); 653 } 654 if (st.st_uid != 0 && st.st_uid != getuid()) { 655 warnx("%s: owner not root or current user", fname); 656 return (-1); 657 } 658 if (st.st_mode & (S_IRWXG | S_IRWXO)) { 659 warnx("%s: group/world readable/writeable", fname); 660 return (-1); 661 } 662 return (0); 663 } 664 665 struct file * 666 pushfile(const char *name, int secret) 667 { 668 struct file *nfile; 669 670 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 671 warn("malloc"); 672 return (NULL); 673 } 674 if ((nfile->name = strdup(name)) == NULL) { 675 warn("malloc"); 676 free(nfile); 677 return (NULL); 678 } 679 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 680 warnx("%s", nfile->name); 681 free(nfile->name); 682 free(nfile); 683 return (NULL); 684 } else if (secret && 685 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 686 fclose(nfile->stream); 687 free(nfile->name); 688 free(nfile); 689 return (NULL); 690 } 691 nfile->lineno = 1; 692 TAILQ_INSERT_TAIL(&files, nfile, entry); 693 return (nfile); 694 } 695 696 int 697 popfile(void) 698 { 699 struct file *prev; 700 701 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 702 prev->errors += file->errors; 703 704 TAILQ_REMOVE(&files, file, entry); 705 fclose(file->stream); 706 free(file->name); 707 free(file); 708 file = prev; 709 return (file ? 0 : EOF); 710 } 711 712 struct ifsd_config * 713 parse_config(char *filename, int opts) 714 { 715 int errors = 0; 716 struct sym *sym, *next; 717 struct ifsd_state *state; 718 719 if ((conf = calloc(1, sizeof(struct ifsd_config))) == NULL) { 720 errx(1, "parse_config calloc"); 721 return (NULL); 722 } 723 724 if ((file = pushfile(filename, 0)) == NULL) { 725 free(conf); 726 return (NULL); 727 } 728 topfile = file; 729 730 TAILQ_INIT(&conf->states); 731 732 init_state(&conf->always); 733 curaction = conf->always.always; 734 conf->opts = opts; 735 736 yyparse(); 737 738 /* Link states */ 739 TAILQ_FOREACH(state, &conf->states, entries) { 740 link_states(state->init); 741 link_states(state->always); 742 } 743 744 errors = file->errors; 745 popfile(); 746 747 if (start_state != NULL) { 748 TAILQ_FOREACH(state, &conf->states, entries) { 749 if (strcmp(start_state, state->name) == 0) { 750 conf->curstate = state; 751 break; 752 } 753 } 754 if (conf->curstate == NULL) 755 errx(1, "invalid start state %s", start_state); 756 } else { 757 conf->curstate = TAILQ_FIRST(&conf->states); 758 } 759 760 /* Free macros and check which have not been used. */ 761 for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { 762 next = TAILQ_NEXT(sym, entry); 763 if ((conf->opts & IFSD_OPT_VERBOSE2) && !sym->used) 764 fprintf(stderr, "warning: macro '%s' not " 765 "used\n", sym->nam); 766 if (!sym->persist) { 767 free(sym->nam); 768 free(sym->val); 769 TAILQ_REMOVE(&symhead, sym, entry); 770 free(sym); 771 } 772 } 773 774 if (errors) { 775 clear_config(conf); 776 errors = 0; 777 return (NULL); 778 } 779 780 return (conf); 781 } 782 783 void 784 link_states(struct ifsd_action *action) 785 { 786 struct ifsd_action *subaction; 787 788 switch (action->type) { 789 default: 790 case IFSD_ACTION_COMMAND: 791 break; 792 case IFSD_ACTION_CHANGESTATE: { 793 struct ifsd_state *state; 794 795 TAILQ_FOREACH(state, &conf->states, entries) { 796 if (strcmp(action->act.statename, 797 state->name) == 0) { 798 action->act.nextstate = state; 799 break; 800 } 801 } 802 if (state == NULL) { 803 fprintf(stderr, "error: state '%s' not declared\n", 804 action->act.statename); 805 file->errors++; 806 } 807 break; 808 } 809 case IFSD_ACTION_CONDITION: 810 TAILQ_FOREACH(subaction, &action->act.c.actions, entries) 811 link_states(subaction); 812 break; 813 } 814 } 815 816 int 817 symset(const char *nam, const char *val, int persist) 818 { 819 struct sym *sym; 820 821 for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); 822 sym = TAILQ_NEXT(sym, entry)) 823 ; /* nothing */ 824 825 if (sym != NULL) { 826 if (sym->persist == 1) 827 return (0); 828 else { 829 free(sym->nam); 830 free(sym->val); 831 TAILQ_REMOVE(&symhead, sym, entry); 832 free(sym); 833 } 834 } 835 if ((sym = calloc(1, sizeof(*sym))) == NULL) 836 return (-1); 837 838 sym->nam = strdup(nam); 839 if (sym->nam == NULL) { 840 free(sym); 841 return (-1); 842 } 843 sym->val = strdup(val); 844 if (sym->val == NULL) { 845 free(sym->nam); 846 free(sym); 847 return (-1); 848 } 849 sym->used = 0; 850 sym->persist = persist; 851 TAILQ_INSERT_TAIL(&symhead, sym, entry); 852 return (0); 853 } 854 855 int 856 cmdline_symset(char *s) 857 { 858 char *sym, *val; 859 int ret; 860 size_t len; 861 862 if ((val = strrchr(s, '=')) == NULL) 863 return (-1); 864 865 len = strlen(s) - strlen(val) + 1; 866 if ((sym = malloc(len)) == NULL) 867 errx(1, "cmdline_symset: malloc"); 868 869 strlcpy(sym, s, len); 870 871 ret = symset(sym, val + 1, 1); 872 free(sym); 873 874 return (ret); 875 } 876 877 char * 878 symget(const char *nam) 879 { 880 struct sym *sym; 881 882 TAILQ_FOREACH(sym, &symhead, entry) 883 if (strcmp(nam, sym->nam) == 0) { 884 sym->used = 1; 885 return (sym->val); 886 } 887 return (NULL); 888 } 889 890 void 891 set_expression_depth(struct ifsd_expression *expression, int depth) 892 { 893 expression->depth = depth; 894 if (conf->maxdepth < depth) 895 conf->maxdepth = depth; 896 if (expression->left != NULL) 897 set_expression_depth(expression->left, depth + 1); 898 if (expression->right != NULL) 899 set_expression_depth(expression->right, depth + 1); 900 } 901 902 void 903 init_state(struct ifsd_state *state) 904 { 905 TAILQ_INIT(&state->interface_states); 906 TAILQ_INIT(&state->external_tests); 907 908 if ((state->init = calloc(1, sizeof(*state->init))) == NULL) 909 err(1, "init_state: calloc"); 910 state->init->type = IFSD_ACTION_CONDITION; 911 TAILQ_INIT(&state->init->act.c.actions); 912 913 if ((state->always = calloc(1, sizeof(*state->always))) == NULL) 914 err(1, "init_state: calloc"); 915 state->always->type = IFSD_ACTION_CONDITION; 916 TAILQ_INIT(&state->always->act.c.actions); 917 } 918 919 struct ifsd_ifstate * 920 new_ifstate(u_short ifindex, int s) 921 { 922 struct ifsd_ifstate *ifstate = NULL; 923 struct ifsd_state *state; 924 925 if (curstate != NULL) 926 state = curstate; 927 else 928 state = &conf->always; 929 930 TAILQ_FOREACH(ifstate, &state->interface_states, entries) 931 if (ifstate->ifindex == ifindex && ifstate->ifstate == s) 932 break; 933 if (ifstate == NULL) { 934 if ((ifstate = calloc(1, sizeof(*ifstate))) == NULL) 935 errx(1, "new_ifstate: calloc"); 936 ifstate->ifindex = ifindex; 937 ifstate->ifstate = s; 938 TAILQ_INIT(&ifstate->expressions); 939 TAILQ_INSERT_TAIL(&state->interface_states, ifstate, entries); 940 } 941 ifstate->prevstate = -1; 942 ifstate->refcount++; 943 return (ifstate); 944 } 945 946 struct ifsd_external * 947 new_external(char *command, u_int32_t frequency) 948 { 949 struct ifsd_external *external = NULL; 950 struct ifsd_state *state; 951 952 if (curstate != NULL) 953 state = curstate; 954 else 955 state = &conf->always; 956 957 TAILQ_FOREACH(external, &state->external_tests, entries) 958 if (strcmp(external->command, command) == 0 && 959 external->frequency == frequency) 960 break; 961 if (external == NULL) { 962 if ((external = calloc(1, sizeof(*external))) == NULL) 963 errx(1, "new_external: calloc"); 964 if ((external->command = strdup(command)) == NULL) 965 errx(1, "new_external: strdup"); 966 external->frequency = frequency; 967 TAILQ_INIT(&external->expressions); 968 TAILQ_INSERT_TAIL(&state->external_tests, external, entries); 969 } 970 external->prevstatus = -1; 971 external->refcount++; 972 return (external); 973 } 974