1 /*
2  * Copyright (C) 2004-2006
3  * 	Hartmut Brandt.
4  * 	All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $Begemot: gensnmpdef.c 383 2006-05-30 07:40:49Z brandt_h $
30  */
31 #include <sys/queue.h>
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <err.h>
39 #include <assert.h>
40 #include <smi.h>
41 
42 static const char usgtxt[] =
43 "Usage: gensnmpdef [-hEe] [-c <cut>] MIB [MIB ...]\n"
44 "Options:\n"
45 "  -c	specify the number of initial sub-oids to cut from the oids\n"
46 "  -E	extract named enum types. Print a typedef for all enums defined\n"
47 "	in syntax clauses of normal objects. Suppress normal output.\n"
48 "  -e	extract unnamed enum types. Print a typedef for all enums defined\n"
49 "	as textual conventions. Suppress normal output.\n"
50 "  -h	print this help\n"
51 "MIBs are searched according to the libsmi(3) search rules and can\n"
52 "be specified either by path or module name\n";
53 
54 static SmiNode *last_node;
55 static u_int cut = 3;
56 
57 struct tdef {
58 	char *name;
59 	SLIST_ENTRY(tdef) link;
60 };
61 
62 static SLIST_HEAD(, tdef) tdefs = SLIST_HEAD_INITIALIZER(tdefs);
63 static int do_typedef = 0;
64 
65 static void print_node(SmiNode *n, u_int level);
66 
67 static void
68 save_node(SmiNode *n)
69 {
70 	if (n != NULL)
71 		last_node = n;
72 }
73 
74 static void
75 pindent(u_int level)
76 {
77 	if (level >= cut)
78 		printf("%*s", (level - cut) * 2, "");
79 }
80 
81 static void
82 print_name(SmiNode *n)
83 {
84 	char *p;
85 
86 	for (p = n->name; *p != '\0'; p++) {
87 		if (*p == '-')
88 			printf("_");
89 		else
90 			printf("%c", *p);
91 	}
92 }
93 
94 static u_int
95 close_node(u_int n, u_int level)
96 {
97 	while (n--) {
98 		pindent(level);
99 		level--;
100 		if (level >= cut)
101 			printf(")\n");
102 	}
103 	return (level);
104 }
105 
106 static u_int
107 open_node(const SmiNode *n, u_int level, SmiNode **last)
108 {
109 	SmiNode *n1;
110 	u_int i;
111 
112 	if (*last != NULL) {
113 		for (i = 0; i < (*last)->oidlen - 1; i++) {
114 			if (i >= n->oidlen) {
115 				level = close_node((*last)->oidlen -
116 				    n->oidlen, level);
117 				break;
118 			}
119 			if ((*last)->oid[i] != n->oid[i])
120 				break;
121 		}
122 		if (i < (*last)->oidlen - 1)
123 			level = close_node((*last)->oidlen - 1 - i,
124 			    level - 1) + 1;
125 	}
126 
127 	while (level < n->oidlen - 1) {
128 		if (level >= cut) {
129 			n1 = smiGetNodeByOID(level + 1, n->oid);
130 			if (n1 == NULL)
131 				continue;
132 			pindent(level);
133 			printf("(%u", n->oid[level]);
134 			printf(" ");
135 			print_name(n1);
136 			printf("\n");
137 		}
138 		level++;
139 	}
140 	return (level);
141 }
142 
143 static const char *const type_names[] = {
144 	[SMI_BASETYPE_UNKNOWN] =	"UNKNOWN_TYPE",
145 	[SMI_BASETYPE_INTEGER32] =	"INTEGER",
146 	[SMI_BASETYPE_OCTETSTRING] =	"OCTETSTRING",
147 	[SMI_BASETYPE_OBJECTIDENTIFIER] =	"OID",
148 	[SMI_BASETYPE_UNSIGNED32] =	"UNSIGNED32",
149 	[SMI_BASETYPE_INTEGER64] =	"INTEGER64",
150 	[SMI_BASETYPE_UNSIGNED64] =	"UNSIGNED64",
151 	[SMI_BASETYPE_FLOAT32] =	"FLOAT32",
152 	[SMI_BASETYPE_FLOAT64] =	"FLOAT64",
153 	[SMI_BASETYPE_FLOAT128] =	"FLOAT128",
154 	[SMI_BASETYPE_ENUM] =	"ENUM",
155 	[SMI_BASETYPE_BITS] =	"BITS",
156 };
157 
158 static const char *const type_map[] = {
159 	"Gauge32",	"GAUGE",
160 	"Gauge",	"GAUGE",
161 	"TimeTicks",	"TIMETICKS",
162 	"Counter32",	"COUNTER",
163 	"Counter",	"COUNTER",
164 	"Counter64",	"COUNTER64",
165 	"Integer32",	"INTEGER32",
166 	"IpAddress",	"IPADDRESS",
167 	NULL
168 };
169 
170 static void
171 print_enum(SmiType *t)
172 {
173 	SmiNamedNumber *nnum;
174 
175 	printf(" (");
176 	for (nnum = smiGetFirstNamedNumber(t); nnum != NULL;
177 	    nnum = smiGetNextNamedNumber(nnum))
178 		printf(" %ld %s", nnum->value.value.integer32, nnum->name);
179 	printf(" )");
180 }
181 
182 static void
183 print_type(SmiNode *n)
184 {
185 	SmiType *type;
186 	u_int m;
187 
188 	type = smiGetNodeType(n);
189 	assert(type != NULL);
190 
191 	if (type->name != NULL) {
192 		for (m = 0; type_map[m] != NULL; m += 2)
193 			if (strcmp(type_map[m], type->name) == 0) {
194 				printf("%s", type_map[m + 1]);
195 				return;
196 			}
197 	}
198 	printf("%s", type_names[type->basetype]);
199 
200 	if (type->basetype == SMI_BASETYPE_ENUM ||
201 	    type->basetype == SMI_BASETYPE_BITS)
202 		print_enum(type);
203 
204 	else if (type->basetype == SMI_BASETYPE_OCTETSTRING &&
205 	    type->name != NULL)
206 		printf(" | %s", type->name);
207 }
208 
209 static void
210 print_access(SmiAccess a)
211 {
212 	if (a == SMI_ACCESS_READ_ONLY)
213 		printf(" GET");
214 	else if (a == SMI_ACCESS_READ_WRITE)
215 		printf(" GET SET");
216 }
217 
218 static void
219 print_scalar(SmiNode *n, u_int level)
220 {
221 	SmiNode *p;
222 
223 	assert (n->nodekind == SMI_NODEKIND_SCALAR);
224 
225 	save_node(n);
226 
227 	pindent(level);
228 	printf("(%u ", n->oid[level]);
229 	print_name(n);
230 	printf(" ");
231 	print_type(n);
232 
233 	/* generate the operation from the parent node name */
234 	p = smiGetParentNode(n);
235 	printf(" op_%s", p->name);
236 
237 	print_access(n->access);
238 
239 	printf(")\n");
240 }
241 
242 static void
243 print_notification(SmiNode *n, u_int level)
244 {
245 
246 	assert (n->nodekind == SMI_NODEKIND_NOTIFICATION);
247 
248 	save_node(n);
249 
250 	pindent(level);
251 	printf("(%u ", n->oid[level]);
252 	print_name(n);
253 	printf(" OID");
254 
255 	printf(" op_%s)\n", n->name);
256 }
257 
258 static void
259 print_col(SmiNode *n, u_int level)
260 {
261 	assert (n->nodekind == SMI_NODEKIND_COLUMN);
262 
263 	save_node(n);
264 
265 	pindent(level);
266 	printf("(%u ", n->oid[level]);
267 	print_name(n);
268 	printf(" ");
269 	print_type(n);
270 	print_access(n->access);
271 	printf(")\n");
272 }
273 
274 static void
275 print_index(SmiNode *row)
276 {
277 	SmiElement *e;
278 
279 	e = smiGetFirstElement(row);
280 	while (e != NULL) {
281 		printf(" ");
282 		print_type(smiGetElementNode(e));
283 		e = smiGetNextElement(e);
284 	}
285 }
286 
287 static void
288 print_table(SmiNode *n, u_int level)
289 {
290 	SmiNode *row, *col, *rel;
291 
292 	assert (n->nodekind == SMI_NODEKIND_TABLE);
293 
294 	save_node(n);
295 
296 	pindent(level);
297 	printf("(%u ", n->oid[level]);
298 	print_name(n);
299 	printf("\n");
300 
301 	row = smiGetFirstChildNode(n);
302 	if (row->nodekind != SMI_NODEKIND_ROW)
303 		errx(1, "%s: kind %u, not row", __func__, row->nodekind);
304 
305 	save_node(n);
306 
307 	pindent(level + 1);
308 	printf("(%u ", row->oid[level + 1]);
309 	print_name(row);
310 	printf(" :");
311 
312 	/* index */
313 	rel = smiGetRelatedNode(row);
314 	switch (row->indexkind) {
315 
316 	  case SMI_INDEX_INDEX:
317 		print_index(row);
318 		break;
319 
320 	  case SMI_INDEX_AUGMENT:
321 		if (rel == NULL)
322 			errx(1, "%s: cannot find augemented table", row->name);
323 		print_index(rel);
324 		break;
325 
326 	  default:
327 		errx(1, "%s: cannot handle index kind %u", row->name,
328 		    row->indexkind);
329 	}
330 
331 	printf(" op_%s", n->name);
332 	printf("\n");
333 
334 	col = smiGetFirstChildNode(row);
335 	while (col != NULL) {
336 		print_col(col, level + 2);
337 		col = smiGetNextChildNode(col);
338 	}
339 	pindent(level + 1);
340 	printf(")\n");
341 
342 	pindent(level);
343 	printf(")\n");
344 }
345 
346 static void
347 print_it(SmiNode *n, u_int level)
348 {
349 	switch (n->nodekind) {
350 
351 	  case SMI_NODEKIND_NODE:
352 		print_node(n, level);
353 		break;
354 
355 	  case SMI_NODEKIND_SCALAR:
356 		print_scalar(n, level);
357 		break;
358 
359 	  case SMI_NODEKIND_TABLE:
360 		print_table(n, level);
361 		break;
362 
363 	  case SMI_NODEKIND_COMPLIANCE:
364 	  case SMI_NODEKIND_GROUP:
365 		save_node(n);
366 		break;
367 
368 	  case SMI_NODEKIND_NOTIFICATION:
369 		print_notification(n, level);
370 		break;
371 
372 	  default:
373 		errx(1, "cannot handle %u nodes", n->nodekind);
374 	}
375 }
376 
377 static void
378 print_node(SmiNode *n, u_int level)
379 {
380 	assert (n->nodekind == SMI_NODEKIND_NODE);
381 
382 	save_node(n);
383 
384 	pindent(level);
385 	printf("(%u ", n->oid[level]);
386 	print_name(n);
387 	printf("\n");
388 
389 	n = smiGetFirstChildNode(n);
390 	while (n != NULL) {
391 		print_it(n, level + 1);
392 		n = smiGetNextChildNode(n);
393 	}
394 	pindent(level);
395 	printf(")\n");
396 }
397 
398 static void
399 save_typdef(char *name)
400 {
401 	struct tdef *t;
402 
403 	t = calloc(1, sizeof(struct tdef));
404 	if (t == NULL)
405 		err(1, NULL);
406 
407 	t->name = name;
408 	SLIST_INSERT_HEAD(&tdefs, t, link);
409 }
410 
411 static void
412 tdefs_cleanup(void)
413 {
414 	struct tdef *t;
415 
416 	while ((t = SLIST_FIRST(&tdefs)) != NULL) {
417 		SLIST_REMOVE_HEAD(&tdefs, link);
418 		free(t);
419 	}
420 }
421 
422 static void
423 print_enum_typedef(SmiType *t)
424 {
425 	SmiNamedNumber *nnum;
426 
427 	for (nnum = smiGetFirstNamedNumber(t); nnum != NULL;
428 	    nnum = smiGetNextNamedNumber(nnum)) {
429 		printf("\t%ld %s\n" , nnum->value.value.integer32, nnum->name);
430 	}
431 }
432 
433 static void
434 print_stype(SmiNode *n)
435 {
436 	SmiType *type;
437 	struct tdef *t = NULL;
438 
439 	type = smiGetNodeType(n);
440 	assert(type != NULL);
441 
442 	if (type->basetype == SMI_BASETYPE_ENUM) {
443 		if (do_typedef == 'e' && type->name != NULL) {
444 			SLIST_FOREACH(t, &tdefs, link) {
445 				if (strcmp(t->name, type->name) == 0)
446 					return;
447 			}
448 			save_typdef(type->name);
449 			printf("typedef %s ENUM (\n", type->name);
450 		} else if (do_typedef == 'E' && type->name == NULL)
451 			printf("typedef %sType ENUM (\n", n->name);
452 		else
453 			return;
454 
455 		print_enum_typedef(type);
456 		printf(")\n\n");
457 
458 	} else if (type->basetype == SMI_BASETYPE_BITS) {
459 		if (do_typedef == 'e' && type->name != NULL) {
460 			SLIST_FOREACH(t, &tdefs, link) {
461 				if (strcmp(t->name, type->name) == 0)
462 					return;
463 			}
464 			save_typdef(type->name);
465 			printf("typedef %s BITS (\n", type->name);
466 		} else if (do_typedef == 'E' && type->name == NULL)
467 			printf("typedef %sType BITS (\n", n->name);
468 		else
469 			return;
470 
471 		print_enum_typedef(type);
472 		printf(")\n\n");
473 	}
474 }
475 
476 static void
477 print_typdefs(SmiNode *n)
478 {
479 	SmiNode *p;
480 
481 	p = n;
482 	n = smiGetFirstChildNode(n);
483 	while (n != NULL) {
484 		switch (n->nodekind) {
485 		  case SMI_NODEKIND_SCALAR:
486 		  case SMI_NODEKIND_COLUMN:
487 			print_stype(n);
488 			break;
489 		  case SMI_NODEKIND_COMPLIANCE:
490 	  	  case SMI_NODEKIND_GROUP:
491 			save_node(n);
492 			return;
493 		  default:
494 			break;
495 		}
496 		n = smiGetNextChildNode(n);
497 	}
498 
499 	save_node(p);
500 }
501 
502 int
503 main(int argc, char *argv[])
504 {
505 	int opt;
506 	int flags;
507 	SmiModule **mods;
508 	char *name;
509 	SmiNode *n, *last;
510 	u_int level;
511 	long u;
512 	char *end;
513 
514 	smiInit(NULL);
515 
516 	while ((opt = getopt(argc, argv, "c:Eeh")) != -1)
517 		switch (opt) {
518 
519 		  case 'c':
520 			errno = 0;
521 			u = strtol(optarg, &end, 10);
522 			if (errno != 0)
523 				err(1, "argument to -c");
524 			if (*end != '\0')
525 				err(1, "%s: not a number", optarg);
526 			if (u < 0 || u > 5)
527 				err(1, "%s: out of range", optarg);
528 			cut = (u_int)u;
529 			break;
530 
531 		  case 'E':
532 			do_typedef = 'E';
533 			break;
534 
535 		  case 'e':
536 			do_typedef = 'e';
537 			break;
538 
539 		  case 'h':
540 			fprintf(stderr, usgtxt);
541 			exit(0);
542 		}
543 
544 	argc -= optind;
545 	argv += optind;
546 
547 	flags = smiGetFlags();
548 	flags |= SMI_FLAG_ERRORS;
549 	smiSetFlags(flags);
550 
551 	mods = malloc(sizeof(mods[0]) * argc);
552 	if (mods == NULL)
553 		err(1, NULL);
554 
555 	for (opt = 0; opt < argc; opt++) {
556 		if ((name = smiLoadModule(argv[opt])) == NULL)
557 			err(1, "%s: cannot load", argv[opt]);
558 		mods[opt] = smiGetModule(name);
559 	}
560 	level = 0;
561 	last = NULL;
562 	for (opt = 0; opt < argc; opt++) {
563 		if (mods[opt] == NULL) /* smiGetModule failed above */
564 			continue;
565 		n = smiGetFirstNode(mods[opt], SMI_NODEKIND_ANY);
566 		if (n == NULL)
567 			continue;
568 		for (;;) {
569 			if (do_typedef == 0) {
570 				level = open_node(n, level, &last);
571 				print_it(n, level);
572 				last = n;
573 			} else
574 				print_typdefs(n);
575 
576 			if (last_node == NULL ||
577 			    (n = smiGetNextNode(last_node, SMI_NODEKIND_ANY))
578 			    == NULL)
579 				break;
580 		}
581 	}
582 	if (last != NULL && do_typedef == 0)
583 		level = close_node(last->oidlen - 1, level - 1);
584 	else if (do_typedef != 0)
585 		tdefs_cleanup();
586 
587 	return (0);
588 }
589