1 /* $OpenBSD: parse.y,v 1.30 2010/08/03 18:42:40 henning 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 file->lineno++; 561 continue; 562 } else 563 lungetc(next); 564 } else if (c == quotec) { 565 *p = '\0'; 566 break; 567 } 568 if (p + 1 >= buf + sizeof(buf) - 1) { 569 yyerror("string too long"); 570 return (findeol()); 571 } 572 *p++ = (char)c; 573 } 574 yylval.v.string = strdup(buf); 575 if (yylval.v.string == NULL) 576 err(1, "yylex: strdup"); 577 return (STRING); 578 } 579 580 #define allowed_to_end_number(x) \ 581 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 582 583 if (c == '-' || isdigit(c)) { 584 do { 585 *p++ = c; 586 if ((unsigned)(p-buf) >= sizeof(buf)) { 587 yyerror("string too long"); 588 return (findeol()); 589 } 590 } while ((c = lgetc(0)) != EOF && isdigit(c)); 591 lungetc(c); 592 if (p == buf + 1 && buf[0] == '-') 593 goto nodigits; 594 if (c == EOF || allowed_to_end_number(c)) { 595 const char *errstr = NULL; 596 597 *p = '\0'; 598 yylval.v.number = strtonum(buf, LLONG_MIN, 599 LLONG_MAX, &errstr); 600 if (errstr) { 601 yyerror("\"%s\" invalid number: %s", 602 buf, errstr); 603 return (findeol()); 604 } 605 return (NUMBER); 606 } else { 607 nodigits: 608 while (p > buf + 1) 609 lungetc(*--p); 610 c = *--p; 611 if (c == '-') 612 return (c); 613 } 614 } 615 616 #define allowed_in_string(x) \ 617 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 618 x != '{' && x != '}' && \ 619 x != '!' && x != '=' && x != '#' && \ 620 x != ',' && x != '.')) 621 622 if (isalnum(c) || c == ':' || c == '_' || c == '&' || c == '|') { 623 do { 624 *p++ = c; 625 if ((unsigned)(p-buf) >= sizeof(buf)) { 626 yyerror("string too long"); 627 return (findeol()); 628 } 629 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 630 lungetc(c); 631 *p = '\0'; 632 if ((token = lookup(buf)) == STRING) 633 if ((yylval.v.string = strdup(buf)) == NULL) 634 err(1, "yylex: strdup"); 635 return (token); 636 } 637 if (c == '\n') { 638 yylval.lineno = file->lineno; 639 file->lineno++; 640 } 641 if (c == EOF) 642 return (0); 643 return (c); 644 } 645 646 int 647 check_file_secrecy(int fd, const char *fname) 648 { 649 struct stat st; 650 651 if (fstat(fd, &st)) { 652 warn("cannot stat %s", fname); 653 return (-1); 654 } 655 if (st.st_uid != 0 && st.st_uid != getuid()) { 656 warnx("%s: owner not root or current user", fname); 657 return (-1); 658 } 659 if (st.st_mode & (S_IRWXG | S_IRWXO)) { 660 warnx("%s: group/world readable/writeable", fname); 661 return (-1); 662 } 663 return (0); 664 } 665 666 struct file * 667 pushfile(const char *name, int secret) 668 { 669 struct file *nfile; 670 671 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 672 warn("malloc"); 673 return (NULL); 674 } 675 if ((nfile->name = strdup(name)) == NULL) { 676 warn("malloc"); 677 free(nfile); 678 return (NULL); 679 } 680 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 681 warnx("%s", nfile->name); 682 free(nfile->name); 683 free(nfile); 684 return (NULL); 685 } else if (secret && 686 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 687 fclose(nfile->stream); 688 free(nfile->name); 689 free(nfile); 690 return (NULL); 691 } 692 nfile->lineno = 1; 693 TAILQ_INSERT_TAIL(&files, nfile, entry); 694 return (nfile); 695 } 696 697 int 698 popfile(void) 699 { 700 struct file *prev; 701 702 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 703 prev->errors += file->errors; 704 705 TAILQ_REMOVE(&files, file, entry); 706 fclose(file->stream); 707 free(file->name); 708 free(file); 709 file = prev; 710 return (file ? 0 : EOF); 711 } 712 713 struct ifsd_config * 714 parse_config(char *filename, int opts) 715 { 716 int errors = 0; 717 struct sym *sym, *next; 718 struct ifsd_state *state; 719 720 if ((conf = calloc(1, sizeof(struct ifsd_config))) == NULL) { 721 errx(1, "parse_config calloc"); 722 return (NULL); 723 } 724 725 if ((file = pushfile(filename, 0)) == NULL) { 726 free(conf); 727 return (NULL); 728 } 729 topfile = file; 730 731 TAILQ_INIT(&conf->states); 732 733 init_state(&conf->always); 734 curaction = conf->always.always; 735 conf->opts = opts; 736 737 yyparse(); 738 739 /* Link states */ 740 TAILQ_FOREACH(state, &conf->states, entries) { 741 link_states(state->init); 742 link_states(state->always); 743 } 744 745 errors = file->errors; 746 popfile(); 747 748 if (start_state != NULL) { 749 TAILQ_FOREACH(state, &conf->states, entries) { 750 if (strcmp(start_state, state->name) == 0) { 751 conf->curstate = state; 752 break; 753 } 754 } 755 if (conf->curstate == NULL) 756 errx(1, "invalid start state %s", start_state); 757 } else { 758 conf->curstate = TAILQ_FIRST(&conf->states); 759 } 760 761 /* Free macros and check which have not been used. */ 762 for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { 763 next = TAILQ_NEXT(sym, entry); 764 if ((conf->opts & IFSD_OPT_VERBOSE2) && !sym->used) 765 fprintf(stderr, "warning: macro '%s' not " 766 "used\n", sym->nam); 767 if (!sym->persist) { 768 free(sym->nam); 769 free(sym->val); 770 TAILQ_REMOVE(&symhead, sym, entry); 771 free(sym); 772 } 773 } 774 775 if (errors) { 776 clear_config(conf); 777 errors = 0; 778 return (NULL); 779 } 780 781 return (conf); 782 } 783 784 void 785 link_states(struct ifsd_action *action) 786 { 787 struct ifsd_action *subaction; 788 789 switch (action->type) { 790 default: 791 case IFSD_ACTION_COMMAND: 792 break; 793 case IFSD_ACTION_CHANGESTATE: { 794 struct ifsd_state *state; 795 796 TAILQ_FOREACH(state, &conf->states, entries) { 797 if (strcmp(action->act.statename, 798 state->name) == 0) { 799 action->act.nextstate = state; 800 break; 801 } 802 } 803 if (state == NULL) { 804 fprintf(stderr, "error: state '%s' not declared\n", 805 action->act.statename); 806 file->errors++; 807 } 808 break; 809 } 810 case IFSD_ACTION_CONDITION: 811 TAILQ_FOREACH(subaction, &action->act.c.actions, entries) 812 link_states(subaction); 813 break; 814 } 815 } 816 817 int 818 symset(const char *nam, const char *val, int persist) 819 { 820 struct sym *sym; 821 822 for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); 823 sym = TAILQ_NEXT(sym, entry)) 824 ; /* nothing */ 825 826 if (sym != NULL) { 827 if (sym->persist == 1) 828 return (0); 829 else { 830 free(sym->nam); 831 free(sym->val); 832 TAILQ_REMOVE(&symhead, sym, entry); 833 free(sym); 834 } 835 } 836 if ((sym = calloc(1, sizeof(*sym))) == NULL) 837 return (-1); 838 839 sym->nam = strdup(nam); 840 if (sym->nam == NULL) { 841 free(sym); 842 return (-1); 843 } 844 sym->val = strdup(val); 845 if (sym->val == NULL) { 846 free(sym->nam); 847 free(sym); 848 return (-1); 849 } 850 sym->used = 0; 851 sym->persist = persist; 852 TAILQ_INSERT_TAIL(&symhead, sym, entry); 853 return (0); 854 } 855 856 int 857 cmdline_symset(char *s) 858 { 859 char *sym, *val; 860 int ret; 861 size_t len; 862 863 if ((val = strrchr(s, '=')) == NULL) 864 return (-1); 865 866 len = strlen(s) - strlen(val) + 1; 867 if ((sym = malloc(len)) == NULL) 868 errx(1, "cmdline_symset: malloc"); 869 870 strlcpy(sym, s, len); 871 872 ret = symset(sym, val + 1, 1); 873 free(sym); 874 875 return (ret); 876 } 877 878 char * 879 symget(const char *nam) 880 { 881 struct sym *sym; 882 883 TAILQ_FOREACH(sym, &symhead, entry) 884 if (strcmp(nam, sym->nam) == 0) { 885 sym->used = 1; 886 return (sym->val); 887 } 888 return (NULL); 889 } 890 891 void 892 set_expression_depth(struct ifsd_expression *expression, int depth) 893 { 894 expression->depth = depth; 895 if (conf->maxdepth < depth) 896 conf->maxdepth = depth; 897 if (expression->left != NULL) 898 set_expression_depth(expression->left, depth + 1); 899 if (expression->right != NULL) 900 set_expression_depth(expression->right, depth + 1); 901 } 902 903 void 904 init_state(struct ifsd_state *state) 905 { 906 TAILQ_INIT(&state->interface_states); 907 TAILQ_INIT(&state->external_tests); 908 909 if ((state->init = calloc(1, sizeof(*state->init))) == NULL) 910 err(1, "init_state: calloc"); 911 state->init->type = IFSD_ACTION_CONDITION; 912 TAILQ_INIT(&state->init->act.c.actions); 913 914 if ((state->always = calloc(1, sizeof(*state->always))) == NULL) 915 err(1, "init_state: calloc"); 916 state->always->type = IFSD_ACTION_CONDITION; 917 TAILQ_INIT(&state->always->act.c.actions); 918 } 919 920 struct ifsd_ifstate * 921 new_ifstate(u_short ifindex, int s) 922 { 923 struct ifsd_ifstate *ifstate = NULL; 924 struct ifsd_state *state; 925 926 if (curstate != NULL) 927 state = curstate; 928 else 929 state = &conf->always; 930 931 TAILQ_FOREACH(ifstate, &state->interface_states, entries) 932 if (ifstate->ifindex == ifindex && ifstate->ifstate == s) 933 break; 934 if (ifstate == NULL) { 935 if ((ifstate = calloc(1, sizeof(*ifstate))) == NULL) 936 errx(1, "new_ifstate: calloc"); 937 ifstate->ifindex = ifindex; 938 ifstate->ifstate = s; 939 TAILQ_INIT(&ifstate->expressions); 940 TAILQ_INSERT_TAIL(&state->interface_states, ifstate, entries); 941 } 942 ifstate->prevstate = -1; 943 ifstate->refcount++; 944 return (ifstate); 945 } 946 947 struct ifsd_external * 948 new_external(char *command, u_int32_t frequency) 949 { 950 struct ifsd_external *external = NULL; 951 struct ifsd_state *state; 952 953 if (curstate != NULL) 954 state = curstate; 955 else 956 state = &conf->always; 957 958 TAILQ_FOREACH(external, &state->external_tests, entries) 959 if (strcmp(external->command, command) == 0 && 960 external->frequency == frequency) 961 break; 962 if (external == NULL) { 963 if ((external = calloc(1, sizeof(*external))) == NULL) 964 errx(1, "new_external: calloc"); 965 if ((external->command = strdup(command)) == NULL) 966 errx(1, "new_external: strdup"); 967 external->frequency = frequency; 968 TAILQ_INIT(&external->expressions); 969 TAILQ_INSERT_TAIL(&state->external_tests, external, entries); 970 } 971 external->prevstatus = -1; 972 external->refcount++; 973 return (external); 974 } 975