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 %#jx", 827 (uintmax_t)numval); 828 if (oid->len == ASN_MAXOIDLEN) 829 report("index too long"); 830 if (gettoken() != ':') 831 oid->subs[oid->len++] = numval; 832 else { 833 str_oid.len = 0; 834 str_oid.subs[str_oid.len++] = numval; 835 while (gettoken() == TOK_NUM) { 836 str_oid.subs[str_oid.len++] = numval; 837 if (gettoken() != ':') 838 break; 839 } 840 oid->subs[oid->len++] = str_oid.len; 841 asn_append_oid(oid, &str_oid); 842 } 843 844 } else if (token == TOK_STR) { 845 if (strvallen + oid->len + 1 > ASN_MAXOIDLEN) 846 report("oid too long"); 847 oid->subs[oid->len++] = strvallen; 848 for (i = 0; i < strvallen; i++) 849 oid->subs[oid->len++] = strval[i]; 850 gettoken(); 851 852 } else if (token == TOK_HOST) { 853 gethost(strval, ip); 854 if (oid->len + 4 > ASN_MAXOIDLEN) 855 report("index too long"); 856 for (i = 0; i < 4; i++) 857 oid->subs[oid->len++] = ip[i]; 858 gettoken(); 859 } else 860 report("bad token in index"); 861 } 862 863 return (node); 864 } 865 866 /* 867 * Parse the value for an assignment. 868 */ 869 static void 870 parse_syntax_null(struct snmp_value *value __unused) 871 { 872 if (token != TOK_EOL) 873 report("bad NULL syntax"); 874 } 875 876 static void 877 parse_syntax_integer(struct snmp_value *value) 878 { 879 if (token != TOK_NUM) 880 report("bad INTEGER syntax"); 881 if (numval > 0x7fffffff) 882 report("INTEGER too large %ju", (uintmax_t)numval); 883 884 value->v.integer = numval; 885 gettoken(); 886 } 887 888 static void 889 parse_syntax_counter64(struct snmp_value *value) 890 { 891 if (token != TOK_NUM) 892 report("bad COUNTER64 syntax"); 893 894 value->v.counter64 = numval; 895 gettoken(); 896 } 897 898 static void 899 parse_syntax_octetstring(struct snmp_value *value) 900 { 901 u_long alloc; 902 u_char *noct; 903 904 if (token == TOK_STR) { 905 value->v.octetstring.len = strvallen; 906 value->v.octetstring.octets = malloc(strvallen); 907 (void)memcpy(value->v.octetstring.octets, strval, strvallen); 908 gettoken(); 909 return; 910 } 911 912 /* XX:XX:XX syntax */ 913 value->v.octetstring.octets = NULL; 914 value->v.octetstring.len = 0; 915 916 if (token != TOK_NUM) 917 /* empty string is allowed */ 918 return; 919 920 if (ERRPUSH()) { 921 free(value->v.octetstring.octets); 922 ERRNEXT(); 923 } 924 925 alloc = 0; 926 for (;;) { 927 if (token != TOK_NUM) 928 report("bad OCTETSTRING syntax"); 929 if (numval > 0xff) 930 report("byte value too large"); 931 if (alloc == value->v.octetstring.len) { 932 alloc += 100; 933 noct = realloc(value->v.octetstring.octets, alloc); 934 if (noct == NULL) 935 report("%m"); 936 value->v.octetstring.octets = noct; 937 } 938 value->v.octetstring.octets[value->v.octetstring.len++] 939 = numval; 940 if (gettoken() != ':') 941 break; 942 gettoken(); 943 } 944 ERRPOP(); 945 } 946 947 static void 948 parse_syntax_oid(struct snmp_value *value) 949 { 950 value->v.oid.len = 0; 951 952 if (token != TOK_NUM) 953 return; 954 955 for (;;) { 956 if (token != TOK_NUM) 957 report("bad OID syntax"); 958 if (numval > ASN_MAXID) 959 report("subid too large"); 960 if (value->v.oid.len == ASN_MAXOIDLEN) 961 report("OID too long"); 962 value->v.oid.subs[value->v.oid.len++] = numval; 963 if (gettoken() != '.') 964 break; 965 gettoken(); 966 } 967 } 968 969 static void 970 parse_syntax_ipaddress(struct snmp_value *value) 971 { 972 int i; 973 u_char ip[4]; 974 975 if (token == TOK_NUM) { 976 /* numerical address */ 977 i = 0; 978 for (;;) { 979 if (numval >= 256) 980 report("ip address part too large"); 981 value->v.ipaddress[i++] = numval; 982 if (i == 4) 983 break; 984 if (gettoken() != '.') 985 report("expecting '.' in ip address"); 986 } 987 gettoken(); 988 989 } else if (token == TOK_HOST) { 990 /* host name */ 991 gethost(strval, ip); 992 for (i = 0; i < 4; i++) 993 value->v.ipaddress[i] = ip[i]; 994 gettoken(); 995 996 } else 997 report("bad ip address syntax"); 998 } 999 1000 static void 1001 parse_syntax_uint32(struct snmp_value *value) 1002 { 1003 1004 if (token != TOK_NUM) 1005 report("bad number syntax"); 1006 if (numval > 0xffffffff) 1007 report("number too large"); 1008 value->v.uint32 = numval; 1009 gettoken(); 1010 } 1011 1012 /* 1013 * Parse an assignement line 1014 */ 1015 static void 1016 parse_assign(const char *varname) 1017 { 1018 struct snmp_value value; 1019 struct asn_oid vindex; 1020 const struct snmp_node *node; 1021 1022 node = parse_oid(varname, &vindex); 1023 if (token != '=') 1024 report("'=' expected, got '%c'", token); 1025 gettoken(); 1026 1027 if (ignore) { 1028 /* skip rest of line */ 1029 while (token != TOK_EOL && token != TOK_EOF) 1030 gettoken(); 1031 return; 1032 } 1033 if (node == NULL) 1034 report("unknown variable"); 1035 1036 switch (value.syntax = node->syntax) { 1037 1038 case SNMP_SYNTAX_NULL: 1039 parse_syntax_null(&value); 1040 break; 1041 1042 case SNMP_SYNTAX_INTEGER: 1043 parse_syntax_integer(&value); 1044 break; 1045 1046 case SNMP_SYNTAX_COUNTER64: 1047 parse_syntax_counter64(&value); 1048 break; 1049 1050 case SNMP_SYNTAX_OCTETSTRING: 1051 parse_syntax_octetstring(&value); 1052 break; 1053 1054 case SNMP_SYNTAX_OID: 1055 parse_syntax_oid(&value); 1056 break; 1057 1058 case SNMP_SYNTAX_IPADDRESS: 1059 parse_syntax_ipaddress(&value); 1060 break; 1061 1062 case SNMP_SYNTAX_COUNTER: 1063 case SNMP_SYNTAX_GAUGE: 1064 case SNMP_SYNTAX_TIMETICKS: 1065 parse_syntax_uint32(&value); 1066 break; 1067 1068 case SNMP_SYNTAX_NOSUCHOBJECT: 1069 case SNMP_SYNTAX_NOSUCHINSTANCE: 1070 case SNMP_SYNTAX_ENDOFMIBVIEW: 1071 abort(); 1072 } 1073 1074 if (ERRPUSH()) { 1075 snmp_value_free(&value); 1076 ERRNEXT(); 1077 } 1078 1079 handle_assignment(node, &vindex, &value); 1080 1081 ERRPOP(); 1082 } 1083 1084 /* 1085 * Handle macro definition line 1086 * We have already seen the := and the input now stands at the character 1087 * after the =. Skip whitespace and then call the input routine directly to 1088 * eat up characters. 1089 */ 1090 static void 1091 parse_define(const char *varname) 1092 { 1093 char *volatile string; 1094 char *new; 1095 volatile size_t alloc, length; 1096 int c; 1097 struct macro *m; 1098 int t = token; 1099 1100 alloc = 100; 1101 length = 0; 1102 if ((string = malloc(alloc)) == NULL) 1103 report("%m"); 1104 1105 if (ERRPUSH()) { 1106 free(string); 1107 ERRNEXT(); 1108 } 1109 1110 while ((c = input_getc_plain()) != EOF) { 1111 if (c == '\n' || !isspace(c)) 1112 break; 1113 } 1114 1115 while (c != EOF && c != '#' && c != '\n') { 1116 if (alloc == length) { 1117 alloc *= 2; 1118 if ((new = realloc(string, alloc)) == NULL) 1119 report("%m"); 1120 string = new; 1121 } 1122 string[length++] = c; 1123 c = input_getc_plain(); 1124 } 1125 if (c == '#') { 1126 while ((c = input_getc_plain()) != EOF && c != '\n') 1127 ; 1128 } 1129 if (c == EOF) 1130 report("EOF in macro definition"); 1131 1132 LIST_FOREACH(m, ¯os, link) 1133 if (strcmp(m->name, varname) == 0) 1134 break; 1135 1136 if (m == NULL) { 1137 if ((m = malloc(sizeof(*m))) == NULL) 1138 report("%m"); 1139 if ((m->name = malloc(strlen(varname) + 1)) == NULL) { 1140 free(m); 1141 report("%m"); 1142 } 1143 strcpy(m->name, varname); 1144 m->perm = 0; 1145 LIST_INSERT_HEAD(¯os, m, link); 1146 1147 m->value = string; 1148 m->length = length; 1149 } else { 1150 if (t == TOK_ASSIGN) { 1151 free(m->value); 1152 m->value = string; 1153 m->length = length; 1154 } else 1155 free(string); 1156 } 1157 1158 token = TOK_EOL; 1159 1160 ERRPOP(); 1161 } 1162 1163 /* 1164 * Free all macros 1165 */ 1166 static void 1167 macro_free_all(void) 1168 { 1169 static struct macro *m, *m1; 1170 1171 m = LIST_FIRST(¯os); 1172 while (m != NULL) { 1173 m1 = LIST_NEXT(m, link); 1174 if (!m->perm) { 1175 free(m->name); 1176 free(m->value); 1177 LIST_REMOVE(m, link); 1178 free(m); 1179 } 1180 m = m1; 1181 } 1182 } 1183 1184 /* 1185 * Parse an include directive and switch to the new file 1186 */ 1187 static void 1188 parse_include(void) 1189 { 1190 int sysdir = 0; 1191 char fname[_POSIX2_LINE_MAX]; 1192 1193 if (gettoken() == '<') { 1194 sysdir = 1; 1195 if (gettoken() != TOK_STR) 1196 report("expecting filename after in .include"); 1197 } else if (token != TOK_STR) 1198 report("expecting filename after in .include"); 1199 1200 strcpy(fname, strval); 1201 if (sysdir && gettoken() != '>') 1202 report("expecting '>'"); 1203 gettoken(); 1204 if (input_open_file(fname, sysdir) == -1) 1205 report("%s: %m", fname); 1206 } 1207 1208 /* 1209 * Parse the configuration file 1210 */ 1211 static void 1212 parse_file(const struct lmodule *mod) 1213 { 1214 char varname[_POSIX2_LINE_MAX]; 1215 1216 while (gettoken() != TOK_EOF) { 1217 if (token == TOK_EOL) 1218 /* empty line */ 1219 continue; 1220 if (token == '%') { 1221 gettoken(); 1222 parse_section(mod); 1223 } else if (token == '.') { 1224 if (gettoken() != TOK_STR) 1225 report("keyword expected after '.'"); 1226 if (strcmp(strval, "include") == 0) 1227 parse_include(); 1228 else 1229 report("unknown keyword '%s'", strval); 1230 } else if (token == TOK_STR) { 1231 strcpy(varname, strval); 1232 if (gettoken() == TOK_ASSIGN || token == TOK_QASSIGN) 1233 parse_define(varname); 1234 else 1235 parse_assign(varname); 1236 } 1237 if (token != TOK_EOL) 1238 report("eol expected"); 1239 } 1240 } 1241 1242 /* 1243 * Do rollback on errors 1244 */ 1245 static void 1246 do_rollback(void) 1247 { 1248 struct assign *tp; 1249 struct snmp_node *node; 1250 1251 while ((tp = TAILQ_LAST(&assigns, assigns)) != NULL) { 1252 TAILQ_REMOVE(&assigns, tp, link); 1253 for (node = tree; node < &tree[tree_size]; node++) 1254 if (node->name == tp->node_name) { 1255 snmp_ctx->scratch = &tp->scratch; 1256 (void)(*node->op)(snmp_ctx, &tp->value, 1257 node->oid.len, node->index, 1258 SNMP_OP_ROLLBACK); 1259 break; 1260 } 1261 if (node == &tree[tree_size]) 1262 syslog(LOG_ERR, "failed to find node for " 1263 "rollback"); 1264 snmp_value_free(&tp->value); 1265 free(tp); 1266 } 1267 } 1268 1269 /* 1270 * Do commit 1271 */ 1272 static void 1273 do_commit(void) 1274 { 1275 struct assign *tp; 1276 struct snmp_node *node; 1277 1278 while ((tp = TAILQ_FIRST(&assigns)) != NULL) { 1279 TAILQ_REMOVE(&assigns, tp, link); 1280 for (node = tree; node < &tree[tree_size]; node++) 1281 if (node->name == tp->node_name) { 1282 snmp_ctx->scratch = &tp->scratch; 1283 (void)(*node->op)(snmp_ctx, &tp->value, 1284 node->oid.len, node->index, SNMP_OP_COMMIT); 1285 break; 1286 } 1287 if (node == &tree[tree_size]) 1288 syslog(LOG_ERR, "failed to find node for commit"); 1289 snmp_value_free(&tp->value); 1290 free(tp); 1291 } 1292 } 1293 1294 /* 1295 * Read the configuration file. Handle the entire file as one transaction. 1296 * 1297 * If lodmod is NULL, the sections for 'snmpd' and all loaded modules are 1298 * executed. If it is not NULL, only the sections for that module are handled. 1299 */ 1300 int 1301 read_config(const char *fname, struct lmodule *lodmod) 1302 { 1303 int err; 1304 char objbuf[ASN_OIDSTRLEN]; 1305 char idxbuf[ASN_OIDSTRLEN]; 1306 1307 ignore = 0; 1308 1309 input_push = 0; 1310 1311 if (ERRPUSH()) 1312 return (-1); 1313 if (input_open_file(fname, 0) == -1) { 1314 syslog(LOG_ERR, "%s: %m", fname); 1315 return (-1); 1316 } 1317 ERRPOP(); 1318 community = COMM_INITIALIZE; 1319 1320 if ((snmp_ctx = snmp_init_context()) == NULL) { 1321 input_close_all(); 1322 syslog(LOG_ERR, "%m"); 1323 return (-1); 1324 } 1325 1326 if (ERRPUSH()) { 1327 do_rollback(); 1328 input_close_all(); 1329 macro_free_all(); 1330 free(snmp_ctx); 1331 return (-1); 1332 } 1333 parse_file(lodmod); 1334 ERRPOP(); 1335 1336 if ((err = snmp_dep_commit(snmp_ctx)) != SNMP_ERR_NOERROR) { 1337 syslog(LOG_ERR, "init dep failed: %u %s %s", err, 1338 asn_oid2str_r(&snmp_ctx->dep->obj, objbuf), 1339 asn_oid2str_r(&snmp_ctx->dep->idx, idxbuf)); 1340 snmp_dep_rollback(snmp_ctx); 1341 do_rollback(); 1342 input_close_all(); 1343 macro_free_all(); 1344 free(snmp_ctx); 1345 return (-1); 1346 } 1347 1348 do_commit(); 1349 snmp_dep_finish(snmp_ctx); 1350 1351 macro_free_all(); 1352 1353 free(snmp_ctx); 1354 1355 return (0); 1356 } 1357 1358 /* 1359 * Define a permanent macro 1360 */ 1361 int 1362 define_macro(const char *name, const char *value) 1363 { 1364 struct macro *m; 1365 1366 if ((m = malloc(sizeof(*m))) == NULL) 1367 return (-1); 1368 if ((m->name = malloc(strlen(name) + 1)) == NULL) { 1369 free(m); 1370 return (-1); 1371 } 1372 strcpy(m->name, name); 1373 if ((m->value = malloc(strlen(value) + 1)) == NULL) { 1374 free(m->name); 1375 free(m); 1376 return (-1); 1377 } 1378 strcpy(m->value, value); 1379 m->length = strlen(value); 1380 m->perm = 1; 1381 LIST_INSERT_HEAD(¯os, m, link); 1382 return (0); 1383 } 1384