1 /* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Copyright (c) 2004 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: bsnmp/gensnmptree/gensnmptree.c,v 1.38 2004/08/06 08:46:46 brandt Exp $ 34 * 35 * Generate OID table from table description. 36 * 37 * Syntax is: 38 * --------- 39 * file := tree | tree file 40 * 41 * tree := head elements ')' 42 * 43 * entry := head ':' index STRING elements ')' 44 * 45 * leaf := head TYPE STRING ACCESS ')' 46 * 47 * column := head TYPE ACCESS ')' 48 * 49 * head := '(' INT STRING 50 * 51 * elements := EMPTY | elements element 52 * 53 * element := tree | leaf 54 * 55 * index := TYPE | index TYPE 56 * 57 */ 58 #include <sys/types.h> 59 #include <sys/param.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <stdarg.h> 63 #include <unistd.h> 64 #include <string.h> 65 #include <ctype.h> 66 #include <inttypes.h> 67 #include <errno.h> 68 #ifdef HAVE_ERR_H 69 #include <err.h> 70 #endif 71 #include <sys/queue.h> 72 #include "support.h" 73 #include "asn1.h" 74 #include "snmp.h" 75 #include "snmpagent.h" 76 77 /* 78 * Constant prefix for all OIDs 79 */ 80 static const asn_subid_t prefix[] = { 1, 3, 6 }; 81 #define PREFIX_LEN (sizeof(prefix) / sizeof(prefix[0])) 82 83 u_int tree_size; 84 static const char *file_prefix = ""; 85 static FILE *fp; 86 87 /* if true generate local include paths */ 88 static int localincs = 0; 89 90 static const char usgtxt[] = "\ 91 Generate SNMP tables. Copyright (c) 2001-2002 Fraunhofer Institute for\n\ 92 Open Communication Systems (FhG Fokus). All rights reserved.\n\ 93 usage: gensnmptree [-hel] [-p prefix] [name]...\n\ 94 options:\n\ 95 -h print this info\n\ 96 -e extrace the named oids\n\ 97 -l generate local include directives\n\ 98 -p prefix prepend prefix to file and variable names\n\ 99 "; 100 101 /* 102 * A node in the OID tree 103 */ 104 enum ntype { 105 NODE_LEAF = 1, 106 NODE_TREE, 107 NODE_ENTRY, 108 NODE_COLUMN 109 }; 110 111 enum { 112 FL_GET = 0x01, 113 FL_SET = 0x02, 114 }; 115 116 struct node; 117 TAILQ_HEAD(node_list, node); 118 119 struct node { 120 enum ntype type; 121 asn_subid_t id; /* last element of OID */ 122 char *name; /* name of node */ 123 TAILQ_ENTRY(node) link; 124 u_int lno; /* starting line number */ 125 u_int flags; /* allowed operations */ 126 127 union { 128 struct tree { 129 struct node_list subs; 130 } tree; 131 132 struct entry { 133 uint32_t index; /* index for table entry */ 134 char *func; /* function for tables */ 135 struct node_list subs; 136 } entry; 137 138 struct leaf { 139 enum snmp_syntax syntax; /* syntax for this leaf */ 140 char *func; /* function name */ 141 } leaf; 142 143 struct column { 144 enum snmp_syntax syntax; /* syntax for this column */ 145 } column; 146 } u; 147 }; 148 149 struct func { 150 const char *name; 151 LIST_ENTRY(func) link; 152 }; 153 154 static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs); 155 156 /************************************************************ 157 * 158 * Allocate memory and panic just in the case... 159 */ 160 static void * 161 xalloc(size_t size) 162 { 163 void *ptr; 164 165 if ((ptr = malloc(size)) == NULL) 166 err(1, "allocing %u bytes", size); 167 168 return (ptr); 169 } 170 171 /************************************************************ 172 * 173 * Parsing input 174 */ 175 enum tok { 176 TOK_EOF = 0200, /* end-of-file seen */ 177 TOK_NUM, /* number */ 178 TOK_STR, /* string */ 179 TOK_ACCESS, /* access operator */ 180 TOK_TYPE, /* type operator */ 181 }; 182 183 static const struct { 184 const char *str; 185 enum tok tok; 186 u_int val; 187 } keywords[] = { 188 { "GET", TOK_ACCESS, FL_GET }, 189 { "SET", TOK_ACCESS, FL_SET }, 190 { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL }, 191 { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER }, 192 { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER }, 193 { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE }, 194 { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING }, 195 { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS }, 196 { "OID", TOK_TYPE, SNMP_SYNTAX_OID }, 197 { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS }, 198 { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER }, 199 { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE }, 200 { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 }, 201 { NULL, 0, 0 } 202 }; 203 204 /* arbitrary upper limit on node names and function names */ 205 #define MAXSTR 1000 206 char str[MAXSTR]; 207 u_long val; /* integer values */ 208 u_int lno = 1; /* current line number */ 209 int all_cond; /* all conditions are true */ 210 211 static void report(const char *, ...) __dead2 __printflike(1, 2); 212 static void report_node(const struct node *, const char *, ...) 213 __dead2 __printflike(2, 3); 214 215 /* 216 * Report an error and exit. 217 */ 218 static void 219 report(const char *fmt, ...) 220 { 221 va_list ap; 222 int c; 223 224 va_start(ap, fmt); 225 fprintf(stderr, "line %u: ", lno); 226 vfprintf(stderr, fmt, ap); 227 fprintf(stderr, "\n"); 228 fprintf(stderr, "context: \""); 229 while ((c = getchar()) != EOF && c != '\n') 230 fprintf(stderr, "%c", c); 231 fprintf(stderr, "\n"); 232 va_end(ap); 233 exit(1); 234 } 235 static void 236 report_node(const struct node *np, const char *fmt, ...) 237 { 238 va_list ap; 239 240 va_start(ap, fmt); 241 fprintf(stderr, "line %u, node %s: ", np->lno, np->name); 242 vfprintf(stderr, fmt, ap); 243 fprintf(stderr, "\n"); 244 va_end(ap); 245 exit(1); 246 } 247 248 /* 249 * Return a fresh copy of the string constituting the current token. 250 */ 251 static char * 252 savetok(void) 253 { 254 return (strcpy(xalloc(strlen(str)+1), str)); 255 } 256 257 /* 258 * Get the next token from input. 259 */ 260 static int 261 gettoken(void) 262 { 263 int c; 264 265 again: 266 /* 267 * Skip any whitespace before the next token 268 */ 269 while ((c = getchar()) != EOF) { 270 if (c == '\n') 271 lno++; 272 if (!isspace(c)) 273 break; 274 } 275 if (c == EOF) 276 return (TOK_EOF); 277 if (!isascii(c)) 278 report("unexpected character %#2x", (u_int)c); 279 280 /* 281 * Skip comments 282 */ 283 if (c == '#') { 284 while ((c = getchar()) != EOF) { 285 if (c == '\n') { 286 lno++; 287 goto again; 288 } 289 } 290 report("unexpected EOF in comment"); 291 } 292 293 /* 294 * Single character tokens 295 */ 296 if (c == ')' || c == '(' || c == ':') 297 return (c); 298 299 /* 300 * Sort out numbers 301 */ 302 if (isdigit(c)) { 303 ungetc(c, stdin); 304 scanf("%lu", &val); 305 return (TOK_NUM); 306 } 307 308 /* 309 * So that has to be a string. 310 */ 311 if (isalpha(c) || c == '_') { 312 size_t n = 0; 313 str[n++] = c; 314 while ((c = getchar()) != EOF) { 315 if (!isalnum(c) && c != '_') { 316 ungetc(c, stdin); 317 break; 318 } 319 if (n == sizeof(str) - 1) { 320 str[n++] = '\0'; 321 report("string too long '%s...'", str); 322 } 323 str[n++] = c; 324 } 325 str[n++] = '\0'; 326 327 /* 328 * Keywords 329 */ 330 for (c = 0; keywords[c].str != NULL; c++) 331 if (strcmp(keywords[c].str, str) == 0) { 332 val = keywords[c].val; 333 return (keywords[c].tok); 334 } 335 336 return (TOK_STR); 337 } 338 if (isprint(c)) 339 errx(1, "%u: unexpected character '%c'", lno, c); 340 else 341 errx(1, "%u: unexpected character 0x%02x", lno, (u_int)c); 342 } 343 344 /* 345 * Parse the next node (complete with all subnodes) 346 */ 347 static struct node * 348 parse(enum tok tok) 349 { 350 struct node *node; 351 struct node *sub; 352 u_int index_count; 353 354 node = xalloc(sizeof(struct node)); 355 node->lno = lno; 356 357 if (tok != '(') 358 report("'(' expected at begin of node"); 359 if (gettoken() != TOK_NUM) 360 report("node id expected after opening '('"); 361 if (val > ASN_MAXID) 362 report("subid too large '%lu'", val); 363 node->id = (asn_subid_t)val; 364 if (gettoken() != TOK_STR) 365 report("node name expected after '(' ID"); 366 node->name = savetok(); 367 368 if ((tok = gettoken()) == TOK_TYPE) { 369 /* LEAF or COLUM */ 370 u_int syntax = val; 371 372 if ((tok = gettoken()) == TOK_STR) { 373 /* LEAF */ 374 node->type = NODE_LEAF; 375 node->u.leaf.func = savetok(); 376 node->u.leaf.syntax = syntax; 377 tok = gettoken(); 378 } else { 379 /* COLUMN */ 380 node->type = NODE_COLUMN; 381 node->u.column.syntax = syntax; 382 } 383 384 while (tok != ')') { 385 if (tok != TOK_ACCESS) 386 report("access keyword or ')' expected"); 387 node->flags |= (u_int)val; 388 tok = gettoken(); 389 } 390 391 } else if (tok == ':') { 392 /* ENTRY */ 393 node->type = NODE_ENTRY; 394 TAILQ_INIT(&node->u.entry.subs); 395 396 index_count = 0; 397 node->u.entry.index = 0; 398 while ((tok = gettoken()) == TOK_TYPE) { 399 if (index_count++ == SNMP_INDEXES_MAX) 400 report("too many table indexes"); 401 node->u.entry.index |= 402 val << (SNMP_INDEX_SHIFT * index_count); 403 } 404 node->u.entry.index |= index_count; 405 if (index_count == 0) 406 report("need at least one index"); 407 408 if (tok != TOK_STR) 409 report("function name expected"); 410 411 node->u.entry.func = savetok(); 412 413 tok = gettoken(); 414 415 while (tok != ')') { 416 sub = parse(tok); 417 TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link); 418 tok = gettoken(); 419 } 420 421 } else { 422 /* subtree */ 423 node->type = NODE_TREE; 424 TAILQ_INIT(&node->u.tree.subs); 425 426 while (tok != ')') { 427 sub = parse(tok); 428 TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link); 429 tok = gettoken(); 430 } 431 } 432 return (node); 433 } 434 435 /* 436 * Generate the C-code table part for one node. 437 */ 438 static void 439 gen_node(struct node *np, struct asn_oid *oid, u_int idx, const char *func) 440 { 441 u_int n; 442 struct node *sub; 443 u_int syntax; 444 445 if (oid->len == ASN_MAXOIDLEN) 446 report_node(np, "OID too long"); 447 oid->subs[oid->len++] = np->id; 448 449 if (np->type == NODE_TREE) { 450 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 451 gen_node(sub, oid, 0, NULL); 452 oid->len--; 453 return; 454 } 455 if (np->type == NODE_ENTRY) { 456 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 457 gen_node(sub, oid, np->u.entry.index, np->u.entry.func); 458 oid->len--; 459 return; 460 } 461 462 /* leaf or column */ 463 if ((np->flags & (FL_GET|FL_SET)) == 0) { 464 oid->len--; 465 return; 466 } 467 468 fprintf(fp, " {{ %u, {", oid->len); 469 for (n = 0; n < oid->len; n++) 470 fprintf(fp, " %u,", oid->subs[n]); 471 fprintf(fp, " }}, \"%s\", ", np->name); 472 473 if (np->type == NODE_COLUMN) { 474 syntax = np->u.column.syntax; 475 fprintf(fp, "SNMP_NODE_COLUMN, "); 476 } else { 477 syntax = np->u.leaf.syntax; 478 fprintf(fp, "SNMP_NODE_LEAF, "); 479 } 480 481 switch (syntax) { 482 483 case SNMP_SYNTAX_NULL: 484 fprintf(fp, "SNMP_SYNTAX_NULL, "); 485 break; 486 487 case SNMP_SYNTAX_INTEGER: 488 fprintf(fp, "SNMP_SYNTAX_INTEGER, "); 489 break; 490 491 case SNMP_SYNTAX_OCTETSTRING: 492 fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, "); 493 break; 494 495 case SNMP_SYNTAX_IPADDRESS: 496 fprintf(fp, "SNMP_SYNTAX_IPADDRESS, "); 497 break; 498 499 case SNMP_SYNTAX_OID: 500 fprintf(fp, "SNMP_SYNTAX_OID, "); 501 break; 502 503 case SNMP_SYNTAX_TIMETICKS: 504 fprintf(fp, "SNMP_SYNTAX_TIMETICKS, "); 505 break; 506 507 case SNMP_SYNTAX_COUNTER: 508 fprintf(fp, "SNMP_SYNTAX_COUNTER, "); 509 break; 510 511 case SNMP_SYNTAX_GAUGE: 512 fprintf(fp, "SNMP_SYNTAX_GAUGE, "); 513 break; 514 515 case SNMP_SYNTAX_COUNTER64: 516 fprintf(fp, "SNMP_SYNTAX_COUNTER64, "); 517 break; 518 519 case SNMP_SYNTAX_NOSUCHOBJECT: 520 case SNMP_SYNTAX_NOSUCHINSTANCE: 521 case SNMP_SYNTAX_ENDOFMIBVIEW: 522 abort(); 523 } 524 525 if (np->type == NODE_COLUMN) 526 fprintf(fp, "%s, ", func); 527 else 528 fprintf(fp, "%s, ", np->u.leaf.func); 529 530 fprintf(fp, "0"); 531 if (np->flags & FL_SET) 532 fprintf(fp, "|SNMP_NODE_CANSET"); 533 fprintf(fp, ", %#x, NULL, NULL },\n", idx); 534 oid->len--; 535 return; 536 } 537 538 /* 539 * Generate the header file with the function declarations. 540 */ 541 static void 542 gen_header(struct node *np, u_int oidlen, const char *func) 543 { 544 char f[MAXSTR + 4]; 545 struct node *sub; 546 struct func *ptr; 547 548 oidlen++; 549 if (np->type == NODE_TREE) { 550 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 551 gen_header(sub, oidlen, NULL); 552 return; 553 } 554 if (np->type == NODE_ENTRY) { 555 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 556 gen_header(sub, oidlen, np->u.entry.func); 557 return; 558 } 559 560 if((np->flags & (FL_GET|FL_SET)) == 0) 561 return; 562 563 if (np->type == NODE_COLUMN) 564 sprintf(f, "%s", func); 565 else 566 sprintf(f, "%s", np->u.leaf.func); 567 568 LIST_FOREACH(ptr, &funcs, link) 569 if (strcmp(ptr->name, f) == 0) 570 break; 571 572 if (ptr == NULL) { 573 ptr = xalloc(sizeof(*ptr)); 574 ptr->name = strcpy(xalloc(strlen(f)+1), f); 575 LIST_INSERT_HEAD(&funcs, ptr, link); 576 577 fprintf(fp, "int %s(struct snmp_context *, " 578 "struct snmp_value *, u_int, u_int, " 579 "enum snmp_op);\n", f); 580 } 581 582 fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id); 583 } 584 585 /* 586 * Generate the OID table. 587 */ 588 static void 589 gen_table(struct node *node) 590 { 591 struct asn_oid oid; 592 593 fprintf(fp, "#include <sys/types.h>\n"); 594 fprintf(fp, "#include <stdio.h>\n"); 595 fprintf(fp, "#include <stdint.h>\n"); 596 if (localincs) { 597 fprintf(fp, "#include \"asn1.h\"\n"); 598 fprintf(fp, "#include \"snmp.h\"\n"); 599 fprintf(fp, "#include \"snmpagent.h\"\n"); 600 } else { 601 fprintf(fp, "#include <bsnmp/asn1.h>\n"); 602 fprintf(fp, "#include <bsnmp/snmp.h>\n"); 603 fprintf(fp, "#include <bsnmp/snmpagent.h>\n"); 604 } 605 fprintf(fp, "#include \"%stree.h\"\n", file_prefix); 606 fprintf(fp, "\n"); 607 608 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix); 609 610 oid.len = PREFIX_LEN; 611 memcpy(oid.subs, prefix, sizeof(prefix)); 612 gen_node(node, &oid, 0, NULL); 613 614 fprintf(fp, "};\n\n"); 615 } 616 617 static void 618 print_syntax(u_int syntax) 619 { 620 u_int i; 621 622 for (i = 0; keywords[i].str != NULL; i++) 623 if (keywords[i].tok == TOK_TYPE && 624 keywords[i].val == syntax) { 625 printf(" %s", keywords[i].str); 626 return; 627 } 628 abort(); 629 } 630 631 /* 632 * Generate a tree definition file 633 */ 634 static void 635 gen_tree(const struct node *np, int level) 636 { 637 const struct node *sp; 638 u_int i; 639 640 printf("%*s(%u %s", 2 * level, "", np->id, np->name); 641 642 switch (np->type) { 643 644 case NODE_LEAF: 645 print_syntax(np->u.leaf.syntax); 646 printf(" %s%s%s)\n", np->u.leaf.func, 647 (np->flags & FL_GET) ? " GET" : "", 648 (np->flags & FL_SET) ? " SET" : ""); 649 break; 650 651 case NODE_TREE: 652 if (TAILQ_EMPTY(&np->u.tree.subs)) { 653 printf(")\n"); 654 } else { 655 printf("\n"); 656 TAILQ_FOREACH(sp, &np->u.tree.subs, link) 657 gen_tree(sp, level + 1); 658 printf("%*s)\n", 2 * level, ""); 659 } 660 break; 661 662 case NODE_ENTRY: 663 printf(" :"); 664 665 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) 666 print_syntax(SNMP_INDEX(np->u.entry.index, i)); 667 printf(" %s\n", np->u.entry.func); 668 TAILQ_FOREACH(sp, &np->u.entry.subs, link) 669 gen_tree(sp, level + 1); 670 printf("%*s)\n", 2 * level, ""); 671 break; 672 673 case NODE_COLUMN: 674 print_syntax(np->u.column.syntax); 675 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "", 676 (np->flags & FL_SET) ? " SET" : ""); 677 break; 678 679 } 680 } 681 682 static int 683 extract(const struct node *np, struct asn_oid *oid, const char *obj, 684 const struct asn_oid *idx, const char *iname) 685 { 686 struct node *sub; 687 u_long n; 688 689 if (oid->len == ASN_MAXOIDLEN) 690 report_node(np, "OID too long"); 691 oid->subs[oid->len++] = np->id; 692 693 if (strcmp(obj, np->name) == 0) { 694 if (oid->len + idx->len >= ASN_MAXOIDLEN) 695 report_node(np, "OID too long"); 696 fprintf(fp, "#define OID_%s%s\t%u\n", np->name, 697 iname ? iname : "", np->id); 698 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name, 699 iname ? iname : "", oid->len + idx->len); 700 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name, 701 iname ? iname : "", oid->len + idx->len); 702 for (n = 0; n < oid->len; n++) 703 fprintf(fp, " %u,", oid->subs[n]); 704 for (n = 0; n < idx->len; n++) 705 fprintf(fp, " %u,", idx->subs[n]); 706 fprintf(fp, " } }\n"); 707 return (0); 708 } 709 710 if (np->type == NODE_TREE) { 711 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 712 if (!extract(sub, oid, obj, idx, iname)) 713 return (0); 714 } else if (np->type == NODE_ENTRY) { 715 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 716 if (!extract(sub, oid, obj, idx, iname)) 717 return (0); 718 } 719 oid->len--; 720 return (1); 721 } 722 723 static int 724 gen_extract(const struct node *root, char *object) 725 { 726 struct asn_oid oid; 727 struct asn_oid idx; 728 char *s, *e, *end, *iname; 729 u_long ul; 730 int ret; 731 732 /* look whether the object to extract has an index part */ 733 idx.len = 0; 734 iname = NULL; 735 s = strchr(object, '.'); 736 if (s != NULL) { 737 iname = malloc(strlen(s) + 1); 738 if (iname == NULL) 739 err(1, "cannot allocated index"); 740 741 strcpy(iname, s); 742 for (e = iname; *e != '\0'; e++) 743 if (*e == '.') 744 *e = '_'; 745 746 *s++ = '\0'; 747 while (s != NULL) { 748 if (*s == '\0') 749 errx(1, "bad index syntax"); 750 if ((e = strchr(s, '.')) != NULL) 751 *e++ = '\0'; 752 753 errno = 0; 754 ul = strtoul(s, &end, 0); 755 if (*end != '\0') 756 errx(1, "bad index syntax '%s'", end); 757 if (errno != 0) 758 err(1, "bad index syntax"); 759 760 if (idx.len == ASN_MAXOIDLEN) 761 errx(1, "index oid too large"); 762 idx.subs[idx.len++] = ul; 763 764 s = e; 765 } 766 } 767 768 oid.len = PREFIX_LEN; 769 memcpy(oid.subs, prefix, sizeof(prefix)); 770 ret = extract(root, &oid, object, &idx, iname); 771 if (iname != NULL) 772 free(iname); 773 774 return (ret); 775 } 776 777 778 static void 779 check_sub_order(const struct node *np, const struct node_list *subs) 780 { 781 int first; 782 const struct node *sub; 783 asn_subid_t maxid = 0; 784 785 /* ensure, that subids are ordered */ 786 first = 1; 787 TAILQ_FOREACH(sub, subs, link) { 788 if (!first && sub->id <= maxid) 789 report_node(np, "subids not ordered at %s", sub->name); 790 maxid = sub->id; 791 first = 0; 792 } 793 } 794 795 /* 796 * Do some sanity checks on the tree definition and do some computations. 797 */ 798 static void 799 check_tree(struct node *np) 800 { 801 struct node *sub; 802 803 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) { 804 if ((np->flags & (FL_GET|FL_SET)) != 0) 805 tree_size++; 806 return; 807 } 808 809 if (np->type == NODE_ENTRY) { 810 check_sub_order(np, &np->u.entry.subs); 811 812 /* ensure all subnodes are columns */ 813 TAILQ_FOREACH(sub, &np->u.entry.subs, link) { 814 if (sub->type != NODE_COLUMN) 815 report_node(np, "entry subnode '%s' is not " 816 "a column", sub->name); 817 check_tree(sub); 818 } 819 } else { 820 check_sub_order(np, &np->u.tree.subs); 821 822 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 823 check_tree(sub); 824 } 825 } 826 827 static void 828 merge_subs(struct node_list *s1, struct node_list *s2) 829 { 830 struct node *n1, *n2; 831 832 while (!TAILQ_EMPTY(s2)) { 833 n2 = TAILQ_FIRST(s2); 834 TAILQ_REMOVE(s2, n2, link); 835 836 TAILQ_FOREACH(n1, s1, link) 837 if (n1->id >= n2->id) 838 break; 839 if (n1 == NULL) 840 TAILQ_INSERT_TAIL(s1, n2, link); 841 else if (n1->id > n2->id) 842 TAILQ_INSERT_BEFORE(n1, n2, link); 843 else { 844 if (n1->type == NODE_TREE && n2->type == NODE_TREE) { 845 if (strcmp(n1->name, n2->name) != 0) 846 errx(1, "trees to merge must have " 847 "same name '%s' '%s'", n1->name, 848 n2->name); 849 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs); 850 free(n2); 851 } else if (n1->type == NODE_ENTRY && 852 n2->type == NODE_ENTRY) { 853 if (strcmp(n1->name, n2->name) != 0) 854 errx(1, "entries to merge must have " 855 "same name '%s' '%s'", n1->name, 856 n2->name); 857 if (n1->u.entry.index != n2->u.entry.index) 858 errx(1, "entries to merge must have " 859 "same index '%s'", n1->name); 860 if (strcmp(n1->u.entry.func, 861 n2->u.entry.func) != 0) 862 errx(1, "entries to merge must have " 863 "same op '%s'", n1->name); 864 merge_subs(&n1->u.entry.subs, 865 &n2->u.entry.subs); 866 free(n2); 867 } else 868 errx(1, "entities to merge must be both " 869 "trees or both entries: %s, %s", 870 n1->name, n2->name); 871 } 872 } 873 } 874 875 static void 876 merge(struct node *root, struct node *t) 877 { 878 879 /* both must be trees */ 880 if (root->type != NODE_TREE) 881 errx(1, "root is not a tree"); 882 if (t->type != NODE_TREE) 883 errx(1, "can merge only with tree"); 884 if (root->id != t->id) 885 errx(1, "trees to merge must have same id"); 886 887 merge_subs(&root->u.tree.subs, &t->u.tree.subs); 888 } 889 890 int 891 main(int argc, char *argv[]) 892 { 893 int do_extract = 0; 894 int do_tree = 0; 895 int opt; 896 struct node *root; 897 char fname[MAXPATHLEN + 1]; 898 int tok; 899 900 while ((opt = getopt(argc, argv, "help:t")) != EOF) 901 switch (opt) { 902 903 case 'h': 904 fprintf(stderr, "%s", usgtxt); 905 exit(0); 906 907 case 'e': 908 do_extract = 1; 909 break; 910 911 case 'l': 912 localincs = 1; 913 break; 914 915 case 'p': 916 file_prefix = optarg; 917 if (strlen(file_prefix) + strlen("tree.c") > 918 MAXPATHLEN) 919 errx(1, "prefix too long"); 920 break; 921 922 case 't': 923 do_tree = 1; 924 break; 925 } 926 927 if (do_extract && do_tree) 928 errx(1, "conflicting options -e and -t"); 929 if (!do_extract && argc != optind) 930 errx(1, "no arguments allowed"); 931 if (do_extract && argc == optind) 932 errx(1, "no objects specified"); 933 934 root = parse(gettoken()); 935 while ((tok = gettoken()) != TOK_EOF) 936 merge(root, parse(tok)); 937 938 check_tree(root); 939 940 if (do_extract) { 941 fp = stdout; 942 while (optind < argc) { 943 if (gen_extract(root, argv[optind])) 944 errx(1, "object not found: %s", argv[optind]); 945 optind++; 946 } 947 948 return (0); 949 } 950 if (do_tree) { 951 gen_tree(root, 0); 952 return (0); 953 } 954 sprintf(fname, "%stree.h", file_prefix); 955 if ((fp = fopen(fname, "w")) == NULL) 956 err(1, "%s: ", fname); 957 gen_header(root, PREFIX_LEN, NULL); 958 959 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size); 960 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix); 961 962 fclose(fp); 963 964 sprintf(fname, "%stree.c", file_prefix); 965 if ((fp = fopen(fname, "w")) == NULL) 966 err(1, "%s: ", fname); 967 gen_table(root); 968 fclose(fp); 969 970 return (0); 971 } 972