2 * Copyright (c) 2006 The FreeBSD Project
5 * Author: Shteryana Shopova <syrinx@FreeBSD.org>
7 * Redistribution of this software and documentation and use in source and
8 * binary forms, with or without modification, are permitted provided that
9 * the following conditions are met:
11 * 1. Redistributions of source code or documentation must retain the above
12 * copyright 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.
17 * THIS SOFTWARE IS PROVIDED BY THE 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 THE 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
33 * Read file containing table description - reuse magic from gensnmptree.c.
34 * Hopefully one day most of the code here will be part of libbsnmp and
35 * this duplication won't be necessary.
39 * file := top | top file
41 * top := tree | typedef | include
43 * tree := head elements ')'
45 * entry := head ':' index STRING elements ')'
47 * leaf := head type STRING ACCESS ')'
49 * column := head type ACCESS ')'
51 * type := BASETYPE | BASETYPE '|' subtype | enum | bits
55 * enum := ENUM '(' value ')'
57 * bits := BITS '(' value ')'
59 * value := INT STRING | INT STRING value
61 * head := '(' INT STRING
63 * elements := EMPTY | elements element
65 * element := tree | leaf | column
67 * index := type | index type
69 * typedef := 'typedef' STRING type
71 * include := 'include' filespec
73 * filespec := '"' STRING '"' | '<' STRING '>'
76 #include <sys/param.h>
77 #include <sys/queue.h>
90 #include <bsnmp/asn1.h>
91 #include <bsnmp/snmp.h>
92 #include <bsnmp/snmpagent.h> /* SNMP_INDEXES_MAX */
94 #include "bsnmptools.h"
107 /************************************************************
109 * Allocate memory and panic just in the case...
116 if ((ptr = malloc(size)) == NULL)
117 err(1, "allocing %zu bytes", size);
123 savestr(const char *s)
128 return (strcpy(xalloc(strlen(s) + 1), s));
131 /************************************************************
140 LIST_ENTRY(input) link;
143 LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs);
144 struct input *input = NULL;
147 #define MAX_PATHS 100
149 static const char *paths[MAX_PATHS + 1] = {
150 "/usr/share/snmp/defs",
151 "/usr/local/share/snmp/defs",
156 input_new(FILE *fp, const char *path, const char *fname)
160 ip = xalloc(sizeof(*ip));
163 ip->fname = savestr(fname);
164 ip->path = savestr(path);
165 LIST_INSERT_HEAD(&inputs, ip, link);
179 LIST_REMOVE(input, link);
182 input = LIST_FIRST(&inputs);
186 tryopen(const char *path, const char *fname)
194 fn = xalloc(strlen(path) + strlen(fname) + 2);
195 sprintf(fn, "%s/%s", path, fname);
203 input_fopen(const char *fname)
208 if (fname[0] == '/' || fname[0] == '.' || fname[0] == '~') {
209 if ((fp = tryopen(NULL, fname)) != NULL) {
210 input_new(fp, NULL, fname);
216 for (p = 0; paths[p] != NULL; p++)
217 if ((fp = tryopen(paths[p], fname)) != NULL) {
218 input_new(fp, paths[p], fname);
223 warnx("cannot open '%s'", fname);
242 if ((c = getc(input->fp)) != EOF)
260 /************************************************************
265 TOK_EOF = 0200, /* end-of-file seen */
266 TOK_NUM, /* number */
267 TOK_STR, /* string */
268 TOK_ACCESS, /* access operator */
269 TOK_TYPE, /* type operator */
270 TOK_ENUM, /* enum token (kind of a type) */
271 TOK_TYPEDEF, /* typedef directive */
272 TOK_DEFTYPE, /* defined type */
273 TOK_INCLUDE, /* include directive */
274 TOK_FILENAME, /* filename ("foo.bar" or <foo.bar>) */
275 TOK_BITS, /* bits token (kind of a type) */
276 TOK_ERR /* unexpected char - exit */
279 static const struct {
284 { "GET", TOK_ACCESS, FL_GET },
285 { "SET", TOK_ACCESS, FL_SET },
286 { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },
287 { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },
288 { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },
289 { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },
290 { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },
291 { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },
292 { "OID", TOK_TYPE, SNMP_SYNTAX_OID },
293 { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },
294 { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },
295 { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },
296 { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },
297 { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER },
298 { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING },
299 { "typedef", TOK_TYPEDEF, 0 },
300 { "include", TOK_INCLUDE, 0 },
305 /* Current OID type, regarding table membership. */
306 enum snmp_tbl_entry tbl_type;
307 /* A pointer to a structure in table list to add to its members. */
308 struct snmp_index_entry *table_idx;
311 struct asn_oid current_oid;
312 char nexttok[MAXSTR];
313 u_long val; /* integer values */
314 int32_t all_cond; /* all conditions are true */
315 int32_t saved_token = -1;
317 /* Prepare the global data before parsing a new file. */
319 snmp_import_init(struct asn_oid *append)
321 memset(&table_data, 0, sizeof(table_data));
322 memset(¤t_oid, 0, sizeof(struct asn_oid));
323 memset(nexttok, 0, MAXSTR);
326 asn_append_oid(¤t_oid, append);
334 gettoken(struct snmp_toolinfo *snmptoolctx)
339 if (saved_token != -1) {
347 * Skip any whitespace before the next token.
349 while ((c = tgetc()) != EOF) {
359 warnx("unexpected character %#2x", (u_int) c);
367 while ((c = tgetc()) != EOF) {
373 warnx("unexpected EOF in comment");
378 * Single character tokens.
380 if (strchr("():|", c) != NULL)
383 if (c == '"' || c == '<') {
393 while ((c = tgetc()) != EOF) {
396 if (n == sizeof(nexttok) - 1) {
398 warnx("filename too long '%s...'", nexttok);
404 return (TOK_FILENAME);
413 while ((c = tgetc()) != EOF) {
419 if (n == sizeof(nexttok) - 1) {
421 warnx("number too long '%s...'", nexttok);
427 sscanf(nexttok, "%lu", &val);
432 * So that has to be a string.
434 if (isalpha(c) || c == '_' || c == '-') {
437 while ((c = tgetc()) != EOF) {
438 if (!isalnum(c) && c != '_' && c != '-') {
443 if (n == sizeof(nexttok) - 1) {
445 warnx("string too long '%s...'", nexttok);
455 for (c = 0; keywords[c].str != NULL; c++)
456 if (strcmp(keywords[c].str, nexttok) == 0) {
457 val = keywords[c].val;
458 return (keywords[c].tok);
461 if ((t = snmp_enumtc_lookup(snmptoolctx, nexttok)) != NULL) {
463 return (TOK_DEFTYPE);
470 warnx("%u: unexpected character '%c'", input->lno, c);
472 warnx("%u: unexpected character 0x%02x", input->lno, (u_int) c);
478 * Update table information.
480 static struct snmp_index_entry *
481 snmp_import_update_table(enum snmp_tbl_entry te, struct snmp_index_entry *tbl)
485 if (table_data.tbl_type == ENTRY_NONE)
487 if (table_data.tbl_type == ENTRY_INDEX)
488 table_data.table_idx = NULL;
489 table_data.tbl_type--;
494 warnx("No table_index to add!!!");
495 table_data.table_idx = tbl;
496 table_data.tbl_type = ENTRY_INDEX;
500 if (table_data.tbl_type == ENTRY_INDEX) {
501 table_data.tbl_type = ENTRY_DATA;
502 return (table_data.table_idx);
508 warnx("Unknown table entry type!!!");
516 parse_enum(struct snmp_toolinfo *snmptoolctx, enum tok *tok,
517 struct enum_pairs *enums)
519 while ((*tok = gettoken(snmptoolctx)) == TOK_STR) {
520 if (enum_pair_insert(enums, val, nexttok) < 0)
522 if ((*tok = gettoken(snmptoolctx)) != TOK_NUM)
527 warnx("')' at end of enums");
535 parse_subtype(struct snmp_toolinfo *snmptoolctx, enum tok *tok,
538 if ((*tok = gettoken(snmptoolctx)) != TOK_STR) {
539 warnx("subtype expected after '|'");
543 *tc = snmp_get_tc(nexttok);
544 *tok = gettoken(snmptoolctx);
550 parse_type(struct snmp_toolinfo *snmptoolctx, enum tok *tok,
551 enum snmp_tc *tc, struct enum_pairs **snmp_enum)
558 if (*tok == TOK_ENUM || *tok == TOK_BITS) {
559 if (*snmp_enum == NULL) {
560 if ((*snmp_enum = enum_pairs_init()) == NULL)
567 if (gettoken(snmptoolctx) != '(') {
568 warnx("'(' expected after ENUM/BITS");
572 if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) {
573 warnx("need value for ENUM//BITS");
581 if (parse_enum(snmptoolctx, tok, *snmp_enum) < 0) {
582 enum_pairs_free(*snmp_enum);
587 *tok = gettoken(snmptoolctx);
589 } else if (*tok == TOK_DEFTYPE) {
593 t = snmp_enumtc_lookup(snmptoolctx, nexttok);
595 *snmp_enum = t->snmp_enum;
597 *tok = gettoken(snmptoolctx);
600 if ((*tok = gettoken(snmptoolctx)) == '|') {
601 if (parse_subtype(snmptoolctx, tok, tc) < 0)
610 snmp_import_head(struct snmp_toolinfo *snmptoolctx)
614 if ((tok = gettoken(snmptoolctx)) == '(')
615 tok = gettoken(snmptoolctx);
617 if (tok != TOK_NUM || val > ASN_MAXID ) {
618 warnx("Suboid expected - line %d", input->lno);
622 if (gettoken(snmptoolctx) != TOK_STR) {
623 warnx("Node name expected at line %d", input->lno);
631 snmp_import_table(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *obj)
636 struct snmp_index_entry *entry;
638 if ((entry = malloc(sizeof(struct snmp_index_entry))) == NULL) {
639 syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
643 memset(entry, 0, sizeof(struct snmp_index_entry));
644 STAILQ_INIT(&(entry->index_list));
646 for (i = 0, tok = gettoken(snmptoolctx); i < SNMP_INDEXES_MAX; i++) {
648 struct enum_pairs *enums = NULL;
650 if (tok != TOK_TYPE && tok != TOK_DEFTYPE && tok != TOK_ENUM &&
654 if ((syntax = parse_type(snmptoolctx, &tok, &tc, &enums)) < 0) {
655 enum_pairs_free(enums);
656 snmp_index_listfree(&(entry->index_list));
661 if (snmp_syntax_insert(&(entry->index_list), enums, syntax,
663 snmp_index_listfree(&(entry->index_list));
664 enum_pairs_free(enums);
670 if (i == 0 || i > SNMP_INDEXES_MAX) {
671 warnx("Bad number of indexes at line %d", input->lno);
672 snmp_index_listfree(&(entry->index_list));
677 if (tok != TOK_STR) {
678 warnx("String expected after indexes at line %d", input->lno);
679 snmp_index_listfree(&(entry->index_list));
684 entry->string = obj->string;
685 entry->strlen = obj->strlen;
686 asn_append_oid(&(entry->var), &(obj->var));
688 if ((i = snmp_table_insert(snmptoolctx, entry)) < 0) {
689 snmp_index_listfree(&(entry->index_list));
693 /* Same entry already present in lists. */
698 (void) snmp_import_update_table(ENTRY_INDEX, entry);
704 * Read everything after the syntax type that is certainly a leaf OID info.
707 snmp_import_leaf(struct snmp_toolinfo *snmptoolctx, enum tok *tok,
708 struct snmp_oid2str *oid2str)
712 if ((syntax = parse_type(snmptoolctx, tok, &(oid2str->tc), &(oid2str->snmp_enum)))
716 oid2str->syntax = syntax;
718 * That is the name of the function, corresponding to the entry.
719 * It is used by bsnmpd, but is not interesting for us.
722 *tok = gettoken(snmptoolctx);
724 for (i = 0; i < SNMP_ACCESS_GETSET && *tok == TOK_ACCESS; i++) {
725 oid2str->access |= (uint32_t) val;
726 *tok = gettoken(snmptoolctx);
730 warnx("')' expected at end of line %d", input->lno);
734 oid2str->table_idx = snmp_import_update_table(ENTRY_DATA, NULL);
736 if ((i = snmp_leaf_insert(snmptoolctx, oid2str)) < 0) {
737 warnx("Error adding leaf %s to list", oid2str->string);
742 * Same entry is already present in the mapping lists and
743 * the new one was not inserted.
746 free(oid2str->string);
750 (void) snmp_import_update_table(ENTRY_NONE, NULL);
756 snmp_import_object(struct snmp_toolinfo *snmptoolctx)
761 struct snmp_oid2str *oid2str;
763 if (snmp_import_head(snmptoolctx) < 0)
766 if ((oid2str = malloc(sizeof(struct snmp_oid2str))) == NULL) {
767 syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
771 if ((string = malloc(strlen(nexttok) + 1)) == NULL) {
772 syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
777 memset(oid2str, 0, sizeof(struct snmp_oid2str));
778 strlcpy(string, nexttok, strlen(nexttok) + 1);
779 oid2str->string = string;
780 oid2str->strlen = strlen(nexttok);
782 asn_append_oid(&(oid2str->var), &(current_oid));
783 if (snmp_suboid_append(&(oid2str->var), (asn_subid_t) val) < 0)
787 * Prepared the entry - now figure out where to insert it.
788 * After the object we have following options:
789 * 1) new line, blank, ) - then it is an enum oid -> snmp_enumlist;
790 * 2) new line , ( - nonleaf oid -> snmp_nodelist;
791 * 2) ':' - table entry - a variable length SYNTAX_TYPE (one or more)
792 * may follow and second string must end line -> snmp_tablelist;
793 * 3) OID , string ) - this is a trap entry or a leaf -> snmp_oidlist;
794 * 4) SYNTAX_TYPE, string (not always), get/set modifier - always last
795 * and )- this is definitely a leaf.
798 switch (tok = gettoken(snmptoolctx)) {
800 if ((i = snmp_enum_insert(snmptoolctx, oid2str)) < 0)
803 free(oid2str->string);
809 if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0)
813 * Ignore the error for nodes since the .def files currently
814 * contain different strings for 1.3.6.1.2.1 - mibII. Only make
815 * sure the memory is freed and don't complain.
817 if ((i = snmp_node_insert(snmptoolctx, oid2str)) <= 0) {
821 return (snmp_import_object(snmptoolctx));
824 if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0)
826 if (snmp_import_table(snmptoolctx, oid2str) < 0)
829 * A different table entry type was malloced and the data is
842 if (snmp_import_leaf(snmptoolctx, &tok, oid2str) < 0)
847 warnx("Unexpected token at line %d - %s", input->lno,
853 snmp_mapping_entryfree(oid2str);
859 snmp_import_tree(struct snmp_toolinfo *snmptoolctx, enum tok *tok)
861 while (*tok != TOK_EOF) {
866 if (snmp_import_object(snmptoolctx) < 0)
870 if (snmp_suboid_pop(¤t_oid) < 0)
872 (void) snmp_import_update_table(ENTRY_NONE, NULL);
875 /* Anything else here would be illegal. */
878 *tok = gettoken(snmptoolctx);
885 snmp_import_top(struct snmp_toolinfo *snmptoolctx, enum tok *tok)
891 return (snmp_import_tree(snmptoolctx, tok));
893 if (*tok == TOK_TYPEDEF) {
894 if ((*tok = gettoken(snmptoolctx)) != TOK_STR) {
895 warnx("type name expected after typedef - %s",
900 t = snmp_enumtc_init(nexttok);
902 *tok = gettoken(snmptoolctx);
903 t->is_enum = (*tok == TOK_ENUM);
904 t->is_bits = (*tok == TOK_BITS);
905 t->syntax = parse_type(snmptoolctx, tok, &tc, &(t->snmp_enum));
906 snmp_enumtc_insert(snmptoolctx, t);
911 if (*tok == TOK_INCLUDE) {
914 *tok = gettoken(snmptoolctx);
915 if (*tok != TOK_FILENAME) {
916 warnx("filename expected in include directive - %s",
921 if (( i = add_filename(snmptoolctx, nexttok, NULL, 1)) == 0) {
922 *tok = gettoken(snmptoolctx);
929 input_fopen(nexttok);
930 *tok = gettoken(snmptoolctx);
934 warnx("'(' or 'typedef' expected - %s", nexttok);
939 snmp_import(struct snmp_toolinfo *snmptoolctx)
944 tok = gettoken(snmptoolctx);
947 i = snmp_import_top(snmptoolctx, &tok);
954 * Read a .def file and import oid<->string mapping.
955 * Mappings are inserted into a global structure containing list for each OID
959 snmp_import_file(struct snmp_toolinfo *snmptoolctx, struct fname *file)
963 snmp_import_init(&(file->cut));
964 input_fopen(file->name);
965 if ((idx = snmp_import(snmptoolctx)) < 0)
966 warnx("Failed to read mappings from file %s", file->name);