Deleted Added
sdiff udiff text old ( 154182 ) new ( 156066 )
full compact
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 */
80static const asn_subid_t prefix[] = { 1, 3, 6 };
81#define PREFIX_LEN (sizeof(prefix) / sizeof(prefix[0]))
82
83u_int tree_size;
84static const char *file_prefix = "";
85static FILE *fp;
86
87/* if true generate local include paths */
88static int localincs = 0;
89
90static const char usgtxt[] = "\
91Generate SNMP tables. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
92Open Communication Systems (FhG Fokus). All rights reserved.\n\
93usage: gensnmptree [-hel] [-p prefix] [name]...\n\
94options:\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 */
104enum ntype {
105 NODE_LEAF = 1,
106 NODE_TREE,
107 NODE_ENTRY,
108 NODE_COLUMN
109};
110
111enum {
112 FL_GET = 0x01,
113 FL_SET = 0x02,
114};
115
116struct node;
117TAILQ_HEAD(node_list, node);
118
119struct 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
149struct func {
150 const char *name;
151 LIST_ENTRY(func) link;
152};
153
154static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
155
156/************************************************************
157 *
158 * Allocate memory and panic just in the case...
159 */
160static void *
161xalloc(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 */
175enum 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
183static 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
206char str[MAXSTR];
207u_long val; /* integer values */
208u_int lno = 1; /* current line number */
209int all_cond; /* all conditions are true */
210
211static void report(const char *, ...) __dead2 __printflike(1, 2);
212static void report_node(const struct node *, const char *, ...)
213 __dead2 __printflike(2, 3);
214
215/*
216 * Report an error and exit.
217 */
218static void
219report(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}
235static void
236report_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 */
251static char *
252savetok(void)
253{
254 return (strcpy(xalloc(strlen(str)+1), str));
255}
256
257/*
258 * Get the next token from input.
259 */
260static int
261gettoken(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 */
347static struct node *
348parse(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 */
439static void
440gen_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 */
542static void
543gen_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 */
592static void
593gen_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
623static void
624print_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 */
640static void
641gen_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
688static int
689extract(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
729static int
730gen_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
784static void
785check_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 */
804static void
805check_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
833static void
834merge_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
881static void
882merge(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
896int
897main(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}