Deleted Added
full compact
gensnmptree.c (133429) gensnmptree.c (142810)
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 *
1/*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Copyright (c) 2004
7 * Hartmut Brandt.
8 * All rights reserved.
9 *
10 * Author: Harti Brandt <harti@freebsd.org>
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $Begemot: bsnmp/gensnmptree/gensnmptree.c,v 1.38 2004/08/06 08:46:46 brandt Exp $
33 * $Begemot: bsnmp/gensnmptree/gensnmptree.c,v 1.41 2005/02/25 11:51:12 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 *
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
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 %u 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
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 */
438static void
439gen_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 */
541static void
542gen_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
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 %u 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
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 */
438static void
439gen_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 */
541static void
542gen_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)
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);
564 sprintf(f, "%s", func);
567 sprintf(f, "%s", func);
565 else
568 } else
566 sprintf(f, "%s", np->u.leaf.func);
567
568 LIST_FOREACH(ptr, &funcs, link)
569 if (strcmp(ptr->name, f) == 0)
570 break;
571
572 if (ptr == NULL) {
573 ptr = xalloc(sizeof(*ptr));
574 ptr->name = strcpy(xalloc(strlen(f)+1), f);
575 LIST_INSERT_HEAD(&funcs, ptr, link);
576
577 fprintf(fp, "int %s(struct snmp_context *, "
578 "struct snmp_value *, u_int, u_int, "
579 "enum snmp_op);\n", f);
580 }
581
582 fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id);
583}
584
585/*
586 * Generate the OID table.
587 */
588static void
589gen_table(struct node *node)
590{
591 struct asn_oid oid;
592
593 fprintf(fp, "#include <sys/types.h>\n");
594 fprintf(fp, "#include <stdio.h>\n");
595 fprintf(fp, "#include <stdint.h>\n");
596 if (localincs) {
597 fprintf(fp, "#include \"asn1.h\"\n");
598 fprintf(fp, "#include \"snmp.h\"\n");
599 fprintf(fp, "#include \"snmpagent.h\"\n");
600 } else {
601 fprintf(fp, "#include <bsnmp/asn1.h>\n");
602 fprintf(fp, "#include <bsnmp/snmp.h>\n");
603 fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
604 }
605 fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
606 fprintf(fp, "\n");
607
608 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
609
610 oid.len = PREFIX_LEN;
611 memcpy(oid.subs, prefix, sizeof(prefix));
612 gen_node(node, &oid, 0, NULL);
613
614 fprintf(fp, "};\n\n");
615}
616
617static void
618print_syntax(u_int syntax)
619{
620 u_int i;
621
622 for (i = 0; keywords[i].str != NULL; i++)
623 if (keywords[i].tok == TOK_TYPE &&
624 keywords[i].val == syntax) {
625 printf(" %s", keywords[i].str);
626 return;
627 }
628 abort();
629}
630
631/*
632 * Generate a tree definition file
633 */
634static void
635gen_tree(const struct node *np, int level)
636{
637 const struct node *sp;
638 u_int i;
639
640 printf("%*s(%u %s", 2 * level, "", np->id, np->name);
641
642 switch (np->type) {
643
644 case NODE_LEAF:
645 print_syntax(np->u.leaf.syntax);
646 printf(" %s%s%s)\n", np->u.leaf.func,
647 (np->flags & FL_GET) ? " GET" : "",
648 (np->flags & FL_SET) ? " SET" : "");
649 break;
650
651 case NODE_TREE:
652 if (TAILQ_EMPTY(&np->u.tree.subs)) {
653 printf(")\n");
654 } else {
655 printf("\n");
656 TAILQ_FOREACH(sp, &np->u.tree.subs, link)
657 gen_tree(sp, level + 1);
658 printf("%*s)\n", 2 * level, "");
659 }
660 break;
661
662 case NODE_ENTRY:
663 printf(" :");
664
665 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++)
666 print_syntax(SNMP_INDEX(np->u.entry.index, i));
667 printf(" %s\n", np->u.entry.func);
668 TAILQ_FOREACH(sp, &np->u.entry.subs, link)
669 gen_tree(sp, level + 1);
670 printf("%*s)\n", 2 * level, "");
671 break;
672
673 case NODE_COLUMN:
674 print_syntax(np->u.column.syntax);
675 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",
676 (np->flags & FL_SET) ? " SET" : "");
677 break;
678
679 }
680}
681
682static int
683extract(const struct node *np, struct asn_oid *oid, const char *obj,
684 const struct asn_oid *idx, const char *iname)
685{
686 struct node *sub;
687 u_long n;
688
689 if (oid->len == ASN_MAXOIDLEN)
690 report_node(np, "OID too long");
691 oid->subs[oid->len++] = np->id;
692
693 if (strcmp(obj, np->name) == 0) {
694 if (oid->len + idx->len >= ASN_MAXOIDLEN)
695 report_node(np, "OID too long");
696 fprintf(fp, "#define OID_%s%s\t%u\n", np->name,
697 iname ? iname : "", np->id);
698 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name,
699 iname ? iname : "", oid->len + idx->len);
700 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name,
701 iname ? iname : "", oid->len + idx->len);
702 for (n = 0; n < oid->len; n++)
703 fprintf(fp, " %u,", oid->subs[n]);
704 for (n = 0; n < idx->len; n++)
705 fprintf(fp, " %u,", idx->subs[n]);
706 fprintf(fp, " } }\n");
707 return (0);
708 }
709
710 if (np->type == NODE_TREE) {
711 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
712 if (!extract(sub, oid, obj, idx, iname))
713 return (0);
714 } else if (np->type == NODE_ENTRY) {
715 TAILQ_FOREACH(sub, &np->u.entry.subs, link)
716 if (!extract(sub, oid, obj, idx, iname))
717 return (0);
718 }
719 oid->len--;
720 return (1);
721}
722
723static int
724gen_extract(const struct node *root, char *object)
725{
726 struct asn_oid oid;
727 struct asn_oid idx;
728 char *s, *e, *end, *iname;
729 u_long ul;
730 int ret;
731
732 /* look whether the object to extract has an index part */
733 idx.len = 0;
734 iname = NULL;
735 s = strchr(object, '.');
736 if (s != NULL) {
737 iname = malloc(strlen(s) + 1);
738 if (iname == NULL)
739 err(1, "cannot allocated index");
740
741 strcpy(iname, s);
742 for (e = iname; *e != '\0'; e++)
743 if (*e == '.')
744 *e = '_';
745
746 *s++ = '\0';
747 while (s != NULL) {
748 if (*s == '\0')
749 errx(1, "bad index syntax");
750 if ((e = strchr(s, '.')) != NULL)
751 *e++ = '\0';
752
753 errno = 0;
754 ul = strtoul(s, &end, 0);
755 if (*end != '\0')
756 errx(1, "bad index syntax '%s'", end);
757 if (errno != 0)
758 err(1, "bad index syntax");
759
760 if (idx.len == ASN_MAXOIDLEN)
761 errx(1, "index oid too large");
762 idx.subs[idx.len++] = ul;
763
764 s = e;
765 }
766 }
767
768 oid.len = PREFIX_LEN;
769 memcpy(oid.subs, prefix, sizeof(prefix));
770 ret = extract(root, &oid, object, &idx, iname);
771 if (iname != NULL)
772 free(iname);
773
774 return (ret);
775}
776
777
778static void
779check_sub_order(const struct node *np, const struct node_list *subs)
780{
781 int first;
782 const struct node *sub;
783 asn_subid_t maxid = 0;
784
785 /* ensure, that subids are ordered */
786 first = 1;
787 TAILQ_FOREACH(sub, subs, link) {
788 if (!first && sub->id <= maxid)
789 report_node(np, "subids not ordered at %s", sub->name);
790 maxid = sub->id;
791 first = 0;
792 }
793}
794
795/*
796 * Do some sanity checks on the tree definition and do some computations.
797 */
798static void
799check_tree(struct node *np)
800{
801 struct node *sub;
802
803 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
804 if ((np->flags & (FL_GET|FL_SET)) != 0)
805 tree_size++;
806 return;
807 }
808
809 if (np->type == NODE_ENTRY) {
810 check_sub_order(np, &np->u.entry.subs);
811
812 /* ensure all subnodes are columns */
813 TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
814 if (sub->type != NODE_COLUMN)
815 report_node(np, "entry subnode '%s' is not "
816 "a column", sub->name);
817 check_tree(sub);
818 }
819 } else {
820 check_sub_order(np, &np->u.tree.subs);
821
822 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
823 check_tree(sub);
824 }
825}
826
827static void
828merge_subs(struct node_list *s1, struct node_list *s2)
829{
830 struct node *n1, *n2;
831
832 while (!TAILQ_EMPTY(s2)) {
833 n2 = TAILQ_FIRST(s2);
834 TAILQ_REMOVE(s2, n2, link);
835
836 TAILQ_FOREACH(n1, s1, link)
837 if (n1->id >= n2->id)
838 break;
839 if (n1 == NULL)
840 TAILQ_INSERT_TAIL(s1, n2, link);
841 else if (n1->id > n2->id)
842 TAILQ_INSERT_BEFORE(n1, n2, link);
843 else {
844 if (n1->type == NODE_TREE && n2->type == NODE_TREE) {
845 if (strcmp(n1->name, n2->name) != 0)
846 errx(1, "trees to merge must have "
847 "same name '%s' '%s'", n1->name,
848 n2->name);
849 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs);
850 free(n2);
851 } else if (n1->type == NODE_ENTRY &&
852 n2->type == NODE_ENTRY) {
853 if (strcmp(n1->name, n2->name) != 0)
854 errx(1, "entries to merge must have "
855 "same name '%s' '%s'", n1->name,
856 n2->name);
857 if (n1->u.entry.index != n2->u.entry.index)
858 errx(1, "entries to merge must have "
859 "same index '%s'", n1->name);
860 if (strcmp(n1->u.entry.func,
861 n2->u.entry.func) != 0)
862 errx(1, "entries to merge must have "
863 "same op '%s'", n1->name);
864 merge_subs(&n1->u.entry.subs,
865 &n2->u.entry.subs);
866 free(n2);
867 } else
868 errx(1, "entities to merge must be both "
869 "trees or both entries: %s, %s",
870 n1->name, n2->name);
871 }
872 }
873}
874
875static void
876merge(struct node *root, struct node *t)
877{
878
879 /* both must be trees */
880 if (root->type != NODE_TREE)
881 errx(1, "root is not a tree");
882 if (t->type != NODE_TREE)
883 errx(1, "can merge only with tree");
884 if (root->id != t->id)
885 errx(1, "trees to merge must have same id");
886
887 merge_subs(&root->u.tree.subs, &t->u.tree.subs);
888}
889
890int
891main(int argc, char *argv[])
892{
893 int do_extract = 0;
894 int do_tree = 0;
895 int opt;
896 struct node *root;
897 char fname[MAXPATHLEN + 1];
898 int tok;
899
900 while ((opt = getopt(argc, argv, "help:t")) != EOF)
901 switch (opt) {
902
903 case 'h':
904 fprintf(stderr, "%s", usgtxt);
905 exit(0);
906
907 case 'e':
908 do_extract = 1;
909 break;
910
911 case 'l':
912 localincs = 1;
913 break;
914
915 case 'p':
916 file_prefix = optarg;
917 if (strlen(file_prefix) + strlen("tree.c") >
918 MAXPATHLEN)
919 errx(1, "prefix too long");
920 break;
921
922 case 't':
923 do_tree = 1;
924 break;
925 }
926
927 if (do_extract && do_tree)
928 errx(1, "conflicting options -e and -t");
929 if (!do_extract && argc != optind)
930 errx(1, "no arguments allowed");
931 if (do_extract && argc == optind)
932 errx(1, "no objects specified");
933
934 root = parse(gettoken());
935 while ((tok = gettoken()) != TOK_EOF)
936 merge(root, parse(tok));
937
938 check_tree(root);
939
940 if (do_extract) {
941 fp = stdout;
942 while (optind < argc) {
943 if (gen_extract(root, argv[optind]))
944 errx(1, "object not found: %s", argv[optind]);
945 optind++;
946 }
947
948 return (0);
949 }
950 if (do_tree) {
951 gen_tree(root, 0);
952 return (0);
953 }
954 sprintf(fname, "%stree.h", file_prefix);
955 if ((fp = fopen(fname, "w")) == NULL)
956 err(1, "%s: ", fname);
957 gen_header(root, PREFIX_LEN, NULL);
958
959 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
960 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
961
962 fclose(fp);
963
964 sprintf(fname, "%stree.c", file_prefix);
965 if ((fp = fopen(fname, "w")) == NULL)
966 err(1, "%s: ", fname);
967 gen_table(root);
968 fclose(fp);
969
970 return (0);
971}
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 */
591static void
592gen_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 fprintf(fp, "#include <stdint.h>\n");
599 if (localincs) {
600 fprintf(fp, "#include \"asn1.h\"\n");
601 fprintf(fp, "#include \"snmp.h\"\n");
602 fprintf(fp, "#include \"snmpagent.h\"\n");
603 } else {
604 fprintf(fp, "#include <bsnmp/asn1.h>\n");
605 fprintf(fp, "#include <bsnmp/snmp.h>\n");
606 fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
607 }
608 fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
609 fprintf(fp, "\n");
610
611 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
612
613 oid.len = PREFIX_LEN;
614 memcpy(oid.subs, prefix, sizeof(prefix));
615 gen_node(node, &oid, 0, NULL);
616
617 fprintf(fp, "};\n\n");
618}
619
620static void
621print_syntax(u_int syntax)
622{
623 u_int i;
624
625 for (i = 0; keywords[i].str != NULL; i++)
626 if (keywords[i].tok == TOK_TYPE &&
627 keywords[i].val == syntax) {
628 printf(" %s", keywords[i].str);
629 return;
630 }
631 abort();
632}
633
634/*
635 * Generate a tree definition file
636 */
637static void
638gen_tree(const struct node *np, int level)
639{
640 const struct node *sp;
641 u_int i;
642
643 printf("%*s(%u %s", 2 * level, "", np->id, np->name);
644
645 switch (np->type) {
646
647 case NODE_LEAF:
648 print_syntax(np->u.leaf.syntax);
649 printf(" %s%s%s)\n", np->u.leaf.func,
650 (np->flags & FL_GET) ? " GET" : "",
651 (np->flags & FL_SET) ? " SET" : "");
652 break;
653
654 case NODE_TREE:
655 if (TAILQ_EMPTY(&np->u.tree.subs)) {
656 printf(")\n");
657 } else {
658 printf("\n");
659 TAILQ_FOREACH(sp, &np->u.tree.subs, link)
660 gen_tree(sp, level + 1);
661 printf("%*s)\n", 2 * level, "");
662 }
663 break;
664
665 case NODE_ENTRY:
666 printf(" :");
667
668 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++)
669 print_syntax(SNMP_INDEX(np->u.entry.index, i));
670 printf(" %s\n", np->u.entry.func);
671 TAILQ_FOREACH(sp, &np->u.entry.subs, link)
672 gen_tree(sp, level + 1);
673 printf("%*s)\n", 2 * level, "");
674 break;
675
676 case NODE_COLUMN:
677 print_syntax(np->u.column.syntax);
678 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",
679 (np->flags & FL_SET) ? " SET" : "");
680 break;
681
682 }
683}
684
685static int
686extract(const struct node *np, struct asn_oid *oid, const char *obj,
687 const struct asn_oid *idx, const char *iname)
688{
689 struct node *sub;
690 u_long n;
691
692 if (oid->len == ASN_MAXOIDLEN)
693 report_node(np, "OID too long");
694 oid->subs[oid->len++] = np->id;
695
696 if (strcmp(obj, np->name) == 0) {
697 if (oid->len + idx->len >= ASN_MAXOIDLEN)
698 report_node(np, "OID too long");
699 fprintf(fp, "#define OID_%s%s\t%u\n", np->name,
700 iname ? iname : "", np->id);
701 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name,
702 iname ? iname : "", oid->len + idx->len);
703 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name,
704 iname ? iname : "", oid->len + idx->len);
705 for (n = 0; n < oid->len; n++)
706 fprintf(fp, " %u,", oid->subs[n]);
707 for (n = 0; n < idx->len; n++)
708 fprintf(fp, " %u,", idx->subs[n]);
709 fprintf(fp, " } }\n");
710 return (0);
711 }
712
713 if (np->type == NODE_TREE) {
714 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
715 if (!extract(sub, oid, obj, idx, iname))
716 return (0);
717 } else if (np->type == NODE_ENTRY) {
718 TAILQ_FOREACH(sub, &np->u.entry.subs, link)
719 if (!extract(sub, oid, obj, idx, iname))
720 return (0);
721 }
722 oid->len--;
723 return (1);
724}
725
726static int
727gen_extract(const struct node *root, char *object)
728{
729 struct asn_oid oid;
730 struct asn_oid idx;
731 char *s, *e, *end, *iname;
732 u_long ul;
733 int ret;
734
735 /* look whether the object to extract has an index part */
736 idx.len = 0;
737 iname = NULL;
738 s = strchr(object, '.');
739 if (s != NULL) {
740 iname = malloc(strlen(s) + 1);
741 if (iname == NULL)
742 err(1, "cannot allocated index");
743
744 strcpy(iname, s);
745 for (e = iname; *e != '\0'; e++)
746 if (*e == '.')
747 *e = '_';
748
749 *s++ = '\0';
750 while (s != NULL) {
751 if (*s == '\0')
752 errx(1, "bad index syntax");
753 if ((e = strchr(s, '.')) != NULL)
754 *e++ = '\0';
755
756 errno = 0;
757 ul = strtoul(s, &end, 0);
758 if (*end != '\0')
759 errx(1, "bad index syntax '%s'", end);
760 if (errno != 0)
761 err(1, "bad index syntax");
762
763 if (idx.len == ASN_MAXOIDLEN)
764 errx(1, "index oid too large");
765 idx.subs[idx.len++] = ul;
766
767 s = e;
768 }
769 }
770
771 oid.len = PREFIX_LEN;
772 memcpy(oid.subs, prefix, sizeof(prefix));
773 ret = extract(root, &oid, object, &idx, iname);
774 if (iname != NULL)
775 free(iname);
776
777 return (ret);
778}
779
780
781static void
782check_sub_order(const struct node *np, const struct node_list *subs)
783{
784 int first;
785 const struct node *sub;
786 asn_subid_t maxid = 0;
787
788 /* ensure, that subids are ordered */
789 first = 1;
790 TAILQ_FOREACH(sub, subs, link) {
791 if (!first && sub->id <= maxid)
792 report_node(np, "subids not ordered at %s", sub->name);
793 maxid = sub->id;
794 first = 0;
795 }
796}
797
798/*
799 * Do some sanity checks on the tree definition and do some computations.
800 */
801static void
802check_tree(struct node *np)
803{
804 struct node *sub;
805
806 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
807 if ((np->flags & (FL_GET|FL_SET)) != 0)
808 tree_size++;
809 return;
810 }
811
812 if (np->type == NODE_ENTRY) {
813 check_sub_order(np, &np->u.entry.subs);
814
815 /* ensure all subnodes are columns */
816 TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
817 if (sub->type != NODE_COLUMN)
818 report_node(np, "entry subnode '%s' is not "
819 "a column", sub->name);
820 check_tree(sub);
821 }
822 } else {
823 check_sub_order(np, &np->u.tree.subs);
824
825 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
826 check_tree(sub);
827 }
828}
829
830static void
831merge_subs(struct node_list *s1, struct node_list *s2)
832{
833 struct node *n1, *n2;
834
835 while (!TAILQ_EMPTY(s2)) {
836 n2 = TAILQ_FIRST(s2);
837 TAILQ_REMOVE(s2, n2, link);
838
839 TAILQ_FOREACH(n1, s1, link)
840 if (n1->id >= n2->id)
841 break;
842 if (n1 == NULL)
843 TAILQ_INSERT_TAIL(s1, n2, link);
844 else if (n1->id > n2->id)
845 TAILQ_INSERT_BEFORE(n1, n2, link);
846 else {
847 if (n1->type == NODE_TREE && n2->type == NODE_TREE) {
848 if (strcmp(n1->name, n2->name) != 0)
849 errx(1, "trees to merge must have "
850 "same name '%s' '%s'", n1->name,
851 n2->name);
852 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs);
853 free(n2);
854 } else if (n1->type == NODE_ENTRY &&
855 n2->type == NODE_ENTRY) {
856 if (strcmp(n1->name, n2->name) != 0)
857 errx(1, "entries to merge must have "
858 "same name '%s' '%s'", n1->name,
859 n2->name);
860 if (n1->u.entry.index != n2->u.entry.index)
861 errx(1, "entries to merge must have "
862 "same index '%s'", n1->name);
863 if (strcmp(n1->u.entry.func,
864 n2->u.entry.func) != 0)
865 errx(1, "entries to merge must have "
866 "same op '%s'", n1->name);
867 merge_subs(&n1->u.entry.subs,
868 &n2->u.entry.subs);
869 free(n2);
870 } else
871 errx(1, "entities to merge must be both "
872 "trees or both entries: %s, %s",
873 n1->name, n2->name);
874 }
875 }
876}
877
878static void
879merge(struct node *root, struct node *t)
880{
881
882 /* both must be trees */
883 if (root->type != NODE_TREE)
884 errx(1, "root is not a tree");
885 if (t->type != NODE_TREE)
886 errx(1, "can merge only with tree");
887 if (root->id != t->id)
888 errx(1, "trees to merge must have same id");
889
890 merge_subs(&root->u.tree.subs, &t->u.tree.subs);
891}
892
893int
894main(int argc, char *argv[])
895{
896 int do_extract = 0;
897 int do_tree = 0;
898 int opt;
899 struct node *root;
900 char fname[MAXPATHLEN + 1];
901 int tok;
902
903 while ((opt = getopt(argc, argv, "help:t")) != EOF)
904 switch (opt) {
905
906 case 'h':
907 fprintf(stderr, "%s", usgtxt);
908 exit(0);
909
910 case 'e':
911 do_extract = 1;
912 break;
913
914 case 'l':
915 localincs = 1;
916 break;
917
918 case 'p':
919 file_prefix = optarg;
920 if (strlen(file_prefix) + strlen("tree.c") >
921 MAXPATHLEN)
922 errx(1, "prefix too long");
923 break;
924
925 case 't':
926 do_tree = 1;
927 break;
928 }
929
930 if (do_extract && do_tree)
931 errx(1, "conflicting options -e and -t");
932 if (!do_extract && argc != optind)
933 errx(1, "no arguments allowed");
934 if (do_extract && argc == optind)
935 errx(1, "no objects specified");
936
937 root = parse(gettoken());
938 while ((tok = gettoken()) != TOK_EOF)
939 merge(root, parse(tok));
940
941 check_tree(root);
942
943 if (do_extract) {
944 fp = stdout;
945 while (optind < argc) {
946 if (gen_extract(root, argv[optind]))
947 errx(1, "object not found: %s", argv[optind]);
948 optind++;
949 }
950
951 return (0);
952 }
953 if (do_tree) {
954 gen_tree(root, 0);
955 return (0);
956 }
957 sprintf(fname, "%stree.h", file_prefix);
958 if ((fp = fopen(fname, "w")) == NULL)
959 err(1, "%s: ", fname);
960 gen_header(root, PREFIX_LEN, NULL);
961
962 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
963 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
964
965 fclose(fp);
966
967 sprintf(fname, "%stree.c", file_prefix);
968 if ((fp = fopen(fname, "w")) == NULL)
969 err(1, "%s: ", fname);
970 gen_table(root);
971 fclose(fp);
972
973 return (0);
974}