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