blob: 891efa8c23e680419a409f878e4e8021725d5066 [file] [log] [blame]
#!/usr/bin/perl
#!/usr/bin/perl -w
#
# Description:
#
# This program, given an OID reference as an argument, creates some
# template mib module files to be used with the net-snmp agent. It is
# far from perfect and will not generate working modules, but it
# significantly shortens development time by outlining the basic
# structure.
#
# Its up to you to verify what it does and change the default values
# it returns.
#
# SNMP
my $havesnmp = eval {require SNMP;};
if (!$havesnmp) {
print "
ERROR: You don't have the SNMP perl module installed. Please obtain
this by getting the latest source release of the net-snmp toolkit from
http://www.net-snmp.org/download/ . Once you download the source and
unpack it, the perl module is contained in the perl/SNMP directory.
See the INSTALL file there for instructions.
";
exit;
}
if ($havesnmp) {
eval { import SNMP; }
}
use FileHandle;
#use strict 'vars';
$SNMP::save_descriptions=1;
$SNMP::use_long_names=1;
$SNMP::use_enums=1;
SNMP::initMib();
$configfile="mib2c.conf";
$debug=0;
$quiet=0;
$nostats = 0;
$noindent = 0;
sub usage {
print "$0 [-h] [-c configfile] [-f prefix] mibNode\n\n";
print " -h\t\tThis message.\n\n";
print " -c configfile\tSpecifies the configuration file to use\n\t\tthat dictates what the output of mib2c will look like.\n\n";
print " -f prefix\tSpecifies the output prefix to use. All code\n\t\twill be put into prefix.c and prefix.h\n\n";
print " mibNode\tThe name of the top level mib node you want to\n\t\tgenerate code for. By default, the code will be stored in\n\t\tmibNode.c and mibNode.h (use the -f flag to change this)\n\n";
print " -d\t\tdebugging output (dont do it. trust me.)\n\n";
print " -s\t\tDon't display statistics at the end\n\n";
print " -S VAR=VAL\tSet $VAR variable to $VAL\n";
print " -i Don't run indent on the resulting code\n";
1;
}
while($#ARGV >= 0) {
$_ = shift;
$configfile = shift if (/^-c/);
$debug = 1 if (/^-d/);
if (/-S/) {
my $expr = shift;
my ($var, $val) = ($expr =~ /([^=]*)=(.*)/);
die "no variable specified for -S flag." if (!$var);
$vars{$var} = $val;
}
$quiet = 1 if (/^-q/);
$nostats = 1 if (/^-s/);
$noindent = 1 if (/^-i/);
usage && exit(1) if (/^-h/);
$outputName = shift if (/^-f/);
$oid = $_ if (/^[^-]/);
}
#
# internal conversion tables
#
%accessToIsWritable = qw(ReadOnly 0 ReadWrite 1
WriteOnly 1 Create 1);
%perltoctypes = qw(OCTETSTR ASN_OCTET_STR
INTEGER ASN_INTEGER
INTEGER32 ASN_INTEGER
UNSIGNED32 ASN_UNSIGNED
OBJECTID ASN_OBJECT_ID
COUNTER64 ASN_COUNTER64
COUNTER ASN_COUNTER
NETADDR ASN_COUNTER
UINTEGER ASN_UINTEGER
IPADDR ASN_IPADDRESS
BITS ASN_OCTET_STR
TICKS ASN_TIMETICKS
GAUGE ASN_GAUGE
OPAQUE ASN_OPAQUE);
%perltodecl = ("OCTETSTR", "char",
"INTEGER", "long",
"INTEGER32", "long",
"UNSIGNED32", "u_long",
"UINTEGER", "u_long",
"OBJECTID", "oid",
"COUNTER64", "counter64",
"COUNTER", "u_long",
"IPADDR", "u_long",
"BITS", "char",
"TICKS", "u_long",
"GAUGE", "u_long",
"OPAQUE", "char");
my $mibnode = $SNMP::MIB{$oid};
die "you didn't give me a valid OID to start with" if (!$mibnode);
# setup
$outputName = $mibnode->{'label'} if (!defined($outputName));
$vars{'name'} = $outputName;
$vars{'oid'} = $oid;
# loop through mib nodes, remembering stuff.
setup_data($mibnode);
# process .conf file
$fh = new IO::File;
if (-f "$configfile") {
$fh->open("$configfile");
} elsif(-f "/usr/local/share/snmp/$configfile") {
$fh->open("/usr/local/share/snmp/$configfile");
} else {
print STDERR "Can't find a configuration file called $configfile\n";
print STDERR "I looked in . and /usr/local/share/snmp\n";
exit;
}
process();
$fh->close;
if (!$noindent) {
foreach $i (keys(%written)) {
next if ($i eq "-");
print STDERR "running indent on $i\n" if (!$quiet);
system("indent -orig -nbc -bap -nut -nfca -T netsnmp_mib_handler -T netsnmp_handler_registration -T netsnmp_delegated_cache -T netsnmp_mib_handler_methods -T netsnmp_old_api_info -T netsnmp_old_api_cache -T netsnmp_set_info -T netsnmp_request_info -T netsnmp_set_info -T netsnmp_tree_cache -T netsnmp_agent_request_info -T netsnmp_cachemap -T netsnmp_agent_session -T netsnmp_array_group_item -T netsnmp_array_group -T netsnmp_table_array_callbacks -T netsnmp_table_row -T netsnmp_table_data -T netsnmp_table_data_set_storage -T netsnmp_table_data_set -T netsnmp_column_info -T netsnmp_table_registration_info -T netsnmp_table_request_info -T netsnmp_iterator_info -T netsnmp_data_list -T netsnmp_oid_array_header -T netsnmp_oid_array_header_wrapper -T netsnmp_oid_stash_node -T netsnmp_pdu -T netsnmp_request_list -T netsnmp_callback_pass -T netsnmp_callback_info -T netsnmp_transport -T netsnmp_transport_list -T netsnmp_tdomain $i");
}
}
sub tocommas {
my $oid = $_[0];
$oid =~ s/\./,/g;
$oid =~ s/^\s*,//;
return $oid;
}
sub oidlength {
return scalar ($_[0] =~ /\./);
}
# replaces $VAR type expressions and $VAR.subcomponent expressions
# with data from the mib tree and loop variables.
# possible uses:
#
# $var -- as defined by loops, etc.
# ${var}otherstuff -- appending text to variable contents
# $var.uc -- all upper case version of $var
#
# Mib components, $var must first expand to a mib node name:
#
# $var.objectID -- dotted full OID
# $var.commaoid -- comma separated OID for array init
# $var.subid -- last number component of oid
# $var.oidlength -- length of the oid
# $var.type -- node's ASN_XXX type
# $var.settable -- 1 if it's writable, 0 if not
# $var.noaccess -- 1 if not-accessible, 0 if not
# $var.access -- node's access type
# $var.status -- node's status
# $var.syntax -- node's syntax
# $var.decl -- C data type
sub process_vars {
my $it = shift;
# mib substitutions ($var.type -> $mibnode->{'type'})
$it =~ s/\$(\w+)\.(uc)/uc($vars{$1})/eg; # make something uppercase
$it =~ s/\$(\w+)\.(commaoid)/tocommas($SNMP::MIB{$vars{$1}}{objectID})/eg;
$it =~ s/\$(\w+)\.(oidlength)/oidlength($SNMP::MIB{$vars{$1}}{objectID})/eg;
$it =~ s/\$(\w+)\.(perltype)/$SNMP::MIB{$vars{$1}}{type}/g;
$it =~ s/\$(\w+)\.(type)/$perltoctypes{$SNMP::MIB{$vars{$1}}{$2}}/g;
$it =~ s/\$(\w+)\.(subid)/$SNMP::MIB{$vars{$1}}{subID}/g;
$it =~ s/\$(\w+)\.(settable)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(ReadWrite|Create|Writeonly)\/)?1:0)/eg;
$it =~ s/\$(\w+)\.(noaccess)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(NoAccess)\/)?1:0)/eg;
$it =~ s/\$(\w+)\.(objectID|label|subID|access|status|syntax)/$SNMP::MIB{$vars{$1}}{$2}/g;
$it =~ s/\$(\w+)\.(decl)/$perltodecl{$SNMP::MIB{$vars{$1}}{type}}/g;
# normal variable substitions
$it =~ s/\$\{(\w+)\}/$vars{$1}/g;
$it =~ s/\$(\w+)/$vars{$1}/g;
return $it;
}
# process various types of statements
#
# which include:
# @open FILE@
# writes generated output to FILE
# @foreach $VAR scalar@
# repeat iterate over code until @end@ setting $VAR to all known scalars
# @foreach $VAR table@
# repeat iterate over code until @end@ setting $VAR to all known tables
# @foreach $VAR column@
# repeat iterate over code until @end@ setting $VAR to all known
# columns within a given table. Obviously this must be called
# within a foreach-table clause.
# @foreach $VAR index@
# repeat iterate over code until @end@ setting $VAR to all known
# indexes within a given table. Obviously this must be called
# within a foreach-table clause.
# @foreach $LABEL, $VALUE enum@
# repeat iterate over code until @end@ setting $LABEL and $VALUE
# to the label and values from the enum list.
# @foreach $RANGE_START, $RANGE_END range NODE@
# repeat iterate over code until @end@ setting $RANGE_START and $RANGE_END
# to the legal accepted range set for a given mib NODE.
# @eval $VAR = expression@
# evaluates expression and assigns the results to $VAR
# @perleval STUFF@
# evaluates STUFF directly in perl. Note that all mib2c variables
# interpereted within .conf files are in $vars{NAME}.
# @skip@
# skips everything till the appropriately matched @end@
# @if expression@
# evaluates expression, and if expression is true processes
# contained part until appropriate @end@ is reached.
sub skippart {
my $endcount = 1;
while(<$fh>) {
if (/\@end\@/) {
return if ($endcount == 1);
$endcount--;
}
if (/\@else\@/) {
return if ($endcount == 1);
}
if (/\@(foreach|if)/) {
$endcount++;
}
}
}
sub process {
while(<$fh>) {
if (/^\#\#/) {
# noop, it's a comment
} elsif (/\@open\s+([^\@]+)\@/) {
my $spec = process_vars($1);
$out->close() if ($out);
$out = new IO::File;
$out->open(">$spec") || die "failed to open $spec";
print STDERR "writing to $spec\n" if (!$quiet);
$written{$spec} = '1';
} elsif (/\@end\@/) {
return;
} elsif (/\@if\s+([^@]*)\@/) {
if (eval(process_vars($1))) {
process();
} else {
skippart();
}
} elsif (/\@eval\s+\$(\w+)\s*=\s*([^\@]*)/) {
my ($v, $e) = ($1, $2);
my $e = process_vars($e);
$vars{$v} = eval($e);
} elsif (/\@perleval\s*(.*)\@/) {
eval($1);
} elsif (/\@skip\@/) {
skippart();
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+scalars*\s*\@/) {
my $var = $1;
my $startpos = $fh->tell();
my $scalar;
my @thekeys = keys(%scalars);
if ($#thekeys == -1) {
skippart();
} else {
foreach $scalar (@thekeys) {
$fh->seek($startpos, 0); # go to top of section.
my $oldvar = $vars{$var};
$vars{$var} = $scalar;
my $oldscalar = $currentscalar;
$currentscalar = $scalar;
process();
$vars{$var} = $oldvar;
$currentscalar = $oldscalar;
}
}
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+tables*\s*\@/) {
my $var = $1;
my $startpos = $fh->tell();
my $table;
my @thekeys = keys(%tables);
if ($#thekeys == -1) {
skippart();
} else {
foreach $table (@thekeys) {
$fh->seek($startpos, 0); # go to top of section.
my $oldvar = $vars{$var};
$vars{$var} = $table;
my $oldtable = $currenttable;
$currenttable = $table;
process();
$vars{$var} = $oldvar;
$currenttable = $oldtable;
}
}
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+(column|index)\s*\@/) {
my ($var, $type) = ($1, $2);
my $startpos = $fh->tell();
my $column;
if ($#{$tables{$currenttable}{$type}} == -1) {
skippart();
} else {
foreach $column (@{$tables{$currenttable}{$type}}) {
# print "looping on $var for $type -> $column\n";
$fh->seek($startpos, 0); # go to top of section.
my $oldvar = $vars{$var};
$vars{$var} = $column;
my $oldcolumn = $currentcolumn;
$currentcolumn = $column;
process();
$vars{$var} = $oldvar;
$currentcolumn = $oldcolumn;
}
}
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+\$([^\@]+)\s+range\s+([^\@]+)\@/) {
my ($svar, $evar, $node) = ($1, $2, $3);
my $startpos = $fh->tell();
my $range;
$node = $currentcolumn if (!$node);
my $mibn = $SNMP::MIB{process_vars($node)};
die "no such mib node: $node" if (!$mibn);
my @ranges = @{$mibn->{'ranges'}};
if ($#ranges > -1) {
foreach $range (@ranges) {
# print "looping on $var for $type -> $column\n";
$fh->seek($startpos, 0); # go to top of section.
my $oldvars = $vars{$svar};
my $oldvare = $vars{$evar};
$vars{$svar} = $range->{'low'};
$vars{$evar} = $range->{'high'};
process();
$vars{$svar} = $oldvars;
$vars{$evar} = $oldvare;
}
} else {
skippart();
}
} elsif (/\@\s*foreach\s+\$([^\@]+)\s*,*\s+\$([^\@]+)\s+(enums*)\s*\@/) {
my ($varvar, $varval, $type) = ($1, $2, $3);
my $startpos = $fh->tell();
my $enum;
my @keys = sort { $SNMP::MIB{$currentcolumn}{'enums'}{$a} <=>
$SNMP::MIB{$currentcolumn}{'enums'}{$b} } (keys(%{$SNMP::MIB{$currentcolumn}{'enums'}}));
if ($#keys > -1) {
foreach $enum (@keys) {
$fh->seek($startpos, 0); # go to top of section.
my $oldvarvar = $vars{$varvar};
my $oldvarval = $vars{$varval};
$vars{$varvar} = $enum;
$vars{$varval} = $SNMP::MIB{$currentcolumn}{'enums'}{$enum};
process();
$vars{$varvar} = $oldvarvar;
$vars{$varval} = $oldvarval;
}
} else {
skippart();
}
} else {
die "no output file specified" if (!$out);
print $out process_vars($_);
}
}
}
sub setup_data {
my $mib = shift;
if ($mib->{label} =~ /Table$/) {
my $tablename = $mib->{label};
my $entry = $mib->{children};
my $columns = $entry->[0]{children};
foreach my $col (sort { $a->{'subID'} <=> $b->{'subID'} } @$columns) {
# store by numeric key so we can sort them later
push @{$tables{$tablename}{'column'}}, $col->{'label'};
}
foreach my $index (@{$entry->[0]{'indexes'}}) {
my $node = $SNMP::MIB{$index} ||
die "can't find info about index $index in table $tablename\n";
push @{$tables{$tablename}{'index'}}, $index;
}
} else {
my $children = $mib->{children};
if ($#children == -1 && $mib->{type}) {
# scalar
$scalars{$mib->{label}} = 1;
} else {
my $i;
for($i = 0; $i <= $#$children; $i++) {
setup_data($children->[$i]);
}
}
}
}
sub min {
return $_[0] if ($_[0] < $_[1]);
return $_[1];
}
sub max {
return $_[0] if ($_[0] > $_[1]);
return $_[1];
}