1 /*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Copyright (c) 2004-2006
7 * Hartmut Brandt.
8 * All rights reserved.
9 *
10 * Author: Harti Brandt <harti@freebsd.org>
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $Begemot: gensnmptree.c 383 2006-05-30 07:40:49Z brandt_h $
34 *
35 * Generate OID table from table description.
36 *
37 * Syntax is:
38 * ---------
39 * file := top | top file
40 *
41 * top := tree | typedef | include
42 *
43 * tree := head elements ')'
44 *
45 * entry := head ':' index STRING elements ')'
46 *
47 * leaf := head type STRING ACCESS ')'
48 *
49 * column := head type ACCESS ')'
50 *
51 * type := BASETYPE | BASETYPE '|' subtype | enum | bits
52 *
53 * subtype := STRING
54 *
55 * enum := ENUM '(' value ')'
56 *
57 * bits := BITS '(' value ')'
58 *
59 * value := optminus INT STRING | optminus INT STRING value
60 *
61 * optminus := '-' | EMPTY
62 *
63 * head := '(' INT STRING
64 *
65 * elements := EMPTY | elements element
66 *
67 * element := tree | leaf | column
68 *
69 * index := type | index type
70 *
71 * typedef := 'typedef' STRING type
72 *
73 * include := 'include' filespec
74 *
75 * filespec := '"' STRING '"' | '<' STRING '>'
76 */
77 #include <sys/types.h>
78 #include <sys/param.h>
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <stdarg.h>
82 #include <unistd.h>
83 #include <string.h>
84 #include <ctype.h>
85 #include <inttypes.h>
86 #include <errno.h>
87 #ifdef HAVE_ERR_H
88 #include <err.h>
89 #endif
90 #include <sys/queue.h>
91 #include "support.h"
92 #include "asn1.h"
93 #include "snmp.h"
94 #include "snmpagent.h"
95
96 /*
97 * Constant prefix for all OIDs
98 */
99 static const asn_subid_t prefix[] = { 1, 3, 6 };
100 #define PREFIX_LEN (sizeof(prefix) / sizeof(prefix[0]))
101
102 u_int tree_size;
103 static const char *file_prefix = "";
104
105 /* if true generate local include paths */
106 static int localincs = 0;
107
108 /* if true print tokens */
109 static int debug;
110
111 static const char usgtxt[] = "\
112 Generate SNMP tables.\n\
113 usage: gensnmptree [-dEehlt] [-I directory] [-i infile] [-p prefix]\n\
114 [name]...\n\
115 options:\n\
116 -d debug mode\n\
117 -E extract the named enums and bits only\n\
118 -e extract the named oids or enums\n\
119 -h print this info\n\
120 -I directory add directory to include path\n\
121 -i ifile read from the named file instead of stdin\n\
122 -l generate local include directives\n\
123 -p prefix prepend prefix to file and variable names\n\
124 -t generated a .def file\n\
125 ";
126
127 /*
128 * A node in the OID tree
129 */
130 enum ntype {
131 NODE_LEAF = 1,
132 NODE_TREE,
133 NODE_ENTRY,
134 NODE_COLUMN
135 };
136
137 enum {
138 FL_GET = 0x01,
139 FL_SET = 0x02,
140 };
141
142 struct node;
143 TAILQ_HEAD(node_list, node);
144
145 struct node {
146 enum ntype type;
147 asn_subid_t id; /* last element of OID */
148 char *name; /* name of node */
149 TAILQ_ENTRY(node) link;
150 u_int lno; /* starting line number */
151 u_int flags; /* allowed operations */
152
153 union {
154 struct tree {
155 struct node_list subs;
156 } tree;
157
158 struct entry {
159 uint32_t index; /* index for table entry */
160 char *func; /* function for tables */
161 struct node_list subs;
162 } entry;
163
164 struct leaf {
165 enum snmp_syntax syntax; /* syntax for this leaf */
166 char *func; /* function name */
167 } leaf;
168
169 struct column {
170 enum snmp_syntax syntax; /* syntax for this column */
171 } column;
172 } u;
173 };
174
175 struct func {
176 const char *name;
177 LIST_ENTRY(func) link;
178 };
179
180 static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
181
182 struct enums {
183 const char *name;
184 long value;
185 TAILQ_ENTRY(enums) link;
186 };
187
188 struct type {
189 const char *name;
190 const char *from_fname;
191 u_int from_lno;
192 u_int syntax;
193 int is_enum;
194 int is_bits;
195 TAILQ_HEAD(, enums) enums;
196 LIST_ENTRY(type) link;
197 };
198
199 static LIST_HEAD(, type) types = LIST_HEAD_INITIALIZER(types);
200
201 static void report(const char *, ...) __dead2 __printflike(1, 2);
202 static void report_node(const struct node *, const char *, ...)
203 __dead2 __printflike(2, 3);
204
205 /************************************************************
206 *
207 * Allocate memory and panic just in the case...
208 */
209 static void *
xalloc(size_t size)210 xalloc(size_t size)
211 {
212 void *ptr;
213
214 if ((ptr = malloc(size)) == NULL)
215 err(1, "allocing %zu bytes", size);
216
217 return (ptr);
218 }
219
220 static char *
savestr(const char * s)221 savestr(const char *s)
222 {
223
224 if (s == NULL)
225 return (NULL);
226 return (strcpy(xalloc(strlen(s) + 1), s));
227 }
228
229 /************************************************************
230 *
231 * Input stack
232 */
233 struct input {
234 FILE *fp;
235 u_int lno;
236 char *fname;
237 char *path;
238 LIST_ENTRY(input) link;
239 };
240 static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs);
241 static struct input *input = NULL;
242
243 #define MAX_PATHS 100
244 static u_int npaths = 2;
245 static u_int stdpaths = 2;
246 static const char *paths[MAX_PATHS + 1] = {
247 "/usr/share/snmp/defs",
248 "/usr/local/share/snmp/defs",
249 NULL
250 };
251
252 static int pbchar = -1;
253
254 static void
path_new(const char * path)255 path_new(const char *path)
256 {
257 if (npaths >= MAX_PATHS)
258 report("too many -I directives");
259 memmove(&paths[npaths - stdpaths + 1], &paths[npaths - stdpaths],
260 sizeof(path[0]) * stdpaths);
261 paths[npaths - stdpaths] = savestr(path);
262 npaths++;
263 }
264
265 static void
input_new(FILE * fp,const char * path,const char * fname)266 input_new(FILE *fp, const char *path, const char *fname)
267 {
268 struct input *ip;
269
270 ip = xalloc(sizeof(*ip));
271 ip->fp = fp;
272 ip->lno = 1;
273 ip->fname = savestr(fname);
274 ip->path = savestr(path);
275 LIST_INSERT_HEAD(&inputs, ip, link);
276
277 input = ip;
278 }
279
280 static void
input_close(void)281 input_close(void)
282 {
283
284 if (input == NULL)
285 return;
286 fclose(input->fp);
287 free(input->fname);
288 free(input->path);
289 LIST_REMOVE(input, link);
290 free(input);
291
292 input = LIST_FIRST(&inputs);
293 }
294
295 static FILE *
tryopen(const char * path,const char * fname)296 tryopen(const char *path, const char *fname)
297 {
298 char *fn;
299 FILE *fp;
300
301 if (path == NULL)
302 fn = savestr(fname);
303 else {
304 fn = xalloc(strlen(path) + strlen(fname) + 2);
305 sprintf(fn, "%s/%s", path, fname);
306 }
307 fp = fopen(fn, "r");
308 free(fn);
309 return (fp);
310 }
311
312 static void
input_fopen(const char * fname,int loc)313 input_fopen(const char *fname, int loc)
314 {
315 FILE *fp;
316 char *path;
317 u_int p;
318
319 if (fname[0] == '/') {
320 if ((fp = tryopen(NULL, fname)) != NULL) {
321 input_new(fp, NULL, fname);
322 return;
323 }
324
325 } else {
326 if (loc) {
327 if (input == NULL)
328 path = NULL;
329 else
330 path = input->path;
331
332 if ((fp = tryopen(path, fname)) != NULL) {
333 input_new(fp, NULL, fname);
334 return;
335 }
336 }
337
338 for (p = 0; paths[p] != NULL; p++)
339 if ((fp = tryopen(paths[p], fname)) != NULL) {
340 input_new(fp, paths[p], fname);
341 return;
342 }
343 }
344 report("cannot open '%s'", fname);
345 }
346
347 static int
tgetc(void)348 tgetc(void)
349 {
350 int c;
351
352 if (pbchar != -1) {
353 c = pbchar;
354 pbchar = -1;
355 return (c);
356 }
357
358 for (;;) {
359 if (input == NULL)
360 return (EOF);
361
362 if ((c = getc(input->fp)) != EOF)
363 return (c);
364
365 input_close();
366 }
367 }
368
369 static void
tungetc(int c)370 tungetc(int c)
371 {
372
373 if (pbchar != -1)
374 abort();
375 pbchar = c;
376 }
377
378 /************************************************************
379 *
380 * Parsing input
381 */
382 enum tok {
383 TOK_EOF = 0200, /* end-of-file seen */
384 TOK_NUM, /* number */
385 TOK_STR, /* string */
386 TOK_ACCESS, /* access operator */
387 TOK_TYPE, /* type operator */
388 TOK_ENUM, /* enum token (kind of a type) */
389 TOK_TYPEDEF, /* typedef directive */
390 TOK_DEFTYPE, /* defined type */
391 TOK_INCLUDE, /* include directive */
392 TOK_FILENAME, /* filename ("foo.bar" or <foo.bar>) */
393 TOK_BITS, /* bits token (kind of a type) */
394 };
395
396 static const struct {
397 const char *str;
398 enum tok tok;
399 u_int val;
400 } keywords[] = {
401 { "GET", TOK_ACCESS, FL_GET },
402 { "SET", TOK_ACCESS, FL_SET },
403 { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },
404 { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },
405 { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },
406 { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },
407 { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },
408 { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },
409 { "OID", TOK_TYPE, SNMP_SYNTAX_OID },
410 { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },
411 { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },
412 { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },
413 { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },
414 { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER },
415 { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING },
416 { "typedef", TOK_TYPEDEF, 0 },
417 { "include", TOK_INCLUDE, 0 },
418 { NULL, 0, 0 }
419 };
420
421 /* arbitrary upper limit on node names and function names */
422 #define MAXSTR 1000
423 char str[MAXSTR];
424 u_long val; /* integer values */
425 int all_cond; /* all conditions are true */
426 int saved_token = -1;
427
428 /*
429 * Report an error and exit.
430 */
431 static void
report(const char * fmt,...)432 report(const char *fmt, ...)
433 {
434 va_list ap;
435 int c;
436
437 va_start(ap, fmt);
438 fprintf(stderr, "line %u: ", input->lno);
439 vfprintf(stderr, fmt, ap);
440 fprintf(stderr, "\n");
441 fprintf(stderr, "context: \"");
442 while ((c = tgetc()) != EOF && c != '\n')
443 fprintf(stderr, "%c", c);
444 fprintf(stderr, "\n");
445 va_end(ap);
446 exit(1);
447 }
448 static void
report_node(const struct node * np,const char * fmt,...)449 report_node(const struct node *np, const char *fmt, ...)
450 {
451 va_list ap;
452
453 va_start(ap, fmt);
454 fprintf(stderr, "line %u, node %s: ", np->lno, np->name);
455 vfprintf(stderr, fmt, ap);
456 fprintf(stderr, "\n");
457 va_end(ap);
458 exit(1);
459 }
460
461 /*
462 * Return a fresh copy of the string constituting the current token.
463 */
464 static char *
savetok(void)465 savetok(void)
466 {
467 return (savestr(str));
468 }
469
470 /*
471 * Get the next token from input.
472 */
473 static int
gettoken_internal(void)474 gettoken_internal(void)
475 {
476 int c;
477 struct type *t;
478
479 if (saved_token != -1) {
480 c = saved_token;
481 saved_token = -1;
482 return (c);
483 }
484
485 again:
486 /*
487 * Skip any whitespace before the next token
488 */
489 while ((c = tgetc()) != EOF) {
490 if (c == '\n')
491 input->lno++;
492 if (!isspace(c))
493 break;
494 }
495 if (c == EOF)
496 return (TOK_EOF);
497 if (!isascii(c))
498 report("unexpected character %#2x", (u_int)c);
499
500 /*
501 * Skip comments
502 */
503 if (c == '#') {
504 while ((c = tgetc()) != EOF) {
505 if (c == '\n') {
506 input->lno++;
507 goto again;
508 }
509 }
510 report("unexpected EOF in comment");
511 }
512
513 /*
514 * Single character tokens
515 */
516 if (strchr("():|-", c) != NULL)
517 return (c);
518
519 if (c == '"' || c == '<') {
520 int end = c;
521 size_t n = 0;
522
523 val = 1;
524 if (c == '<') {
525 val = 0;
526 end = '>';
527 }
528
529 while ((c = tgetc()) != EOF) {
530 if (c == end)
531 break;
532 if (n == sizeof(str) - 1) {
533 str[n++] = '\0';
534 report("filename too long '%s...'", str);
535 }
536 str[n++] = c;
537 }
538 str[n++] = '\0';
539 return (TOK_FILENAME);
540 }
541
542 /*
543 * Sort out numbers
544 */
545 if (isdigit(c)) {
546 size_t n = 0;
547 str[n++] = c;
548 while ((c = tgetc()) != EOF) {
549 if (!isdigit(c)) {
550 tungetc(c);
551 break;
552 }
553 if (n == sizeof(str) - 1) {
554 str[n++] = '\0';
555 report("number too long '%s...'", str);
556 }
557 str[n++] = c;
558 }
559 str[n++] = '\0';
560 sscanf(str, "%lu", &val);
561 return (TOK_NUM);
562 }
563
564 /*
565 * So that has to be a string.
566 */
567 if (isalpha(c) || c == '_') {
568 size_t n = 0;
569 str[n++] = c;
570 while ((c = tgetc()) != EOF) {
571 if (!isalnum(c) && c != '_' && c != '-') {
572 tungetc(c);
573 break;
574 }
575 if (n == sizeof(str) - 1) {
576 str[n++] = '\0';
577 report("string too long '%s...'", str);
578 }
579 str[n++] = c;
580 }
581 str[n++] = '\0';
582
583 /*
584 * Keywords
585 */
586 for (c = 0; keywords[c].str != NULL; c++)
587 if (strcmp(keywords[c].str, str) == 0) {
588 val = keywords[c].val;
589 return (keywords[c].tok);
590 }
591
592 LIST_FOREACH(t, &types, link) {
593 if (strcmp(t->name, str) == 0) {
594 val = t->syntax;
595 return (TOK_DEFTYPE);
596 }
597 }
598 return (TOK_STR);
599 }
600 if (isprint(c))
601 errx(1, "%u: unexpected character '%c'", input->lno, c);
602 else
603 errx(1, "%u: unexpected character 0x%02x", input->lno,
604 (u_int)c);
605 }
606 static int
gettoken(void)607 gettoken(void)
608 {
609 int tok = gettoken_internal();
610
611 if (debug) {
612 switch (tok) {
613
614 case TOK_EOF:
615 fprintf(stderr, "EOF ");
616 break;
617
618 case TOK_NUM:
619 fprintf(stderr, "NUM(%lu) ", val);
620 break;
621
622 case TOK_STR:
623 fprintf(stderr, "STR(%s) ", str);
624 break;
625
626 case TOK_ACCESS:
627 fprintf(stderr, "ACCESS(%lu) ", val);
628 break;
629
630 case TOK_TYPE:
631 fprintf(stderr, "TYPE(%lu) ", val);
632 break;
633
634 case TOK_ENUM:
635 fprintf(stderr, "ENUM ");
636 break;
637
638 case TOK_BITS:
639 fprintf(stderr, "BITS ");
640 break;
641
642 case TOK_TYPEDEF:
643 fprintf(stderr, "TYPEDEF ");
644 break;
645
646 case TOK_DEFTYPE:
647 fprintf(stderr, "DEFTYPE(%s,%lu) ", str, val);
648 break;
649
650 case TOK_INCLUDE:
651 fprintf(stderr, "INCLUDE ");
652 break;
653
654 case TOK_FILENAME:
655 fprintf(stderr, "FILENAME ");
656 break;
657
658 default:
659 if (tok < TOK_EOF) {
660 if (isprint(tok))
661 fprintf(stderr, "'%c' ", tok);
662 else if (tok == '\n')
663 fprintf(stderr, "\n");
664 else
665 fprintf(stderr, "%02x ", tok);
666 } else
667 abort();
668 break;
669 }
670 }
671 return (tok);
672 }
673
674 /**
675 * Pushback a token
676 */
677 static void
pushback(enum tok tok)678 pushback(enum tok tok)
679 {
680
681 if (saved_token != -1)
682 abort();
683 saved_token = tok;
684 }
685
686 /*
687 * Create a new type
688 */
689 static struct type *
make_type(const char * s)690 make_type(const char *s)
691 {
692 struct type *t;
693
694 t = xalloc(sizeof(*t));
695 t->name = savestr(s);
696 t->is_enum = 0;
697 t->syntax = SNMP_SYNTAX_NULL;
698 t->from_fname = savestr(input->fname);
699 t->from_lno = input->lno;
700 TAILQ_INIT(&t->enums);
701 LIST_INSERT_HEAD(&types, t, link);
702
703 return (t);
704 }
705
706 /*
707 * Parse a type. We've seen the ENUM or type keyword already. Leave next
708 * token.
709 */
710 static u_int
parse_type(enum tok * tok,struct type * t,const char * vname)711 parse_type(enum tok *tok, struct type *t, const char *vname)
712 {
713 u_int syntax;
714 struct enums *e;
715
716 syntax = val;
717
718 if (*tok == TOK_ENUM || *tok == TOK_BITS) {
719 if (t == NULL && vname != NULL) {
720 t = make_type(vname);
721 t->is_enum = (*tok == TOK_ENUM);
722 t->is_bits = (*tok == TOK_BITS);
723 t->syntax = syntax;
724 }
725 if (gettoken() != '(')
726 report("'(' expected after ENUM");
727
728 if ((*tok = gettoken()) == TOK_EOF)
729 report("unexpected EOF in ENUM");
730 do {
731 e = NULL;
732 if (t != NULL) {
733 e = xalloc(sizeof(*e));
734 }
735 if (*tok == '-') {
736 if ((*tok = gettoken()) == TOK_EOF)
737 report("unexpected EOF in ENUM");
738 e->value = -(long)val;
739 } else
740 e->value = val;
741
742 if (*tok != TOK_NUM)
743 report("need value for ENUM/BITS");
744 if (gettoken() != TOK_STR)
745 report("need string in ENUM/BITS");
746 if (e != NULL) {
747 e->name = savetok();
748 TAILQ_INSERT_TAIL(&t->enums, e, link);
749 }
750 if ((*tok = gettoken()) == TOK_EOF)
751 report("unexpected EOF in ENUM/BITS");
752 } while (*tok != ')');
753 *tok = gettoken();
754
755 } else if (*tok == TOK_DEFTYPE) {
756 *tok = gettoken();
757
758 } else {
759 if ((*tok = gettoken()) == '|') {
760 if (gettoken() != TOK_STR)
761 report("subtype expected after '|'");
762 *tok = gettoken();
763 }
764 }
765
766 return (syntax);
767 }
768
769 /*
770 * Parse the next node (complete with all subnodes)
771 */
772 static struct node *
parse(enum tok tok)773 parse(enum tok tok)
774 {
775 struct node *node;
776 struct node *sub;
777 u_int index_count;
778
779 node = xalloc(sizeof(struct node));
780 node->lno = input->lno;
781 node->flags = 0;
782
783 if (tok != '(')
784 report("'(' expected at begin of node");
785 if (gettoken() != TOK_NUM)
786 report("node id expected after opening '('");
787 if (val > ASN_MAXID)
788 report("subid too large '%lu'", val);
789 node->id = (asn_subid_t)val;
790 if (gettoken() != TOK_STR)
791 report("node name expected after '(' ID");
792 node->name = savetok();
793
794 if ((tok = gettoken()) == TOK_TYPE || tok == TOK_DEFTYPE ||
795 tok == TOK_ENUM || tok == TOK_BITS) {
796 /* LEAF or COLUM */
797 u_int syntax = parse_type(&tok, NULL, node->name);
798
799 if (tok == TOK_STR) {
800 /* LEAF */
801 node->type = NODE_LEAF;
802 node->u.leaf.func = savetok();
803 node->u.leaf.syntax = syntax;
804 tok = gettoken();
805 } else {
806 /* COLUMN */
807 node->type = NODE_COLUMN;
808 node->u.column.syntax = syntax;
809 }
810
811 while (tok != ')') {
812 if (tok != TOK_ACCESS)
813 report("access keyword or ')' expected");
814 node->flags |= (u_int)val;
815 tok = gettoken();
816 }
817
818 } else if (tok == ':') {
819 /* ENTRY */
820 node->type = NODE_ENTRY;
821 TAILQ_INIT(&node->u.entry.subs);
822
823 index_count = 0;
824 node->u.entry.index = 0;
825 tok = gettoken();
826 while (tok == TOK_TYPE || tok == TOK_DEFTYPE ||
827 tok == TOK_ENUM || tok == TOK_BITS) {
828 u_int syntax = parse_type(&tok, NULL, node->name);
829 if (index_count++ == SNMP_INDEXES_MAX)
830 report("too many table indexes");
831 node->u.entry.index |=
832 syntax << (SNMP_INDEX_SHIFT * index_count);
833 }
834 node->u.entry.index |= index_count;
835 if (index_count == 0)
836 report("need at least one index");
837 if (tok != TOK_STR)
838 report("function name expected");
839
840 node->u.entry.func = savetok();
841
842 tok = gettoken();
843
844 while (tok != ')') {
845 sub = parse(tok);
846 TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link);
847 tok = gettoken();
848 }
849
850 } else {
851 /* subtree */
852 node->type = NODE_TREE;
853 TAILQ_INIT(&node->u.tree.subs);
854
855 while (tok != ')') {
856 sub = parse(tok);
857 TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link);
858 tok = gettoken();
859 }
860 }
861 return (node);
862 }
863
864 /*
865 * Parse a top level element. Return the tree if it was a tree, NULL
866 * otherwise.
867 */
868 static struct node *
parse_top(enum tok tok)869 parse_top(enum tok tok)
870 {
871 struct type *t;
872
873 if (tok == '(')
874 return (parse(tok));
875
876 if (tok == TOK_TYPEDEF) {
877 if (gettoken() != TOK_STR)
878 report("type name expected after typedef");
879
880 t = make_type(str);
881
882 tok = gettoken();
883 t->is_enum = (tok == TOK_ENUM);
884 t->is_bits = (tok == TOK_BITS);
885 t->syntax = parse_type(&tok, t, NULL);
886 pushback(tok);
887
888 return (NULL);
889 }
890
891 if (tok == TOK_INCLUDE) {
892 if (gettoken() != TOK_FILENAME)
893 report("filename expected in include directive");
894
895 input_fopen(str, val);
896 return (NULL);
897 }
898
899 report("'(' or 'typedef' expected");
900 }
901
902 /*
903 * Generate the C-code table part for one node.
904 */
905 static void
gen_node(FILE * fp,struct node * np,struct asn_oid * oid,u_int idx,const char * func)906 gen_node(FILE *fp, struct node *np, struct asn_oid *oid, u_int idx,
907 const char *func)
908 {
909 u_int n;
910 struct node *sub;
911 u_int syntax;
912
913 if (oid->len == ASN_MAXOIDLEN)
914 report_node(np, "OID too long");
915 oid->subs[oid->len++] = np->id;
916
917 if (np->type == NODE_TREE) {
918 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
919 gen_node(fp, sub, oid, 0, NULL);
920 oid->len--;
921 return;
922 }
923 if (np->type == NODE_ENTRY) {
924 TAILQ_FOREACH(sub, &np->u.entry.subs, link)
925 gen_node(fp, sub, oid, np->u.entry.index,
926 np->u.entry.func);
927 oid->len--;
928 return;
929 }
930
931 /* leaf or column */
932 if ((np->flags & (FL_GET|FL_SET)) == 0) {
933 oid->len--;
934 return;
935 }
936
937 fprintf(fp, " {{ %u, {", oid->len);
938 for (n = 0; n < oid->len; n++)
939 fprintf(fp, " %u,", oid->subs[n]);
940 fprintf(fp, " }}, \"%s\", ", np->name);
941
942 if (np->type == NODE_COLUMN) {
943 syntax = np->u.column.syntax;
944 fprintf(fp, "SNMP_NODE_COLUMN, ");
945 } else {
946 syntax = np->u.leaf.syntax;
947 fprintf(fp, "SNMP_NODE_LEAF, ");
948 }
949
950 switch (syntax) {
951
952 case SNMP_SYNTAX_NULL:
953 fprintf(fp, "SNMP_SYNTAX_NULL, ");
954 break;
955
956 case SNMP_SYNTAX_INTEGER:
957 fprintf(fp, "SNMP_SYNTAX_INTEGER, ");
958 break;
959
960 case SNMP_SYNTAX_OCTETSTRING:
961 fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, ");
962 break;
963
964 case SNMP_SYNTAX_IPADDRESS:
965 fprintf(fp, "SNMP_SYNTAX_IPADDRESS, ");
966 break;
967
968 case SNMP_SYNTAX_OID:
969 fprintf(fp, "SNMP_SYNTAX_OID, ");
970 break;
971
972 case SNMP_SYNTAX_TIMETICKS:
973 fprintf(fp, "SNMP_SYNTAX_TIMETICKS, ");
974 break;
975
976 case SNMP_SYNTAX_COUNTER:
977 fprintf(fp, "SNMP_SYNTAX_COUNTER, ");
978 break;
979
980 case SNMP_SYNTAX_GAUGE:
981 fprintf(fp, "SNMP_SYNTAX_GAUGE, ");
982 break;
983
984 case SNMP_SYNTAX_COUNTER64:
985 fprintf(fp, "SNMP_SYNTAX_COUNTER64, ");
986 break;
987
988 case SNMP_SYNTAX_NOSUCHOBJECT:
989 case SNMP_SYNTAX_NOSUCHINSTANCE:
990 case SNMP_SYNTAX_ENDOFMIBVIEW:
991 abort();
992 }
993
994 if (np->type == NODE_COLUMN)
995 fprintf(fp, "%s, ", func);
996 else
997 fprintf(fp, "%s, ", np->u.leaf.func);
998
999 fprintf(fp, "0");
1000 if (np->flags & FL_SET)
1001 fprintf(fp, "|SNMP_NODE_CANSET");
1002 fprintf(fp, ", %#x, NULL, NULL },\n", idx);
1003 oid->len--;
1004 return;
1005 }
1006
1007 /*
1008 * Generate the header file with the function declarations.
1009 */
1010 static void
gen_header(FILE * fp,struct node * np,u_int oidlen,const char * func)1011 gen_header(FILE *fp, struct node *np, u_int oidlen, const char *func)
1012 {
1013 char f[MAXSTR + 4];
1014 struct node *sub;
1015 struct func *ptr;
1016
1017 oidlen++;
1018 if (np->type == NODE_TREE) {
1019 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
1020 gen_header(fp, sub, oidlen, NULL);
1021 return;
1022 }
1023 if (np->type == NODE_ENTRY) {
1024 TAILQ_FOREACH(sub, &np->u.entry.subs, link)
1025 gen_header(fp, sub, oidlen, np->u.entry.func);
1026 return;
1027 }
1028
1029 if((np->flags & (FL_GET|FL_SET)) == 0)
1030 return;
1031
1032 if (np->type == NODE_COLUMN) {
1033 if (func == NULL)
1034 errx(1, "column without function (%s) - probably "
1035 "outside of a table", np->name);
1036 sprintf(f, "%s", func);
1037 } else
1038 sprintf(f, "%s", np->u.leaf.func);
1039
1040 LIST_FOREACH(ptr, &funcs, link)
1041 if (strcmp(ptr->name, f) == 0)
1042 break;
1043
1044 if (ptr == NULL) {
1045 ptr = xalloc(sizeof(*ptr));
1046 ptr->name = savestr(f);
1047 LIST_INSERT_HEAD(&funcs, ptr, link);
1048
1049 fprintf(fp, "int %s(struct snmp_context *, "
1050 "struct snmp_value *, u_int, u_int, "
1051 "enum snmp_op);\n", f);
1052 }
1053
1054 fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id);
1055 }
1056
1057 /*
1058 * Generate the OID table.
1059 */
1060 static void
gen_table(FILE * fp,struct node * node)1061 gen_table(FILE *fp, struct node *node)
1062 {
1063 struct asn_oid oid;
1064
1065 fprintf(fp, "#include <sys/types.h>\n");
1066 fprintf(fp, "#include <stdio.h>\n");
1067 #ifdef HAVE_STDINT_H
1068 fprintf(fp, "#include <stdint.h>\n");
1069 #endif
1070 if (localincs) {
1071 fprintf(fp, "#include \"asn1.h\"\n");
1072 fprintf(fp, "#include \"snmp.h\"\n");
1073 fprintf(fp, "#include \"snmpagent.h\"\n");
1074 } else {
1075 fprintf(fp, "#include <bsnmp/asn1.h>\n");
1076 fprintf(fp, "#include <bsnmp/snmp.h>\n");
1077 fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
1078 }
1079 fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
1080 fprintf(fp, "\n");
1081
1082 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
1083
1084 oid.len = PREFIX_LEN;
1085 memcpy(oid.subs, prefix, sizeof(prefix));
1086 gen_node(fp, node, &oid, 0, NULL);
1087
1088 fprintf(fp, "};\n\n");
1089 }
1090
1091 static void
print_syntax(u_int syntax)1092 print_syntax(u_int syntax)
1093 {
1094 u_int i;
1095
1096 for (i = 0; keywords[i].str != NULL; i++)
1097 if (keywords[i].tok == TOK_TYPE &&
1098 keywords[i].val == syntax) {
1099 printf(" %s", keywords[i].str);
1100 return;
1101 }
1102 abort();
1103 }
1104
1105 /*
1106 * Generate a tree definition file
1107 */
1108 static void
gen_tree(const struct node * np,int level)1109 gen_tree(const struct node *np, int level)
1110 {
1111 const struct node *sp;
1112 u_int i;
1113
1114 printf("%*s(%u %s", 2 * level, "", np->id, np->name);
1115
1116 switch (np->type) {
1117
1118 case NODE_LEAF:
1119 print_syntax(np->u.leaf.syntax);
1120 printf(" %s%s%s)\n", np->u.leaf.func,
1121 (np->flags & FL_GET) ? " GET" : "",
1122 (np->flags & FL_SET) ? " SET" : "");
1123 break;
1124
1125 case NODE_TREE:
1126 if (TAILQ_EMPTY(&np->u.tree.subs)) {
1127 printf(")\n");
1128 } else {
1129 printf("\n");
1130 TAILQ_FOREACH(sp, &np->u.tree.subs, link)
1131 gen_tree(sp, level + 1);
1132 printf("%*s)\n", 2 * level, "");
1133 }
1134 break;
1135
1136 case NODE_ENTRY:
1137 printf(" :");
1138
1139 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++)
1140 print_syntax(SNMP_INDEX(np->u.entry.index, i));
1141 printf(" %s\n", np->u.entry.func);
1142 TAILQ_FOREACH(sp, &np->u.entry.subs, link)
1143 gen_tree(sp, level + 1);
1144 printf("%*s)\n", 2 * level, "");
1145 break;
1146
1147 case NODE_COLUMN:
1148 print_syntax(np->u.column.syntax);
1149 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",
1150 (np->flags & FL_SET) ? " SET" : "");
1151 break;
1152 }
1153 }
1154
1155 static int
extract(FILE * fp,const struct node * np,struct asn_oid * oid,const char * obj,const struct asn_oid * idx,const char * iname)1156 extract(FILE *fp, const struct node *np, struct asn_oid *oid, const char *obj,
1157 const struct asn_oid *idx, const char *iname)
1158 {
1159 struct node *sub;
1160 u_long n;
1161
1162 if (oid->len == ASN_MAXOIDLEN)
1163 report_node(np, "OID too long");
1164 oid->subs[oid->len++] = np->id;
1165
1166 if (strcmp(obj, np->name) == 0) {
1167 if (oid->len + idx->len >= ASN_MAXOIDLEN)
1168 report_node(np, "OID too long");
1169 fprintf(fp, "#define OID_%s%s\t%u\n", np->name,
1170 iname ? iname : "", np->id);
1171 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name,
1172 iname ? iname : "", oid->len + idx->len);
1173 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name,
1174 iname ? iname : "", oid->len + idx->len);
1175 for (n = 0; n < oid->len; n++)
1176 fprintf(fp, " %u,", oid->subs[n]);
1177 for (n = 0; n < idx->len; n++)
1178 fprintf(fp, " %u,", idx->subs[n]);
1179 fprintf(fp, " } }\n");
1180 return (0);
1181 }
1182
1183 if (np->type == NODE_TREE) {
1184 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
1185 if (!extract(fp, sub, oid, obj, idx, iname))
1186 return (0);
1187 } else if (np->type == NODE_ENTRY) {
1188 TAILQ_FOREACH(sub, &np->u.entry.subs, link)
1189 if (!extract(fp, sub, oid, obj, idx, iname))
1190 return (0);
1191 }
1192 oid->len--;
1193 return (1);
1194 }
1195
1196 static int
gen_extract(FILE * fp,const struct node * root,char * object)1197 gen_extract(FILE *fp, const struct node *root, char *object)
1198 {
1199 struct asn_oid oid;
1200 struct asn_oid idx;
1201 char *s, *e, *end, *iname;
1202 u_long ul;
1203 int ret;
1204
1205 /* look whether the object to extract has an index part */
1206 idx.len = 0;
1207 iname = NULL;
1208 s = strchr(object, '.');
1209 if (s != NULL) {
1210 iname = malloc(strlen(s) + 1);
1211 if (iname == NULL)
1212 err(1, "cannot allocated index");
1213
1214 strcpy(iname, s);
1215 for (e = iname; *e != '\0'; e++)
1216 if (*e == '.')
1217 *e = '_';
1218
1219 *s++ = '\0';
1220 while (s != NULL) {
1221 if (*s == '\0')
1222 errx(1, "bad index syntax");
1223 if ((e = strchr(s, '.')) != NULL)
1224 *e++ = '\0';
1225
1226 errno = 0;
1227 ul = strtoul(s, &end, 0);
1228 if (*end != '\0')
1229 errx(1, "bad index syntax '%s'", end);
1230 if (errno != 0)
1231 err(1, "bad index syntax");
1232
1233 if (idx.len == ASN_MAXOIDLEN)
1234 errx(1, "index oid too large");
1235 idx.subs[idx.len++] = ul;
1236
1237 s = e;
1238 }
1239 }
1240
1241 oid.len = PREFIX_LEN;
1242 memcpy(oid.subs, prefix, sizeof(prefix));
1243 ret = extract(fp, root, &oid, object, &idx, iname);
1244 if (iname != NULL)
1245 free(iname);
1246
1247 return (ret);
1248 }
1249
1250
1251 static void
check_sub_order(const struct node * np,const struct node_list * subs)1252 check_sub_order(const struct node *np, const struct node_list *subs)
1253 {
1254 int first;
1255 const struct node *sub;
1256 asn_subid_t maxid = 0;
1257
1258 /* ensure, that subids are ordered */
1259 first = 1;
1260 TAILQ_FOREACH(sub, subs, link) {
1261 if (!first && sub->id <= maxid)
1262 report_node(np, "subids not ordered at %s", sub->name);
1263 maxid = sub->id;
1264 first = 0;
1265 }
1266 }
1267
1268 /*
1269 * Do some sanity checks on the tree definition and do some computations.
1270 */
1271 static void
check_tree(struct node * np)1272 check_tree(struct node *np)
1273 {
1274 struct node *sub;
1275
1276 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
1277 if ((np->flags & (FL_GET|FL_SET)) != 0)
1278 tree_size++;
1279 return;
1280 }
1281
1282 if (np->type == NODE_ENTRY) {
1283 check_sub_order(np, &np->u.entry.subs);
1284
1285 /* ensure all subnodes are columns */
1286 TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
1287 if (sub->type != NODE_COLUMN)
1288 report_node(np, "entry subnode '%s' is not "
1289 "a column", sub->name);
1290 check_tree(sub);
1291 }
1292 } else {
1293 check_sub_order(np, &np->u.tree.subs);
1294
1295 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
1296 check_tree(sub);
1297 }
1298 }
1299
1300 static void
merge_subs(struct node_list * s1,struct node_list * s2)1301 merge_subs(struct node_list *s1, struct node_list *s2)
1302 {
1303 struct node *n1, *n2;
1304
1305 while (!TAILQ_EMPTY(s2)) {
1306 n2 = TAILQ_FIRST(s2);
1307 TAILQ_REMOVE(s2, n2, link);
1308
1309 TAILQ_FOREACH(n1, s1, link)
1310 if (n1->id >= n2->id)
1311 break;
1312 if (n1 == NULL)
1313 TAILQ_INSERT_TAIL(s1, n2, link);
1314 else if (n1->id > n2->id)
1315 TAILQ_INSERT_BEFORE(n1, n2, link);
1316 else {
1317 if (n1->type == NODE_TREE && n2->type == NODE_TREE) {
1318 if (strcmp(n1->name, n2->name) != 0)
1319 errx(1, "trees to merge must have "
1320 "same name '%s' '%s'", n1->name,
1321 n2->name);
1322 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs);
1323 free(n2);
1324 } else if (n1->type == NODE_ENTRY &&
1325 n2->type == NODE_ENTRY) {
1326 if (strcmp(n1->name, n2->name) != 0)
1327 errx(1, "entries to merge must have "
1328 "same name '%s' '%s'", n1->name,
1329 n2->name);
1330 if (n1->u.entry.index != n2->u.entry.index)
1331 errx(1, "entries to merge must have "
1332 "same index '%s'", n1->name);
1333 if (strcmp(n1->u.entry.func,
1334 n2->u.entry.func) != 0)
1335 errx(1, "entries to merge must have "
1336 "same op '%s'", n1->name);
1337 merge_subs(&n1->u.entry.subs,
1338 &n2->u.entry.subs);
1339 free(n2);
1340 } else
1341 errx(1, "entities to merge must be both "
1342 "trees or both entries: %s, %s",
1343 n1->name, n2->name);
1344 }
1345 }
1346 }
1347
1348 static void
merge(struct node ** root,struct node * t)1349 merge(struct node **root, struct node *t)
1350 {
1351
1352 if (*root == NULL) {
1353 *root = t;
1354 return;
1355 }
1356 if (t == NULL)
1357 return;
1358
1359 /* both must be trees */
1360 if ((*root)->type != NODE_TREE)
1361 errx(1, "root is not a tree");
1362 if (t->type != NODE_TREE)
1363 errx(1, "can merge only with tree");
1364 if ((*root)->id != t->id)
1365 errx(1, "trees to merge must have same id");
1366
1367 merge_subs(&(*root)->u.tree.subs, &t->u.tree.subs);
1368 }
1369
1370 static void
unminus(FILE * fp,const char * s)1371 unminus(FILE *fp, const char *s)
1372 {
1373
1374 while (*s != '\0') {
1375 if (*s == '-')
1376 fprintf(fp, "_");
1377 else
1378 fprintf(fp, "%c", *s);
1379 s++;
1380 }
1381 }
1382
1383 static void
gen_enum(FILE * fp,const struct type * t)1384 gen_enum(FILE *fp, const struct type *t)
1385 {
1386 const struct enums *e;
1387 long min = LONG_MAX;
1388
1389 fprintf(fp, "\n");
1390 fprintf(fp, "#ifndef %s_defined__\n", t->name);
1391 fprintf(fp, "#define %s_defined__\n", t->name);
1392 fprintf(fp, "/*\n");
1393 fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno);
1394 fprintf(fp, " */\n");
1395 fprintf(fp, "enum %s {\n", t->name);
1396 TAILQ_FOREACH(e, &t->enums, link) {
1397 fprintf(fp, "\t%s_", t->name);
1398 unminus(fp, e->name);
1399 fprintf(fp, " = %ld,\n", e->value);
1400 if (e->value < min)
1401 min = e->value;
1402 }
1403 fprintf(fp, "};\n");
1404 fprintf(fp, "#define STROFF_%s %ld\n", t->name, min);
1405 fprintf(fp, "#define STRING_%s \\\n", t->name);
1406 TAILQ_FOREACH(e, &t->enums, link) {
1407 fprintf(fp, "\t[%ld] \"%s_", e->value - min, t->name);
1408 unminus(fp, e->name);
1409 fprintf(fp, "\",\\\n");
1410 }
1411 fprintf(fp, "\n");
1412 fprintf(fp, "#endif /* %s_defined__ */\n", t->name);
1413 }
1414
1415 static void
gen_enums(FILE * fp)1416 gen_enums(FILE *fp)
1417 {
1418 const struct type *t;
1419
1420 LIST_FOREACH(t, &types, link)
1421 if (t->is_enum || t->is_bits)
1422 gen_enum(fp, t);
1423 }
1424
1425 static int
extract_enum(FILE * fp,const char * name)1426 extract_enum(FILE *fp, const char *name)
1427 {
1428 const struct type *t;
1429
1430 LIST_FOREACH(t, &types, link)
1431 if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) {
1432 gen_enum(fp, t);
1433 return (0);
1434 }
1435 return (-1);
1436 }
1437
1438 int
main(int argc,char * argv[])1439 main(int argc, char *argv[])
1440 {
1441 int do_extract = 0;
1442 int do_tree = 0;
1443 int do_enums = 0;
1444 int opt;
1445 struct node *root;
1446 char fname[MAXPATHLEN + 1];
1447 int tok;
1448 FILE *fp;
1449 char *infile = NULL;
1450
1451 while ((opt = getopt(argc, argv, "dEehI:i:lp:t")) != EOF)
1452 switch (opt) {
1453
1454 case 'd':
1455 debug = 1;
1456 break;
1457
1458 case 'h':
1459 fprintf(stderr, "%s", usgtxt);
1460 exit(0);
1461
1462 case 'E':
1463 do_enums = 1;
1464 break;
1465
1466 case 'e':
1467 do_extract = 1;
1468 break;
1469
1470 case 'I':
1471 path_new(optarg);
1472 break;
1473
1474 case 'i':
1475 infile = optarg;
1476 break;
1477
1478 case 'l':
1479 localincs = 1;
1480 break;
1481
1482 case 'p':
1483 file_prefix = optarg;
1484 if (strlen(file_prefix) + strlen("tree.c") >
1485 MAXPATHLEN)
1486 errx(1, "prefix too long");
1487 break;
1488
1489 case 't':
1490 do_tree = 1;
1491 break;
1492 }
1493
1494 if (do_extract + do_tree + do_enums > 1)
1495 errx(1, "conflicting options -e/-t/-E");
1496 if (!do_extract && !do_enums && argc != optind)
1497 errx(1, "no arguments allowed");
1498 if ((do_extract || do_enums) && argc == optind)
1499 errx(1, "no objects specified");
1500
1501 if (infile == NULL) {
1502 input_new(stdin, NULL, "<stdin>");
1503 } else {
1504 if ((fp = fopen(infile, "r")) == NULL)
1505 err(1, "%s", infile);
1506 input_new(fp, NULL, infile);
1507 }
1508
1509 root = parse_top(gettoken());
1510 while ((tok = gettoken()) != TOK_EOF)
1511 merge(&root, parse_top(tok));
1512
1513 check_tree(root);
1514
1515 if (do_extract) {
1516 while (optind < argc) {
1517 if (gen_extract(stdout, root, argv[optind]))
1518 errx(1, "object not found: %s", argv[optind]);
1519 optind++;
1520 }
1521 return (0);
1522 }
1523 if (do_enums) {
1524 while (optind < argc) {
1525 if (extract_enum(stdout, argv[optind]))
1526 errx(1, "enum not found: %s", argv[optind]);
1527 optind++;
1528 }
1529 return (0);
1530 }
1531 if (do_tree) {
1532 gen_tree(root, 0);
1533 return (0);
1534 }
1535 sprintf(fname, "%stree.h", file_prefix);
1536 if ((fp = fopen(fname, "w")) == NULL)
1537 err(1, "%s: ", fname);
1538 gen_header(fp, root, PREFIX_LEN, NULL);
1539
1540 fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n");
1541 gen_enums(fp);
1542 fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n");
1543
1544 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
1545 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
1546
1547 fclose(fp);
1548
1549 sprintf(fname, "%stree.c", file_prefix);
1550 if ((fp = fopen(fname, "w")) == NULL)
1551 err(1, "%s: ", fname);
1552 gen_table(fp, root);
1553 fclose(fp);
1554
1555 return (0);
1556 }
1557