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 e->name = savetok(); 747 TAILQ_INSERT_TAIL(&t->enums, e, link); 748 if ((*tok = gettoken()) == TOK_EOF) 749 report("unexpected EOF in ENUM/BITS"); 750 } while (*tok != ')'); 751 *tok = gettoken(); 752 753 } else if (*tok == TOK_DEFTYPE) { 754 *tok = gettoken(); 755 756 } else { 757 if ((*tok = gettoken()) == '|') { 758 if (gettoken() != TOK_STR) 759 report("subtype expected after '|'"); 760 *tok = gettoken(); 761 } 762 } 763 764 return (syntax); 765 } 766 767 /* 768 * Parse the next node (complete with all subnodes) 769 */ 770 static struct node * 771 parse(enum tok tok) 772 { 773 struct node *node; 774 struct node *sub; 775 u_int index_count; 776 777 node = xalloc(sizeof(struct node)); 778 node->lno = input->lno; 779 node->flags = 0; 780 781 if (tok != '(') 782 report("'(' expected at begin of node"); 783 if (gettoken() != TOK_NUM) 784 report("node id expected after opening '('"); 785 if (val > ASN_MAXID) 786 report("subid too large '%lu'", val); 787 node->id = (asn_subid_t)val; 788 if (gettoken() != TOK_STR) 789 report("node name expected after '(' ID"); 790 node->name = savetok(); 791 792 if ((tok = gettoken()) == TOK_TYPE || tok == TOK_DEFTYPE || 793 tok == TOK_ENUM || tok == TOK_BITS) { 794 /* LEAF or COLUM */ 795 u_int syntax = parse_type(&tok, NULL, node->name); 796 797 if (tok == TOK_STR) { 798 /* LEAF */ 799 node->type = NODE_LEAF; 800 node->u.leaf.func = savetok(); 801 node->u.leaf.syntax = syntax; 802 tok = gettoken(); 803 } else { 804 /* COLUMN */ 805 node->type = NODE_COLUMN; 806 node->u.column.syntax = syntax; 807 } 808 809 while (tok != ')') { 810 if (tok != TOK_ACCESS) 811 report("access keyword or ')' expected"); 812 node->flags |= (u_int)val; 813 tok = gettoken(); 814 } 815 816 } else if (tok == ':') { 817 /* ENTRY */ 818 node->type = NODE_ENTRY; 819 TAILQ_INIT(&node->u.entry.subs); 820 821 index_count = 0; 822 node->u.entry.index = 0; 823 tok = gettoken(); 824 while (tok == TOK_TYPE || tok == TOK_DEFTYPE || 825 tok == TOK_ENUM || tok == TOK_BITS) { 826 u_int syntax = parse_type(&tok, NULL, node->name); 827 if (index_count++ == SNMP_INDEXES_MAX) 828 report("too many table indexes"); 829 node->u.entry.index |= 830 syntax << (SNMP_INDEX_SHIFT * index_count); 831 } 832 node->u.entry.index |= index_count; 833 if (index_count == 0) 834 report("need at least one index"); 835 if (tok != TOK_STR) 836 report("function name expected"); 837 838 node->u.entry.func = savetok(); 839 840 tok = gettoken(); 841 842 while (tok != ')') { 843 sub = parse(tok); 844 TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link); 845 tok = gettoken(); 846 } 847 848 } else { 849 /* subtree */ 850 node->type = NODE_TREE; 851 TAILQ_INIT(&node->u.tree.subs); 852 853 while (tok != ')') { 854 sub = parse(tok); 855 TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link); 856 tok = gettoken(); 857 } 858 } 859 return (node); 860 } 861 862 /* 863 * Parse a top level element. Return the tree if it was a tree, NULL 864 * otherwise. 865 */ 866 static struct node * 867 parse_top(enum tok tok) 868 { 869 struct type *t; 870 871 if (tok == '(') 872 return (parse(tok)); 873 874 if (tok == TOK_TYPEDEF) { 875 if (gettoken() != TOK_STR) 876 report("type name expected after typedef"); 877 878 t = make_type(str); 879 880 tok = gettoken(); 881 t->is_enum = (tok == TOK_ENUM); 882 t->is_bits = (tok == TOK_BITS); 883 t->syntax = parse_type(&tok, t, NULL); 884 pushback(tok); 885 886 return (NULL); 887 } 888 889 if (tok == TOK_INCLUDE) { 890 if (gettoken() != TOK_FILENAME) 891 report("filename expected in include directive"); 892 893 input_fopen(str, val); 894 return (NULL); 895 } 896 897 report("'(' or 'typedef' expected"); 898 } 899 900 /* 901 * Generate the C-code table part for one node. 902 */ 903 static void 904 gen_node(FILE *fp, struct node *np, struct asn_oid *oid, u_int idx, 905 const char *func) 906 { 907 u_int n; 908 struct node *sub; 909 u_int syntax; 910 911 if (oid->len == ASN_MAXOIDLEN) 912 report_node(np, "OID too long"); 913 oid->subs[oid->len++] = np->id; 914 915 if (np->type == NODE_TREE) { 916 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 917 gen_node(fp, sub, oid, 0, NULL); 918 oid->len--; 919 return; 920 } 921 if (np->type == NODE_ENTRY) { 922 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 923 gen_node(fp, sub, oid, np->u.entry.index, 924 np->u.entry.func); 925 oid->len--; 926 return; 927 } 928 929 /* leaf or column */ 930 if ((np->flags & (FL_GET|FL_SET)) == 0) { 931 oid->len--; 932 return; 933 } 934 935 fprintf(fp, " {{ %u, {", oid->len); 936 for (n = 0; n < oid->len; n++) 937 fprintf(fp, " %u,", oid->subs[n]); 938 fprintf(fp, " }}, \"%s\", ", np->name); 939 940 if (np->type == NODE_COLUMN) { 941 syntax = np->u.column.syntax; 942 fprintf(fp, "SNMP_NODE_COLUMN, "); 943 } else { 944 syntax = np->u.leaf.syntax; 945 fprintf(fp, "SNMP_NODE_LEAF, "); 946 } 947 948 switch (syntax) { 949 950 case SNMP_SYNTAX_NULL: 951 fprintf(fp, "SNMP_SYNTAX_NULL, "); 952 break; 953 954 case SNMP_SYNTAX_INTEGER: 955 fprintf(fp, "SNMP_SYNTAX_INTEGER, "); 956 break; 957 958 case SNMP_SYNTAX_OCTETSTRING: 959 fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, "); 960 break; 961 962 case SNMP_SYNTAX_IPADDRESS: 963 fprintf(fp, "SNMP_SYNTAX_IPADDRESS, "); 964 break; 965 966 case SNMP_SYNTAX_OID: 967 fprintf(fp, "SNMP_SYNTAX_OID, "); 968 break; 969 970 case SNMP_SYNTAX_TIMETICKS: 971 fprintf(fp, "SNMP_SYNTAX_TIMETICKS, "); 972 break; 973 974 case SNMP_SYNTAX_COUNTER: 975 fprintf(fp, "SNMP_SYNTAX_COUNTER, "); 976 break; 977 978 case SNMP_SYNTAX_GAUGE: 979 fprintf(fp, "SNMP_SYNTAX_GAUGE, "); 980 break; 981 982 case SNMP_SYNTAX_COUNTER64: 983 fprintf(fp, "SNMP_SYNTAX_COUNTER64, "); 984 break; 985 986 case SNMP_SYNTAX_NOSUCHOBJECT: 987 case SNMP_SYNTAX_NOSUCHINSTANCE: 988 case SNMP_SYNTAX_ENDOFMIBVIEW: 989 abort(); 990 } 991 992 if (np->type == NODE_COLUMN) 993 fprintf(fp, "%s, ", func); 994 else 995 fprintf(fp, "%s, ", np->u.leaf.func); 996 997 fprintf(fp, "0"); 998 if (np->flags & FL_SET) 999 fprintf(fp, "|SNMP_NODE_CANSET"); 1000 fprintf(fp, ", %#x, NULL, NULL },\n", idx); 1001 oid->len--; 1002 return; 1003 } 1004 1005 /* 1006 * Generate the header file with the function declarations. 1007 */ 1008 static void 1009 gen_header(FILE *fp, struct node *np, u_int oidlen, const char *func) 1010 { 1011 char f[MAXSTR + 4]; 1012 struct node *sub; 1013 struct func *ptr; 1014 1015 oidlen++; 1016 if (np->type == NODE_TREE) { 1017 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 1018 gen_header(fp, sub, oidlen, NULL); 1019 return; 1020 } 1021 if (np->type == NODE_ENTRY) { 1022 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 1023 gen_header(fp, sub, oidlen, np->u.entry.func); 1024 return; 1025 } 1026 1027 if((np->flags & (FL_GET|FL_SET)) == 0) 1028 return; 1029 1030 if (np->type == NODE_COLUMN) { 1031 if (func == NULL) 1032 errx(1, "column without function (%s) - probably " 1033 "outside of a table", np->name); 1034 sprintf(f, "%s", func); 1035 } else 1036 sprintf(f, "%s", np->u.leaf.func); 1037 1038 LIST_FOREACH(ptr, &funcs, link) 1039 if (strcmp(ptr->name, f) == 0) 1040 break; 1041 1042 if (ptr == NULL) { 1043 ptr = xalloc(sizeof(*ptr)); 1044 ptr->name = savestr(f); 1045 LIST_INSERT_HEAD(&funcs, ptr, link); 1046 1047 fprintf(fp, "int %s(struct snmp_context *, " 1048 "struct snmp_value *, u_int, u_int, " 1049 "enum snmp_op);\n", f); 1050 } 1051 1052 fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id); 1053 } 1054 1055 /* 1056 * Generate the OID table. 1057 */ 1058 static void 1059 gen_table(FILE *fp, struct node *node) 1060 { 1061 struct asn_oid oid; 1062 1063 fprintf(fp, "#include <sys/types.h>\n"); 1064 fprintf(fp, "#include <stdio.h>\n"); 1065 #ifdef HAVE_STDINT_H 1066 fprintf(fp, "#include <stdint.h>\n"); 1067 #endif 1068 if (localincs) { 1069 fprintf(fp, "#include \"asn1.h\"\n"); 1070 fprintf(fp, "#include \"snmp.h\"\n"); 1071 fprintf(fp, "#include \"snmpagent.h\"\n"); 1072 } else { 1073 fprintf(fp, "#include <bsnmp/asn1.h>\n"); 1074 fprintf(fp, "#include <bsnmp/snmp.h>\n"); 1075 fprintf(fp, "#include <bsnmp/snmpagent.h>\n"); 1076 } 1077 fprintf(fp, "#include \"%stree.h\"\n", file_prefix); 1078 fprintf(fp, "\n"); 1079 1080 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix); 1081 1082 oid.len = PREFIX_LEN; 1083 memcpy(oid.subs, prefix, sizeof(prefix)); 1084 gen_node(fp, node, &oid, 0, NULL); 1085 1086 fprintf(fp, "};\n\n"); 1087 } 1088 1089 static void 1090 print_syntax(u_int syntax) 1091 { 1092 u_int i; 1093 1094 for (i = 0; keywords[i].str != NULL; i++) 1095 if (keywords[i].tok == TOK_TYPE && 1096 keywords[i].val == syntax) { 1097 printf(" %s", keywords[i].str); 1098 return; 1099 } 1100 abort(); 1101 } 1102 1103 /* 1104 * Generate a tree definition file 1105 */ 1106 static void 1107 gen_tree(const struct node *np, int level) 1108 { 1109 const struct node *sp; 1110 u_int i; 1111 1112 printf("%*s(%u %s", 2 * level, "", np->id, np->name); 1113 1114 switch (np->type) { 1115 1116 case NODE_LEAF: 1117 print_syntax(np->u.leaf.syntax); 1118 printf(" %s%s%s)\n", np->u.leaf.func, 1119 (np->flags & FL_GET) ? " GET" : "", 1120 (np->flags & FL_SET) ? " SET" : ""); 1121 break; 1122 1123 case NODE_TREE: 1124 if (TAILQ_EMPTY(&np->u.tree.subs)) { 1125 printf(")\n"); 1126 } else { 1127 printf("\n"); 1128 TAILQ_FOREACH(sp, &np->u.tree.subs, link) 1129 gen_tree(sp, level + 1); 1130 printf("%*s)\n", 2 * level, ""); 1131 } 1132 break; 1133 1134 case NODE_ENTRY: 1135 printf(" :"); 1136 1137 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) 1138 print_syntax(SNMP_INDEX(np->u.entry.index, i)); 1139 printf(" %s\n", np->u.entry.func); 1140 TAILQ_FOREACH(sp, &np->u.entry.subs, link) 1141 gen_tree(sp, level + 1); 1142 printf("%*s)\n", 2 * level, ""); 1143 break; 1144 1145 case NODE_COLUMN: 1146 print_syntax(np->u.column.syntax); 1147 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "", 1148 (np->flags & FL_SET) ? " SET" : ""); 1149 break; 1150 } 1151 } 1152 1153 static int 1154 extract(FILE *fp, const struct node *np, struct asn_oid *oid, const char *obj, 1155 const struct asn_oid *idx, const char *iname) 1156 { 1157 struct node *sub; 1158 u_long n; 1159 1160 if (oid->len == ASN_MAXOIDLEN) 1161 report_node(np, "OID too long"); 1162 oid->subs[oid->len++] = np->id; 1163 1164 if (strcmp(obj, np->name) == 0) { 1165 if (oid->len + idx->len >= ASN_MAXOIDLEN) 1166 report_node(np, "OID too long"); 1167 fprintf(fp, "#define OID_%s%s\t%u\n", np->name, 1168 iname ? iname : "", np->id); 1169 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name, 1170 iname ? iname : "", oid->len + idx->len); 1171 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name, 1172 iname ? iname : "", oid->len + idx->len); 1173 for (n = 0; n < oid->len; n++) 1174 fprintf(fp, " %u,", oid->subs[n]); 1175 for (n = 0; n < idx->len; n++) 1176 fprintf(fp, " %u,", idx->subs[n]); 1177 fprintf(fp, " } }\n"); 1178 return (0); 1179 } 1180 1181 if (np->type == NODE_TREE) { 1182 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 1183 if (!extract(fp, sub, oid, obj, idx, iname)) 1184 return (0); 1185 } else if (np->type == NODE_ENTRY) { 1186 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 1187 if (!extract(fp, sub, oid, obj, idx, iname)) 1188 return (0); 1189 } 1190 oid->len--; 1191 return (1); 1192 } 1193 1194 static int 1195 gen_extract(FILE *fp, const struct node *root, char *object) 1196 { 1197 struct asn_oid oid; 1198 struct asn_oid idx; 1199 char *s, *e, *end, *iname; 1200 u_long ul; 1201 int ret; 1202 1203 /* look whether the object to extract has an index part */ 1204 idx.len = 0; 1205 iname = NULL; 1206 s = strchr(object, '.'); 1207 if (s != NULL) { 1208 iname = malloc(strlen(s) + 1); 1209 if (iname == NULL) 1210 err(1, "cannot allocated index"); 1211 1212 strcpy(iname, s); 1213 for (e = iname; *e != '\0'; e++) 1214 if (*e == '.') 1215 *e = '_'; 1216 1217 *s++ = '\0'; 1218 while (s != NULL) { 1219 if (*s == '\0') 1220 errx(1, "bad index syntax"); 1221 if ((e = strchr(s, '.')) != NULL) 1222 *e++ = '\0'; 1223 1224 errno = 0; 1225 ul = strtoul(s, &end, 0); 1226 if (*end != '\0') 1227 errx(1, "bad index syntax '%s'", end); 1228 if (errno != 0) 1229 err(1, "bad index syntax"); 1230 1231 if (idx.len == ASN_MAXOIDLEN) 1232 errx(1, "index oid too large"); 1233 idx.subs[idx.len++] = ul; 1234 1235 s = e; 1236 } 1237 } 1238 1239 oid.len = PREFIX_LEN; 1240 memcpy(oid.subs, prefix, sizeof(prefix)); 1241 ret = extract(fp, root, &oid, object, &idx, iname); 1242 if (iname != NULL) 1243 free(iname); 1244 1245 return (ret); 1246 } 1247 1248 1249 static void 1250 check_sub_order(const struct node *np, const struct node_list *subs) 1251 { 1252 int first; 1253 const struct node *sub; 1254 asn_subid_t maxid = 0; 1255 1256 /* ensure, that subids are ordered */ 1257 first = 1; 1258 TAILQ_FOREACH(sub, subs, link) { 1259 if (!first && sub->id <= maxid) 1260 report_node(np, "subids not ordered at %s", sub->name); 1261 maxid = sub->id; 1262 first = 0; 1263 } 1264 } 1265 1266 /* 1267 * Do some sanity checks on the tree definition and do some computations. 1268 */ 1269 static void 1270 check_tree(struct node *np) 1271 { 1272 struct node *sub; 1273 1274 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) { 1275 if ((np->flags & (FL_GET|FL_SET)) != 0) 1276 tree_size++; 1277 return; 1278 } 1279 1280 if (np->type == NODE_ENTRY) { 1281 check_sub_order(np, &np->u.entry.subs); 1282 1283 /* ensure all subnodes are columns */ 1284 TAILQ_FOREACH(sub, &np->u.entry.subs, link) { 1285 if (sub->type != NODE_COLUMN) 1286 report_node(np, "entry subnode '%s' is not " 1287 "a column", sub->name); 1288 check_tree(sub); 1289 } 1290 } else { 1291 check_sub_order(np, &np->u.tree.subs); 1292 1293 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 1294 check_tree(sub); 1295 } 1296 } 1297 1298 static void 1299 merge_subs(struct node_list *s1, struct node_list *s2) 1300 { 1301 struct node *n1, *n2; 1302 1303 while (!TAILQ_EMPTY(s2)) { 1304 n2 = TAILQ_FIRST(s2); 1305 TAILQ_REMOVE(s2, n2, link); 1306 1307 TAILQ_FOREACH(n1, s1, link) 1308 if (n1->id >= n2->id) 1309 break; 1310 if (n1 == NULL) 1311 TAILQ_INSERT_TAIL(s1, n2, link); 1312 else if (n1->id > n2->id) 1313 TAILQ_INSERT_BEFORE(n1, n2, link); 1314 else { 1315 if (n1->type == NODE_TREE && n2->type == NODE_TREE) { 1316 if (strcmp(n1->name, n2->name) != 0) 1317 errx(1, "trees to merge must have " 1318 "same name '%s' '%s'", n1->name, 1319 n2->name); 1320 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs); 1321 free(n2); 1322 } else if (n1->type == NODE_ENTRY && 1323 n2->type == NODE_ENTRY) { 1324 if (strcmp(n1->name, n2->name) != 0) 1325 errx(1, "entries to merge must have " 1326 "same name '%s' '%s'", n1->name, 1327 n2->name); 1328 if (n1->u.entry.index != n2->u.entry.index) 1329 errx(1, "entries to merge must have " 1330 "same index '%s'", n1->name); 1331 if (strcmp(n1->u.entry.func, 1332 n2->u.entry.func) != 0) 1333 errx(1, "entries to merge must have " 1334 "same op '%s'", n1->name); 1335 merge_subs(&n1->u.entry.subs, 1336 &n2->u.entry.subs); 1337 free(n2); 1338 } else 1339 errx(1, "entities to merge must be both " 1340 "trees or both entries: %s, %s", 1341 n1->name, n2->name); 1342 } 1343 } 1344 } 1345 1346 static void 1347 merge(struct node **root, struct node *t) 1348 { 1349 1350 if (*root == NULL) { 1351 *root = t; 1352 return; 1353 } 1354 if (t == NULL) 1355 return; 1356 1357 /* both must be trees */ 1358 if ((*root)->type != NODE_TREE) 1359 errx(1, "root is not a tree"); 1360 if (t->type != NODE_TREE) 1361 errx(1, "can merge only with tree"); 1362 if ((*root)->id != t->id) 1363 errx(1, "trees to merge must have same id"); 1364 1365 merge_subs(&(*root)->u.tree.subs, &t->u.tree.subs); 1366 } 1367 1368 static void 1369 unminus(FILE *fp, const char *s) 1370 { 1371 1372 while (*s != '\0') { 1373 if (*s == '-') 1374 fprintf(fp, "_"); 1375 else 1376 fprintf(fp, "%c", *s); 1377 s++; 1378 } 1379 } 1380 1381 static void 1382 gen_enum(FILE *fp, const struct type *t) 1383 { 1384 const struct enums *e; 1385 long min = LONG_MAX; 1386 1387 fprintf(fp, "\n"); 1388 fprintf(fp, "#ifndef %s_defined__\n", t->name); 1389 fprintf(fp, "#define %s_defined__\n", t->name); 1390 fprintf(fp, "/*\n"); 1391 fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno); 1392 fprintf(fp, " */\n"); 1393 fprintf(fp, "enum %s {\n", t->name); 1394 TAILQ_FOREACH(e, &t->enums, link) { 1395 fprintf(fp, "\t%s_", t->name); 1396 unminus(fp, e->name); 1397 fprintf(fp, " = %ld,\n", e->value); 1398 if (e->value < min) 1399 min = e->value; 1400 } 1401 fprintf(fp, "};\n"); 1402 fprintf(fp, "#define STROFF_%s %ld\n", t->name, min); 1403 fprintf(fp, "#define STRING_%s \\\n", t->name); 1404 TAILQ_FOREACH(e, &t->enums, link) { 1405 fprintf(fp, "\t[%ld] \"%s_", e->value - min, t->name); 1406 unminus(fp, e->name); 1407 fprintf(fp, "\",\\\n"); 1408 } 1409 fprintf(fp, "\n"); 1410 fprintf(fp, "#endif /* %s_defined__ */\n", t->name); 1411 } 1412 1413 static void 1414 gen_enums(FILE *fp) 1415 { 1416 const struct type *t; 1417 1418 LIST_FOREACH(t, &types, link) 1419 if (t->is_enum || t->is_bits) 1420 gen_enum(fp, t); 1421 } 1422 1423 static int 1424 extract_enum(FILE *fp, const char *name) 1425 { 1426 const struct type *t; 1427 1428 LIST_FOREACH(t, &types, link) 1429 if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) { 1430 gen_enum(fp, t); 1431 return (0); 1432 } 1433 return (-1); 1434 } 1435 1436 int 1437 main(int argc, char *argv[]) 1438 { 1439 int do_extract = 0; 1440 int do_tree = 0; 1441 int do_enums = 0; 1442 int opt; 1443 struct node *root; 1444 char fname[MAXPATHLEN + 1]; 1445 int tok; 1446 FILE *fp; 1447 char *infile = NULL; 1448 1449 while ((opt = getopt(argc, argv, "dEehI:i:lp:t")) != EOF) 1450 switch (opt) { 1451 1452 case 'd': 1453 debug = 1; 1454 break; 1455 1456 case 'h': 1457 fprintf(stderr, "%s", usgtxt); 1458 exit(0); 1459 1460 case 'E': 1461 do_enums = 1; 1462 break; 1463 1464 case 'e': 1465 do_extract = 1; 1466 break; 1467 1468 case 'I': 1469 path_new(optarg); 1470 break; 1471 1472 case 'i': 1473 infile = optarg; 1474 break; 1475 1476 case 'l': 1477 localincs = 1; 1478 break; 1479 1480 case 'p': 1481 file_prefix = optarg; 1482 if (strlen(file_prefix) + strlen("tree.c") > 1483 MAXPATHLEN) 1484 errx(1, "prefix too long"); 1485 break; 1486 1487 case 't': 1488 do_tree = 1; 1489 break; 1490 } 1491 1492 if (do_extract + do_tree + do_enums > 1) 1493 errx(1, "conflicting options -e/-t/-E"); 1494 if (!do_extract && !do_enums && argc != optind) 1495 errx(1, "no arguments allowed"); 1496 if ((do_extract || do_enums) && argc == optind) 1497 errx(1, "no objects specified"); 1498 1499 if (infile == NULL) { 1500 input_new(stdin, NULL, "<stdin>"); 1501 } else { 1502 if ((fp = fopen(infile, "r")) == NULL) 1503 err(1, "%s", infile); 1504 input_new(fp, NULL, infile); 1505 } 1506 1507 root = parse_top(gettoken()); 1508 while ((tok = gettoken()) != TOK_EOF) 1509 merge(&root, parse_top(tok)); 1510 1511 check_tree(root); 1512 1513 if (do_extract) { 1514 while (optind < argc) { 1515 if (gen_extract(stdout, root, argv[optind])) 1516 errx(1, "object not found: %s", argv[optind]); 1517 optind++; 1518 } 1519 return (0); 1520 } 1521 if (do_enums) { 1522 while (optind < argc) { 1523 if (extract_enum(stdout, argv[optind])) 1524 errx(1, "enum not found: %s", argv[optind]); 1525 optind++; 1526 } 1527 return (0); 1528 } 1529 if (do_tree) { 1530 gen_tree(root, 0); 1531 return (0); 1532 } 1533 sprintf(fname, "%stree.h", file_prefix); 1534 if ((fp = fopen(fname, "w")) == NULL) 1535 err(1, "%s: ", fname); 1536 gen_header(fp, root, PREFIX_LEN, NULL); 1537 1538 fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n"); 1539 gen_enums(fp); 1540 fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n"); 1541 1542 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size); 1543 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix); 1544 1545 fclose(fp); 1546 1547 sprintf(fname, "%stree.c", file_prefix); 1548 if ((fp = fopen(fname, "w")) == NULL) 1549 err(1, "%s: ", fname); 1550 gen_table(fp, root); 1551 fclose(fp); 1552 1553 return (0); 1554 } 1555