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 			pindent(level);
130 			printf("(%u", n->oid[level]);
131 			n1 = smiGetNodeByOID(level + 1, n->oid);
132 			printf(" ");
133 			print_name(n1);
134 			printf("\n");
135 		}
136 		level++;
137 	}
138 	return (level);
139 }
140 
141 static const char *const type_names[] = {
142 	[SMI_BASETYPE_UNKNOWN] =	"UNKNOWN_TYPE",
143 	[SMI_BASETYPE_INTEGER32] =	"INTEGER",
144 	[SMI_BASETYPE_OCTETSTRING] =	"OCTETSTRING",
145 	[SMI_BASETYPE_OBJECTIDENTIFIER] =	"OID",
146 	[SMI_BASETYPE_UNSIGNED32] =	"UNSIGNED32",
147 	[SMI_BASETYPE_INTEGER64] =	"INTEGER64",
148 	[SMI_BASETYPE_UNSIGNED64] =	"UNSIGNED64",
149 	[SMI_BASETYPE_FLOAT32] =	"FLOAT32",
150 	[SMI_BASETYPE_FLOAT64] =	"FLOAT64",
151 	[SMI_BASETYPE_FLOAT128] =	"FLOAT128",
152 	[SMI_BASETYPE_ENUM] =	"ENUM",
153 	[SMI_BASETYPE_BITS] =	"BITS",
154 };
155 
156 static const char *const type_map[] = {
157 	"Gauge32",	"GAUGE",
158 	"Gauge",	"GAUGE",
159 	"TimeTicks",	"TIMETICKS",
160 	"Counter32",	"COUNTER",
161 	"Counter",	"COUNTER",
162 	"Counter64",	"COUNTER64",
163 	"Integer32",	"INTEGER32",
164 	"IpAddress",	"IPADDRESS",
165 	NULL
166 };
167 
168 static void
169 print_enum(SmiType *t)
170 {
171 	SmiNamedNumber *nnum;
172 
173 	printf(" (");
174 	for (nnum = smiGetFirstNamedNumber(t); nnum != NULL;
175 	    nnum = smiGetNextNamedNumber(nnum))
176 		printf(" %ld %s", nnum->value.value.integer32, nnum->name);
177 	printf(" )");
178 }
179 
180 static void
181 print_type(SmiNode *n)
182 {
183 	SmiType *type;
184 	u_int m;
185 
186 	type = smiGetNodeType(n);
187 	assert(type != NULL);
188 
189 	if (type->name != NULL) {
190 		for (m = 0; type_map[m] != NULL; m += 2)
191 			if (strcmp(type_map[m], type->name) == 0) {
192 				printf("%s", type_map[m + 1]);
193 				return;
194 			}
195 	}
196 	printf("%s", type_names[type->basetype]);
197 
198 	if (type->basetype == SMI_BASETYPE_ENUM ||
199 	    type->basetype == SMI_BASETYPE_BITS)
200 		print_enum(type);
201 
202 	else if (type->basetype == SMI_BASETYPE_OCTETSTRING &&
203 	    type->name != NULL)
204 		printf(" | %s", type->name);
205 }
206 
207 static void
208 print_access(SmiAccess a)
209 {
210 	if (a == SMI_ACCESS_READ_ONLY)
211 		printf(" GET");
212 	else if (a == SMI_ACCESS_READ_WRITE)
213 		printf(" GET SET");
214 }
215 
216 static void
217 print_scalar(SmiNode *n, u_int level)
218 {
219 	SmiNode *p;
220 
221 	assert (n->nodekind == SMI_NODEKIND_SCALAR);
222 
223 	save_node(n);
224 
225 	pindent(level);
226 	printf("(%u ", n->oid[level]);
227 	print_name(n);
228 	printf(" ");
229 	print_type(n);
230 
231 	/* generate the operation from the parent node name */
232 	p = smiGetParentNode(n);
233 	printf(" op_%s", p->name);
234 
235 	print_access(n->access);
236 
237 	printf(")\n");
238 }
239 
240 static void
241 print_notification(SmiNode *n, u_int level)
242 {
243 
244 	assert (n->nodekind == SMI_NODEKIND_NOTIFICATION);
245 
246 	save_node(n);
247 
248 	pindent(level);
249 	printf("(%u ", n->oid[level]);
250 	print_name(n);
251 	printf(" OID");
252 
253 	printf(" op_%s)\n", n->name);
254 }
255 
256 static void
257 print_col(SmiNode *n, u_int level)
258 {
259 	assert (n->nodekind == SMI_NODEKIND_COLUMN);
260 
261 	save_node(n);
262 
263 	pindent(level);
264 	printf("(%u ", n->oid[level]);
265 	print_name(n);
266 	printf(" ");
267 	print_type(n);
268 	print_access(n->access);
269 	printf(")\n");
270 }
271 
272 static void
273 print_index(SmiNode *row)
274 {
275 	SmiElement *e;
276 
277 	e = smiGetFirstElement(row);
278 	while (e != NULL) {
279 		printf(" ");
280 		print_type(smiGetElementNode(e));
281 		e = smiGetNextElement(e);
282 	}
283 }
284 
285 static void
286 print_table(SmiNode *n, u_int level)
287 {
288 	SmiNode *row, *col, *rel;
289 
290 	assert (n->nodekind == SMI_NODEKIND_TABLE);
291 
292 	save_node(n);
293 
294 	pindent(level);
295 	printf("(%u ", n->oid[level]);
296 	print_name(n);
297 	printf("\n");
298 
299 	row = smiGetFirstChildNode(n);
300 	if (row->nodekind != SMI_NODEKIND_ROW)
301 		errx(1, "%s: kind %u, not row", __func__, row->nodekind);
302 
303 	save_node(n);
304 
305 	pindent(level + 1);
306 	printf("(%u ", row->oid[level + 1]);
307 	print_name(row);
308 	printf(" :");
309 
310 	/* index */
311 	rel = smiGetRelatedNode(row);
312 	switch (row->indexkind) {
313 
314 	  case SMI_INDEX_INDEX:
315 		print_index(row);
316 		break;
317 
318 	  case SMI_INDEX_AUGMENT:
319 		if (rel == NULL)
320 			errx(1, "%s: cannot find augemented table", row->name);
321 		print_index(rel);
322 		break;
323 
324 	  default:
325 		errx(1, "%s: cannot handle index kind %u", row->name,
326 		    row->indexkind);
327 	}
328 
329 	printf(" op_%s", n->name);
330 	printf("\n");
331 
332 	col = smiGetFirstChildNode(row);
333 	while (col != NULL) {
334 		print_col(col, level + 2);
335 		col = smiGetNextChildNode(col);
336 	}
337 	pindent(level + 1);
338 	printf(")\n");
339 
340 	pindent(level);
341 	printf(")\n");
342 }
343 
344 static void
345 print_it(SmiNode *n, u_int level)
346 {
347 	switch (n->nodekind) {
348 
349 	  case SMI_NODEKIND_NODE:
350 		print_node(n, level);
351 		break;
352 
353 	  case SMI_NODEKIND_SCALAR:
354 		print_scalar(n, level);
355 		break;
356 
357 	  case SMI_NODEKIND_TABLE:
358 		print_table(n, level);
359 		break;
360 
361 	  case SMI_NODEKIND_COMPLIANCE:
362 	  case SMI_NODEKIND_GROUP:
363 		save_node(n);
364 		break;
365 
366 	  case SMI_NODEKIND_NOTIFICATION:
367 		print_notification(n, level);
368 		break;
369 
370 	  default:
371 		errx(1, "cannot handle %u nodes", n->nodekind);
372 	}
373 }
374 
375 static void
376 print_node(SmiNode *n, u_int level)
377 {
378 	assert (n->nodekind == SMI_NODEKIND_NODE);
379 
380 	save_node(n);
381 
382 	pindent(level);
383 	printf("(%u ", n->oid[level]);
384 	print_name(n);
385 	printf("\n");
386 
387 	n = smiGetFirstChildNode(n);
388 	while (n != NULL) {
389 		print_it(n, level + 1);
390 		n = smiGetNextChildNode(n);
391 	}
392 	pindent(level);
393 	printf(")\n");
394 }
395 
396 static void
397 save_typdef(char *name)
398 {
399 	struct tdef *t;
400 	t = malloc(sizeof(struct tdef));
401 
402 	if (t == NULL)
403 		err(1, NULL);
404 
405 	memset(t, 0 , sizeof(struct tdef));
406 	t->name = name;
407 	SLIST_INSERT_HEAD(&tdefs, t, link);
408 }
409 
410 static void
411 tdefs_cleanup(void)
412 {
413 	struct tdef *t;
414 
415 	while ((t = SLIST_FIRST(&tdefs)) != NULL) {
416 		SLIST_REMOVE_HEAD(&tdefs, link);
417 		free(t);
418 	}
419 }
420 
421 static void
422 print_enum_typedef(SmiType *t)
423 {
424 	SmiNamedNumber *nnum;
425 
426 	for (nnum = smiGetFirstNamedNumber(t); nnum != NULL;
427 	    nnum = smiGetNextNamedNumber(nnum)) {
428 		printf("\t%ld %s\n" , nnum->value.value.integer32, nnum->name);
429 	}
430 }
431 
432 static void
433 print_stype(SmiNode *n)
434 {
435 	SmiType *type;
436 	struct tdef *t = NULL;
437 
438 	type = smiGetNodeType(n);
439 	assert(type != NULL);
440 
441 	if (type->basetype == SMI_BASETYPE_ENUM) {
442 		if (do_typedef == 'e' && type->name != NULL) {
443 			SLIST_FOREACH(t, &tdefs, link) {
444 				if (strcmp(t->name, type->name) == 0)
445 					return;
446 			}
447 			save_typdef(type->name);
448 			printf("typedef %s ENUM (\n", type->name);
449 		} else if (do_typedef == 'E' && type->name == NULL)
450 			printf("typedef %sType ENUM (\n", n->name);
451 		else
452 			return;
453 
454 		print_enum_typedef(type);
455 		printf(")\n\n");
456 
457 	} else if (type->basetype == SMI_BASETYPE_BITS) {
458 		if (do_typedef == 'e' && type->name != NULL) {
459 			SLIST_FOREACH(t, &tdefs, link) {
460 				if (strcmp(t->name, type->name) == 0)
461 					return;
462 			}
463 			save_typdef(type->name);
464 			printf("typedef %s BITS (\n", type->name);
465 		} else if (do_typedef == 'E' && type->name == NULL)
466 			printf("typedef %sType BITS (\n", n->name);
467 		else
468 			return;
469 
470 		print_enum_typedef(type);
471 		printf(")\n\n");
472 	}
473 }
474 
475 static void
476 print_typdefs(SmiNode *n)
477 {
478 	SmiNode *p;
479 
480 	p = n;
481 	n = smiGetFirstChildNode(n);
482 	while (n != NULL) {
483 		switch (n->nodekind) {
484 		  case SMI_NODEKIND_SCALAR:
485 		  case SMI_NODEKIND_COLUMN:
486 			print_stype(n);
487 			break;
488 		  case SMI_NODEKIND_COMPLIANCE:
489 	  	  case SMI_NODEKIND_GROUP:
490 			save_node(n);
491 			return;
492 		  default:
493 			break;
494 		}
495 		n = smiGetNextChildNode(n);
496 	}
497 
498 	save_node(p);
499 }
500 
501 int
502 main(int argc, char *argv[])
503 {
504 	int opt;
505 	int flags;
506 	SmiModule **mods;
507 	char *name;
508 	SmiNode *n, *last;
509 	u_int level;
510 	long u;
511 	char *end;
512 
513 	smiInit(NULL);
514 
515 	while ((opt = getopt(argc, argv, "c:Eeh")) != -1)
516 		switch (opt) {
517 
518 		  case 'c':
519 			errno = 0;
520 			u = strtol(optarg, &end, 10);
521 			if (errno != 0)
522 				err(1, "argument to -c");
523 			if (*end != '\0')
524 				err(1, "%s: not a number", optarg);
525 			if (u < 0 || u > 5)
526 				err(1, "%s: out of range", optarg);
527 			cut = (u_int)u;
528 			break;
529 
530 		  case 'E':
531 			do_typedef = 'E';
532 			break;
533 
534 		  case 'e':
535 			do_typedef = 'e';
536 			break;
537 
538 		  case 'h':
539 			fprintf(stderr, usgtxt);
540 			exit(0);
541 		}
542 
543 	argc -= optind;
544 	argv += optind;
545 
546 	flags = smiGetFlags();
547 	flags |= SMI_FLAG_ERRORS;
548 	smiSetFlags(flags);
549 
550 	mods = malloc(sizeof(mods[0]) * argc);
551 	if (mods == NULL)
552 		err(1, NULL);
553 
554 	for (opt = 0; opt < argc; opt++) {
555 		if ((name = smiLoadModule(argv[opt])) == NULL)
556 			err(1, "%s: cannot load", argv[opt]);
557 		mods[opt] = smiGetModule(name);
558 	}
559 	level = 0;
560 	last = NULL;
561 	for (opt = 0; opt < argc; opt++) {
562 		n = smiGetFirstNode(mods[opt], SMI_NODEKIND_ANY);
563 		for (;;) {
564 			if (do_typedef == 0) {
565 				level = open_node(n, level, &last);
566 				print_it(n, level);
567 				last = n;
568 			} else
569 				print_typdefs(n);
570 
571 			if (last_node == NULL ||
572 			    (n = smiGetNextNode(last_node, SMI_NODEKIND_ANY))
573 			    == NULL)
574 				break;
575 		}
576 	}
577 	if (last != NULL && do_typedef == 0)
578 		level = close_node(last->oidlen - 1, level - 1);
579 	else if (do_typedef != 0)
580 		tdefs_cleanup();
581 
582 	return (0);
583 }
584