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