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.44 2006/02/14 09:04:17 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 node->flags = 0; 357 358 if (tok != '(') 359 report("'(' expected at begin of node"); 360 if (gettoken() != TOK_NUM) 361 report("node id expected after opening '('"); 362 if (val > ASN_MAXID) 363 report("subid too large '%lu'", val); 364 node->id = (asn_subid_t)val; 365 if (gettoken() != TOK_STR) 366 report("node name expected after '(' ID"); 367 node->name = savetok(); 368 369 if ((tok = gettoken()) == TOK_TYPE) { 370 /* LEAF or COLUM */ 371 u_int syntax = val; 372 373 if ((tok = gettoken()) == TOK_STR) { 374 /* LEAF */ 375 node->type = NODE_LEAF; 376 node->u.leaf.func = savetok(); 377 node->u.leaf.syntax = syntax; 378 tok = gettoken(); 379 } else { 380 /* COLUMN */ 381 node->type = NODE_COLUMN; 382 node->u.column.syntax = syntax; 383 } 384 385 while (tok != ')') { 386 if (tok != TOK_ACCESS) 387 report("access keyword or ')' expected"); 388 node->flags |= (u_int)val; 389 tok = gettoken(); 390 } 391 392 } else if (tok == ':') { 393 /* ENTRY */ 394 node->type = NODE_ENTRY; 395 TAILQ_INIT(&node->u.entry.subs); 396 397 index_count = 0; 398 node->u.entry.index = 0; 399 while ((tok = gettoken()) == TOK_TYPE) { 400 if (index_count++ == SNMP_INDEXES_MAX) 401 report("too many table indexes"); 402 node->u.entry.index |= 403 val << (SNMP_INDEX_SHIFT * index_count); 404 } 405 node->u.entry.index |= index_count; 406 if (index_count == 0) 407 report("need at least one index"); 408 409 if (tok != TOK_STR) 410 report("function name expected"); 411 412 node->u.entry.func = savetok(); 413 414 tok = gettoken(); 415 416 while (tok != ')') { 417 sub = parse(tok); 418 TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link); 419 tok = gettoken(); 420 } 421 422 } else { 423 /* subtree */ 424 node->type = NODE_TREE; 425 TAILQ_INIT(&node->u.tree.subs); 426 427 while (tok != ')') { 428 sub = parse(tok); 429 TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link); 430 tok = gettoken(); 431 } 432 } 433 return (node); 434 } 435 436 /* 437 * Generate the C-code table part for one node. 438 */ 439 static void 440 gen_node(struct node *np, struct asn_oid *oid, u_int idx, const char *func) 441 { 442 u_int n; 443 struct node *sub; 444 u_int syntax; 445 446 if (oid->len == ASN_MAXOIDLEN) 447 report_node(np, "OID too long"); 448 oid->subs[oid->len++] = np->id; 449 450 if (np->type == NODE_TREE) { 451 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 452 gen_node(sub, oid, 0, NULL); 453 oid->len--; 454 return; 455 } 456 if (np->type == NODE_ENTRY) { 457 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 458 gen_node(sub, oid, np->u.entry.index, np->u.entry.func); 459 oid->len--; 460 return; 461 } 462 463 /* leaf or column */ 464 if ((np->flags & (FL_GET|FL_SET)) == 0) { 465 oid->len--; 466 return; 467 } 468 469 fprintf(fp, " {{ %u, {", oid->len); 470 for (n = 0; n < oid->len; n++) 471 fprintf(fp, " %u,", oid->subs[n]); 472 fprintf(fp, " }}, \"%s\", ", np->name); 473 474 if (np->type == NODE_COLUMN) { 475 syntax = np->u.column.syntax; 476 fprintf(fp, "SNMP_NODE_COLUMN, "); 477 } else { 478 syntax = np->u.leaf.syntax; 479 fprintf(fp, "SNMP_NODE_LEAF, "); 480 } 481 482 switch (syntax) { 483 484 case SNMP_SYNTAX_NULL: 485 fprintf(fp, "SNMP_SYNTAX_NULL, "); 486 break; 487 488 case SNMP_SYNTAX_INTEGER: 489 fprintf(fp, "SNMP_SYNTAX_INTEGER, "); 490 break; 491 492 case SNMP_SYNTAX_OCTETSTRING: 493 fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, "); 494 break; 495 496 case SNMP_SYNTAX_IPADDRESS: 497 fprintf(fp, "SNMP_SYNTAX_IPADDRESS, "); 498 break; 499 500 case SNMP_SYNTAX_OID: 501 fprintf(fp, "SNMP_SYNTAX_OID, "); 502 break; 503 504 case SNMP_SYNTAX_TIMETICKS: 505 fprintf(fp, "SNMP_SYNTAX_TIMETICKS, "); 506 break; 507 508 case SNMP_SYNTAX_COUNTER: 509 fprintf(fp, "SNMP_SYNTAX_COUNTER, "); 510 break; 511 512 case SNMP_SYNTAX_GAUGE: 513 fprintf(fp, "SNMP_SYNTAX_GAUGE, "); 514 break; 515 516 case SNMP_SYNTAX_COUNTER64: 517 fprintf(fp, "SNMP_SYNTAX_COUNTER64, "); 518 break; 519 520 case SNMP_SYNTAX_NOSUCHOBJECT: 521 case SNMP_SYNTAX_NOSUCHINSTANCE: 522 case SNMP_SYNTAX_ENDOFMIBVIEW: 523 abort(); 524 } 525 526 if (np->type == NODE_COLUMN) 527 fprintf(fp, "%s, ", func); 528 else 529 fprintf(fp, "%s, ", np->u.leaf.func); 530 531 fprintf(fp, "0"); 532 if (np->flags & FL_SET) 533 fprintf(fp, "|SNMP_NODE_CANSET"); 534 fprintf(fp, ", %#x, NULL, NULL },\n", idx); 535 oid->len--; 536 return; 537 } 538 539 /* 540 * Generate the header file with the function declarations. 541 */ 542 static void 543 gen_header(struct node *np, u_int oidlen, const char *func) 544 { 545 char f[MAXSTR + 4]; 546 struct node *sub; 547 struct func *ptr; 548 549 oidlen++; 550 if (np->type == NODE_TREE) { 551 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 552 gen_header(sub, oidlen, NULL); 553 return; 554 } 555 if (np->type == NODE_ENTRY) { 556 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 557 gen_header(sub, oidlen, np->u.entry.func); 558 return; 559 } 560 561 if((np->flags & (FL_GET|FL_SET)) == 0) 562 return; 563 564 if (np->type == NODE_COLUMN) { 565 if (func == NULL) 566 errx(1, "column without function (%s) - probably " 567 "outside of a table", np->name); 568 sprintf(f, "%s", func); 569 } else 570 sprintf(f, "%s", np->u.leaf.func); 571 572 LIST_FOREACH(ptr, &funcs, link) 573 if (strcmp(ptr->name, f) == 0) 574 break; 575 576 if (ptr == NULL) { 577 ptr = xalloc(sizeof(*ptr)); 578 ptr->name = strcpy(xalloc(strlen(f)+1), f); 579 LIST_INSERT_HEAD(&funcs, ptr, link); 580 581 fprintf(fp, "int %s(struct snmp_context *, " 582 "struct snmp_value *, u_int, u_int, " 583 "enum snmp_op);\n", f); 584 } 585 586 fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id); 587 } 588 589 /* 590 * Generate the OID table. 591 */ 592 static void 593 gen_table(struct node *node) 594 { 595 struct asn_oid oid; 596 597 fprintf(fp, "#include <sys/types.h>\n"); 598 fprintf(fp, "#include <stdio.h>\n"); 599 #ifdef HAVE_STDINT_H 600 fprintf(fp, "#include <stdint.h>\n"); 601 #endif 602 if (localincs) { 603 fprintf(fp, "#include \"asn1.h\"\n"); 604 fprintf(fp, "#include \"snmp.h\"\n"); 605 fprintf(fp, "#include \"snmpagent.h\"\n"); 606 } else { 607 fprintf(fp, "#include <bsnmp/asn1.h>\n"); 608 fprintf(fp, "#include <bsnmp/snmp.h>\n"); 609 fprintf(fp, "#include <bsnmp/snmpagent.h>\n"); 610 } 611 fprintf(fp, "#include \"%stree.h\"\n", file_prefix); 612 fprintf(fp, "\n"); 613 614 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix); 615 616 oid.len = PREFIX_LEN; 617 memcpy(oid.subs, prefix, sizeof(prefix)); 618 gen_node(node, &oid, 0, NULL); 619 620 fprintf(fp, "};\n\n"); 621 } 622 623 static void 624 print_syntax(u_int syntax) 625 { 626 u_int i; 627 628 for (i = 0; keywords[i].str != NULL; i++) 629 if (keywords[i].tok == TOK_TYPE && 630 keywords[i].val == syntax) { 631 printf(" %s", keywords[i].str); 632 return; 633 } 634 abort(); 635 } 636 637 /* 638 * Generate a tree definition file 639 */ 640 static void 641 gen_tree(const struct node *np, int level) 642 { 643 const struct node *sp; 644 u_int i; 645 646 printf("%*s(%u %s", 2 * level, "", np->id, np->name); 647 648 switch (np->type) { 649 650 case NODE_LEAF: 651 print_syntax(np->u.leaf.syntax); 652 printf(" %s%s%s)\n", np->u.leaf.func, 653 (np->flags & FL_GET) ? " GET" : "", 654 (np->flags & FL_SET) ? " SET" : ""); 655 break; 656 657 case NODE_TREE: 658 if (TAILQ_EMPTY(&np->u.tree.subs)) { 659 printf(")\n"); 660 } else { 661 printf("\n"); 662 TAILQ_FOREACH(sp, &np->u.tree.subs, link) 663 gen_tree(sp, level + 1); 664 printf("%*s)\n", 2 * level, ""); 665 } 666 break; 667 668 case NODE_ENTRY: 669 printf(" :"); 670 671 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) 672 print_syntax(SNMP_INDEX(np->u.entry.index, i)); 673 printf(" %s\n", np->u.entry.func); 674 TAILQ_FOREACH(sp, &np->u.entry.subs, link) 675 gen_tree(sp, level + 1); 676 printf("%*s)\n", 2 * level, ""); 677 break; 678 679 case NODE_COLUMN: 680 print_syntax(np->u.column.syntax); 681 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "", 682 (np->flags & FL_SET) ? " SET" : ""); 683 break; 684 685 } 686 } 687 688 static int 689 extract(const struct node *np, struct asn_oid *oid, const char *obj, 690 const struct asn_oid *idx, const char *iname) 691 { 692 struct node *sub; 693 u_long n; 694 695 if (oid->len == ASN_MAXOIDLEN) 696 report_node(np, "OID too long"); 697 oid->subs[oid->len++] = np->id; 698 699 if (strcmp(obj, np->name) == 0) { 700 if (oid->len + idx->len >= ASN_MAXOIDLEN) 701 report_node(np, "OID too long"); 702 fprintf(fp, "#define OID_%s%s\t%u\n", np->name, 703 iname ? iname : "", np->id); 704 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name, 705 iname ? iname : "", oid->len + idx->len); 706 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name, 707 iname ? iname : "", oid->len + idx->len); 708 for (n = 0; n < oid->len; n++) 709 fprintf(fp, " %u,", oid->subs[n]); 710 for (n = 0; n < idx->len; n++) 711 fprintf(fp, " %u,", idx->subs[n]); 712 fprintf(fp, " } }\n"); 713 return (0); 714 } 715 716 if (np->type == NODE_TREE) { 717 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 718 if (!extract(sub, oid, obj, idx, iname)) 719 return (0); 720 } else if (np->type == NODE_ENTRY) { 721 TAILQ_FOREACH(sub, &np->u.entry.subs, link) 722 if (!extract(sub, oid, obj, idx, iname)) 723 return (0); 724 } 725 oid->len--; 726 return (1); 727 } 728 729 static int 730 gen_extract(const struct node *root, char *object) 731 { 732 struct asn_oid oid; 733 struct asn_oid idx; 734 char *s, *e, *end, *iname; 735 u_long ul; 736 int ret; 737 738 /* look whether the object to extract has an index part */ 739 idx.len = 0; 740 iname = NULL; 741 s = strchr(object, '.'); 742 if (s != NULL) { 743 iname = malloc(strlen(s) + 1); 744 if (iname == NULL) 745 err(1, "cannot allocated index"); 746 747 strcpy(iname, s); 748 for (e = iname; *e != '\0'; e++) 749 if (*e == '.') 750 *e = '_'; 751 752 *s++ = '\0'; 753 while (s != NULL) { 754 if (*s == '\0') 755 errx(1, "bad index syntax"); 756 if ((e = strchr(s, '.')) != NULL) 757 *e++ = '\0'; 758 759 errno = 0; 760 ul = strtoul(s, &end, 0); 761 if (*end != '\0') 762 errx(1, "bad index syntax '%s'", end); 763 if (errno != 0) 764 err(1, "bad index syntax"); 765 766 if (idx.len == ASN_MAXOIDLEN) 767 errx(1, "index oid too large"); 768 idx.subs[idx.len++] = ul; 769 770 s = e; 771 } 772 } 773 774 oid.len = PREFIX_LEN; 775 memcpy(oid.subs, prefix, sizeof(prefix)); 776 ret = extract(root, &oid, object, &idx, iname); 777 if (iname != NULL) 778 free(iname); 779 780 return (ret); 781 } 782 783 784 static void 785 check_sub_order(const struct node *np, const struct node_list *subs) 786 { 787 int first; 788 const struct node *sub; 789 asn_subid_t maxid = 0; 790 791 /* ensure, that subids are ordered */ 792 first = 1; 793 TAILQ_FOREACH(sub, subs, link) { 794 if (!first && sub->id <= maxid) 795 report_node(np, "subids not ordered at %s", sub->name); 796 maxid = sub->id; 797 first = 0; 798 } 799 } 800 801 /* 802 * Do some sanity checks on the tree definition and do some computations. 803 */ 804 static void 805 check_tree(struct node *np) 806 { 807 struct node *sub; 808 809 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) { 810 if ((np->flags & (FL_GET|FL_SET)) != 0) 811 tree_size++; 812 return; 813 } 814 815 if (np->type == NODE_ENTRY) { 816 check_sub_order(np, &np->u.entry.subs); 817 818 /* ensure all subnodes are columns */ 819 TAILQ_FOREACH(sub, &np->u.entry.subs, link) { 820 if (sub->type != NODE_COLUMN) 821 report_node(np, "entry subnode '%s' is not " 822 "a column", sub->name); 823 check_tree(sub); 824 } 825 } else { 826 check_sub_order(np, &np->u.tree.subs); 827 828 TAILQ_FOREACH(sub, &np->u.tree.subs, link) 829 check_tree(sub); 830 } 831 } 832 833 static void 834 merge_subs(struct node_list *s1, struct node_list *s2) 835 { 836 struct node *n1, *n2; 837 838 while (!TAILQ_EMPTY(s2)) { 839 n2 = TAILQ_FIRST(s2); 840 TAILQ_REMOVE(s2, n2, link); 841 842 TAILQ_FOREACH(n1, s1, link) 843 if (n1->id >= n2->id) 844 break; 845 if (n1 == NULL) 846 TAILQ_INSERT_TAIL(s1, n2, link); 847 else if (n1->id > n2->id) 848 TAILQ_INSERT_BEFORE(n1, n2, link); 849 else { 850 if (n1->type == NODE_TREE && n2->type == NODE_TREE) { 851 if (strcmp(n1->name, n2->name) != 0) 852 errx(1, "trees to merge must have " 853 "same name '%s' '%s'", n1->name, 854 n2->name); 855 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs); 856 free(n2); 857 } else if (n1->type == NODE_ENTRY && 858 n2->type == NODE_ENTRY) { 859 if (strcmp(n1->name, n2->name) != 0) 860 errx(1, "entries to merge must have " 861 "same name '%s' '%s'", n1->name, 862 n2->name); 863 if (n1->u.entry.index != n2->u.entry.index) 864 errx(1, "entries to merge must have " 865 "same index '%s'", n1->name); 866 if (strcmp(n1->u.entry.func, 867 n2->u.entry.func) != 0) 868 errx(1, "entries to merge must have " 869 "same op '%s'", n1->name); 870 merge_subs(&n1->u.entry.subs, 871 &n2->u.entry.subs); 872 free(n2); 873 } else 874 errx(1, "entities to merge must be both " 875 "trees or both entries: %s, %s", 876 n1->name, n2->name); 877 } 878 } 879 } 880 881 static void 882 merge(struct node *root, struct node *t) 883 { 884 885 /* both must be trees */ 886 if (root->type != NODE_TREE) 887 errx(1, "root is not a tree"); 888 if (t->type != NODE_TREE) 889 errx(1, "can merge only with tree"); 890 if (root->id != t->id) 891 errx(1, "trees to merge must have same id"); 892 893 merge_subs(&root->u.tree.subs, &t->u.tree.subs); 894 } 895 896 int 897 main(int argc, char *argv[]) 898 { 899 int do_extract = 0; 900 int do_tree = 0; 901 int opt; 902 struct node *root; 903 char fname[MAXPATHLEN + 1]; 904 int tok; 905 906 while ((opt = getopt(argc, argv, "help:t")) != EOF) 907 switch (opt) { 908 909 case 'h': 910 fprintf(stderr, "%s", usgtxt); 911 exit(0); 912 913 case 'e': 914 do_extract = 1; 915 break; 916 917 case 'l': 918 localincs = 1; 919 break; 920 921 case 'p': 922 file_prefix = optarg; 923 if (strlen(file_prefix) + strlen("tree.c") > 924 MAXPATHLEN) 925 errx(1, "prefix too long"); 926 break; 927 928 case 't': 929 do_tree = 1; 930 break; 931 } 932 933 if (do_extract && do_tree) 934 errx(1, "conflicting options -e and -t"); 935 if (!do_extract && argc != optind) 936 errx(1, "no arguments allowed"); 937 if (do_extract && argc == optind) 938 errx(1, "no objects specified"); 939 940 root = parse(gettoken()); 941 while ((tok = gettoken()) != TOK_EOF) 942 merge(root, parse(tok)); 943 944 check_tree(root); 945 946 if (do_extract) { 947 fp = stdout; 948 while (optind < argc) { 949 if (gen_extract(root, argv[optind])) 950 errx(1, "object not found: %s", argv[optind]); 951 optind++; 952 } 953 954 return (0); 955 } 956 if (do_tree) { 957 gen_tree(root, 0); 958 return (0); 959 } 960 sprintf(fname, "%stree.h", file_prefix); 961 if ((fp = fopen(fname, "w")) == NULL) 962 err(1, "%s: ", fname); 963 gen_header(root, PREFIX_LEN, NULL); 964 965 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size); 966 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix); 967 968 fclose(fp); 969 970 sprintf(fname, "%stree.c", file_prefix); 971 if ((fp = fopen(fname, "w")) == NULL) 972 err(1, "%s: ", fname); 973 gen_table(root); 974 fclose(fp); 975 976 return (0); 977 } 978