1 /* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Copyright (c) 2004-2006 7 * Hartmut Brandt. 8 * All rights reserved. 9 * 10 * Author: Harti Brandt <harti@freebsd.org> 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $Begemot: gensnmptree.c 383 2006-05-30 07:40:49Z brandt_h $ 34 * 35 * Generate OID table from table description. 36 * 37 * Syntax is: 38 * --------- 39 * file := top | top file 40 * 41 * top := tree | typedef | include 42 * 43 * tree := head elements ')' 44 * 45 * entry := head ':' index STRING elements ')' 46 * 47 * leaf := head type STRING ACCESS ')' 48 * 49 * column := head type ACCESS ')' 50 * 51 * type := BASETYPE | BASETYPE '|' subtype | enum | bits 52 * 53 * subtype := STRING 54 * 55 * enum := ENUM '(' value ')' 56 * 57 * bits := BITS '(' value ')' 58 * 59 * value := optminus INT STRING | optminus INT STRING value 60 * 61 * optminus := '-' | EMPTY 62 * 63 * head := '(' INT STRING 64 * 65 * elements := EMPTY | elements element 66 * 67 * element := tree | leaf | column 68 * 69 * index := type | index type 70 * 71 * typedef := 'typedef' STRING type 72 * 73 * include := 'include' filespec 74 * 75 * filespec := '"' STRING '"' | '<' STRING '>' 76 */ 77 #include <sys/types.h> 78 #include <sys/param.h> 79 #include <stdio.h> 80 #include <stdlib.h> 81 #include <stdarg.h> 82 #include <unistd.h> 83 #include <string.h> 84 #include <ctype.h> 85 #include <inttypes.h> 86 #include <errno.h> 87 #ifdef HAVE_ERR_H 88 #include <err.h> 89 #endif 90 #include <sys/queue.h> 91 #include "support.h" 92 #include "asn1.h" 93 #include "snmp.h" 94 #include "snmpagent.h" 95 96 /* 97 * Constant prefix for all OIDs 98 */ 99 static const asn_subid_t prefix[] = { 1, 3, 6 }; 100 #define PREFIX_LEN (sizeof(prefix) / sizeof(prefix[0])) 101 102 u_int tree_size; 103 static const char *file_prefix = ""; 104 105 /* if true generate local include paths */ 106 static int localincs = 0; 107 108 /* if true print tokens */ 109 static int debug; 110 111 static const char usgtxt[] = "\ 112 Generate SNMP tables.\n\ 113 usage: gensnmptree [-dEehlt] [-I directory] [-i infile] [-p prefix]\n\ 114 [name]...\n\ 115 options:\n\ 116 -d debug mode\n\ 117 -E extract the named enums and bits only\n\ 118 -e extract the named oids or enums\n\ 119 -h print this info\n\ 120 -I directory add directory to include path\n\ 121 -i ifile read from the named file instead of stdin\n\ 122 -l generate local include directives\n\ 123 -p prefix prepend prefix to file and variable names\n\ 124 -t generated a .def file\n\ 125 "; 126 127 /* 128 * A node in the OID tree 129 */ 130 enum ntype { 131 NODE_LEAF = 1, 132 NODE_TREE, 133 NODE_ENTRY, 134 NODE_COLUMN 135 }; 136 137 enum { 138 FL_GET = 0x01, 139 FL_SET = 0x02, 140 }; 141 142 struct node; 143 TAILQ_HEAD(node_list, node); 144 145 struct node { 146 enum ntype type; 147 asn_subid_t id; /* last element of OID */ 148 char *name; /* name of node */ 149 TAILQ_ENTRY(node) link; 150 u_int lno; /* starting line number */ 151 u_int flags; /* allowed operations */ 152 153 union { 154 struct tree { 155 struct node_list subs; 156 } tree; 157 158 struct entry { 159 uint32_t index; /* index for table entry */ 160 char *func; /* function for tables */ 161 struct node_list subs; 162 } entry; 163 164 struct leaf { 165 enum snmp_syntax syntax; /* syntax for this leaf */ 166 char *func; /* function name */ 167 } leaf; 168 169 struct column { 170 enum snmp_syntax syntax; /* syntax for this column */ 171 } column; 172 } u; 173 }; 174 175 struct func { 176 const char *name; 177 LIST_ENTRY(func) link; 178 }; 179 180 static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs); 181 182 struct enums { 183 const char *name; 184 long value; 185 TAILQ_ENTRY(enums) link; 186 }; 187 188 struct type { 189 const char *name; 190 const char *from_fname; 191 u_int from_lno; 192 u_int syntax; 193 int is_enum; 194 int is_bits; 195 TAILQ_HEAD(, enums) enums; 196 LIST_ENTRY(type) link; 197 }; 198 199 static LIST_HEAD(, type) types = LIST_HEAD_INITIALIZER(types); 200 201 static void report(const char *, ...) __dead2 __printflike(1, 2); 202 static void report_node(const struct node *, const char *, ...) 203 __dead2 __printflike(2, 3); 204 205 /************************************************************ 206 * 207 * Allocate memory and panic just in the case... 208 */ 209 static void * 210 xalloc(size_t size) 211 { 212 void *ptr; 213 214 if ((ptr = malloc(size)) == NULL) 215 err(1, "allocing %zu bytes", size); 216 217 return (ptr); 218 } 219 220 static char * 221 savestr(const char *s) 222 { 223 224 if (s == NULL) 225 return (NULL); 226 return (strcpy(xalloc(strlen(s) + 1), s)); 227 } 228 229 /************************************************************ 230 * 231 * Input stack 232 */ 233 struct input { 234 FILE *fp; 235 u_int lno; 236 char *fname; 237 char *path; 238 LIST_ENTRY(input) link; 239 }; 240 static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs); 241 static struct input *input = NULL; 242 243 #define MAX_PATHS 100 244 static u_int npaths = 2; 245 static u_int stdpaths = 2; 246 static const char *paths[MAX_PATHS + 1] = { 247 "/usr/share/snmp/defs", 248 "/usr/local/share/snmp/defs", 249 NULL 250 }; 251 252 static int pbchar = -1; 253 254 static void 255 path_new(const char *path) 256 { 257 if (npaths >= MAX_PATHS) 258 report("too many -I directives"); 259 memmove(&paths[npaths - stdpaths + 1], &paths[npaths - stdpaths], 260 sizeof(path[0]) * stdpaths); 261 paths[npaths - stdpaths] = savestr(path); 262 npaths++; 263 } 264 265 static void 266 input_new(FILE *fp, const char *path, const char *fname) 267 { 268 struct input *ip; 269 270 ip = xalloc(sizeof(*ip)); 271 ip->fp = fp; 272 ip->lno = 1; 273 ip->fname = savestr(fname); 274 ip->path = savestr(path); 275 LIST_INSERT_HEAD(&inputs, ip, link); 276 277 input = ip; 278 } 279 280 static void 281 input_close(void) 282 { 283 284 if (input == NULL) 285 return; 286 fclose(input->fp); 287 free(input->fname); 288 free(input->path); 289 LIST_REMOVE(input, link); 290 free(input); 291 292 input = LIST_FIRST(&inputs); 293 } 294 295 static FILE * 296 tryopen(const char *path, const char *fname) 297 { 298 char *fn; 299 FILE *fp; 300 301 if (path == NULL) 302 fn = savestr(fname); 303 else { 304 fn = xalloc(strlen(path) + strlen(fname) + 2); 305 sprintf(fn, "%s/%s", path, fname); 306 } 307 fp = fopen(fn, "r"); 308 free(fn); 309 return (fp); 310 } 311 312 static void 313 input_fopen(const char *fname, int loc) 314 { 315 FILE *fp; 316 char *path; 317 u_int p; 318 319 if (fname[0] == '/') { 320 if ((fp = tryopen(NULL, fname)) != NULL) { 321 input_new(fp, NULL, fname); 322 return; 323 } 324 325 } else { 326 if (loc) { 327 if (input == NULL) 328 path = NULL; 329 else 330 path = input->path; 331 332 if ((fp = tryopen(path, fname)) != NULL) { 333 input_new(fp, NULL, fname); 334 return; 335 } 336 } 337 338 for (p = 0; paths[p] != NULL; p++) 339 if ((fp = tryopen(paths[p], fname)) != NULL) { 340 input_new(fp, paths[p], fname); 341 return; 342 } 343 } 344 report("cannot open '%s'", fname); 345 } 346 347 static int 348 tgetc(void) 349 { 350 int c; 351 352 if (pbchar != -1) { 353 c = pbchar; 354 pbchar = -1; 355 return (c); 356 } 357 358 for (;;) { 359 if (input == NULL) 360 return (EOF); 361 362 if ((c = getc(input->fp)) != EOF) 363 return (c); 364 365 input_close(); 366 } 367 } 368 369 static void 370 tungetc(int c) 371 { 372 373 if (pbchar != -1) 374 abort(); 375 pbchar = c; 376 } 377 378 /************************************************************ 379 * 380 * Parsing input 381 */ 382 enum tok { 383 TOK_EOF = 0200, /* end-of-file seen */ 384 TOK_NUM, /* number */ 385 TOK_STR, /* string */ 386 TOK_ACCESS, /* access operator */ 387 TOK_TYPE, /* type operator */ 388 TOK_ENUM, /* enum token (kind of a type) */ 389 TOK_TYPEDEF, /* typedef directive */ 390 TOK_DEFTYPE, /* defined type */ 391 TOK_INCLUDE, /* include directive */ 392 TOK_FILENAME, /* filename ("foo.bar" or <foo.bar>) */ 393 TOK_BITS, /* bits token (kind of a type) */ 394 }; 395 396 static const struct { 397 const char *str; 398 enum tok tok; 399 u_int val; 400 } keywords[] = { 401 { "GET", TOK_ACCESS, FL_GET }, 402 { "SET", TOK_ACCESS, FL_SET }, 403 { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL }, 404 { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER }, 405 { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER }, 406 { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE }, 407 { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING }, 408 { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS }, 409 { "OID", TOK_TYPE, SNMP_SYNTAX_OID }, 410 { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS }, 411 { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER }, 412 { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE }, 413 { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 }, 414 { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER }, 415 { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING }, 416 { "typedef", TOK_TYPEDEF, 0 }, 417 { "include", TOK_INCLUDE, 0 }, 418 { NULL, 0, 0 } 419 }; 420 421 /* arbitrary upper limit on node names and function names */ 422 #define MAXSTR 1000 423 char str[MAXSTR]; 424 u_long val; /* integer values */ 425 int all_cond; /* all conditions are true */ 426 int saved_token = -1; 427 428 /* 429 * Report an error and exit. 430 */ 431 static void 432 report(const char *fmt, ...) 433 { 434 va_list ap; 435 int c; 436 437 va_start(ap, fmt); 438 fprintf(stderr, "line %u: ", input->lno); 439 vfprintf(stderr, fmt, ap); 440 fprintf(stderr, "\n"); 441 fprintf(stderr, "context: \""); 442 while ((c = tgetc()) != EOF && c != '\n') 443 fprintf(stderr, "%c", c); 444 fprintf(stderr, "\n"); 445 va_end(ap); 446 exit(1); 447 } 448 static void 449 report_node(const struct node *np, const char *fmt, ...) 450 { 451 va_list ap; 452 453 va_start(ap, fmt); 454 fprintf(stderr, "line %u, node %s: ", np->lno, np->name); 455 vfprintf(stderr, fmt, ap); 456 fprintf(stderr, "\n"); 457 va_end(ap); 458 exit(1); 459 } 460 461 /* 462 * Return a fresh copy of the string constituting the current token. 463 */ 464 static char * 465 savetok(void) 466 { 467 return (savestr(str)); 468 } 469 470 /* 471 * Get the next token from input. 472 */ 473 static int 474 gettoken_internal(void) 475 { 476 int c; 477 struct type *t; 478 479 if (saved_token != -1) { 480 c = saved_token; 481 saved_token = -1; 482 return (c); 483 } 484 485 again: 486 /* 487 * Skip any whitespace before the next token 488 */ 489 while ((c = tgetc()) != EOF) { 490 if (c == '\n') 491 input->lno++; 492 if (!isspace(c)) 493 break; 494 } 495 if (c == EOF) 496 return (TOK_EOF); 497 if (!isascii(c)) 498 report("unexpected character %#2x", (u_int)c); 499 500 /* 501 * Skip comments 502 */ 503 if (c == '#') { 504 while ((c = tgetc()) != EOF) { 505 if (c == '\n') { 506 input->lno++; 507 goto again; 508 } 509 } 510 report("unexpected EOF in comment"); 511 } 512 513 /* 514 * Single character tokens 515 */ 516 if (strchr("():|-", c) != NULL) 517 return (c); 518 519 if (c == '"' || c == '<') { 520 int end = c; 521 size_t n = 0; 522 523 val = 1; 524 if (c == '<') { 525 val = 0; 526 end = '>'; 527 } 528 529 while ((c = tgetc()) != EOF) { 530 if (c == end) 531 break; 532 if (n == sizeof(str) - 1) { 533 str[n++] = '\0'; 534 report("filename too long '%s...'", str); 535 } 536 str[n++] = c; 537 } 538 str[n++] = '\0'; 539 return (TOK_FILENAME); 540 } 541 542 /* 543 * Sort out numbers 544 */ 545 if (isdigit(c)) { 546 size_t n = 0; 547 str[n++] = c; 548 while ((c = tgetc()) != EOF) { 549 if (!isdigit(c)) { 550 tungetc(c); 551 break; 552 } 553 if (n == sizeof(str) - 1) { 554 str[n++] = '\0'; 555 report("number too long '%s...'", str); 556 } 557 str[n++] = c; 558 } 559 str[n++] = '\0'; 560 sscanf(str, "%lu", &val); 561 return (TOK_NUM); 562 } 563 564 /* 565 * So that has to be a string. 566 */ 567 if (isalpha(c) || c == '_') { 568 size_t n = 0; 569 str[n++] = c; 570 while ((c = tgetc()) != EOF) { 571 if (!isalnum(c) && c != '_' && c != '-') { 572 tungetc(c); 573 break; 574 } 575 if (n == sizeof(str) - 1) { 576 str[n++] = '\0'; 577 report("string too long '%s...'", str); 578 } 579 str[n++] = c; 580 } 581 str[n++] = '\0'; 582 583 /* 584 * Keywords 585 */ 586 for (c = 0; keywords[c].str != NULL; c++) 587 if (strcmp(keywords[c].str, str) == 0) { 588 val = keywords[c].val; 589 return (keywords[c].tok); 590 } 591 592 LIST_FOREACH(t, &types, link) { 593 if (strcmp(t->name, str) == 0) { 594 val = t->syntax; 595 return (TOK_DEFTYPE); 596 } 597 } 598 return (TOK_STR); 599 } 600 if (isprint(c)) 601 errx(1, "%u: unexpected character '%c'", input->lno, c); 602 else 603 errx(1, "%u: unexpected character 0x%02x", input->lno, 604 (u_int)c); 605 } 606 static int 607 gettoken(void) 608 { 609 int tok = gettoken_internal(); 610 611 if (debug) { 612 switch (tok) { 613 614 case TOK_EOF: 615 fprintf(stderr, "EOF "); 616 break; 617 618 case TOK_NUM: 619 fprintf(stderr, "NUM(%lu) ", val); 620 break; 621 622 case TOK_STR: 623 fprintf(stderr, "STR(%s) ", str); 624 break; 625 626 case TOK_ACCESS: 627 fprintf(stderr, "ACCESS(%lu) ", val); 628 break; 629 630 case TOK_TYPE: 631 fprintf(stderr, "TYPE(%lu) ", val); 632 break; 633 634 case TOK_ENUM: 635 fprintf(stderr, "ENUM "); 636 break; 637 638 case TOK_BITS: 639 fprintf(stderr, "BITS "); 640 break; 641 642 case TOK_TYPEDEF: 643 fprintf(stderr, "TYPEDEF "); 644 break; 645 646 case TOK_DEFTYPE: 647 fprintf(stderr, "DEFTYPE(%s,%lu) ", str, val); 648 break; 649 650 case TOK_INCLUDE: 651 fprintf(stderr, "INCLUDE "); 652 break; 653 654 case TOK_FILENAME: 655 fprintf(stderr, "FILENAME "); 656 break; 657 658 default: 659 if (tok < TOK_EOF) { 660 if (isprint(tok)) 661 fprintf(stderr, "'%c' ", tok); 662 else if (tok == '\n') 663 fprintf(stderr, "\n"); 664 else 665 fprintf(stderr, "%02x ", tok); 666 } else 667 abort(); 668 break; 669 } 670 } 671 return (tok); 672 } 673 674 /** 675 * Pushback a token 676 */ 677 static void 678 pushback(enum tok tok) 679 { 680 681 if (saved_token != -1) 682 abort(); 683 saved_token = tok; 684 } 685 686 /* 687 * Create a new type 688 */ 689 static struct type * 690 make_type(const char *s) 691 { 692 struct type *t; 693 694 t = xalloc(sizeof(*t)); 695 t->name = savestr(s); 696 t->is_enum = 0; 697 t->syntax = SNMP_SYNTAX_NULL; 698 t->from_fname = savestr(input->fname); 699 t->from_lno = input->lno; 700 TAILQ_INIT(&t->enums); 701 LIST_INSERT_HEAD(&types, t, link); 702 703 return (t); 704 } 705 706 /* 707 * Parse a type. We've seen the ENUM or type keyword already. Leave next 708 * token. 709 */ 710 static u_int 711 parse_type(enum tok *tok, struct type *t, const char *vname) 712 { 713 u_int syntax; 714 struct enums *e; 715 716 syntax = val; 717 718 if (*tok == TOK_ENUM || *tok == TOK_BITS) { 719 if (t == NULL && vname != NULL) { 720 t = make_type(vname); 721 t->is_enum = (*tok == TOK_ENUM); 722 t->is_bits = (*tok == TOK_BITS); 723 t->syntax = syntax; 724 } 725 if (gettoken() != '(') 726 report("'(' expected after ENUM"); 727 728 if ((*tok = gettoken()) == TOK_EOF) 729 report("unexpected EOF in ENUM"); 730 do { 731 e = NULL; 732 if (t != NULL) { 733 e = xalloc(sizeof(*e)); 734 } 735 if (*tok == '-') { 736 if ((*tok = gettoken()) == TOK_EOF) 737 report("unexpected EOF in ENUM"); 738 e->value = -(long)val; 739 } else 740 e->value = val; 741 742 if (*tok != TOK_NUM) 743 report("need value for ENUM/BITS"); 744 if (gettoken() != TOK_STR) 745 report("need string in ENUM/BITS"); 746 if (e != NULL) { 747 e->name = savetok(); 748 TAILQ_INSERT_TAIL(&t->enums, e, link); 749 } 750 if ((*tok = gettoken()) == TOK_EOF) 751 report("unexpected EOF in ENUM/BITS"); 752 } while (*tok != ')'); 753 *tok = gettoken(); 754 755 } else if (*tok == TOK_DEFTYPE) { 756 *tok = gettoken(); 757 758 } else { 759 if ((*tok = gettoken()) == '|') { 760 if (gettoken() != TOK_STR) 761 report("subtype expected after '|'"); 762 *tok = gettoken(); 763 } 764 } 765 766 return (syntax); 767 } 768 769 /* 770 * Parse the next node (complete with all subnodes) 771 */ 772 static struct node * 773 parse(enum tok tok) 774 { 775 struct node *node; 776 struct node *sub; 777 u_int index_count; 778 779 node = xalloc(sizeof(struct node)); 780 node->lno = input->lno; 781 node->flags = 0; 782 783 if (tok != '(') 784 report("'(' expected at begin of node"); 785 if (gettoken() != TOK_NUM) 786 report("node id expected after opening '('"); 787 if (val > ASN_MAXID) 788 report("subid too large '%lu'", val); 789 node->id = (asn_subid_t)val; 790 if (gettoken() != TOK_STR) 791 report("node name expected after '(' ID"); 792 node->name = savetok(); 793 794 if ((tok = gettoken()) == TOK_TYPE || tok == TOK_DEFTYPE || 795 tok == TOK_ENUM || tok == TOK_BITS) { 796 /* LEAF or COLUM */ 797 u_int syntax = parse_type(&tok, NULL, node->name); 798 799 if (tok == TOK_STR) { 800 /* LEAF */ 801 node->type = NODE_LEAF; 802 node->u.leaf.func = savetok(); 803 node->u.leaf.syntax = syntax; 804 tok = gettoken(); 805 } else { 806 /* COLUMN */ 807 node->type = NODE_COLUMN; 808 node->u.column.syntax = syntax; 809 } 810 811 while (tok != ')') { 812 if (tok != TOK_ACCESS) 813 report("access keyword or ')' expected"); 814 node->flags |= (u_int)val; 815 tok = gettoken(); 816 } 817 818 } else if (tok == ':') { 819 /* ENTRY */ 820 node->type = NODE_ENTRY; 821 TAILQ_INIT(&node->u.entry.subs); 822 823 index_count = 0; 824 node->u.entry.index = 0; 825 tok = gettoken(); 826 while (tok == TOK_TYPE || tok == TOK_DEFTYPE || 827 tok == TOK_ENUM || tok == TOK_BITS) { 828 u_int syntax = parse_type(&tok, NULL, node->name); 829 if (index_count++ == SNMP_INDEXES_MAX) 830 report("too many table indexes"); 831 node->u.entry.index |= 832 syntax << (SNMP_INDEX_SHIFT * index_count); 833 } 834 node->u.entry.index |= index_count; 835 if (index_count == 0) 836 report("need at least one index"); 837 if (tok != TOK_STR) 838 report("function name expected"); 839 840 node->u.entry.func = savetok(); 841 842 tok = gettoken(); 843 844 while (tok != ')') { 845 sub = parse(tok); 846 TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link); 847 tok = gettoken(); 848 } 849 850 } else { 851 /* subtree */ 852 node->type = NODE_TREE; 853 TAILQ_INIT(&node->u.tree.subs); 854 855 while (tok != ')') { 856 sub = parse(tok); 857 TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link); 858 tok = gettoken(); 859 } 860 } 861 return (node); 862 } 863 864 /* 865 * Parse a top level element. Return the tree if it was a tree, NULL 866 * otherwise. 867 */ 868 static struct node * 869 parse_top(enum tok tok) 870 { 871 struct type *t; 872 873 if (tok == '(') 874 return (parse(tok)); 875 876 if (tok == TOK_TYPEDEF) { 877 if (gettoken() != TOK_STR) 878 report("type name expected after typedef"); 879 880 t = make_type(str); 881 882 tok = gettoken(); 883 t->is_enum = (tok == TOK_ENUM); 884 t->is_bits = (tok == TOK_BITS); 885 t->syntax = parse_type(&tok, t, NULL); 886 pushback(tok); 887 888 return (NULL); 889 } 890 891 if (tok == TOK_INCLUDE) { 892 if (gettoken() != TOK_FILENAME) 893 report("filename expected in include directive"); 894 895 input_fopen(str, val); 896 return (NULL); 897 } 898 899 report("'(' or 'typedef' expected"); 900 } 901 902 /* 903 * Generate the C-code table part for one node. 904 */ 905 static void 906 gen_node(FILE *fp, struct node *np, struct asn_oid *oid, u_int idx, 907 const char *func) 908 { 909 u_int n; 910 struct node *sub; 911 u_int syntax; 912 913 if (oid->len == ASN_MAXOIDLEN) 914 report_node(np, "OID too long"); 915 oid->subs[oid->len++] = np->id; 916 917 if (np->type == NODE_TREE) { 918 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 919 gen_node(fp, sub, oid, 0, NULL); 920 oid->len--; 921 return; 922 } 923 if (np->type == NODE_ENTRY) { 924 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 925 gen_node(fp, sub, oid, np->u.entry.index, 926 np->u.entry.func); 927 oid->len--; 928 return; 929 } 930 931 /* leaf or column */ 932 if ((np->flags & (FL_GET|FL_SET)) == 0) { 933 oid->len--; 934 return; 935 } 936 937 fprintf(fp, " {{ %u, {", oid->len); 938 for (n = 0; n < oid->len; n++) 939 fprintf(fp, " %u,", oid->subs[n]); 940 fprintf(fp, " }}, \"%s\", ", np->name); 941 942 if (np->type == NODE_COLUMN) { 943 syntax = np->u.column.syntax; 944 fprintf(fp, "SNMP_NODE_COLUMN, "); 945 } else { 946 syntax = np->u.leaf.syntax; 947 fprintf(fp, "SNMP_NODE_LEAF, "); 948 } 949 950 switch (syntax) { 951 952 case SNMP_SYNTAX_NULL: 953 fprintf(fp, "SNMP_SYNTAX_NULL, "); 954 break; 955 956 case SNMP_SYNTAX_INTEGER: 957 fprintf(fp, "SNMP_SYNTAX_INTEGER, "); 958 break; 959 960 case SNMP_SYNTAX_OCTETSTRING: 961 fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, "); 962 break; 963 964 case SNMP_SYNTAX_IPADDRESS: 965 fprintf(fp, "SNMP_SYNTAX_IPADDRESS, "); 966 break; 967 968 case SNMP_SYNTAX_OID: 969 fprintf(fp, "SNMP_SYNTAX_OID, "); 970 break; 971 972 case SNMP_SYNTAX_TIMETICKS: 973 fprintf(fp, "SNMP_SYNTAX_TIMETICKS, "); 974 break; 975 976 case SNMP_SYNTAX_COUNTER: 977 fprintf(fp, "SNMP_SYNTAX_COUNTER, "); 978 break; 979 980 case SNMP_SYNTAX_GAUGE: 981 fprintf(fp, "SNMP_SYNTAX_GAUGE, "); 982 break; 983 984 case SNMP_SYNTAX_COUNTER64: 985 fprintf(fp, "SNMP_SYNTAX_COUNTER64, "); 986 break; 987 988 case SNMP_SYNTAX_NOSUCHOBJECT: 989 case SNMP_SYNTAX_NOSUCHINSTANCE: 990 case SNMP_SYNTAX_ENDOFMIBVIEW: 991 abort(); 992 } 993 994 if (np->type == NODE_COLUMN) 995 fprintf(fp, "%s, ", func); 996 else 997 fprintf(fp, "%s, ", np->u.leaf.func); 998 999 fprintf(fp, "0"); 1000 if (np->flags & FL_SET) 1001 fprintf(fp, "|SNMP_NODE_CANSET"); 1002 fprintf(fp, ", %#x, NULL, NULL },\n", idx); 1003 oid->len--; 1004 return; 1005 } 1006 1007 /* 1008 * Generate the header file with the function declarations. 1009 */ 1010 static void 1011 gen_header(FILE *fp, struct node *np, u_int oidlen, const char *func) 1012 { 1013 char f[MAXSTR + 4]; 1014 struct node *sub; 1015 struct func *ptr; 1016 1017 oidlen++; 1018 if (np->type == NODE_TREE) { 1019 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 1020 gen_header(fp, sub, oidlen, NULL); 1021 return; 1022 } 1023 if (np->type == NODE_ENTRY) { 1024 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 1025 gen_header(fp, sub, oidlen, np->u.entry.func); 1026 return; 1027 } 1028 1029 if((np->flags & (FL_GET|FL_SET)) == 0) 1030 return; 1031 1032 if (np->type == NODE_COLUMN) { 1033 if (func == NULL) 1034 errx(1, "column without function (%s) - probably " 1035 "outside of a table", np->name); 1036 sprintf(f, "%s", func); 1037 } else 1038 sprintf(f, "%s", np->u.leaf.func); 1039 1040 LIST_FOREACH(ptr, &funcs, link) 1041 if (strcmp(ptr->name, f) == 0) 1042 break; 1043 1044 if (ptr == NULL) { 1045 ptr = xalloc(sizeof(*ptr)); 1046 ptr->name = savestr(f); 1047 LIST_INSERT_HEAD(&funcs, ptr, link); 1048 1049 fprintf(fp, "int %s(struct snmp_context *, " 1050 "struct snmp_value *, u_int, u_int, " 1051 "enum snmp_op);\n", f); 1052 } 1053 1054 fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id); 1055 } 1056 1057 /* 1058 * Generate the OID table. 1059 */ 1060 static void 1061 gen_table(FILE *fp, struct node *node) 1062 { 1063 struct asn_oid oid; 1064 1065 fprintf(fp, "#include <sys/types.h>\n"); 1066 fprintf(fp, "#include <stdio.h>\n"); 1067 #ifdef HAVE_STDINT_H 1068 fprintf(fp, "#include <stdint.h>\n"); 1069 #endif 1070 if (localincs) { 1071 fprintf(fp, "#include \"asn1.h\"\n"); 1072 fprintf(fp, "#include \"snmp.h\"\n"); 1073 fprintf(fp, "#include \"snmpagent.h\"\n"); 1074 } else { 1075 fprintf(fp, "#include <bsnmp/asn1.h>\n"); 1076 fprintf(fp, "#include <bsnmp/snmp.h>\n"); 1077 fprintf(fp, "#include <bsnmp/snmpagent.h>\n"); 1078 } 1079 fprintf(fp, "#include \"%stree.h\"\n", file_prefix); 1080 fprintf(fp, "\n"); 1081 1082 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix); 1083 1084 oid.len = PREFIX_LEN; 1085 memcpy(oid.subs, prefix, sizeof(prefix)); 1086 gen_node(fp, node, &oid, 0, NULL); 1087 1088 fprintf(fp, "};\n\n"); 1089 } 1090 1091 static void 1092 print_syntax(u_int syntax) 1093 { 1094 u_int i; 1095 1096 for (i = 0; keywords[i].str != NULL; i++) 1097 if (keywords[i].tok == TOK_TYPE && 1098 keywords[i].val == syntax) { 1099 printf(" %s", keywords[i].str); 1100 return; 1101 } 1102 abort(); 1103 } 1104 1105 /* 1106 * Generate a tree definition file 1107 */ 1108 static void 1109 gen_tree(const struct node *np, int level) 1110 { 1111 const struct node *sp; 1112 u_int i; 1113 1114 printf("%*s(%u %s", 2 * level, "", np->id, np->name); 1115 1116 switch (np->type) { 1117 1118 case NODE_LEAF: 1119 print_syntax(np->u.leaf.syntax); 1120 printf(" %s%s%s)\n", np->u.leaf.func, 1121 (np->flags & FL_GET) ? " GET" : "", 1122 (np->flags & FL_SET) ? " SET" : ""); 1123 break; 1124 1125 case NODE_TREE: 1126 if (TAILQ_EMPTY(&np->u.tree.subs)) { 1127 printf(")\n"); 1128 } else { 1129 printf("\n"); 1130 TAILQ_FOREACH(sp, &np->u.tree.subs, link) 1131 gen_tree(sp, level + 1); 1132 printf("%*s)\n", 2 * level, ""); 1133 } 1134 break; 1135 1136 case NODE_ENTRY: 1137 printf(" :"); 1138 1139 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) 1140 print_syntax(SNMP_INDEX(np->u.entry.index, i)); 1141 printf(" %s\n", np->u.entry.func); 1142 TAILQ_FOREACH(sp, &np->u.entry.subs, link) 1143 gen_tree(sp, level + 1); 1144 printf("%*s)\n", 2 * level, ""); 1145 break; 1146 1147 case NODE_COLUMN: 1148 print_syntax(np->u.column.syntax); 1149 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "", 1150 (np->flags & FL_SET) ? " SET" : ""); 1151 break; 1152 } 1153 } 1154 1155 static int 1156 extract(FILE *fp, const struct node *np, struct asn_oid *oid, const char *obj, 1157 const struct asn_oid *idx, const char *iname) 1158 { 1159 struct node *sub; 1160 u_long n; 1161 1162 if (oid->len == ASN_MAXOIDLEN) 1163 report_node(np, "OID too long"); 1164 oid->subs[oid->len++] = np->id; 1165 1166 if (strcmp(obj, np->name) == 0) { 1167 if (oid->len + idx->len >= ASN_MAXOIDLEN) 1168 report_node(np, "OID too long"); 1169 fprintf(fp, "#define OID_%s%s\t%u\n", np->name, 1170 iname ? iname : "", np->id); 1171 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name, 1172 iname ? iname : "", oid->len + idx->len); 1173 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name, 1174 iname ? iname : "", oid->len + idx->len); 1175 for (n = 0; n < oid->len; n++) 1176 fprintf(fp, " %u,", oid->subs[n]); 1177 for (n = 0; n < idx->len; n++) 1178 fprintf(fp, " %u,", idx->subs[n]); 1179 fprintf(fp, " } }\n"); 1180 return (0); 1181 } 1182 1183 if (np->type == NODE_TREE) { 1184 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 1185 if (!extract(fp, sub, oid, obj, idx, iname)) 1186 return (0); 1187 } else if (np->type == NODE_ENTRY) { 1188 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 1189 if (!extract(fp, sub, oid, obj, idx, iname)) 1190 return (0); 1191 } 1192 oid->len--; 1193 return (1); 1194 } 1195 1196 static int 1197 gen_extract(FILE *fp, const struct node *root, char *object) 1198 { 1199 struct asn_oid oid; 1200 struct asn_oid idx; 1201 char *s, *e, *end, *iname; 1202 u_long ul; 1203 int ret; 1204 1205 /* look whether the object to extract has an index part */ 1206 idx.len = 0; 1207 iname = NULL; 1208 s = strchr(object, '.'); 1209 if (s != NULL) { 1210 iname = malloc(strlen(s) + 1); 1211 if (iname == NULL) 1212 err(1, "cannot allocated index"); 1213 1214 strcpy(iname, s); 1215 for (e = iname; *e != '\0'; e++) 1216 if (*e == '.') 1217 *e = '_'; 1218 1219 *s++ = '\0'; 1220 while (s != NULL) { 1221 if (*s == '\0') 1222 errx(1, "bad index syntax"); 1223 if ((e = strchr(s, '.')) != NULL) 1224 *e++ = '\0'; 1225 1226 errno = 0; 1227 ul = strtoul(s, &end, 0); 1228 if (*end != '\0') 1229 errx(1, "bad index syntax '%s'", end); 1230 if (errno != 0) 1231 err(1, "bad index syntax"); 1232 1233 if (idx.len == ASN_MAXOIDLEN) 1234 errx(1, "index oid too large"); 1235 idx.subs[idx.len++] = ul; 1236 1237 s = e; 1238 } 1239 } 1240 1241 oid.len = PREFIX_LEN; 1242 memcpy(oid.subs, prefix, sizeof(prefix)); 1243 ret = extract(fp, root, &oid, object, &idx, iname); 1244 if (iname != NULL) 1245 free(iname); 1246 1247 return (ret); 1248 } 1249 1250 1251 static void 1252 check_sub_order(const struct node *np, const struct node_list *subs) 1253 { 1254 int first; 1255 const struct node *sub; 1256 asn_subid_t maxid = 0; 1257 1258 /* ensure, that subids are ordered */ 1259 first = 1; 1260 TAILQ_FOREACH(sub, subs, link) { 1261 if (!first && sub->id <= maxid) 1262 report_node(np, "subids not ordered at %s", sub->name); 1263 maxid = sub->id; 1264 first = 0; 1265 } 1266 } 1267 1268 /* 1269 * Do some sanity checks on the tree definition and do some computations. 1270 */ 1271 static void 1272 check_tree(struct node *np) 1273 { 1274 struct node *sub; 1275 1276 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) { 1277 if ((np->flags & (FL_GET|FL_SET)) != 0) 1278 tree_size++; 1279 return; 1280 } 1281 1282 if (np->type == NODE_ENTRY) { 1283 check_sub_order(np, &np->u.entry.subs); 1284 1285 /* ensure all subnodes are columns */ 1286 TAILQ_FOREACH(sub, &np->u.entry.subs, link) { 1287 if (sub->type != NODE_COLUMN) 1288 report_node(np, "entry subnode '%s' is not " 1289 "a column", sub->name); 1290 check_tree(sub); 1291 } 1292 } else { 1293 check_sub_order(np, &np->u.tree.subs); 1294 1295 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 1296 check_tree(sub); 1297 } 1298 } 1299 1300 static void 1301 merge_subs(struct node_list *s1, struct node_list *s2) 1302 { 1303 struct node *n1, *n2; 1304 1305 while (!TAILQ_EMPTY(s2)) { 1306 n2 = TAILQ_FIRST(s2); 1307 TAILQ_REMOVE(s2, n2, link); 1308 1309 TAILQ_FOREACH(n1, s1, link) 1310 if (n1->id >= n2->id) 1311 break; 1312 if (n1 == NULL) 1313 TAILQ_INSERT_TAIL(s1, n2, link); 1314 else if (n1->id > n2->id) 1315 TAILQ_INSERT_BEFORE(n1, n2, link); 1316 else { 1317 if (n1->type == NODE_TREE && n2->type == NODE_TREE) { 1318 if (strcmp(n1->name, n2->name) != 0) 1319 errx(1, "trees to merge must have " 1320 "same name '%s' '%s'", n1->name, 1321 n2->name); 1322 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs); 1323 free(n2); 1324 } else if (n1->type == NODE_ENTRY && 1325 n2->type == NODE_ENTRY) { 1326 if (strcmp(n1->name, n2->name) != 0) 1327 errx(1, "entries to merge must have " 1328 "same name '%s' '%s'", n1->name, 1329 n2->name); 1330 if (n1->u.entry.index != n2->u.entry.index) 1331 errx(1, "entries to merge must have " 1332 "same index '%s'", n1->name); 1333 if (strcmp(n1->u.entry.func, 1334 n2->u.entry.func) != 0) 1335 errx(1, "entries to merge must have " 1336 "same op '%s'", n1->name); 1337 merge_subs(&n1->u.entry.subs, 1338 &n2->u.entry.subs); 1339 free(n2); 1340 } else 1341 errx(1, "entities to merge must be both " 1342 "trees or both entries: %s, %s", 1343 n1->name, n2->name); 1344 } 1345 } 1346 } 1347 1348 static void 1349 merge(struct node **root, struct node *t) 1350 { 1351 1352 if (*root == NULL) { 1353 *root = t; 1354 return; 1355 } 1356 if (t == NULL) 1357 return; 1358 1359 /* both must be trees */ 1360 if ((*root)->type != NODE_TREE) 1361 errx(1, "root is not a tree"); 1362 if (t->type != NODE_TREE) 1363 errx(1, "can merge only with tree"); 1364 if ((*root)->id != t->id) 1365 errx(1, "trees to merge must have same id"); 1366 1367 merge_subs(&(*root)->u.tree.subs, &t->u.tree.subs); 1368 } 1369 1370 static void 1371 unminus(FILE *fp, const char *s) 1372 { 1373 1374 while (*s != '\0') { 1375 if (*s == '-') 1376 fprintf(fp, "_"); 1377 else 1378 fprintf(fp, "%c", *s); 1379 s++; 1380 } 1381 } 1382 1383 static void 1384 gen_enum(FILE *fp, const struct type *t) 1385 { 1386 const struct enums *e; 1387 long min = LONG_MAX; 1388 1389 fprintf(fp, "\n"); 1390 fprintf(fp, "#ifndef %s_defined__\n", t->name); 1391 fprintf(fp, "#define %s_defined__\n", t->name); 1392 fprintf(fp, "/*\n"); 1393 fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno); 1394 fprintf(fp, " */\n"); 1395 fprintf(fp, "enum %s {\n", t->name); 1396 TAILQ_FOREACH(e, &t->enums, link) { 1397 fprintf(fp, "\t%s_", t->name); 1398 unminus(fp, e->name); 1399 fprintf(fp, " = %ld,\n", e->value); 1400 if (e->value < min) 1401 min = e->value; 1402 } 1403 fprintf(fp, "};\n"); 1404 fprintf(fp, "#define STROFF_%s %ld\n", t->name, min); 1405 fprintf(fp, "#define STRING_%s \\\n", t->name); 1406 TAILQ_FOREACH(e, &t->enums, link) { 1407 fprintf(fp, "\t[%ld] \"%s_", e->value - min, t->name); 1408 unminus(fp, e->name); 1409 fprintf(fp, "\",\\\n"); 1410 } 1411 fprintf(fp, "\n"); 1412 fprintf(fp, "#endif /* %s_defined__ */\n", t->name); 1413 } 1414 1415 static void 1416 gen_enums(FILE *fp) 1417 { 1418 const struct type *t; 1419 1420 LIST_FOREACH(t, &types, link) 1421 if (t->is_enum || t->is_bits) 1422 gen_enum(fp, t); 1423 } 1424 1425 static int 1426 extract_enum(FILE *fp, const char *name) 1427 { 1428 const struct type *t; 1429 1430 LIST_FOREACH(t, &types, link) 1431 if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) { 1432 gen_enum(fp, t); 1433 return (0); 1434 } 1435 return (-1); 1436 } 1437 1438 int 1439 main(int argc, char *argv[]) 1440 { 1441 int do_extract = 0; 1442 int do_tree = 0; 1443 int do_enums = 0; 1444 int opt; 1445 struct node *root; 1446 char fname[MAXPATHLEN + 1]; 1447 int tok; 1448 FILE *fp; 1449 char *infile = NULL; 1450 1451 while ((opt = getopt(argc, argv, "dEehI:i:lp:t")) != EOF) 1452 switch (opt) { 1453 1454 case 'd': 1455 debug = 1; 1456 break; 1457 1458 case 'h': 1459 fprintf(stderr, "%s", usgtxt); 1460 exit(0); 1461 1462 case 'E': 1463 do_enums = 1; 1464 break; 1465 1466 case 'e': 1467 do_extract = 1; 1468 break; 1469 1470 case 'I': 1471 path_new(optarg); 1472 break; 1473 1474 case 'i': 1475 infile = optarg; 1476 break; 1477 1478 case 'l': 1479 localincs = 1; 1480 break; 1481 1482 case 'p': 1483 file_prefix = optarg; 1484 if (strlen(file_prefix) + strlen("tree.c") > 1485 MAXPATHLEN) 1486 errx(1, "prefix too long"); 1487 break; 1488 1489 case 't': 1490 do_tree = 1; 1491 break; 1492 } 1493 1494 if (do_extract + do_tree + do_enums > 1) 1495 errx(1, "conflicting options -e/-t/-E"); 1496 if (!do_extract && !do_enums && argc != optind) 1497 errx(1, "no arguments allowed"); 1498 if ((do_extract || do_enums) && argc == optind) 1499 errx(1, "no objects specified"); 1500 1501 if (infile == NULL) { 1502 input_new(stdin, NULL, "<stdin>"); 1503 } else { 1504 if ((fp = fopen(infile, "r")) == NULL) 1505 err(1, "%s", infile); 1506 input_new(fp, NULL, infile); 1507 } 1508 1509 root = parse_top(gettoken()); 1510 while ((tok = gettoken()) != TOK_EOF) 1511 merge(&root, parse_top(tok)); 1512 1513 check_tree(root); 1514 1515 if (do_extract) { 1516 while (optind < argc) { 1517 if (gen_extract(stdout, root, argv[optind])) 1518 errx(1, "object not found: %s", argv[optind]); 1519 optind++; 1520 } 1521 return (0); 1522 } 1523 if (do_enums) { 1524 while (optind < argc) { 1525 if (extract_enum(stdout, argv[optind])) 1526 errx(1, "enum not found: %s", argv[optind]); 1527 optind++; 1528 } 1529 return (0); 1530 } 1531 if (do_tree) { 1532 gen_tree(root, 0); 1533 return (0); 1534 } 1535 sprintf(fname, "%stree.h", file_prefix); 1536 if ((fp = fopen(fname, "w")) == NULL) 1537 err(1, "%s: ", fname); 1538 gen_header(fp, root, PREFIX_LEN, NULL); 1539 1540 fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n"); 1541 gen_enums(fp); 1542 fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n"); 1543 1544 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size); 1545 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix); 1546 1547 fclose(fp); 1548 1549 sprintf(fname, "%stree.c", file_prefix); 1550 if ((fp = fopen(fname, "w")) == NULL) 1551 err(1, "%s: ", fname); 1552 gen_table(fp, root); 1553 fclose(fp); 1554 1555 return (0); 1556 } 1557