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