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