1 /* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Harti Brandt <harti@freebsd.org> 7 * 8 * Redistribution of this software and documentation and use in source and 9 * binary forms, with or without modification, are permitted provided that 10 * the following conditions are met: 11 * 12 * 1. Redistributions of source code or documentation must retain the above 13 * copyright notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS 22 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 25 * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 28 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * $Begemot: bsnmp/snmpd/config.c,v 1.20 2004/04/13 14:58:46 novo Exp $ 34 * 35 * Parse configuration file. 36 */ 37 #include <sys/types.h> 38 #include <sys/socket.h> 39 #include <sys/un.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <stdarg.h> 44 #include <ctype.h> 45 #include <errno.h> 46 #include <syslog.h> 47 #include <unistd.h> 48 #include <limits.h> 49 #include <netdb.h> 50 #include <setjmp.h> 51 #include <inttypes.h> 52 53 #include "snmpmod.h" 54 #include "snmpd.h" 55 #include "tree.h" 56 57 /* 58 #define DEBUGGING 59 */ 60 61 /* 62 * config_file: EMPTY | config_file line 63 * 64 * line: oid '=' value 65 * | '%' STRING 66 * | STRING := REST_OF_LINE 67 * | STRING ?= REST_OF_LINE 68 * | . INCLUDE STRING 69 * 70 * oid: STRING suboid 71 * 72 * suboid: EMPTY | suboid '.' subid 73 * 74 * subid: NUM | STRING | '[' STRING ']' 75 * 76 * value: EMPTY | STRING | NUM 77 */ 78 79 /* 80 * Input context for macros and includes 81 */ 82 enum input_type { 83 INPUT_FILE = 1, 84 INPUT_STRING 85 }; 86 struct input { 87 enum input_type type; 88 union { 89 struct { 90 FILE *fp; 91 char *filename; 92 u_int lno; 93 } file; 94 struct { 95 char *macro; 96 char *str; 97 char *ptr; 98 size_t left; 99 } str; 100 } u; 101 LIST_ENTRY(input) link; 102 }; 103 static LIST_HEAD(, input) inputs; 104 105 #define input_fp u.file.fp 106 #define input_filename u.file.filename 107 #define input_lno u.file.lno 108 #define input_macro u.str.macro 109 #define input_str u.str.str 110 #define input_ptr u.str.ptr 111 #define input_left u.str.left 112 113 static int input_push; 114 static int input_buf[2]; 115 116 /* 117 * Configuration data. The configuration file is handled as one single 118 * SNMP transaction. So we need to keep the assignment data for the 119 * commit or rollback pass. Note, that dependencies and finish functions 120 * are NOT allowed here. 121 */ 122 struct assign { 123 struct snmp_value value; 124 struct snmp_scratch scratch; 125 const char *node_name; 126 127 TAILQ_ENTRY(assign) link; 128 }; 129 static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns); 130 131 132 static struct snmp_context *snmp_ctx; 133 134 struct macro { 135 char *name; 136 char *value; 137 size_t length; 138 LIST_ENTRY(macro) link; 139 int perm; 140 }; 141 static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(¯os); 142 143 enum { 144 TOK_EOF = 0200, 145 TOK_EOL, 146 TOK_NUM, 147 TOK_STR, 148 TOK_HOST, 149 TOK_ASSIGN, 150 TOK_QASSIGN, 151 }; 152 153 /* lexer values and last token */ 154 static uint64_t numval; 155 static char strval[_POSIX2_LINE_MAX]; 156 static size_t strvallen; 157 static int token; 158 159 /* error return */ 160 static jmp_buf errjmp[4]; 161 static volatile int errstk; 162 163 # define ERRPUSH() (setjmp(errjmp[errstk++])) 164 # define ERRPOP() ((void)(errstk--)) 165 # define ERRNEXT() (longjmp(errjmp[--errstk], 1)) 166 # define ERR() (longjmp(errjmp[--errstk], 1)) 167 168 /* section context */ 169 static int ignore; 170 171 /* 172 * Report an error and jump to the error label 173 */ 174 static void report(const char *fmt, ...) __dead2 __printflike(1, 2); 175 176 static void 177 report(const char *fmt, ...) 178 { 179 va_list ap; 180 const struct input *input; 181 182 va_start(ap, fmt); 183 vsyslog(LOG_ERR, fmt, ap); 184 va_end(ap); 185 186 LIST_FOREACH(input, &inputs, link) { 187 switch (input->type) { 188 189 case INPUT_FILE: 190 syslog(LOG_ERR, " in file %s line %u", 191 input->input_filename, input->input_lno); 192 break; 193 194 case INPUT_STRING: 195 syslog(LOG_ERR, " in macro %s pos %td", 196 input->input_macro, 197 input->input_ptr - input->input_str); 198 break; 199 } 200 } 201 ERR(); 202 } 203 204 /* 205 * Open a file for input 206 */ 207 static int 208 input_open_file(const char *fname, int sysdir) 209 { 210 struct input *input; 211 FILE *fp; 212 char path[PATH_MAX + 1]; 213 char *col; 214 const char *ptr; 215 216 if (sysdir) { 217 ptr = syspath; 218 fp = NULL; 219 while (*ptr != '\0') { 220 if ((col = strchr(ptr, ':')) == NULL) 221 snprintf(path, sizeof(path), "%s/%s", 222 ptr, fname); 223 else if (col == ptr) 224 snprintf(path, sizeof(path), "./%s", fname); 225 else 226 snprintf(path, sizeof(path), "%.*s/%s", 227 (int)(col - ptr), ptr, fname); 228 if ((fp = fopen(path, "r")) != NULL) 229 break; 230 ptr = col + 1; 231 } 232 } else 233 fp = fopen(fname, "r"); 234 235 if (fp == NULL) 236 report("%s: %m", fname); 237 238 if ((input = malloc(sizeof(*input))) == NULL) { 239 fclose(fp); 240 return (-1); 241 } 242 if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) { 243 fclose(fp); 244 free(input); 245 return (-1); 246 } 247 strcpy(input->input_filename, fname); 248 input->input_fp = fp; 249 input->input_lno = 1; 250 input->type = INPUT_FILE; 251 LIST_INSERT_HEAD(&inputs, input, link); 252 return (0); 253 } 254 255 /* 256 * Make a macro the next input 257 */ 258 static void 259 input_open_macro(struct macro *m) 260 { 261 struct input *input; 262 263 if ((input = malloc(sizeof(*input))) == NULL) 264 report("%m"); 265 input->type = INPUT_STRING; 266 input->input_macro = m->name; 267 if ((input->input_str = malloc(m->length)) == NULL) { 268 free(input); 269 report("%m"); 270 } 271 memcpy(input->input_str, m->value, m->length); 272 input->input_ptr = input->input_str; 273 input->input_left = m->length; 274 LIST_INSERT_HEAD(&inputs, input, link); 275 } 276 277 /* 278 * Close top input source 279 */ 280 static void 281 input_close(void) 282 { 283 struct input *input; 284 285 if ((input = LIST_FIRST(&inputs)) == NULL) 286 abort(); 287 switch (input->type) { 288 289 case INPUT_FILE: 290 fclose(input->input_fp); 291 free(input->input_filename); 292 break; 293 294 case INPUT_STRING: 295 free(input->input_str); 296 break; 297 } 298 LIST_REMOVE(input, link); 299 free(input); 300 } 301 302 /* 303 * Close all inputs 304 */ 305 static void 306 input_close_all(void) 307 { 308 while (!LIST_EMPTY(&inputs)) 309 input_close(); 310 } 311 312 /* 313 * Push back one character 314 */ 315 static void 316 input_ungetc(int c) 317 { 318 if (c == EOF) 319 report("pushing EOF"); 320 if (input_push == 2) 321 report("pushing third char"); 322 input_buf[input_push++] = c; 323 } 324 325 326 /* 327 * Return next character from the input without preprocessing. 328 */ 329 static int 330 input_getc_raw(void) 331 { 332 int c; 333 struct input *input; 334 335 if (input_push != 0) { 336 c = input_buf[--input_push]; 337 goto ok; 338 } 339 while ((input = LIST_FIRST(&inputs)) != NULL) { 340 switch (input->type) { 341 342 case INPUT_FILE: 343 if ((c = getc(input->input_fp)) == EOF) { 344 if (ferror(input->input_fp)) 345 report("read error: %m"); 346 input_close(); 347 break; 348 } 349 if (c == '\n') 350 input->input_lno++; 351 goto ok; 352 353 case INPUT_STRING: 354 if (input->input_left-- == 0) { 355 input_close(); 356 break; 357 } 358 c = *input->input_ptr++; 359 goto ok; 360 } 361 } 362 # ifdef DEBUGGING 363 fprintf(stderr, "EOF"); 364 # endif 365 return (EOF); 366 367 ok: 368 # ifdef DEBUGGING 369 if (!isascii(c) || !isprint(c)) 370 fprintf(stderr, "'%#2x'", c); 371 else 372 fprintf(stderr, "'%c'", c); 373 # endif 374 return (c); 375 } 376 377 /* 378 * Get character with and \\n -> processing. 379 */ 380 static int 381 input_getc_plain(void) 382 { 383 int c; 384 385 again: 386 if ((c = input_getc_raw()) == '\\') { 387 if ((c = input_getc_raw()) == '\n') 388 goto again; 389 if (c != EOF) 390 input_ungetc(c); 391 return ('\\'); 392 } 393 return (c); 394 } 395 396 /* 397 * Get next character with substitution of macros 398 */ 399 static int 400 input_getc(void) 401 { 402 int c; 403 struct macro *m; 404 char name[_POSIX2_LINE_MAX]; 405 size_t namelen; 406 407 again: 408 if ((c = input_getc_plain()) != '$') 409 return (c); 410 411 if ((c = input_getc()) == EOF) 412 report("unexpected EOF"); 413 if (c != '(') 414 report("expecting '(' after '$'"); 415 416 namelen = 0; 417 while ((c = input_getc()) != EOF && c != ')') { 418 if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c))) 419 name[namelen++] = c; 420 else 421 goto badchar; 422 } 423 if (c == EOF) 424 report("unexpected EOF"); 425 name[namelen++] = '\0'; 426 427 LIST_FOREACH(m, ¯os, link) 428 if (strcmp(m->name, name) == 0) 429 break; 430 if (m == NULL) 431 report("undefined macro '%s'", name); 432 433 input_open_macro(m); 434 goto again; 435 436 badchar: 437 if (!isascii(c) || !isprint(c)) 438 report("unexpected character %#2x", (u_int)c); 439 else 440 report("bad character '%c'", c); 441 } 442 443 444 static void 445 input_getnum(u_int base, u_int flen) 446 { 447 int c; 448 u_int cnt; 449 450 cnt = 0; 451 numval = 0; 452 while (flen == 0 || cnt < flen) { 453 if ((c = input_getc()) == EOF) { 454 if (cnt == 0) 455 report("bad number"); 456 return; 457 } 458 if (isdigit(c)) { 459 if (base == 8 && (c == '8' || c == '9')) { 460 input_ungetc(c); 461 if (cnt == 0) 462 report("bad number"); 463 return; 464 } 465 numval = numval * base + (c - '0'); 466 } else if (base == 16 && isxdigit(c)) { 467 if (islower(c)) 468 numval = numval * base + (c - 'a' + 10); 469 else 470 numval = numval * base + (c - 'A' + 10); 471 } else { 472 input_ungetc(c); 473 if (cnt == 0) 474 report("bad number"); 475 return; 476 } 477 cnt++; 478 } 479 } 480 481 static int 482 # ifdef DEBUGGING 483 _gettoken(void) 484 # else 485 gettoken(void) 486 # endif 487 { 488 int c; 489 char *end; 490 static const char esc[] = "abfnrtv"; 491 static const char chr[] = "\a\b\f\n\r\t\v"; 492 493 /* 494 * Skip any whitespace before the next token 495 */ 496 while ((c = input_getc()) != EOF) { 497 if (!isspace(c) || c == '\n') 498 break; 499 } 500 if (c == EOF) 501 return (token = TOK_EOF); 502 if (!isascii(c)) 503 goto badchar; 504 505 /* 506 * Skip comments 507 */ 508 if (c == '#') { 509 while ((c = input_getc_plain()) != EOF) { 510 if (c == '\n') 511 return (token = TOK_EOL); 512 } 513 goto badeof; 514 } 515 516 /* 517 * Single character tokens 518 */ 519 if (c == '\n') 520 return (token = TOK_EOL); 521 if (c == '.' || c == '%' || c == '=' || c == '<' || c == '>') 522 return (token = c); 523 if (c == ':') { 524 if ((c = input_getc()) == '=') 525 return (token = TOK_ASSIGN); 526 input_ungetc(c); 527 return (token = ':'); 528 } 529 if (c == '?') { 530 if ((c = input_getc()) == '=') 531 return (token = TOK_QASSIGN); 532 input_ungetc(c); 533 goto badchar; 534 } 535 536 /* 537 * Sort out numbers 538 */ 539 if (isdigit(c)) { 540 if (c == '0') { 541 if ((c = input_getc()) == 'x' || c == 'X') { 542 input_getnum(16, 0); 543 } else if (isdigit(c)) { 544 input_ungetc(c); 545 input_getnum(8, 0); 546 } else { 547 if (c != EOF) 548 input_ungetc(c); 549 numval = 0; 550 c = 1; 551 } 552 } else { 553 input_ungetc(c); 554 input_getnum(10, 0); 555 } 556 return (token = TOK_NUM); 557 } 558 559 /* 560 * Must be a string then 561 */ 562 strvallen = 0; 563 564 # define GETC(C) do { \ 565 if ((c = input_getc()) == EOF) \ 566 goto badeof; \ 567 if (!isascii(c) || (!isprint(c) && c != '\t')) \ 568 goto badchar; \ 569 } while(0) 570 571 if (c == '"') { 572 for(;;) { 573 GETC(c); 574 if (c == '"') { 575 strval[strvallen] = '\0'; 576 break; 577 } 578 if (c != '\\') { 579 strval[strvallen++] = c; 580 continue; 581 } 582 GETC(c); 583 if ((end = strchr(esc, c)) != NULL) { 584 strval[strvallen++] = chr[end - esc]; 585 continue; 586 } 587 if (c == 'x') { 588 input_getnum(16, 2); 589 c = numval; 590 } else if (c >= '0' && c <= '7') { 591 input_ungetc(c); 592 input_getnum(8, 3); 593 c = numval; 594 } 595 strval[strvallen++] = c; 596 } 597 # undef GETC 598 599 } else if (c == '[') { 600 /* 601 * Skip leading space 602 */ 603 while ((c = input_getc()) != EOF && isspace(c)) 604 ; 605 if (c == EOF) 606 goto badeof; 607 while (c != ']' && !isspace(c)) { 608 if (!isalnum(c) && c != '.' && c != '-') 609 goto badchar; 610 strval[strvallen++] = c; 611 if ((c = input_getc()) == EOF) 612 goto badeof; 613 } 614 while (c != ']' && isspace(c)) { 615 if ((c = input_getc()) == EOF) 616 goto badeof; 617 } 618 if (c != ']') 619 goto badchar; 620 strval[strvallen] = '\0'; 621 return (token = TOK_HOST); 622 623 } else if (!isalpha(c) && c != '_') { 624 goto badchar; 625 626 } else { 627 for (;;) { 628 strval[strvallen++] = c; 629 if ((c = input_getc()) == EOF) 630 goto badeof; 631 if (!isalnum(c) && c != '_' && c != '-') { 632 input_ungetc(c); 633 strval[strvallen] = '\0'; 634 break; 635 } 636 } 637 } 638 639 return (token = TOK_STR); 640 641 badeof: 642 report("unexpected EOF"); 643 644 badchar: 645 if (!isascii(c) || !isprint(c)) 646 report("unexpected character %#2x", (u_int)c); 647 else 648 report("bad character '%c'", c); 649 } 650 651 # ifdef DEBUGGING 652 static int 653 gettoken() 654 { 655 _gettoken(); 656 if (isascii(token) && isprint(token)) 657 printf("(%c)", token); 658 else { 659 switch (token) { 660 661 case TOK_EOF: 662 printf("(EOF)"); 663 break; 664 case TOK_EOL: 665 printf("(EOL)"); 666 break; 667 case TOK_NUM: 668 printf("(NUM %llu)", numval); 669 break; 670 case TOK_STR: 671 printf("(STR %.*s)", (int)strvallen, strval); 672 break; 673 case TOK_HOST: 674 printf("(HOST %s)", strval); 675 break; 676 default: 677 printf("(%#2x)", token); 678 break; 679 } 680 } 681 return (token); 682 } 683 #endif 684 685 686 /* 687 * Try to execute the assignment. 688 */ 689 static void 690 handle_assignment(const struct snmp_node *node, struct asn_oid *vindex, 691 const struct snmp_value *value) 692 { 693 u_int i; 694 int err; 695 struct assign *tp; 696 char nodename[100]; 697 698 if (node->type == SNMP_NODE_LEAF) { 699 /* index must be one single zero or no index at all */ 700 if (vindex->len > 1 || (vindex->len == 1 && 701 vindex->subs[0] != 0)) 702 report("bad index on leaf node"); 703 vindex->len = 1; 704 vindex->subs[0] = 0; 705 } else { 706 /* resulting oid must not be too long */ 707 if (node->oid.len + vindex->len > ASN_MAXOIDLEN) 708 report("resulting OID too long"); 709 } 710 711 /* 712 * Get the next assignment entry for the transaction. 713 */ 714 if ((tp = malloc(sizeof(*tp))) == NULL) 715 report("%m"); 716 717 tp->value = *value; 718 tp->node_name = node->name; 719 720 /* 721 * Build the OID 722 */ 723 tp->value.var = node->oid; 724 for (i = 0; i < vindex->len; i++) 725 tp->value.var.subs[tp->value.var.len++] = vindex->subs[i]; 726 727 /* 728 * Puzzle together the variables for the call and call the 729 * set routine. The set routine may make our node pointer 730 * invalid (if we happend to call the module loader) so 731 * get a copy of the node name beforehands. 732 */ 733 snprintf(nodename, sizeof(nodename), "%s", node->name); 734 snmp_ctx->scratch = &tp->scratch; 735 snmp_ctx->var_index = 0; 736 err = (*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index, 737 SNMP_OP_SET); 738 if (err != 0) { 739 free(tp); 740 report("assignment to %s.%s returns %d", nodename, 741 asn_oid2str(vindex), err); 742 } 743 744 TAILQ_INSERT_TAIL(&assigns, tp, link); 745 } 746 747 748 /* 749 * Parse the section statement 750 */ 751 static void 752 parse_section(const struct lmodule *mod) 753 { 754 if (token != TOK_STR) 755 report("expecting section name"); 756 757 if (strcmp(strval, "snmpd") == 0) { 758 if (mod != NULL) 759 /* loading a module - ignore common stuff */ 760 ignore = 1; 761 else 762 /* global configuration - don't ignore */ 763 ignore = 0; 764 } else { 765 if (mod == NULL) { 766 /* global configuration - ignore module stuff */ 767 ignore = 1; 768 } else { 769 /* loading module - check if it's our section */ 770 ignore = (strcmp(strval, mod->section) != 0); 771 } 772 } 773 gettoken(); 774 } 775 776 /* 777 * Convert a hostname to four u_chars 778 */ 779 static void 780 gethost(const char *host, u_char *ip) 781 { 782 struct addrinfo hints, *res; 783 int error; 784 struct sockaddr_in *sain; 785 786 memset(&hints, 0, sizeof(hints)); 787 hints.ai_family = AF_INET; 788 hints.ai_socktype = SOCK_DGRAM; 789 hints.ai_protocol = IPPROTO_UDP; 790 hints.ai_flags = AI_PASSIVE; 791 error = getaddrinfo(host, NULL, &hints, &res); 792 if (error != 0) 793 report("%s: %s", host, gai_strerror(error)); 794 if (res == NULL) 795 report("%s: unknown hostname", host); 796 797 sain = (struct sockaddr_in *)(void *)res->ai_addr; 798 sain->sin_addr.s_addr = ntohl(sain->sin_addr.s_addr); 799 ip[0] = sain->sin_addr.s_addr >> 24; 800 ip[1] = sain->sin_addr.s_addr >> 16; 801 ip[2] = sain->sin_addr.s_addr >> 8; 802 ip[3] = sain->sin_addr.s_addr >> 0; 803 804 freeaddrinfo(res); 805 } 806 807 /* 808 * Parse the left hand side of a config line. 809 */ 810 static const struct snmp_node * 811 parse_oid(const char *varname, struct asn_oid *oid) 812 { 813 struct snmp_node *node; 814 u_int i; 815 u_char ip[4]; 816 817 for (node = tree; node < &tree[tree_size]; node++) 818 if (strcmp(varname, node->name) == 0) 819 break; 820 if (node == &tree[tree_size]) 821 node = NULL; 822 823 oid->len = 0; 824 while (token == '.') { 825 if (gettoken() == TOK_NUM) { 826 if (numval > ASN_MAXID) 827 report("subid too large %#"PRIx64, numval); 828 if (oid->len == ASN_MAXOIDLEN) 829 report("index too long"); 830 oid->subs[oid->len++] = numval; 831 832 } else if (token == TOK_STR) { 833 if (strvallen + oid->len + 1 > ASN_MAXOIDLEN) 834 report("oid too long"); 835 oid->subs[oid->len++] = strvallen; 836 for (i = 0; i < strvallen; i++) 837 oid->subs[oid->len++] = strval[i]; 838 839 } else if (token == TOK_HOST) { 840 gethost(strval, ip); 841 if (oid->len + 4 > ASN_MAXOIDLEN) 842 report("index too long"); 843 for (i = 0; i < 4; i++) 844 oid->subs[oid->len++] = ip[i]; 845 846 } else 847 report("bad token in index"); 848 gettoken(); 849 } 850 851 return (node); 852 } 853 854 /* 855 * Parse the value for an assignment. 856 */ 857 static void 858 parse_syntax_null(struct snmp_value *value __unused) 859 { 860 if (token != TOK_EOL) 861 report("bad NULL syntax"); 862 } 863 864 static void 865 parse_syntax_integer(struct snmp_value *value) 866 { 867 if (token != TOK_NUM) 868 report("bad INTEGER syntax"); 869 if (numval > 0x7fffffff) 870 report("INTEGER too large %"PRIu64, numval); 871 872 value->v.integer = numval; 873 gettoken(); 874 } 875 876 static void 877 parse_syntax_counter64(struct snmp_value *value) 878 { 879 if (token != TOK_NUM) 880 report("bad COUNTER64 syntax"); 881 882 value->v.counter64 = numval; 883 gettoken(); 884 } 885 886 static void 887 parse_syntax_octetstring(struct snmp_value *value) 888 { 889 u_long alloc; 890 u_char *noct; 891 892 if (token == TOK_STR) { 893 value->v.octetstring.len = strvallen; 894 value->v.octetstring.octets = malloc(strvallen); 895 (void)memcpy(value->v.octetstring.octets, strval, strvallen); 896 gettoken(); 897 return; 898 } 899 900 /* XX:XX:XX syntax */ 901 value->v.octetstring.octets = NULL; 902 value->v.octetstring.len = 0; 903 904 if (token != TOK_NUM) 905 /* empty string is allowed */ 906 return; 907 908 if (ERRPUSH()) { 909 free(value->v.octetstring.octets); 910 ERRNEXT(); 911 } 912 913 alloc = 0; 914 for (;;) { 915 if (token != TOK_NUM) 916 report("bad OCTETSTRING syntax"); 917 if (numval > 0xff) 918 report("byte value too large"); 919 if (alloc == value->v.octetstring.len) { 920 alloc += 100; 921 noct = realloc(value->v.octetstring.octets, alloc); 922 if (noct == NULL) 923 report("%m"); 924 value->v.octetstring.octets = noct; 925 } 926 value->v.octetstring.octets[value->v.octetstring.len++] 927 = numval; 928 if (gettoken() != ':') 929 break; 930 gettoken(); 931 } 932 ERRPOP(); 933 } 934 935 static void 936 parse_syntax_oid(struct snmp_value *value) 937 { 938 value->v.oid.len = 0; 939 940 if (token != TOK_NUM) 941 return; 942 943 for (;;) { 944 if (token != TOK_NUM) 945 report("bad OID syntax"); 946 if (numval > ASN_MAXID) 947 report("subid too large"); 948 if (value->v.oid.len == ASN_MAXOIDLEN) 949 report("OID too long"); 950 value->v.oid.subs[value->v.oid.len++] = numval; 951 if (gettoken() != '.') 952 break; 953 gettoken(); 954 } 955 } 956 957 static void 958 parse_syntax_ipaddress(struct snmp_value *value) 959 { 960 int i; 961 u_char ip[4]; 962 963 if (token == TOK_NUM) { 964 /* numerical address */ 965 i = 0; 966 for (;;) { 967 if (numval >= 256) 968 report("ip address part too large"); 969 value->v.ipaddress[i++] = numval; 970 if (i == 4) 971 break; 972 if (gettoken() != '.') 973 report("expecting '.' in ip address"); 974 } 975 gettoken(); 976 977 } else if (token == TOK_HOST) { 978 /* host name */ 979 gethost(strval, ip); 980 for (i = 0; i < 4; i++) 981 value->v.ipaddress[i] = ip[i]; 982 gettoken(); 983 984 } else 985 report("bad ip address syntax"); 986 } 987 988 static void 989 parse_syntax_uint32(struct snmp_value *value) 990 { 991 992 if (token != TOK_NUM) 993 report("bad number syntax"); 994 if (numval > 0xffffffff) 995 report("number too large"); 996 value->v.uint32 = numval; 997 gettoken(); 998 } 999 1000 /* 1001 * Parse an assignement line 1002 */ 1003 static void 1004 parse_assign(const char *varname) 1005 { 1006 struct snmp_value value; 1007 struct asn_oid vindex; 1008 const struct snmp_node *node; 1009 1010 node = parse_oid(varname, &vindex); 1011 if (token != '=') 1012 report("'=' expected"); 1013 gettoken(); 1014 1015 if (ignore) { 1016 /* skip rest of line */ 1017 while (token != TOK_EOL && token != TOK_EOF) 1018 gettoken(); 1019 return; 1020 } 1021 if (node == NULL) 1022 report("unknown variable"); 1023 1024 switch (value.syntax = node->syntax) { 1025 1026 case SNMP_SYNTAX_NULL: 1027 parse_syntax_null(&value); 1028 break; 1029 1030 case SNMP_SYNTAX_INTEGER: 1031 parse_syntax_integer(&value); 1032 break; 1033 1034 case SNMP_SYNTAX_COUNTER64: 1035 parse_syntax_counter64(&value); 1036 break; 1037 1038 case SNMP_SYNTAX_OCTETSTRING: 1039 parse_syntax_octetstring(&value); 1040 break; 1041 1042 case SNMP_SYNTAX_OID: 1043 parse_syntax_oid(&value); 1044 break; 1045 1046 case SNMP_SYNTAX_IPADDRESS: 1047 parse_syntax_ipaddress(&value); 1048 break; 1049 1050 case SNMP_SYNTAX_COUNTER: 1051 case SNMP_SYNTAX_GAUGE: 1052 case SNMP_SYNTAX_TIMETICKS: 1053 parse_syntax_uint32(&value); 1054 break; 1055 1056 case SNMP_SYNTAX_NOSUCHOBJECT: 1057 case SNMP_SYNTAX_NOSUCHINSTANCE: 1058 case SNMP_SYNTAX_ENDOFMIBVIEW: 1059 abort(); 1060 } 1061 1062 if (ERRPUSH()) { 1063 snmp_value_free(&value); 1064 ERRNEXT(); 1065 } 1066 1067 handle_assignment(node, &vindex, &value); 1068 1069 ERRPOP(); 1070 } 1071 1072 /* 1073 * Handle macro definition line 1074 * We have already seen the := and the input now stands at the character 1075 * after the =. Skip whitespace and then call the input routine directly to 1076 * eat up characters. 1077 */ 1078 static void 1079 parse_define(const char *varname) 1080 { 1081 char *volatile string; 1082 char *new; 1083 volatile size_t alloc, length; 1084 int c; 1085 struct macro *m; 1086 int t = token; 1087 1088 alloc = 100; 1089 length = 0; 1090 if ((string = malloc(alloc)) == NULL) 1091 report("%m"); 1092 1093 if (ERRPUSH()) { 1094 free(string); 1095 ERRNEXT(); 1096 } 1097 1098 while ((c = input_getc_plain()) != EOF) { 1099 if (c == '\n' || !isspace(c)) 1100 break; 1101 } 1102 1103 while (c != EOF && c != '#' && c != '\n') { 1104 if (alloc == length) { 1105 alloc *= 2; 1106 if ((new = realloc(string, alloc)) == NULL) 1107 report("%m"); 1108 string = new; 1109 } 1110 string[length++] = c; 1111 c = input_getc_plain(); 1112 } 1113 if (c == '#') { 1114 while ((c = input_getc_plain()) != EOF && c != '\n') 1115 ; 1116 } 1117 if (c == EOF) 1118 report("EOF in macro definition"); 1119 1120 LIST_FOREACH(m, ¯os, link) 1121 if (strcmp(m->name, varname) == 0) 1122 break; 1123 1124 if (m == NULL) { 1125 if ((m = malloc(sizeof(*m))) == NULL) 1126 report("%m"); 1127 if ((m->name = malloc(strlen(varname) + 1)) == NULL) { 1128 free(m); 1129 report("%m"); 1130 } 1131 strcpy(m->name, varname); 1132 m->perm = 0; 1133 LIST_INSERT_HEAD(¯os, m, link); 1134 1135 m->value = string; 1136 m->length = length; 1137 } else { 1138 if (t != TOK_ASSIGN) { 1139 free(m->value); 1140 m->value = string; 1141 m->length = length; 1142 } 1143 } 1144 1145 token = TOK_EOL; 1146 1147 ERRPOP(); 1148 } 1149 1150 /* 1151 * Free all macros 1152 */ 1153 static void 1154 macro_free_all(void) 1155 { 1156 static struct macro *m, *m1; 1157 1158 m = LIST_FIRST(¯os); 1159 while (m != NULL) { 1160 m1 = LIST_NEXT(m, link); 1161 if (!m->perm) { 1162 free(m->name); 1163 free(m->value); 1164 LIST_REMOVE(m, link); 1165 free(m); 1166 } 1167 m = m1; 1168 } 1169 } 1170 1171 /* 1172 * Parse an include directive and switch to the new file 1173 */ 1174 static void 1175 parse_include(void) 1176 { 1177 int sysdir = 0; 1178 char fname[_POSIX2_LINE_MAX]; 1179 1180 if (gettoken() == '<') { 1181 sysdir = 1; 1182 if (gettoken() != TOK_STR) 1183 report("expecting filename after in .include"); 1184 } else if (token != TOK_STR) 1185 report("expecting filename after in .include"); 1186 1187 strcpy(fname, strval); 1188 if (sysdir && gettoken() != '>') 1189 report("expecting '>'"); 1190 gettoken(); 1191 if (input_open_file(fname, sysdir) == -1) 1192 report("%s: %m", fname); 1193 } 1194 1195 /* 1196 * Parse the configuration file 1197 */ 1198 static void 1199 parse_file(const struct lmodule *mod) 1200 { 1201 char varname[_POSIX2_LINE_MAX]; 1202 1203 while (gettoken() != TOK_EOF) { 1204 if (token == TOK_EOL) 1205 /* empty line */ 1206 continue; 1207 if (token == '%') { 1208 gettoken(); 1209 parse_section(mod); 1210 } else if (token == '.') { 1211 if (gettoken() != TOK_STR) 1212 report("keyword expected after '.'"); 1213 if (strcmp(strval, "include") == 0) 1214 parse_include(); 1215 else 1216 report("unknown keyword '%s'", strval); 1217 } else if (token == TOK_STR) { 1218 strcpy(varname, strval); 1219 if (gettoken() == TOK_ASSIGN || token == TOK_QASSIGN) 1220 parse_define(varname); 1221 else 1222 parse_assign(varname); 1223 } 1224 if (token != TOK_EOL) 1225 report("eol expected"); 1226 } 1227 } 1228 1229 /* 1230 * Do rollback on errors 1231 */ 1232 static void 1233 do_rollback(void) 1234 { 1235 struct assign *tp; 1236 struct snmp_node *node; 1237 1238 while ((tp = TAILQ_LAST(&assigns, assigns)) != NULL) { 1239 TAILQ_REMOVE(&assigns, tp, link); 1240 for (node = tree; node < &tree[tree_size]; node++) 1241 if (node->name == tp->node_name) { 1242 snmp_ctx->scratch = &tp->scratch; 1243 (void)(*node->op)(snmp_ctx, &tp->value, 1244 node->oid.len, node->index, 1245 SNMP_OP_ROLLBACK); 1246 break; 1247 } 1248 if (node == &tree[tree_size]) 1249 syslog(LOG_ERR, "failed to find node for " 1250 "rollback"); 1251 snmp_value_free(&tp->value); 1252 free(tp); 1253 } 1254 } 1255 1256 /* 1257 * Do commit 1258 */ 1259 static void 1260 do_commit(void) 1261 { 1262 struct assign *tp; 1263 struct snmp_node *node; 1264 1265 while ((tp = TAILQ_FIRST(&assigns)) != NULL) { 1266 TAILQ_REMOVE(&assigns, tp, link); 1267 for (node = tree; node < &tree[tree_size]; node++) 1268 if (node->name == tp->node_name) { 1269 snmp_ctx->scratch = &tp->scratch; 1270 (void)(*node->op)(snmp_ctx, &tp->value, 1271 node->oid.len, node->index, SNMP_OP_COMMIT); 1272 break; 1273 } 1274 if (node == &tree[tree_size]) 1275 syslog(LOG_ERR, "failed to find node for commit"); 1276 snmp_value_free(&tp->value); 1277 free(tp); 1278 } 1279 } 1280 1281 /* 1282 * Read the configuration file. Handle the entire file as one transaction. 1283 * 1284 * If lodmod is NULL, the sections for 'snmpd' and all loaded modules are 1285 * executed. If it is not NULL, only the sections for that module are handled. 1286 */ 1287 int 1288 read_config(const char *fname, struct lmodule *lodmod) 1289 { 1290 int err; 1291 char objbuf[ASN_OIDSTRLEN]; 1292 char idxbuf[ASN_OIDSTRLEN]; 1293 1294 ignore = 0; 1295 1296 input_push = 0; 1297 if (input_open_file(fname, 0) == -1) { 1298 syslog(LOG_ERR, "%s: %m", fname); 1299 return (-1); 1300 } 1301 community = COMM_INITIALIZE; 1302 1303 if ((snmp_ctx = snmp_init_context()) == NULL) { 1304 syslog(LOG_ERR, "%m"); 1305 return (-1); 1306 } 1307 1308 if (ERRPUSH()) { 1309 do_rollback(); 1310 input_close_all(); 1311 macro_free_all(); 1312 free(snmp_ctx); 1313 return (-1); 1314 } 1315 parse_file(lodmod); 1316 ERRPOP(); 1317 1318 if ((err = snmp_dep_commit(snmp_ctx)) != SNMP_ERR_NOERROR) { 1319 syslog(LOG_ERR, "init dep failed: %u %s %s", err, 1320 asn_oid2str_r(&snmp_ctx->dep->obj, objbuf), 1321 asn_oid2str_r(&snmp_ctx->dep->idx, idxbuf)); 1322 snmp_dep_rollback(snmp_ctx); 1323 do_rollback(); 1324 input_close_all(); 1325 macro_free_all(); 1326 free(snmp_ctx); 1327 return (-1); 1328 } 1329 1330 do_commit(); 1331 snmp_dep_finish(snmp_ctx); 1332 1333 macro_free_all(); 1334 1335 free(snmp_ctx); 1336 1337 return (0); 1338 } 1339 1340 /* 1341 * Define a permanent macro 1342 */ 1343 int 1344 define_macro(const char *name, const char *value) 1345 { 1346 struct macro *m; 1347 1348 if ((m = malloc(sizeof(*m))) == NULL) 1349 return (-1); 1350 if ((m->name = malloc(strlen(name) + 1)) == NULL) { 1351 free(m); 1352 return (-1); 1353 } 1354 strcpy(m->name, name); 1355 if ((m->value = malloc(strlen(value) + 1)) == NULL) { 1356 free(m->name); 1357 free(m); 1358 return (-1); 1359 } 1360 strcpy(m->value, value); 1361 m->length = strlen(value); 1362 return (0); 1363 } 1364