Path: blob/main/contrib/bsnmp/gensnmptree/gensnmptree.c
39478 views
/*1* Copyright (c) 2001-20032* Fraunhofer Institute for Open Communication Systems (FhG Fokus).3* All rights reserved.4*5* Copyright (c) 2004-2006,20186* Hartmut Brandt.7* All rights reserved.8*9* Author: Harti Brandt <[email protected]>10*11* Redistribution and use in source and binary forms, with or without12* modification, are permitted provided that the following conditions13* are met:14* 1. Redistributions of source code must retain the above copyright15* notice, this list of conditions and the following disclaimer.16* 2. Redistributions in binary form must reproduce the above copyright17* notice, this list of conditions and the following disclaimer in the18* documentation and/or other materials provided with the distribution.19*20* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*32* $Begemot: gensnmptree.c 383 2006-05-30 07:40:49Z brandt_h $33*34* Generate OID table from table description.35*36* Syntax is:37* ---------38* file := top | top file39*40* top := tree | typedef | include41*42* tree := head elements ')'43*44* entry := head ':' index STRING elements ')'45*46* leaf := head type STRING ACCESS ')'47*48* column := head type ACCESS ')'49*50* type := BASETYPE | BASETYPE '|' subtype | enum | bits51*52* subtype := STRING53*54* enum := ENUM '(' value ')'55*56* bits := BITS '(' value ')'57*58* value := optminus INT STRING | optminus INT STRING value59*60* optminus := '-' | EMPTY61*62* head := '(' INT STRING63*64* elements := EMPTY | elements element65*66* element := tree | leaf | column67*68* index := type | index type69*70* typedef := 'typedef' STRING type71*72* include := 'include' filespec73*74* filespec := '"' STRING '"' | '<' STRING '>'75*/76#include <sys/types.h>77#include <sys/param.h>78#include <stdio.h>79#include <stdlib.h>80#include <stdarg.h>81#include <unistd.h>82#include <string.h>83#include <ctype.h>84#include <inttypes.h>85#include <errno.h>86#ifdef HAVE_ERR_H87#include <err.h>88#endif89#include <sys/queue.h>90#include "support.h"91#include "asn1.h"92#include "snmp.h"93#include "snmpagent.h"9495/*96* Constant prefix for all OIDs97*/98static const asn_subid_t prefix[] = { 1, 3, 6 };99#define PREFIX_LEN (sizeof(prefix) / sizeof(prefix[0]))100101u_int tree_size;102static const char *file_prefix = "";103104/* if true generate local include paths */105static int localincs = 0;106107/* if true print tokens */108static int debug;109110static const char usgtxt[] = "\111Generate SNMP tables.\n\112usage: gensnmptree [-dEeFfhlt] [-I directory] [-i infile] [-p prefix]\n\113[name]...\n\114options:\n\115-d debug mode\n\116-E extract the named or all enums and bits only\n\117-e extract the named oids or enums\n\118-F generate functions for -E into a .c file\n\119-f generate functions for -E into the header\n\120-h print this info\n\121-I directory add directory to include path\n\122-i ifile read from the named file instead of stdin\n\123-l generate local include directives\n\124-p prefix prepend prefix to file and variable names\n\125-t generate a .def file\n\126";127128/**129* Program operation.130*/131enum op {132/** generate the tree */133OP_GEN,134135/** extract OIDs */136OP_EXTRACT,137138/** print the parsed tree */139OP_TREE,140141/** extract enums */142OP_ENUMS,143};144145/**146* Which functions to create.147*/148enum gen_funcs {149/** none */150GEN_FUNCS_NONE,151152/** functions for header files */153GEN_FUNCS_H,154155/** functions for C files */156GEN_FUNCS_C,157};158159/*160* A node in the OID tree161*/162enum ntype {163NODE_LEAF = 1,164NODE_TREE,165NODE_ENTRY,166NODE_COLUMN167};168169enum {170FL_GET = 0x01,171FL_SET = 0x02,172};173174struct node;175TAILQ_HEAD(node_list, node);176177struct node {178enum ntype type;179asn_subid_t id; /* last element of OID */180char *name; /* name of node */181TAILQ_ENTRY(node) link;182u_int lno; /* starting line number */183u_int flags; /* allowed operations */184185union {186struct tree {187struct node_list subs;188} tree;189190struct entry {191uint32_t index; /* index for table entry */192char *func; /* function for tables */193struct node_list subs;194char *subtypes[SNMP_INDEXES_MAX];195} entry;196197struct leaf {198enum snmp_syntax syntax; /* syntax for this leaf */199char *func; /* function name */200char *subtype; /* subtype */201} leaf;202203struct column {204enum snmp_syntax syntax; /* syntax for this column */205char *subtype; /* subtype */206} column;207} u;208};209210struct func {211const char *name;212LIST_ENTRY(func) link;213};214215static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);216217struct enums {218const char *name;219long value;220TAILQ_ENTRY(enums) link;221};222223struct type {224const char *name;225const char *from_fname;226u_int from_lno;227u_int syntax;228int is_enum;229int is_bits;230TAILQ_HEAD(, enums) enums;231LIST_ENTRY(type) link;232};233234static LIST_HEAD(, type) types = LIST_HEAD_INITIALIZER(types);235236static void report(const char *, ...) __dead2 __printflike(1, 2);237static void report_node(const struct node *, const char *, ...)238__dead2 __printflike(2, 3);239240/************************************************************241*242* Allocate memory and panic just in the case...243*/244static void *245xalloc(size_t size)246{247void *ptr;248249if ((ptr = calloc(1, size)) == NULL)250err(1, "allocing %zu bytes", size);251252return (ptr);253}254255static char *256savestr(const char *s)257{258259if (s == NULL)260return (NULL);261return (strcpy(xalloc(strlen(s) + 1), s));262}263264/************************************************************265*266* Input stack267*/268struct input {269FILE *fp;270u_int lno;271char *fname;272char *path;273LIST_ENTRY(input) link;274};275static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs);276static struct input *input = NULL;277278#define MAX_PATHS 100279static u_int npaths = 2;280static u_int stdpaths = 2;281static const char *paths[MAX_PATHS + 1] = {282"/usr/share/snmp/defs",283"/usr/local/share/snmp/defs",284NULL285};286287static int pbchar = -1;288289static void290path_new(const char *path)291{292if (npaths >= MAX_PATHS)293report("too many -I directives");294memmove(&paths[npaths - stdpaths + 1], &paths[npaths - stdpaths],295sizeof(path[0]) * stdpaths);296paths[npaths - stdpaths] = savestr(path);297npaths++;298}299300static void301input_new(FILE *fp, const char *path, const char *fname)302{303struct input *ip;304305ip = xalloc(sizeof(*ip));306ip->fp = fp;307ip->lno = 1;308ip->fname = savestr(fname);309ip->path = savestr(path);310LIST_INSERT_HEAD(&inputs, ip, link);311312input = ip;313}314315static void316input_close(void)317{318319if (input == NULL)320return;321fclose(input->fp);322free(input->fname);323free(input->path);324LIST_REMOVE(input, link);325free(input);326327input = LIST_FIRST(&inputs);328}329330static FILE *331tryopen(const char *path, const char *fname)332{333char *fn;334FILE *fp;335336if (path == NULL)337fn = savestr(fname);338else {339fn = xalloc(strlen(path) + strlen(fname) + 2);340sprintf(fn, "%s/%s", path, fname);341}342fp = fopen(fn, "r");343free(fn);344return (fp);345}346347static void348input_fopen(const char *fname, int loc)349{350FILE *fp;351char *path;352u_int p;353354if (fname[0] == '/') {355if ((fp = tryopen(NULL, fname)) != NULL) {356input_new(fp, NULL, fname);357return;358}359360} else {361if (loc) {362if (input == NULL)363path = NULL;364else365path = input->path;366367if ((fp = tryopen(path, fname)) != NULL) {368input_new(fp, NULL, fname);369return;370}371}372373for (p = 0; paths[p] != NULL; p++)374if ((fp = tryopen(paths[p], fname)) != NULL) {375input_new(fp, paths[p], fname);376return;377}378}379report("cannot open '%s'", fname);380}381382static int383tgetc(void)384{385int c;386387if (pbchar != -1) {388c = pbchar;389pbchar = -1;390return (c);391}392393for (;;) {394if (input == NULL)395return (EOF);396397if ((c = getc(input->fp)) != EOF)398return (c);399400input_close();401}402}403404static void405tungetc(int c)406{407408if (pbchar != -1)409abort();410pbchar = c;411}412413/************************************************************414*415* Parsing input416*/417enum tok {418TOK_EOF = 0200, /* end-of-file seen */419TOK_NUM, /* number */420TOK_STR, /* string */421TOK_ACCESS, /* access operator */422TOK_TYPE, /* type operator */423TOK_ENUM, /* enum token (kind of a type) */424TOK_TYPEDEF, /* typedef directive */425TOK_DEFTYPE, /* defined type */426TOK_INCLUDE, /* include directive */427TOK_FILENAME, /* filename ("foo.bar" or <foo.bar>) */428TOK_BITS, /* bits token (kind of a type) */429};430431static const struct {432const char *str;433enum tok tok;434u_int val;435} keywords[] = {436{ "GET", TOK_ACCESS, FL_GET },437{ "SET", TOK_ACCESS, FL_SET },438{ "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },439{ "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },440{ "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },441{ "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },442{ "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },443{ "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },444{ "OID", TOK_TYPE, SNMP_SYNTAX_OID },445{ "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },446{ "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },447{ "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },448{ "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },449{ "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER },450{ "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING },451{ "typedef", TOK_TYPEDEF, 0 },452{ "include", TOK_INCLUDE, 0 },453{ NULL, 0, 0 }454};455456/* arbitrary upper limit on node names and function names */457#define MAXSTR 1000458static char str[MAXSTR];459static u_long val; /* integer values */460static int saved_token = -1;461462/*463* Report an error and exit.464*/465static void466report(const char *fmt, ...)467{468va_list ap;469int c;470471va_start(ap, fmt);472fprintf(stderr, "line %u: ", input->lno);473vfprintf(stderr, fmt, ap);474fprintf(stderr, "\n");475fprintf(stderr, "context: \"");476while ((c = tgetc()) != EOF && c != '\n')477fprintf(stderr, "%c", c);478fprintf(stderr, "\n");479va_end(ap);480exit(1);481}482static void483report_node(const struct node *np, const char *fmt, ...)484{485va_list ap;486487va_start(ap, fmt);488fprintf(stderr, "line %u, node %s: ", np->lno, np->name);489vfprintf(stderr, fmt, ap);490fprintf(stderr, "\n");491va_end(ap);492exit(1);493}494495/*496* Return a fresh copy of the string constituting the current token.497*/498static char *499savetok(void)500{501return (savestr(str));502}503504/*505* Get the next token from input.506*/507static int508gettoken_internal(void)509{510int c;511struct type *t;512513if (saved_token != -1) {514c = saved_token;515saved_token = -1;516return (c);517}518519again:520/*521* Skip any whitespace before the next token522*/523while ((c = tgetc()) != EOF) {524if (c == '\n')525input->lno++;526if (!isspace(c))527break;528}529if (c == EOF)530return (TOK_EOF);531if (!isascii(c))532report("unexpected character %#2x", (u_int)c);533534/*535* Skip comments536*/537if (c == '#') {538while ((c = tgetc()) != EOF) {539if (c == '\n') {540input->lno++;541goto again;542}543}544report("unexpected EOF in comment");545}546547/*548* Single character tokens549*/550if (strchr("():|-", c) != NULL)551return (c);552553if (c == '"' || c == '<') {554int end = c;555size_t n = 0;556557val = 1;558if (c == '<') {559val = 0;560end = '>';561}562563while ((c = tgetc()) != EOF) {564if (c == end)565break;566if (n == sizeof(str) - 1) {567str[n++] = '\0';568report("filename too long '%s...'", str);569}570str[n++] = c;571}572str[n++] = '\0';573return (TOK_FILENAME);574}575576/*577* Sort out numbers578*/579if (isdigit(c)) {580size_t n = 0;581str[n++] = c;582while ((c = tgetc()) != EOF) {583if (!isdigit(c)) {584tungetc(c);585break;586}587if (n == sizeof(str) - 1) {588str[n++] = '\0';589report("number too long '%s...'", str);590}591str[n++] = c;592}593str[n++] = '\0';594sscanf(str, "%lu", &val);595return (TOK_NUM);596}597598/*599* So that has to be a string.600*/601if (isalpha(c) || c == '_') {602size_t n = 0;603str[n++] = c;604while ((c = tgetc()) != EOF) {605if (!isalnum(c) && c != '_' && c != '-') {606tungetc(c);607break;608}609if (n == sizeof(str) - 1) {610str[n++] = '\0';611report("string too long '%s...'", str);612}613str[n++] = c;614}615str[n++] = '\0';616617/*618* Keywords619*/620for (c = 0; keywords[c].str != NULL; c++)621if (strcmp(keywords[c].str, str) == 0) {622val = keywords[c].val;623return (keywords[c].tok);624}625626LIST_FOREACH(t, &types, link) {627if (strcmp(t->name, str) == 0) {628val = t->syntax;629return (TOK_DEFTYPE);630}631}632return (TOK_STR);633}634if (isprint(c))635errx(1, "%u: unexpected character '%c'", input->lno, c);636else637errx(1, "%u: unexpected character 0x%02x", input->lno,638(u_int)c);639}640static int641gettoken(void)642{643int tok = gettoken_internal();644645if (debug) {646switch (tok) {647648case TOK_EOF:649fprintf(stderr, "EOF ");650break;651652case TOK_NUM:653fprintf(stderr, "NUM(%lu) ", val);654break;655656case TOK_STR:657fprintf(stderr, "STR(%s) ", str);658break;659660case TOK_ACCESS:661fprintf(stderr, "ACCESS(%lu) ", val);662break;663664case TOK_TYPE:665fprintf(stderr, "TYPE(%lu) ", val);666break;667668case TOK_ENUM:669fprintf(stderr, "ENUM ");670break;671672case TOK_BITS:673fprintf(stderr, "BITS ");674break;675676case TOK_TYPEDEF:677fprintf(stderr, "TYPEDEF ");678break;679680case TOK_DEFTYPE:681fprintf(stderr, "DEFTYPE(%s,%lu) ", str, val);682break;683684case TOK_INCLUDE:685fprintf(stderr, "INCLUDE ");686break;687688case TOK_FILENAME:689fprintf(stderr, "FILENAME ");690break;691692default:693if (tok < TOK_EOF) {694if (isprint(tok))695fprintf(stderr, "'%c' ", tok);696else if (tok == '\n')697fprintf(stderr, "\n");698else699fprintf(stderr, "%02x ", tok);700} else701abort();702break;703}704}705return (tok);706}707708/**709* Pushback a token710*/711static void712pushback(enum tok tok)713{714715if (saved_token != -1)716abort();717saved_token = tok;718}719720/*721* Create a new type722*/723static struct type *724make_type(const char *s)725{726struct type *t;727728t = xalloc(sizeof(*t));729t->name = savestr(s);730t->is_enum = 0;731t->syntax = SNMP_SYNTAX_NULL;732t->from_fname = savestr(input->fname);733t->from_lno = input->lno;734TAILQ_INIT(&t->enums);735LIST_INSERT_HEAD(&types, t, link);736737return (t);738}739740/*741* Parse a type. We've seen the ENUM or type keyword already. Leave next742* token.743*/744static u_int745parse_type(enum tok *tok, struct type *t, const char *vname, char **subtype)746{747u_int syntax;748struct enums *e;749750syntax = val;751if (subtype != NULL)752*subtype = NULL;753754if (*tok == TOK_ENUM || *tok == TOK_BITS) {755if (t == NULL && vname != NULL) {756t = make_type(vname);757t->is_enum = (*tok == TOK_ENUM);758t->is_bits = (*tok == TOK_BITS);759t->syntax = syntax;760}761if (gettoken() != '(')762report("'(' expected after ENUM");763764if ((*tok = gettoken()) == TOK_EOF)765report("unexpected EOF in ENUM");766do {767e = NULL;768if (t != NULL) {769e = xalloc(sizeof(*e));770}771if (*tok == '-') {772if ((*tok = gettoken()) == TOK_EOF)773report("unexpected EOF in ENUM");774e->value = -(long)val;775} else776e->value = val;777778if (*tok != TOK_NUM)779report("need value for ENUM/BITS");780if (gettoken() != TOK_STR)781report("need string in ENUM/BITS");782e->name = savetok();783TAILQ_INSERT_TAIL(&t->enums, e, link);784if ((*tok = gettoken()) == TOK_EOF)785report("unexpected EOF in ENUM/BITS");786} while (*tok != ')');787*tok = gettoken();788789} else if (*tok == TOK_DEFTYPE) {790*tok = gettoken();791792} else {793if ((*tok = gettoken()) == '|') {794if (gettoken() != TOK_STR)795report("subtype expected after '|'");796if (subtype != NULL)797*subtype = savetok();798*tok = gettoken();799}800}801802return (syntax);803}804805/*806* Parse the next node (complete with all subnodes)807*/808static struct node *809parse(enum tok tok)810{811struct node *node;812struct node *sub;813u_int index_count;814815node = xalloc(sizeof(struct node));816node->lno = input->lno;817node->flags = 0;818819if (tok != '(')820report("'(' expected at begin of node");821if (gettoken() != TOK_NUM)822report("node id expected after opening '('");823if (val > ASN_MAXID)824report("subid too large '%lu'", val);825node->id = (asn_subid_t)val;826if (gettoken() != TOK_STR)827report("node name expected after '(' ID");828node->name = savetok();829830if ((tok = gettoken()) == TOK_TYPE || tok == TOK_DEFTYPE ||831tok == TOK_ENUM || tok == TOK_BITS) {832/* LEAF or COLUM */833char *subtype;834u_int syntax = parse_type(&tok, NULL, node->name, &subtype);835836if (tok == TOK_STR) {837/* LEAF */838node->type = NODE_LEAF;839node->u.leaf.func = savetok();840node->u.leaf.syntax = syntax;841node->u.leaf.subtype = subtype;842tok = gettoken();843} else {844/* COLUMN */845node->type = NODE_COLUMN;846node->u.column.syntax = syntax;847node->u.column.subtype = subtype;848}849850while (tok != ')') {851if (tok != TOK_ACCESS)852report("access keyword or ')' expected");853node->flags |= (u_int)val;854tok = gettoken();855}856857} else if (tok == ':') {858/* ENTRY */859node->type = NODE_ENTRY;860TAILQ_INIT(&node->u.entry.subs);861862index_count = 0;863node->u.entry.index = 0;864tok = gettoken();865while (tok == TOK_TYPE || tok == TOK_DEFTYPE ||866tok == TOK_ENUM || tok == TOK_BITS) {867char *subtype;868u_int syntax = parse_type(&tok, NULL, node->name,869&subtype);870if (index_count == SNMP_INDEXES_MAX)871report("too many table indexes");872node->u.entry.subtypes[index_count++] = subtype;873node->u.entry.index |=874syntax << (SNMP_INDEX_SHIFT * index_count);875}876node->u.entry.index |= index_count;877if (index_count == 0)878report("need at least one index");879if (tok != TOK_STR)880report("function name expected");881882node->u.entry.func = savetok();883884tok = gettoken();885886while (tok != ')') {887sub = parse(tok);888TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link);889tok = gettoken();890}891892} else {893/* subtree */894node->type = NODE_TREE;895TAILQ_INIT(&node->u.tree.subs);896897while (tok != ')') {898sub = parse(tok);899TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link);900tok = gettoken();901}902}903return (node);904}905906/*907* Parse a top level element. Return the tree if it was a tree, NULL908* otherwise.909*/910static struct node *911parse_top(enum tok tok)912{913struct type *t;914915if (tok == '(')916return (parse(tok));917918if (tok == TOK_TYPEDEF) {919if (gettoken() != TOK_STR)920report("type name expected after typedef");921922t = make_type(str);923924tok = gettoken();925t->is_enum = (tok == TOK_ENUM);926t->is_bits = (tok == TOK_BITS);927928t->syntax = parse_type(&tok, t, NULL, NULL);929pushback(tok);930931return (NULL);932}933934if (tok == TOK_INCLUDE) {935if (gettoken() != TOK_FILENAME)936report("filename expected in include directive");937938input_fopen(str, val);939return (NULL);940}941942report("'(' or 'typedef' expected");943}944945/*946* Generate the C-code table part for one node.947*/948static void949gen_node(FILE *fp, const struct node *np, struct asn_oid *oid, u_int idx,950const char *func)951{952u_int n;953struct node *sub;954u_int syntax;955956if (oid->len == ASN_MAXOIDLEN)957report_node(np, "OID too long");958oid->subs[oid->len++] = np->id;959960if (np->type == NODE_TREE) {961TAILQ_FOREACH(sub, &np->u.tree.subs, link)962gen_node(fp, sub, oid, 0, NULL);963oid->len--;964return;965}966if (np->type == NODE_ENTRY) {967TAILQ_FOREACH(sub, &np->u.entry.subs, link)968gen_node(fp, sub, oid, np->u.entry.index,969np->u.entry.func);970oid->len--;971return;972}973974/* leaf or column */975if ((np->flags & (FL_GET|FL_SET)) == 0) {976oid->len--;977return;978}979980fprintf(fp, " {{ %u, {", oid->len);981for (n = 0; n < oid->len; n++)982fprintf(fp, " %u,", oid->subs[n]);983fprintf(fp, " }}, \"%s\", ", np->name);984985if (np->type == NODE_COLUMN) {986syntax = np->u.column.syntax;987fprintf(fp, "SNMP_NODE_COLUMN, ");988} else {989syntax = np->u.leaf.syntax;990fprintf(fp, "SNMP_NODE_LEAF, ");991}992993switch (syntax) {994995case SNMP_SYNTAX_NULL:996fprintf(fp, "SNMP_SYNTAX_NULL, ");997break;998999case SNMP_SYNTAX_INTEGER:1000fprintf(fp, "SNMP_SYNTAX_INTEGER, ");1001break;10021003case SNMP_SYNTAX_OCTETSTRING:1004fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, ");1005break;10061007case SNMP_SYNTAX_IPADDRESS:1008fprintf(fp, "SNMP_SYNTAX_IPADDRESS, ");1009break;10101011case SNMP_SYNTAX_OID:1012fprintf(fp, "SNMP_SYNTAX_OID, ");1013break;10141015case SNMP_SYNTAX_TIMETICKS:1016fprintf(fp, "SNMP_SYNTAX_TIMETICKS, ");1017break;10181019case SNMP_SYNTAX_COUNTER:1020fprintf(fp, "SNMP_SYNTAX_COUNTER, ");1021break;10221023case SNMP_SYNTAX_GAUGE:1024fprintf(fp, "SNMP_SYNTAX_GAUGE, ");1025break;10261027case SNMP_SYNTAX_COUNTER64:1028fprintf(fp, "SNMP_SYNTAX_COUNTER64, ");1029break;10301031case SNMP_SYNTAX_NOSUCHOBJECT:1032case SNMP_SYNTAX_NOSUCHINSTANCE:1033case SNMP_SYNTAX_ENDOFMIBVIEW:1034abort();1035}10361037if (np->type == NODE_COLUMN)1038fprintf(fp, "%s, ", func);1039else1040fprintf(fp, "%s, ", np->u.leaf.func);10411042fprintf(fp, "0");1043if (np->flags & FL_SET)1044fprintf(fp, "|SNMP_NODE_CANSET");1045fprintf(fp, ", %#x, NULL, NULL },\n", idx);1046oid->len--;1047return;1048}10491050/*1051* Generate the header file with the function declarations.1052*/1053static void1054gen_header(FILE *fp, const struct node *np, u_int oidlen, const char *func)1055{1056char f[MAXSTR + 4];1057struct node *sub;1058struct func *ptr;10591060oidlen++;1061if (np->type == NODE_TREE) {1062TAILQ_FOREACH(sub, &np->u.tree.subs, link)1063gen_header(fp, sub, oidlen, NULL);1064return;1065}1066if (np->type == NODE_ENTRY) {1067TAILQ_FOREACH(sub, &np->u.entry.subs, link)1068gen_header(fp, sub, oidlen, np->u.entry.func);1069return;1070}10711072if((np->flags & (FL_GET|FL_SET)) == 0)1073return;10741075if (np->type == NODE_COLUMN) {1076if (func == NULL)1077errx(1, "column without function (%s) - probably "1078"outside of a table", np->name);1079sprintf(f, "%s", func);1080} else1081sprintf(f, "%s", np->u.leaf.func);10821083LIST_FOREACH(ptr, &funcs, link)1084if (strcmp(ptr->name, f) == 0)1085break;10861087if (ptr == NULL) {1088ptr = xalloc(sizeof(*ptr));1089ptr->name = savestr(f);1090LIST_INSERT_HEAD(&funcs, ptr, link);10911092fprintf(fp, "int %s(struct snmp_context *, "1093"struct snmp_value *, u_int, u_int, "1094"enum snmp_op);\n", f);1095}10961097fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id);1098}10991100/*1101* Generate the OID table.1102*/1103static void1104gen_table(FILE *fp, const struct node *node)1105{1106struct asn_oid oid;11071108fprintf(fp, "#include <sys/types.h>\n");1109fprintf(fp, "#include <stdio.h>\n");1110#ifdef HAVE_STDINT_H1111fprintf(fp, "#include <stdint.h>\n");1112#endif1113if (localincs) {1114fprintf(fp, "#include \"asn1.h\"\n");1115fprintf(fp, "#include \"snmp.h\"\n");1116fprintf(fp, "#include \"snmpagent.h\"\n");1117} else {1118fprintf(fp, "#include <bsnmp/asn1.h>\n");1119fprintf(fp, "#include <bsnmp/snmp.h>\n");1120fprintf(fp, "#include <bsnmp/snmpagent.h>\n");1121}1122fprintf(fp, "#include \"%stree.h\"\n", file_prefix);1123fprintf(fp, "\n");11241125fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);11261127oid.len = PREFIX_LEN;1128memcpy(oid.subs, prefix, sizeof(prefix));1129gen_node(fp, node, &oid, 0, NULL);11301131fprintf(fp, "};\n\n");1132}11331134static void1135print_syntax(u_int syntax)1136{1137u_int i;11381139for (i = 0; keywords[i].str != NULL; i++)1140if (keywords[i].tok == TOK_TYPE &&1141keywords[i].val == syntax) {1142printf(" %s", keywords[i].str);1143return;1144}1145abort();1146}11471148/*1149* Generate a tree definition file1150*/1151static void1152gen_tree(const struct node *np, int level)1153{1154const struct node *sp;1155u_int i;11561157printf("%*s(%u %s", 2 * level, "", np->id, np->name);11581159switch (np->type) {11601161case NODE_LEAF:1162print_syntax(np->u.leaf.syntax);1163if (np->u.leaf.subtype != NULL)1164printf(" | %s", np->u.leaf.subtype);1165printf(" %s%s%s)\n", np->u.leaf.func,1166(np->flags & FL_GET) ? " GET" : "",1167(np->flags & FL_SET) ? " SET" : "");1168break;11691170case NODE_TREE:1171if (TAILQ_EMPTY(&np->u.tree.subs)) {1172printf(")\n");1173} else {1174printf("\n");1175TAILQ_FOREACH(sp, &np->u.tree.subs, link)1176gen_tree(sp, level + 1);1177printf("%*s)\n", 2 * level, "");1178}1179break;11801181case NODE_ENTRY:1182printf(" :");11831184for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) {1185print_syntax(SNMP_INDEX(np->u.entry.index, i));1186if (np->u.entry.subtypes[i] != NULL)1187printf(" | %s", np->u.entry.subtypes[i]);1188}1189printf(" %s\n", np->u.entry.func);1190TAILQ_FOREACH(sp, &np->u.entry.subs, link)1191gen_tree(sp, level + 1);1192printf("%*s)\n", 2 * level, "");1193break;11941195case NODE_COLUMN:1196print_syntax(np->u.column.syntax);1197if (np->u.column.subtype != NULL)1198printf(" | %s", np->u.column.subtype);1199printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",1200(np->flags & FL_SET) ? " SET" : "");1201break;1202}1203}12041205static int1206extract(FILE *fp, const struct node *np, struct asn_oid *oid, const char *obj,1207const struct asn_oid *idx, const char *iname)1208{1209struct node *sub;1210u_long n;12111212if (oid->len == ASN_MAXOIDLEN)1213report_node(np, "OID too long");1214oid->subs[oid->len++] = np->id;12151216if (strcmp(obj, np->name) == 0) {1217if (oid->len + idx->len >= ASN_MAXOIDLEN)1218report_node(np, "OID too long");1219fprintf(fp, "#define OID_%s%s\t%u\n", np->name,1220iname ? iname : "", np->id);1221fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name,1222iname ? iname : "", oid->len + idx->len);1223fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name,1224iname ? iname : "", oid->len + idx->len);1225for (n = 0; n < oid->len; n++)1226fprintf(fp, " %u,", oid->subs[n]);1227for (n = 0; n < idx->len; n++)1228fprintf(fp, " %u,", idx->subs[n]);1229fprintf(fp, " } }\n");1230return (0);1231}12321233if (np->type == NODE_TREE) {1234TAILQ_FOREACH(sub, &np->u.tree.subs, link)1235if (!extract(fp, sub, oid, obj, idx, iname))1236return (0);1237} else if (np->type == NODE_ENTRY) {1238TAILQ_FOREACH(sub, &np->u.entry.subs, link)1239if (!extract(fp, sub, oid, obj, idx, iname))1240return (0);1241}1242oid->len--;1243return (1);1244}12451246static int1247gen_extract(FILE *fp, const struct node *root, char *object)1248{1249struct asn_oid oid;1250struct asn_oid idx;1251char *s, *e, *end, *iname;1252u_long ul;1253int ret;12541255/* look whether the object to extract has an index part */1256idx.len = 0;1257iname = NULL;1258s = strchr(object, '.');1259if (s != NULL) {1260iname = malloc(strlen(s) + 1);1261if (iname == NULL)1262err(1, "cannot allocated index");12631264strcpy(iname, s);1265for (e = iname; *e != '\0'; e++)1266if (*e == '.')1267*e = '_';12681269*s++ = '\0';1270while (s != NULL) {1271if (*s == '\0')1272errx(1, "bad index syntax");1273if ((e = strchr(s, '.')) != NULL)1274*e++ = '\0';12751276errno = 0;1277ul = strtoul(s, &end, 0);1278if (*end != '\0')1279errx(1, "bad index syntax '%s'", end);1280if (errno != 0)1281err(1, "bad index syntax");12821283if (idx.len == ASN_MAXOIDLEN)1284errx(1, "index oid too large");1285idx.subs[idx.len++] = ul;12861287s = e;1288}1289}12901291oid.len = PREFIX_LEN;1292memcpy(oid.subs, prefix, sizeof(prefix));1293ret = extract(fp, root, &oid, object, &idx, iname);1294if (iname != NULL)1295free(iname);12961297return (ret);1298}129913001301static void1302check_sub_order(const struct node *np, const struct node_list *subs)1303{1304int first;1305const struct node *sub;1306asn_subid_t maxid = 0;13071308/* ensure, that subids are ordered */1309first = 1;1310TAILQ_FOREACH(sub, subs, link) {1311if (!first && sub->id <= maxid)1312report_node(np, "subids not ordered at %s", sub->name);1313maxid = sub->id;1314first = 0;1315}1316}13171318/*1319* Do some sanity checks on the tree definition and do some computations.1320*/1321static void1322check_tree(struct node *np)1323{1324struct node *sub;13251326if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {1327if ((np->flags & (FL_GET|FL_SET)) != 0)1328tree_size++;1329return;1330}13311332if (np->type == NODE_ENTRY) {1333check_sub_order(np, &np->u.entry.subs);13341335/* ensure all subnodes are columns */1336TAILQ_FOREACH(sub, &np->u.entry.subs, link) {1337if (sub->type != NODE_COLUMN)1338report_node(np, "entry subnode '%s' is not "1339"a column", sub->name);1340check_tree(sub);1341}1342} else {1343check_sub_order(np, &np->u.tree.subs);13441345TAILQ_FOREACH(sub, &np->u.tree.subs, link)1346check_tree(sub);1347}1348}13491350static void1351merge_subs(struct node_list *s1, struct node_list *s2)1352{1353struct node *n1, *n2;13541355while (!TAILQ_EMPTY(s2)) {1356n2 = TAILQ_FIRST(s2);1357TAILQ_REMOVE(s2, n2, link);13581359TAILQ_FOREACH(n1, s1, link)1360if (n1->id >= n2->id)1361break;1362if (n1 == NULL)1363TAILQ_INSERT_TAIL(s1, n2, link);1364else if (n1->id > n2->id)1365TAILQ_INSERT_BEFORE(n1, n2, link);1366else {1367if (n1->type == NODE_TREE && n2->type == NODE_TREE) {1368if (strcmp(n1->name, n2->name) != 0)1369errx(1, "trees to merge must have "1370"same name '%s' '%s'", n1->name,1371n2->name);1372merge_subs(&n1->u.tree.subs, &n2->u.tree.subs);1373free(n2);1374} else if (n1->type == NODE_ENTRY &&1375n2->type == NODE_ENTRY) {1376if (strcmp(n1->name, n2->name) != 0)1377errx(1, "entries to merge must have "1378"same name '%s' '%s'", n1->name,1379n2->name);1380if (n1->u.entry.index != n2->u.entry.index)1381errx(1, "entries to merge must have "1382"same index '%s'", n1->name);1383if (strcmp(n1->u.entry.func,1384n2->u.entry.func) != 0)1385errx(1, "entries to merge must have "1386"same op '%s'", n1->name);1387merge_subs(&n1->u.entry.subs,1388&n2->u.entry.subs);1389free(n2);1390} else1391errx(1, "entities to merge must be both "1392"trees or both entries: %s, %s",1393n1->name, n2->name);1394}1395}1396}13971398static void1399merge(struct node **root, struct node *t)1400{14011402if (*root == NULL) {1403*root = t;1404return;1405}1406if (t == NULL)1407return;14081409/* both must be trees */1410if ((*root)->type != NODE_TREE)1411errx(1, "root is not a tree");1412if (t->type != NODE_TREE)1413errx(1, "can merge only with tree");1414if ((*root)->id != t->id)1415errx(1, "trees to merge must have same id");14161417merge_subs(&(*root)->u.tree.subs, &t->u.tree.subs);1418}14191420static void1421unminus(FILE *fp, const char *s)1422{14231424while (*s != '\0') {1425if (*s == '-')1426fprintf(fp, "_");1427else1428fprintf(fp, "%c", *s);1429s++;1430}1431}14321433/**1434* Generate helper functions for an enum.1435*1436* We always generate a switch statement for the isok function. The compiler1437* optimizes this into range checks if possible.1438*1439* \param fp file to write to1440* \param t type1441* \param ccode generate externally visible non-inline functions1442*/1443static void1444gen_enum_funcs(FILE *fp, const struct type *t, int ccode)1445{1446fprintf(fp, "\n");14471448if (!ccode)1449fprintf(fp, "static inline ");1450fprintf(fp, "int\n");1451fprintf(fp, "isok_%s(enum %s s)\n", t->name, t->name);1452fprintf(fp, "{\n");1453fprintf(fp, " switch (s) {\n");14541455const struct enums *e;1456TAILQ_FOREACH(e, &t->enums, link) {1457fprintf(fp, "\t case %s_", t->name);1458unminus(fp, e->name);1459fprintf(fp, ":\n");1460}14611462fprintf(fp, " return (1);\n");1463fprintf(fp, " }\n");1464fprintf(fp, " return (0);\n");1465fprintf(fp, "}\n\n");14661467if (!ccode)1468fprintf(fp, "static inline ");1469fprintf(fp, "const char *\n");1470fprintf(fp, "tostr_%s(enum %s s)\n", t->name, t->name);1471fprintf(fp, "{\n");1472fprintf(fp, " static const char *vals[] = { STRING_%s };\n", t->name);1473fprintf(fp, "\n");1474fprintf(fp, " if (isok_%s(s))\n", t->name);1475fprintf(fp, " return (vals[(int)s - STROFF_%s]);\n", t->name);1476fprintf(fp, " return (\"%s???\");\n", t->name);1477fprintf(fp, "}\n\n");14781479if (!ccode)1480fprintf(fp, "static inline ");1481fprintf(fp, "int\n");1482fprintf(fp, "fromstr_%s(const char *str, enum %s *s)\n",1483t->name, t->name);1484fprintf(fp, "{\n");1485fprintf(fp, " static const char *vals[] = { STRING_%s };\n", t->name);1486fprintf(fp, "\n");1487fprintf(fp, " for (size_t i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {\n");1488fprintf(fp, " if (vals[i] != NULL && strcmp(vals[i], str) == 0) {\n");1489fprintf(fp, " *s = i + STROFF_%s;\n", t->name);1490fprintf(fp, " return (1);\n");1491fprintf(fp, " }\n");1492fprintf(fp, " }\n");1493fprintf(fp, " return (0);\n");1494fprintf(fp, "}\n");1495}14961497/**1498* Generate a definition for the enum packed into a guard against multiple1499* definitions.1500*1501* \param fp file to write definition to1502* \param t type1503* \param dof generate functions too1504*/1505static void1506gen_enum(FILE *fp, const struct type *t, int dof)1507{1508const struct enums *e;1509long min = LONG_MAX;15101511fprintf(fp, "\n");1512fprintf(fp, "#ifndef %s_defined__\n", t->name);1513fprintf(fp, "#define %s_defined__\n", t->name);1514fprintf(fp, "/*\n");1515fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno);1516fprintf(fp, " */\n");1517fprintf(fp, "enum %s {\n", t->name);1518TAILQ_FOREACH(e, &t->enums, link) {1519fprintf(fp, "\t%s_", t->name);1520unminus(fp, e->name);1521fprintf(fp, " = %ld,\n", e->value);1522if (e->value < min)1523min = e->value;1524}1525fprintf(fp, "};\n");1526fprintf(fp, "#define STROFF_%s %ld\n", t->name, min);1527fprintf(fp, "#define STRING_%s \\\n", t->name);1528TAILQ_FOREACH(e, &t->enums, link) {1529fprintf(fp, "\t[%ld] = \"%s_", e->value - min, t->name);1530unminus(fp, e->name);1531fprintf(fp, "\",\\\n");1532}1533fprintf(fp, "\n");1534if (dof) {1535fprintf(fp, "#ifdef SNMPENUM_FUNCS\n");1536fprintf(fp, "\n");1537gen_enum_funcs(fp, t, 0);1538fprintf(fp, "\n");1539fprintf(fp, "#endif\n");1540fprintf(fp, "\n");1541}1542fprintf(fp, "#endif /* %s_defined__ */\n", t->name);1543}15441545/**1546* Generate helper functions for an enum. This generates code for a c file.1547*1548* \param fp file to write to1549* \param name enum name1550*/1551static int1552gen_enum_funcs_str(FILE *fp, const char *name)1553{1554const struct type *t;15551556LIST_FOREACH(t, &types, link)1557if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) {1558gen_enum_funcs(fp, t, 1);1559return (0);1560}15611562return (-1);1563}15641565/**1566* Generate helper functions for all enums.1567*1568* \param fp file to write to1569* \param ccode generate externally visible non-inline functions1570*/1571static void1572gen_all_enum_funcs(FILE *fp, int ccode)1573{1574const struct type *t;15751576LIST_FOREACH(t, &types, link)1577if (t->is_enum || t->is_bits)1578gen_enum_funcs(fp, t, ccode);1579}15801581static void1582gen_enums(FILE *fp, int dof)1583{1584const struct type *t;15851586LIST_FOREACH(t, &types, link)1587if (t->is_enum || t->is_bits)1588gen_enum(fp, t, dof);1589}15901591/**1592* Extract a given enum to the specified file and optionally generate static1593* inline helper functions for them.1594*1595* \param fp file to print on1596* \param name name of the enum1597* \param gen_funcs generate the functions too1598*1599* \return 0 if found, -1 otherwise1600*/1601static int1602extract_enum(FILE *fp, const char *name, int gen_funcs)1603{1604const struct type *t;16051606LIST_FOREACH(t, &types, link)1607if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) {1608gen_enum(fp, t, gen_funcs);1609return (0);1610}1611return (-1);1612}16131614/**1615* Extract all enums to the given file and optionally generate static inline1616* helper functions for them.1617*1618* \param fp file to print on1619* \param gen_funcs generate the functions too1620*/1621static void1622extract_all_enums(FILE *fp, int gen_funcs)1623{1624const struct type *t;16251626LIST_FOREACH(t, &types, link)1627if (t->is_enum || t->is_bits)1628gen_enum(fp, t, gen_funcs);1629}16301631/**1632* Extract enums and optionally generate some helper functions for them.1633*1634* \param argc number of arguments1635* \param argv arguments (enum names)1636* \param gen_funcs which functions to generate1637*/1638static void1639make_enums(int argc, char *argv[], enum gen_funcs gen_funcs)1640{1641if (gen_funcs == GEN_FUNCS_C) {1642if (argc == 0)1643gen_all_enum_funcs(stdout, 1);1644else {1645for (int i = 0; i < argc; i++)1646if (gen_enum_funcs_str(stdout, argv[i]))1647errx(1, "enum not found: %s", argv[i]);1648}1649} else {1650if (argc == 0)1651extract_all_enums(stdout, gen_funcs == GEN_FUNCS_H);1652else {1653for (int i = 0; i < argc; i++)1654if (extract_enum(stdout, argv[i],1655gen_funcs == GEN_FUNCS_H))1656errx(1, "enum not found: %s", argv[i]);1657}1658}1659}16601661/**1662* Produce the operation tables for the daemon or a module.1663*1664* \param root tree root1665* \param gen_funcs generate enum funcs1666*/1667static void1668make_table(const struct node *root, int gen_funcs)1669{1670FILE *fp;16711672char fname[MAXPATHLEN + 1];1673sprintf(fname, "%stree.h", file_prefix);1674if ((fp = fopen(fname, "w")) == NULL)1675err(1, "%s: ", fname);1676gen_header(fp, root, PREFIX_LEN, NULL);16771678fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n");1679gen_enums(fp, gen_funcs);1680fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n");16811682fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);1683fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);16841685fclose(fp);16861687sprintf(fname, "%stree.c", file_prefix);1688if ((fp = fopen(fname, "w")) == NULL)1689err(1, "%s: ", fname);1690gen_table(fp, root);1691fclose(fp);1692}16931694int1695main(int argc, char *argv[])1696{1697enum op op = OP_GEN;1698enum gen_funcs gen_funcs = GEN_FUNCS_NONE;16991700char *infile = NULL;17011702int opt;1703while ((opt = getopt(argc, argv, "dEeFfhI:i:lp:t")) != EOF)1704switch (opt) {17051706case 'd':1707debug = 1;1708break;17091710case 'E':1711if (op != OP_GEN && op != OP_ENUMS)1712errx(1, "-E conflicts with earlier options");1713op = OP_ENUMS;1714break;17151716case 'e':1717if (op != OP_GEN && op != OP_EXTRACT)1718errx(1, "-e conflicts with earlier options");1719op = OP_EXTRACT;1720break;17211722case 'F':1723if (gen_funcs != GEN_FUNCS_NONE &&1724gen_funcs != GEN_FUNCS_C)1725errx(1, "-F conflicts with -f");1726gen_funcs = GEN_FUNCS_C;1727break;17281729case 'f':1730if (gen_funcs != GEN_FUNCS_NONE &&1731gen_funcs != GEN_FUNCS_H)1732errx(1, "-f conflicts with -F");1733gen_funcs = GEN_FUNCS_H;1734break;17351736case 'h':1737fprintf(stderr, "%s", usgtxt);1738exit(0);17391740case 'I':1741path_new(optarg);1742break;17431744case 'i':1745infile = optarg;1746break;17471748case 'l':1749localincs = 1;1750break;17511752case 'p':1753file_prefix = optarg;1754if (strlen(file_prefix) + strlen("tree.c") >1755MAXPATHLEN)1756errx(1, "prefix too long");1757break;17581759case 't':1760if (op != OP_GEN && op != OP_TREE)1761errx(1, "-t conflicts with earlier options");1762op = OP_TREE;1763break;1764}17651766argc -= optind;1767argv += optind;17681769/* open input */1770if (infile == NULL) {1771input_new(stdin, NULL, "<stdin>");1772} else {1773FILE *fp;1774if ((fp = fopen(infile, "r")) == NULL)1775err(1, "%s", infile);1776input_new(fp, NULL, infile);1777}17781779/* parse and check input */1780struct node *root = parse_top(gettoken());17811782int tok;1783while ((tok = gettoken()) != TOK_EOF)1784merge(&root, parse_top(tok));17851786if (root)1787check_tree(root);17881789/* do what the user has requested */1790switch (op) {17911792case OP_EXTRACT:1793if (argc == 0)1794errx(1, "-e requires arguments");17951796for (int i = 0; i < argc; i++)1797if (gen_extract(stdout, root, argv[i]))1798errx(1, "object not found: %s", argv[i]);1799return (0);18001801case OP_ENUMS:1802make_enums(argc, argv, gen_funcs);1803return (0);18041805case OP_TREE:1806if (argc != 0)1807errx(1, "-t allows no arguments");1808gen_tree(root, 0);1809return (0);18101811case OP_GEN:1812if (argc != 0)1813errx(1, "tree generation allows no arguments");1814make_table(root, gen_funcs == GEN_FUNCS_H);1815return (0);1816}1817}181818191820