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.43 2005/10/04 11:21:29 brandt_h 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 | column 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 %zu 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 if (func == NULL) 565 errx(1, "column without function (%s) - probably " 566 "outside of a table", np->name); 567 sprintf(f, "%s", func); 568 } else 569 sprintf(f, "%s", np->u.leaf.func); 570 571 LIST_FOREACH(ptr, &funcs, link) 572 if (strcmp(ptr->name, f) == 0) 573 break; 574 575 if (ptr == NULL) { 576 ptr = xalloc(sizeof(*ptr)); 577 ptr->name = strcpy(xalloc(strlen(f)+1), f); 578 LIST_INSERT_HEAD(&funcs, ptr, link); 579 580 fprintf(fp, "int %s(struct snmp_context *, " 581 "struct snmp_value *, u_int, u_int, " 582 "enum snmp_op);\n", f); 583 } 584 585 fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id); 586 } 587 588 /* 589 * Generate the OID table. 590 */ 591 static void 592 gen_table(struct node *node) 593 { 594 struct asn_oid oid; 595 596 fprintf(fp, "#include <sys/types.h>\n"); 597 fprintf(fp, "#include <stdio.h>\n"); 598 #ifdef HAVE_STDINT_H 599 fprintf(fp, "#include <stdint.h>\n"); 600 #endif 601 if (localincs) { 602 fprintf(fp, "#include \"asn1.h\"\n"); 603 fprintf(fp, "#include \"snmp.h\"\n"); 604 fprintf(fp, "#include \"snmpagent.h\"\n"); 605 } else { 606 fprintf(fp, "#include <bsnmp/asn1.h>\n"); 607 fprintf(fp, "#include <bsnmp/snmp.h>\n"); 608 fprintf(fp, "#include <bsnmp/snmpagent.h>\n"); 609 } 610 fprintf(fp, "#include \"%stree.h\"\n", file_prefix); 611 fprintf(fp, "\n"); 612 613 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix); 614 615 oid.len = PREFIX_LEN; 616 memcpy(oid.subs, prefix, sizeof(prefix)); 617 gen_node(node, &oid, 0, NULL); 618 619 fprintf(fp, "};\n\n"); 620 } 621 622 static void 623 print_syntax(u_int syntax) 624 { 625 u_int i; 626 627 for (i = 0; keywords[i].str != NULL; i++) 628 if (keywords[i].tok == TOK_TYPE && 629 keywords[i].val == syntax) { 630 printf(" %s", keywords[i].str); 631 return; 632 } 633 abort(); 634 } 635 636 /* 637 * Generate a tree definition file 638 */ 639 static void 640 gen_tree(const struct node *np, int level) 641 { 642 const struct node *sp; 643 u_int i; 644 645 printf("%*s(%u %s", 2 * level, "", np->id, np->name); 646 647 switch (np->type) { 648 649 case NODE_LEAF: 650 print_syntax(np->u.leaf.syntax); 651 printf(" %s%s%s)\n", np->u.leaf.func, 652 (np->flags & FL_GET) ? " GET" : "", 653 (np->flags & FL_SET) ? " SET" : ""); 654 break; 655 656 case NODE_TREE: 657 if (TAILQ_EMPTY(&np->u.tree.subs)) { 658 printf(")\n"); 659 } else { 660 printf("\n"); 661 TAILQ_FOREACH(sp, &np->u.tree.subs, link) 662 gen_tree(sp, level + 1); 663 printf("%*s)\n", 2 * level, ""); 664 } 665 break; 666 667 case NODE_ENTRY: 668 printf(" :"); 669 670 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) 671 print_syntax(SNMP_INDEX(np->u.entry.index, i)); 672 printf(" %s\n", np->u.entry.func); 673 TAILQ_FOREACH(sp, &np->u.entry.subs, link) 674 gen_tree(sp, level + 1); 675 printf("%*s)\n", 2 * level, ""); 676 break; 677 678 case NODE_COLUMN: 679 print_syntax(np->u.column.syntax); 680 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "", 681 (np->flags & FL_SET) ? " SET" : ""); 682 break; 683 684 } 685 } 686 687 static int 688 extract(const struct node *np, struct asn_oid *oid, const char *obj, 689 const struct asn_oid *idx, const char *iname) 690 { 691 struct node *sub; 692 u_long n; 693 694 if (oid->len == ASN_MAXOIDLEN) 695 report_node(np, "OID too long"); 696 oid->subs[oid->len++] = np->id; 697 698 if (strcmp(obj, np->name) == 0) { 699 if (oid->len + idx->len >= ASN_MAXOIDLEN) 700 report_node(np, "OID too long"); 701 fprintf(fp, "#define OID_%s%s\t%u\n", np->name, 702 iname ? iname : "", np->id); 703 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name, 704 iname ? iname : "", oid->len + idx->len); 705 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name, 706 iname ? iname : "", oid->len + idx->len); 707 for (n = 0; n < oid->len; n++) 708 fprintf(fp, " %u,", oid->subs[n]); 709 for (n = 0; n < idx->len; n++) 710 fprintf(fp, " %u,", idx->subs[n]); 711 fprintf(fp, " } }\n"); 712 return (0); 713 } 714 715 if (np->type == NODE_TREE) { 716 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 717 if (!extract(sub, oid, obj, idx, iname)) 718 return (0); 719 } else if (np->type == NODE_ENTRY) { 720 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 721 if (!extract(sub, oid, obj, idx, iname)) 722 return (0); 723 } 724 oid->len--; 725 return (1); 726 } 727 728 static int 729 gen_extract(const struct node *root, char *object) 730 { 731 struct asn_oid oid; 732 struct asn_oid idx; 733 char *s, *e, *end, *iname; 734 u_long ul; 735 int ret; 736 737 /* look whether the object to extract has an index part */ 738 idx.len = 0; 739 iname = NULL; 740 s = strchr(object, '.'); 741 if (s != NULL) { 742 iname = malloc(strlen(s) + 1); 743 if (iname == NULL) 744 err(1, "cannot allocated index"); 745 746 strcpy(iname, s); 747 for (e = iname; *e != '\0'; e++) 748 if (*e == '.') 749 *e = '_'; 750 751 *s++ = '\0'; 752 while (s != NULL) { 753 if (*s == '\0') 754 errx(1, "bad index syntax"); 755 if ((e = strchr(s, '.')) != NULL) 756 *e++ = '\0'; 757 758 errno = 0; 759 ul = strtoul(s, &end, 0); 760 if (*end != '\0') 761 errx(1, "bad index syntax '%s'", end); 762 if (errno != 0) 763 err(1, "bad index syntax"); 764 765 if (idx.len == ASN_MAXOIDLEN) 766 errx(1, "index oid too large"); 767 idx.subs[idx.len++] = ul; 768 769 s = e; 770 } 771 } 772 773 oid.len = PREFIX_LEN; 774 memcpy(oid.subs, prefix, sizeof(prefix)); 775 ret = extract(root, &oid, object, &idx, iname); 776 if (iname != NULL) 777 free(iname); 778 779 return (ret); 780 } 781 782 783 static void 784 check_sub_order(const struct node *np, const struct node_list *subs) 785 { 786 int first; 787 const struct node *sub; 788 asn_subid_t maxid = 0; 789 790 /* ensure, that subids are ordered */ 791 first = 1; 792 TAILQ_FOREACH(sub, subs, link) { 793 if (!first && sub->id <= maxid) 794 report_node(np, "subids not ordered at %s", sub->name); 795 maxid = sub->id; 796 first = 0; 797 } 798 } 799 800 /* 801 * Do some sanity checks on the tree definition and do some computations. 802 */ 803 static void 804 check_tree(struct node *np) 805 { 806 struct node *sub; 807 808 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) { 809 if ((np->flags & (FL_GET|FL_SET)) != 0) 810 tree_size++; 811 return; 812 } 813 814 if (np->type == NODE_ENTRY) { 815 check_sub_order(np, &np->u.entry.subs); 816 817 /* ensure all subnodes are columns */ 818 TAILQ_FOREACH(sub, &np->u.entry.subs, link) { 819 if (sub->type != NODE_COLUMN) 820 report_node(np, "entry subnode '%s' is not " 821 "a column", sub->name); 822 check_tree(sub); 823 } 824 } else { 825 check_sub_order(np, &np->u.tree.subs); 826 827 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 828 check_tree(sub); 829 } 830 } 831 832 static void 833 merge_subs(struct node_list *s1, struct node_list *s2) 834 { 835 struct node *n1, *n2; 836 837 while (!TAILQ_EMPTY(s2)) { 838 n2 = TAILQ_FIRST(s2); 839 TAILQ_REMOVE(s2, n2, link); 840 841 TAILQ_FOREACH(n1, s1, link) 842 if (n1->id >= n2->id) 843 break; 844 if (n1 == NULL) 845 TAILQ_INSERT_TAIL(s1, n2, link); 846 else if (n1->id > n2->id) 847 TAILQ_INSERT_BEFORE(n1, n2, link); 848 else { 849 if (n1->type == NODE_TREE && n2->type == NODE_TREE) { 850 if (strcmp(n1->name, n2->name) != 0) 851 errx(1, "trees to merge must have " 852 "same name '%s' '%s'", n1->name, 853 n2->name); 854 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs); 855 free(n2); 856 } else if (n1->type == NODE_ENTRY && 857 n2->type == NODE_ENTRY) { 858 if (strcmp(n1->name, n2->name) != 0) 859 errx(1, "entries to merge must have " 860 "same name '%s' '%s'", n1->name, 861 n2->name); 862 if (n1->u.entry.index != n2->u.entry.index) 863 errx(1, "entries to merge must have " 864 "same index '%s'", n1->name); 865 if (strcmp(n1->u.entry.func, 866 n2->u.entry.func) != 0) 867 errx(1, "entries to merge must have " 868 "same op '%s'", n1->name); 869 merge_subs(&n1->u.entry.subs, 870 &n2->u.entry.subs); 871 free(n2); 872 } else 873 errx(1, "entities to merge must be both " 874 "trees or both entries: %s, %s", 875 n1->name, n2->name); 876 } 877 } 878 } 879 880 static void 881 merge(struct node *root, struct node *t) 882 { 883 884 /* both must be trees */ 885 if (root->type != NODE_TREE) 886 errx(1, "root is not a tree"); 887 if (t->type != NODE_TREE) 888 errx(1, "can merge only with tree"); 889 if (root->id != t->id) 890 errx(1, "trees to merge must have same id"); 891 892 merge_subs(&root->u.tree.subs, &t->u.tree.subs); 893 } 894 895 int 896 main(int argc, char *argv[]) 897 { 898 int do_extract = 0; 899 int do_tree = 0; 900 int opt; 901 struct node *root; 902 char fname[MAXPATHLEN + 1]; 903 int tok; 904 905 while ((opt = getopt(argc, argv, "help:t")) != EOF) 906 switch (opt) { 907 908 case 'h': 909 fprintf(stderr, "%s", usgtxt); 910 exit(0); 911 912 case 'e': 913 do_extract = 1; 914 break; 915 916 case 'l': 917 localincs = 1; 918 break; 919 920 case 'p': 921 file_prefix = optarg; 922 if (strlen(file_prefix) + strlen("tree.c") > 923 MAXPATHLEN) 924 errx(1, "prefix too long"); 925 break; 926 927 case 't': 928 do_tree = 1; 929 break; 930 } 931 932 if (do_extract && do_tree) 933 errx(1, "conflicting options -e and -t"); 934 if (!do_extract && argc != optind) 935 errx(1, "no arguments allowed"); 936 if (do_extract && argc == optind) 937 errx(1, "no objects specified"); 938 939 root = parse(gettoken()); 940 while ((tok = gettoken()) != TOK_EOF) 941 merge(root, parse(tok)); 942 943 check_tree(root); 944 945 if (do_extract) { 946 fp = stdout; 947 while (optind < argc) { 948 if (gen_extract(root, argv[optind])) 949 errx(1, "object not found: %s", argv[optind]); 950 optind++; 951 } 952 953 return (0); 954 } 955 if (do_tree) { 956 gen_tree(root, 0); 957 return (0); 958 } 959 sprintf(fname, "%stree.h", file_prefix); 960 if ((fp = fopen(fname, "w")) == NULL) 961 err(1, "%s: ", fname); 962 gen_header(root, PREFIX_LEN, NULL); 963 964 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size); 965 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix); 966 967 fclose(fp); 968 969 sprintf(fname, "%stree.c", file_prefix); 970 if ((fp = fopen(fname, "w")) == NULL) 971 err(1, "%s: ", fname); 972 gen_table(root); 973 fclose(fp); 974 975 return (0); 976 } 977