jcs's openbsd hax
openbsd
at jcs 988 lines 21 kB view raw
1/* $OpenBSD: parse.y,v 1.56 2021/10/15 15:01:28 naddy 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#include "log.h" 45 46TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 47static struct file { 48 TAILQ_ENTRY(file) entry; 49 FILE *stream; 50 char *name; 51 int lineno; 52 int errors; 53} *file, *topfile; 54struct file *pushfile(const char *, int); 55int popfile(void); 56int check_file_secrecy(int, const char *); 57int yyparse(void); 58int yylex(void); 59int yyerror(const char *, ...) 60 __attribute__((__format__ (printf, 1, 2))) 61 __attribute__((__nonnull__ (1))); 62int kw_cmp(const void *, const void *); 63int lookup(char *); 64int lgetc(int); 65int lungetc(int); 66int findeol(void); 67 68TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 69struct sym { 70 TAILQ_ENTRY(sym) entry; 71 int used; 72 int persist; 73 char *nam; 74 char *val; 75}; 76int symset(const char *, const char *, int); 77char *symget(const char *); 78 79static struct ifsd_config *conf; 80char *start_state; 81 82struct ifsd_action *curaction; 83struct ifsd_state *curstate; 84 85void link_states(struct ifsd_action *); 86void set_expression_depth(struct ifsd_expression *, int); 87void init_state(struct ifsd_state *); 88struct ifsd_ifstate *new_ifstate(char *, int); 89struct ifsd_external *new_external(char *, u_int32_t); 90 91typedef struct { 92 union { 93 int64_t number; 94 char *string; 95 struct in_addr addr; 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 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.string> interface 117%type <v.ifstate> if_test 118%type <v.external> ext_test 119%type <v.expression> expr term 120%% 121 122grammar : /* 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 131string : 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 144varset : 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 free($1); 153 free($3); 154 YYERROR; 155 } 156 } 157 if (symset($1, $3, 0) == -1) { 158 free($1); 159 free($3); 160 yyerror("cannot store variable"); 161 YYERROR; 162 } 163 free($1); 164 free($3); 165 } 166 ; 167 168conf_main : INITSTATE STRING { 169 start_state = $2; 170 } 171 ; 172 173interface : STRING { 174 if (if_nametoindex($1) == 0) { 175 yyerror("unknown interface %s", $1); 176 free($1); 177 YYERROR; 178 } 179 $$ = $1; 180 } 181 ; 182 183optnl : '\n' optnl 184 | 185 ; 186 187nl : '\n' optnl /* one newline or more */ 188 ; 189 190action : RUN STRING { 191 struct ifsd_action *action; 192 193 if ((action = calloc(1, sizeof(*action))) == NULL) 194 err(1, "action: calloc"); 195 action->type = IFSD_ACTION_COMMAND; 196 action->act.command = $2; 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 232action_block : optnl '{' optnl action_l '}' 233 | optnl action 234 ; 235 236action_l : action_l action nl 237 | action nl 238 ; 239 240init : INIT { 241 if (curstate != NULL) 242 curaction = curstate->init; 243 else 244 curaction = conf->initstate.init; 245 } action_block { 246 if (curstate != NULL) 247 curaction = curstate->body; 248 else 249 curaction = conf->initstate.body; 250 } 251 ; 252 253if_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 264ext_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 275term : 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 296expr : '!' 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 327state : 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->body; 342 } optnl '{' optnl stateopts_l '}' { 343 TAILQ_INSERT_TAIL(&conf->states, curstate, entries); 344 curstate = NULL; 345 curaction = conf->initstate.body; 346 } 347 ; 348 349stateopts_l : stateopts_l stateoptsl 350 | stateoptsl 351 ; 352 353stateoptsl : init nl 354 | action nl 355 ; 356 357%% 358 359struct keywords { 360 const char *k_name; 361 int k_val; 362}; 363 364int 365yyerror(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 380int 381kw_cmp(const void *k, const void *e) 382{ 383 return (strcmp(k, ((const struct keywords *)e)->k_name)); 384} 385 386int 387lookup(char *s) 388{ 389 /* this has to be sorted always */ 390 static const struct keywords keywords[] = { 391 { "&&", AND}, 392 { "down", DOWN}, 393 { "every", EVERY}, 394 { "if", IF}, 395 { "init", INIT}, 396 { "init-state", INITSTATE}, 397 { "link", LINK}, 398 { "run", RUN}, 399 { "set-state", SETSTATE}, 400 { "state", STATE}, 401 { "unknown", UNKNOWN}, 402 { "up", UP}, 403 { "||", OR} 404 }; 405 const struct keywords *p; 406 407 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 408 sizeof(keywords[0]), kw_cmp); 409 410 if (p) 411 return (p->k_val); 412 else 413 return (STRING); 414} 415 416#define MAXPUSHBACK 128 417 418char *parsebuf; 419int parseindex; 420char pushback_buffer[MAXPUSHBACK]; 421int pushback_index = 0; 422 423int 424lgetc(int quotec) 425{ 426 int c, next; 427 428 if (parsebuf) { 429 /* Read character from the parsebuffer instead of input. */ 430 if (parseindex >= 0) { 431 c = (unsigned char)parsebuf[parseindex++]; 432 if (c != '\0') 433 return (c); 434 parsebuf = NULL; 435 } else 436 parseindex++; 437 } 438 439 if (pushback_index) 440 return ((unsigned char)pushback_buffer[--pushback_index]); 441 442 if (quotec) { 443 if ((c = getc(file->stream)) == EOF) { 444 yyerror("reached end of file while parsing " 445 "quoted string"); 446 if (file == topfile || popfile() == EOF) 447 return (EOF); 448 return (quotec); 449 } 450 return (c); 451 } 452 453 while ((c = getc(file->stream)) == '\\') { 454 next = getc(file->stream); 455 if (next != '\n') { 456 c = next; 457 break; 458 } 459 yylval.lineno = file->lineno; 460 file->lineno++; 461 } 462 463 while (c == EOF) { 464 if (file == topfile || popfile() == EOF) 465 return (EOF); 466 c = getc(file->stream); 467 } 468 return (c); 469} 470 471int 472lungetc(int c) 473{ 474 if (c == EOF) 475 return (EOF); 476 if (parsebuf) { 477 parseindex--; 478 if (parseindex >= 0) 479 return (c); 480 } 481 if (pushback_index + 1 >= MAXPUSHBACK) 482 return (EOF); 483 pushback_buffer[pushback_index++] = c; 484 return (c); 485} 486 487int 488findeol(void) 489{ 490 int c; 491 492 parsebuf = NULL; 493 494 /* skip to either EOF or the first real EOL */ 495 while (1) { 496 if (pushback_index) 497 c = (unsigned char)pushback_buffer[--pushback_index]; 498 else 499 c = lgetc(0); 500 if (c == '\n') { 501 file->lineno++; 502 break; 503 } 504 if (c == EOF) 505 break; 506 } 507 return (ERROR); 508} 509 510int 511yylex(void) 512{ 513 char buf[8096]; 514 char *p, *val; 515 int quotec, next, c; 516 int token; 517 518top: 519 p = buf; 520 while ((c = lgetc(0)) == ' ' || c == '\t') 521 ; /* nothing */ 522 523 yylval.lineno = file->lineno; 524 if (c == '#') 525 while ((c = lgetc(0)) != '\n' && c != EOF) 526 ; /* nothing */ 527 if (c == '$' && parsebuf == NULL) { 528 while (1) { 529 if ((c = lgetc(0)) == EOF) 530 return (0); 531 532 if (p + 1 >= buf + sizeof(buf) - 1) { 533 yyerror("string too long"); 534 return (findeol()); 535 } 536 if (isalnum(c) || c == '_') { 537 *p++ = c; 538 continue; 539 } 540 *p = '\0'; 541 lungetc(c); 542 break; 543 } 544 val = symget(buf); 545 if (val == NULL) { 546 yyerror("macro '%s' not defined", buf); 547 return (findeol()); 548 } 549 parsebuf = val; 550 parseindex = 0; 551 goto top; 552 } 553 554 switch (c) { 555 case '\'': 556 case '"': 557 quotec = c; 558 while (1) { 559 if ((c = lgetc(quotec)) == EOF) 560 return (0); 561 if (c == '\n') { 562 file->lineno++; 563 continue; 564 } else if (c == '\\') { 565 if ((next = lgetc(quotec)) == EOF) 566 return (0); 567 if (next == quotec || next == ' ' || 568 next == '\t') 569 c = next; 570 else if (next == '\n') { 571 file->lineno++; 572 continue; 573 } else 574 lungetc(next); 575 } else if (c == quotec) { 576 *p = '\0'; 577 break; 578 } else if (c == '\0') { 579 yyerror("syntax error"); 580 return (findeol()); 581 } 582 if (p + 1 >= buf + sizeof(buf) - 1) { 583 yyerror("string too long"); 584 return (findeol()); 585 } 586 *p++ = c; 587 } 588 yylval.v.string = strdup(buf); 589 if (yylval.v.string == NULL) 590 err(1, "%s", __func__); 591 return (STRING); 592 } 593 594#define allowed_to_end_number(x) \ 595 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 596 597 if (c == '-' || isdigit(c)) { 598 do { 599 *p++ = c; 600 if ((size_t)(p-buf) >= sizeof(buf)) { 601 yyerror("string too long"); 602 return (findeol()); 603 } 604 } while ((c = lgetc(0)) != EOF && isdigit(c)); 605 lungetc(c); 606 if (p == buf + 1 && buf[0] == '-') 607 goto nodigits; 608 if (c == EOF || allowed_to_end_number(c)) { 609 const char *errstr = NULL; 610 611 *p = '\0'; 612 yylval.v.number = strtonum(buf, LLONG_MIN, 613 LLONG_MAX, &errstr); 614 if (errstr) { 615 yyerror("\"%s\" invalid number: %s", 616 buf, errstr); 617 return (findeol()); 618 } 619 return (NUMBER); 620 } else { 621nodigits: 622 while (p > buf + 1) 623 lungetc((unsigned char)*--p); 624 c = (unsigned char)*--p; 625 if (c == '-') 626 return (c); 627 } 628 } 629 630#define allowed_in_string(x) \ 631 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 632 x != '{' && x != '}' && \ 633 x != '!' && x != '=' && x != '#' && \ 634 x != ',' && x != '.')) 635 636 if (isalnum(c) || c == ':' || c == '_' || c == '&' || c == '|') { 637 do { 638 *p++ = c; 639 if ((size_t)(p-buf) >= sizeof(buf)) { 640 yyerror("string too long"); 641 return (findeol()); 642 } 643 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 644 lungetc(c); 645 *p = '\0'; 646 if ((token = lookup(buf)) == STRING) 647 if ((yylval.v.string = strdup(buf)) == NULL) 648 err(1, "%s", __func__); 649 return (token); 650 } 651 if (c == '\n') { 652 yylval.lineno = file->lineno; 653 file->lineno++; 654 } 655 if (c == EOF) 656 return (0); 657 return (c); 658} 659 660int 661check_file_secrecy(int fd, const char *fname) 662{ 663 struct stat st; 664 665 if (fstat(fd, &st)) { 666 warn("cannot stat %s", fname); 667 return (-1); 668 } 669 if (st.st_uid != 0 && st.st_uid != getuid()) { 670 warnx("%s: owner not root or current user", fname); 671 return (-1); 672 } 673 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 674 warnx("%s: group writable or world read/writable", fname); 675 return (-1); 676 } 677 return (0); 678} 679 680struct file * 681pushfile(const char *name, int secret) 682{ 683 struct file *nfile; 684 685 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 686 warn("%s", __func__); 687 return (NULL); 688 } 689 if ((nfile->name = strdup(name)) == NULL) { 690 warn("%s", __func__); 691 free(nfile); 692 return (NULL); 693 } 694 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 695 warn("%s: %s", __func__, nfile->name); 696 free(nfile->name); 697 free(nfile); 698 return (NULL); 699 } else if (secret && 700 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 701 fclose(nfile->stream); 702 free(nfile->name); 703 free(nfile); 704 return (NULL); 705 } 706 nfile->lineno = 1; 707 TAILQ_INSERT_TAIL(&files, nfile, entry); 708 return (nfile); 709} 710 711int 712popfile(void) 713{ 714 struct file *prev; 715 716 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 717 prev->errors += file->errors; 718 719 TAILQ_REMOVE(&files, file, entry); 720 fclose(file->stream); 721 free(file->name); 722 free(file); 723 file = prev; 724 return (file ? 0 : EOF); 725} 726 727struct ifsd_config * 728parse_config(char *filename, int opts) 729{ 730 int errors = 0; 731 struct sym *sym, *next; 732 struct ifsd_state *state; 733 734 if ((conf = calloc(1, sizeof(struct ifsd_config))) == NULL) { 735 err(1, "%s", __func__); 736 return (NULL); 737 } 738 739 if ((file = pushfile(filename, 0)) == NULL) { 740 free(conf); 741 return (NULL); 742 } 743 topfile = file; 744 745 TAILQ_INIT(&conf->states); 746 747 init_state(&conf->initstate); 748 curaction = conf->initstate.body; 749 conf->opts = opts; 750 751 yyparse(); 752 753 /* Link states */ 754 TAILQ_FOREACH(state, &conf->states, entries) { 755 link_states(state->init); 756 link_states(state->body); 757 } 758 759 errors = file->errors; 760 popfile(); 761 762 if (start_state != NULL) { 763 TAILQ_FOREACH(state, &conf->states, entries) { 764 if (strcmp(start_state, state->name) == 0) { 765 conf->curstate = state; 766 break; 767 } 768 } 769 if (conf->curstate == NULL) 770 errx(1, "invalid start state %s", start_state); 771 } else { 772 conf->curstate = TAILQ_FIRST(&conf->states); 773 } 774 775 /* Free macros and check which have not been used. */ 776 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 777 if ((conf->opts & IFSD_OPT_VERBOSE2) && !sym->used) 778 fprintf(stderr, "warning: macro '%s' not " 779 "used\n", sym->nam); 780 if (!sym->persist) { 781 free(sym->nam); 782 free(sym->val); 783 TAILQ_REMOVE(&symhead, sym, entry); 784 free(sym); 785 } 786 } 787 788 if (errors) { 789 clear_config(conf); 790 errors = 0; 791 return (NULL); 792 } 793 794 return (conf); 795} 796 797void 798link_states(struct ifsd_action *action) 799{ 800 struct ifsd_action *subaction; 801 802 switch (action->type) { 803 default: 804 case IFSD_ACTION_COMMAND: 805 break; 806 case IFSD_ACTION_CHANGESTATE: { 807 struct ifsd_state *state; 808 809 TAILQ_FOREACH(state, &conf->states, entries) { 810 if (strcmp(action->act.statename, 811 state->name) == 0) { 812 action->act.nextstate = state; 813 break; 814 } 815 } 816 if (state == NULL) { 817 fprintf(stderr, "error: state '%s' not declared\n", 818 action->act.statename); 819 file->errors++; 820 } 821 break; 822 } 823 case IFSD_ACTION_CONDITION: 824 TAILQ_FOREACH(subaction, &action->act.c.actions, entries) 825 link_states(subaction); 826 break; 827 } 828} 829 830int 831symset(const char *nam, const char *val, int persist) 832{ 833 struct sym *sym; 834 835 TAILQ_FOREACH(sym, &symhead, entry) { 836 if (strcmp(nam, sym->nam) == 0) 837 break; 838 } 839 840 if (sym != NULL) { 841 if (sym->persist == 1) 842 return (0); 843 else { 844 free(sym->nam); 845 free(sym->val); 846 TAILQ_REMOVE(&symhead, sym, entry); 847 free(sym); 848 } 849 } 850 if ((sym = calloc(1, sizeof(*sym))) == NULL) 851 return (-1); 852 853 sym->nam = strdup(nam); 854 if (sym->nam == NULL) { 855 free(sym); 856 return (-1); 857 } 858 sym->val = strdup(val); 859 if (sym->val == NULL) { 860 free(sym->nam); 861 free(sym); 862 return (-1); 863 } 864 sym->used = 0; 865 sym->persist = persist; 866 TAILQ_INSERT_TAIL(&symhead, sym, entry); 867 return (0); 868} 869 870int 871cmdline_symset(char *s) 872{ 873 char *sym, *val; 874 int ret; 875 876 if ((val = strrchr(s, '=')) == NULL) 877 return (-1); 878 sym = strndup(s, val - s); 879 if (sym == NULL) 880 err(1, "%s", __func__); 881 ret = symset(sym, val + 1, 1); 882 free(sym); 883 884 return (ret); 885} 886 887char * 888symget(const char *nam) 889{ 890 struct sym *sym; 891 892 TAILQ_FOREACH(sym, &symhead, entry) { 893 if (strcmp(nam, sym->nam) == 0) { 894 sym->used = 1; 895 return (sym->val); 896 } 897 } 898 return (NULL); 899} 900 901void 902set_expression_depth(struct ifsd_expression *expression, int depth) 903{ 904 expression->depth = depth; 905 if (conf->maxdepth < depth) 906 conf->maxdepth = depth; 907 if (expression->left != NULL) 908 set_expression_depth(expression->left, depth + 1); 909 if (expression->right != NULL) 910 set_expression_depth(expression->right, depth + 1); 911} 912 913void 914init_state(struct ifsd_state *state) 915{ 916 TAILQ_INIT(&state->interface_states); 917 TAILQ_INIT(&state->external_tests); 918 919 if ((state->init = calloc(1, sizeof(*state->init))) == NULL) 920 err(1, "%s", __func__); 921 state->init->type = IFSD_ACTION_CONDITION; 922 TAILQ_INIT(&state->init->act.c.actions); 923 924 if ((state->body = calloc(1, sizeof(*state->body))) == NULL) 925 err(1, "%s", __func__); 926 state->body->type = IFSD_ACTION_CONDITION; 927 TAILQ_INIT(&state->body->act.c.actions); 928} 929 930struct ifsd_ifstate * 931new_ifstate(char *ifname, int s) 932{ 933 struct ifsd_ifstate *ifstate = NULL; 934 struct ifsd_state *state; 935 936 if (curstate != NULL) 937 state = curstate; 938 else 939 state = &conf->initstate; 940 941 TAILQ_FOREACH(ifstate, &state->interface_states, entries) 942 if (strcmp(ifstate->ifname, ifname) == 0 && 943 ifstate->ifstate == s) 944 break; 945 if (ifstate == NULL) { 946 if ((ifstate = calloc(1, sizeof(*ifstate))) == NULL) 947 err(1, "%s", __func__); 948 if (strlcpy(ifstate->ifname, ifname, 949 sizeof(ifstate->ifname)) >= sizeof(ifstate->ifname)) 950 errx(1, "ifname strlcpy truncation"); 951 free(ifname); 952 ifstate->ifstate = s; 953 TAILQ_INIT(&ifstate->expressions); 954 TAILQ_INSERT_TAIL(&state->interface_states, ifstate, entries); 955 } 956 ifstate->prevstate = -1; 957 ifstate->refcount++; 958 return (ifstate); 959} 960 961struct ifsd_external * 962new_external(char *command, u_int32_t frequency) 963{ 964 struct ifsd_external *external = NULL; 965 struct ifsd_state *state; 966 967 if (curstate != NULL) 968 state = curstate; 969 else 970 state = &conf->initstate; 971 972 TAILQ_FOREACH(external, &state->external_tests, entries) 973 if (strcmp(external->command, command) == 0 && 974 external->frequency == frequency) 975 break; 976 if (external == NULL) { 977 if ((external = calloc(1, sizeof(*external))) == NULL) 978 err(1, "%s", __func__); 979 if ((external->command = strdup(command)) == NULL) 980 err(1, "%s", __func__); 981 external->frequency = frequency; 982 TAILQ_INIT(&external->expressions); 983 TAILQ_INSERT_TAIL(&state->external_tests, external, entries); 984 } 985 external->prevstatus = -1; 986 external->refcount++; 987 return (external); 988}