5
# $Id: mib2c 17684 2009-07-10 07:46:43Z jsafranek $
9
# This program, given an OID reference as an argument, creates some
10
# template mib module files to be used with the net-snmp agent. It is
11
# far from perfect and will not generate working modules, but it
12
# significantly shortens development time by outlining the basic
15
# Its up to you to verify what it does and change the default values
20
my $havesnmp = eval {require SNMP;};
21
my $havenetsnmpoid = eval {require NetSNMP::OID;};
25
ERROR: You don't have the SNMP perl module installed. Please obtain
26
this by getting the latest source release of the net-snmp toolkit from
27
http://www.net-snmp.org/download/ . Once you download the source and
28
unpack it, the perl module is contained in the perl/SNMP directory.
29
See the README file there for instructions.
39
eval { import NetSNMP::OID; }
44
$SNMP::save_descriptions=1;
45
$SNMP::use_long_names=1;
49
$configfile="mib2c.conf";
52
$strict_unk_token = 0;
58
@def_search_dirs = (".");
61
push @def_search_dirs, split(/:/, $ENV{MIB2C_DIR});
63
push @def_search_dirs, "/usr/local/share/snmp/";
64
push @def_search_dirs, "/usr/local/share/snmp/mib2c-data";
65
push @def_search_dirs, "./mib2c-conf.d";
68
print "$0 [-h] [-c configfile] [-f prefix] mibNode\n\n";
69
print " -h\t\tThis message.\n\n";
70
print " -c configfile\tSpecifies the configuration file to use\n\t\tthat dictates what the output of mib2c will look like.\n\n";
71
print " -I PATH\tSpecifies a path to look for configuration files in\n\n";
72
print " -f prefix\tSpecifies the output prefix to use. All code\n\t\twill be put into prefix.c and prefix.h\n\n";
73
print " -d\t\tdebugging output (don't do it. trust me.)\n\n";
74
print " -S VAR=VAL\tSet \$VAR variable to \$VAL\n\n";
75
print " -i\t\tDon't run indent on the resulting code\n\n";
76
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";
85
if ($args_done != 0) {
86
warn "all argument must be specified before the mibNode!\n";
95
my ($var, $val) = ($expr =~ /([^=]*)=(.*)/);
96
die "no variable specified for -S flag." if (!$var);
97
$assignments{$var} = $val;
108
push @search_dirs, split(/,/,$dirs);
110
warn "Unknown option '$_'\n";
116
warn "Replacing previous mibNode $oid with $_\n" if ($oid);
122
# internal conversion tables
125
%accessToIsWritable = qw(ReadOnly 0 ReadWrite 1
126
WriteOnly 1 Create 1);
127
%perltoctypes = qw(OCTETSTR ASN_OCTET_STR
129
INTEGER32 ASN_INTEGER
130
UNSIGNED32 ASN_UNSIGNED
131
OBJECTID ASN_OBJECT_ID
132
COUNTER64 ASN_COUNTER64
135
UINTEGER ASN_UINTEGER
141
%perltodecl = ("OCTETSTR", "char",
144
"UNSIGNED32", "u_long",
145
"UINTEGER", "u_long",
149
"IPADDR", "in_addr_t",
154
%perltolen = ("OCTETSTR", "1",
168
my $mibnode = $SNMP::MIB{$oid};
173
You didn't give mib2c a valid OID to start with. IE, I could not find
174
any information about the mib node \"$oid\". This could be caused
175
because you supplied an incorrectly node, or by the MIB that you're
176
trying to generate code from isn't loaded. To make sure your mib is
177
loaded, run mib2c using this as an example:
179
env MIBS=\"+MY-PERSONAL-MIB\" mib2c " . join(" ",@origargs) . "
181
You might wish to start by reading the MIB loading tutorial at:
183
http://www.net-snmp.org/tutorial-5/commands/mib-options.html
185
And making sure you can get snmptranslate to display information about
186
your MIB node. Once snmptranslate works, then come back and try mib2c
194
$outputName = $mibnode->{'label'} if (!defined($outputName));
195
$outputName =~ s/-/_/g;
196
$vars{'name'} = $outputName;
198
$vars{'example_start'} = " /*\n" .
199
" ***************************************************\n" .
200
" *** START EXAMPLE CODE ***\n" .
201
" ***---------------------------------------------***/";
202
$vars{'example_end'} = " /*\n" .
203
" ***---------------------------------------------***\n" .
204
" *** END EXAMPLE CODE ***\n" .
205
" ***************************************************/";
207
# loop through mib nodes, remembering stuff.
208
setup_data($mibnode);
210
if(($ENV{HOME}) && (-f "$ENV{HOME}/.snmp/mib2c.conf")) {
211
$fh = open_conf("$ENV{HOME}/.snmp/mib2c.conf");
212
process("-balanced");
216
my $defaults = find_conf("default-$configfile",1);
217
if (-f "$defaults" ) {
218
$fh = open_conf($defaults);
219
process("-balanced");
223
my @theassignments = keys(%assignments);
224
if ($#theassignments != -1) {
225
foreach $var (@theassignments) {
226
$vars{$var} = $assignments{$var};
229
$configfile = find_conf($configfile,0);
230
$fh = open_conf($configfile);
231
process("-balanced");
235
foreach $i (keys(%written)) {
237
next if (!($i =~ /\.[ch]$/));
238
print STDERR "running indent on $i\n" if (!$quiet);
239
system("indent -orig -nbc -bap -nut -nfca -T size_t -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");
244
warn "ERROR: ". $_[0] . "\n";
245
die " at $currentfile:$currentline\n";
256
return (scalar split(/\./, $_[0])) - 1;
259
# replaces $VAR type expressions and $VAR.subcomponent expressions
260
# with data from the mib tree and loop variables.
263
# $var -- as defined by loops, etc.
264
# ${var}otherstuff -- appending text to variable contents
265
# $var.uc -- all upper case version of $var
267
# NOTE: THESE ARE AUTO-EXTRACTED/PROCESSED BY ../mib2c.extract.pl for man pages
269
# Mib components, $var must first expand to a mib node name:
271
# $var.uc -- all upper case version of $var
273
# $var.objectID -- dotted, fully-qualified, and numeric OID
274
# $var.commaoid -- comma separated numeric OID for array initialization
275
# $var.oidlength -- length of the oid
276
# $var.subid -- last number component of oid
277
# $var.module -- MIB name that the object comes from
278
# $var.parent -- contains the label of the parent node of $var.
280
# $var.isscalar -- returns 1 if var contains the name of a scalar
281
# $var.iscolumn -- returns 1 if var contains the name of a column
282
# $var.children -- returns 1 if var has children
284
# $var.perltype -- node's perl SYNTAX ($SNMP::MIB{node}{'syntax'})
285
# $var.type -- node's ASN_XXX type (Net-SNMP specific #define)
286
# $var.decl -- C data type (char, u_long, ...)
288
# $var.readable -- 1 if an object is readable, 0 if not
289
# $var.settable -- 1 if an object is writable, 0 if not
290
# $var.creatable -- 1 if a column object can be created as part of a new row, 0 if not
291
# $var.noaccess -- 1 if not-accessible, 0 if not
292
# $var.accessible -- 1 if accessible, 0 if not
293
# $var.rowstatus -- 1 if an object is a RowStatus object, 0 if not
294
# 'settable', 'creatable' and 'rowstatus' can also be used with table variables
295
# to indicate whether it contains writable, creatable or RowStatus column objects
297
# $var.hasdefval -- returns 1 if var has a DEFVAL clause
298
# $var.defval -- node's DEFVAL
299
# $var.hashint -- returns 1 if var has a HINT clause
300
# $var.hint -- node's HINT
301
# $var.ranges -- returns 1 if var has a value range defined
302
# $var.enums -- returns 1 if var has enums defined for it.
303
# $var.access -- node's access type
304
# $var.status -- node's status
305
# $var.syntax -- node's syntax
306
# $var.reference -- node's reference
307
# $var.description -- node's description
312
# mib substitutions ($var.type -> $mibnode->{'type'})
313
if ( $it =~ /\$(\w+)\.(\w+)/ ) {
314
if ($SNMP::MIB{$vars{$1}} && $SNMP::MIB{$vars{$1}}{'label'} =~ /Table$/) {
315
$it =~ s/\$(\w+)\.(settable)/(table_is_writable($SNMP::MIB{$vars{$1}}{label}))/eg;
316
$it =~ s/\$(\w+)\.(creatable)/(table_has_create($SNMP::MIB{$vars{$1}}{label}))/eg;
317
$it =~ s/\$(\w+)\.(rowstatus)/(table_has_rowstatus($SNMP::MIB{$vars{$1}}{label}))/eg;
318
$it =~ s/\$(\w+)\.(lastchange)/(table_has_lastchange($SNMP::MIB{$vars{$1}}{label}))/eg;
319
$it =~ s/\$(\w+)\.(storagetype)/(table_has_storagetype($SNMP::MIB{$vars{$1}}{label}))/eg;
321
$it =~ s/\$(\w+)\.(uc)/uc($vars{$1})/eg; # make something uppercase
322
$it =~ s/\$(\w+)\.(commaoid)/tocommas($SNMP::MIB{$vars{$1}}{objectID})/eg;
323
$it =~ s/\$(\w+)\.(oidlength)/oidlength($SNMP::MIB{$vars{$1}}{objectID})/eg;
324
$it =~ s/\$(\w+)\.(description)/$SNMP::MIB{$vars{$1}}{description}/g;
325
$it =~ s/\$(\w+)\.(perltype)/$SNMP::MIB{$vars{$1}}{type}/g;
326
$it =~ s/\$(\w+)\.(type)/$perltoctypes{$SNMP::MIB{$vars{$1}}{$2}}/g;
327
$it =~ s/\$(\w+)\.(subid)/$SNMP::MIB{$vars{$1}}{subID}/g;
328
$it =~ s/\$(\w+)\.(module)/$SNMP::MIB{$vars{$1}}{moduleID}/g;
329
$it =~ s/\$(\w+)\.(settable)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(ReadWrite|Create|WriteOnly)\/)?1:0)/eg;
330
$it =~ s/\$(\w+)\.(creatable)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(Create)\/)?1:0)/eg;
331
$it =~ s/\$(\w+)\.(readable)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(Read|Create)\/)?1:0)/eg;
332
$it =~ s/\$(\w+)\.(noaccess)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(NoAccess)\/)?1:0)/eg;
333
$it =~ s/\$(\w+)\.(accessible)/(($SNMP::MIB{$vars{$1}}{access} !~ \/(NoAccess)\/)?1:0)/eg;
334
$it =~ s/\$(\w+)\.(objectID|label|subID|access|status|syntax|reference)/$SNMP::MIB{$vars{$1}}{$2}/g;
335
$it =~ s/\$(\w+)\.(decl)/$perltodecl{$SNMP::MIB{$vars{$1}}{type}}/g;
336
$it =~ s/\$(\w+)\.(needlength)/$perltolen{$SNMP::MIB{$vars{$1}}{type}}/g;
337
$it =~ s/\$(\w+)\.(iscolumn)/($SNMP::MIB{$vars{$1}}{'parent'}{'label'} =~ \/Entry$\/) ? 1 : 0/eg;
338
$it =~ s/\$(\w+)\.(isscalar)/($SNMP::MIB{$vars{$1}}{'parent'}{'label'} !~ \/Entry$\/ && $SNMP::MIB{$vars{$1}}{access}) ? 1 : 0/eg;
339
$it =~ s/\$(\w+)\.(parent)/$SNMP::MIB{$vars{$1}}{'parent'}{'label'}/g;
340
$it =~ s/\$(\w+)\.(children)/($#{$SNMP::MIB{$vars{$1}}{'children'}} == 0) ? 0 : 1/eg;
341
$it =~ s/\$(\w+)\.(hasdefval)/(length($SNMP::MIB{$vars{$1}}{'defaultValue'}) == 0) ? 0 : 1/eg;
342
$it =~ s/\$(\w+)\.(defval)/$SNMP::MIB{$vars{$1}}{'defaultValue'}/g;
343
$it =~ s/\$(\w+)\.(hashint)/(length($SNMP::MIB{$vars{$1}}{'hint'}) == 0) ? 0 : 1/eg;
344
$it =~ s/\$(\w+)\.(hint)/$SNMP::MIB{$vars{$1}}{'hint'}/g;
345
$it =~ s/\$(\w+)\.(ranges)/($#{$SNMP::MIB{$vars{$1}}{'ranges'}} == -1) ? 0 : 1/eg;
347
$it =~ s/\$(\w+)\.(enums)/(%{$SNMP::MIB{$vars{$1}}{'enums'}} == 0) ? 0 : 1/eg;
348
$it =~ s/\$(\w+)\.(enumrange)/%{$SNMP::MIB{$vars{$1}}{'enums'}}/eg;
349
$it =~ s/\$(\w+)\.(rowstatus)/(($SNMP::MIB{$vars{$1}}{syntax} =~ \/(RowStatus)\/)?1:0)/eg;
350
if ( $it =~ /\$(\w+)\.(\w+)/ ) {
351
warn "Possible unknown variable attribute \$$1.$2 at $currentfile:$currentline\n";
354
# normal variable substitions
355
$it =~ s/\$\{(\w+)\}/$vars{$1}/g;
356
$it =~ s/\$(\w+)/$vars{$1}/g;
357
# use $@var to put literal '$var'
358
$it =~ s/\$\@(\w+)/\$$1/g;
362
# process various types of statements
364
# NOTE: THESE ARE AUTO-EXTRACTED/PROCESSED BY ../mib2c.extract.pl for man pages
367
# writes generated output to FILE
368
# note that for file specifications, opening '-' will print to stdout.
370
# appends the given FILE
372
# closes the given FILE
374
# save the current outputs, then clear outputs. Use with @open@
375
# and @pop@ to write to a new file without interfering with current
378
# pop up the process() stack one level. Use after a @push@ to return to
379
# the previous set of open files.
380
# @foreach $VAR scalar@
381
# repeat iterate over code until @end@ setting $VAR to all known scalars
382
# @foreach $VAR table@
383
# repeat iterate over code until @end@ setting $VAR to all known tables
384
# @foreach $VAR column@
385
# repeat iterate over code until @end@ setting $VAR to all known
386
# columns within a given table. Obviously this must be called
387
# within a foreach-table clause.
388
# @foreach $VAR nonindex@
389
# repeat iterate over code until @end@ setting $VAR to all known
390
# non-index columns within a given table. Obviously this must be called
391
# within a foreach-table clause.
392
# @foreach $VAR internalindex@
393
# repeat iterate over code until @end@ setting $VAR to all known internal
394
# index columns within a given table. Obviously this must be called
395
# within a foreach-table clause.
396
# @foreach $VAR externalindex@
397
# repeat iterate over code until @end@ setting $VAR to all known external
398
# index columns within a given table. Obviously this must be called
399
# within a foreach-table clause.
400
# @foreach $VAR index@
401
# repeat iterate over code until @end@ setting $VAR to all known
402
# indexes within a given table. Obviously this must be called
403
# within a foreach-table clause.
404
# @foreach $VAR notifications@
405
# repeat iterate over code until @end@ setting $VAR to all known notifications
406
# @foreach $VAR varbinds@
407
# repeat iterate over code until @end@ setting $VAR to all known varbinds
408
# Obviously this must be called within a foreach-notifications clause.
409
# @foreach $LABEL, $VALUE enum@
410
# repeat iterate over code until @end@ setting $LABEL and $VALUE
411
# to the label and values from the enum list.
412
# @foreach $RANGE_START, $RANGE_END range NODE@
413
# repeat iterate over code until @end@ setting $RANGE_START and $RANGE_END
414
# to the legal accepted range set for a given mib NODE.
415
# @foreach $var stuff a b c d@
416
# repeat iterate over values a, b, c, d as assigned generically
417
# (ie, the values are taken straight from the list with no
418
# mib-expansion, etc).
420
# repeat iterate over code until the expression is false
421
# @eval $VAR = expression@
422
# evaluates expression and assigns the results to $VAR. This is
423
# not a full perl eval, but sort of a "psuedo" eval useful for
424
# simple expressions while keeping the same variable name space.
425
# See below for a full-blown export to perl.
427
# evaluates STUFF directly in perl. Note that all mib2c variables
428
# interpereted within .conf files are in $vars{NAME} and that
429
# a warning will be printed if STUFF does not return 0. (adding a
430
# 'return 0;' at the end of STUFF is a workaround.
433
# treats everything between these tags as perl code, and evaluates it.
435
# restart foreach; should only be used inside a conditional.
436
# skips out of current conditional, then continues to skip to
437
# end for the current foreach clause.
439
# evaluates expression, and if expression is true processes
440
# contained part until appropriate @end@ is reached. If the
441
# expression is false, the next @elsif expression@ expression
442
# (if it exists) will be evaluated, until an expression is
443
# true. If no such expression exists and an @else@
444
# clause is found, it will be evaluated.
446
# If the specified file can be found in the conf file search path,
447
# and if found processes contained part until an appropriate @end@ is
448
# found. As with a regular @if expression@, @elsif expression@ and
449
# @else@ can be used.
451
# If the specified directory exists, process contained part until an
452
# appropriate @end@ is found. As with a regular @if expression@,
453
# @elsif expression@ and @else@ can be used.
456
# Memorizes "stuff" between the define and enddefine tags for
457
# later calling as NAME by @calldefine NAME@.
459
# Executes stuff previously memorized as NAME.
460
# @printf "expression" stuff1, stuff2, ...@
461
# Like all the other printf's you know and love.
463
# Sources the contents of FILE as a mib2c file,
464
# but does not affect current files opened.
466
# Sources the contents of FILE as a mib2c file and appends its
467
# output to the current output.
468
# @prompt $var QUESTION@
469
# Presents the user with QUESTION, expects a response and puts it in $var
471
# Prints stuff directly to the users screen (ie, not to where
472
# normal mib2c output goes)
474
# Bail out (silently)
482
while ($arg =~ s/-(\w+)\s*//) {
483
$rtnelse = 1 if ($1 eq "else");
485
while(get_next_line()) {
487
$_ = process_vars($_) if ($debug);
488
print "$currentfile.$currentline:P$currentlevel:S$endcount.$rtnelse:$_" if ($debug);
489
next if ( /^\s*\#\#/ ); # noop, it's a comment
490
next if (! /^\s*\@/ ); # output
491
if (! /^\s*\@.*\@/ ) {
492
warn "$currentfile:$currentline contained a line that started with a @ but did not match any mib2c configuration tokens.\n";
493
warn "(maybe missing the trailing @?)\n";
494
warn "$currentfile:$currentline [$_]\n";
496
elsif (/\@\s*end\@/) {
497
return "end" if ($endcount == 1);
500
elsif (/\@\s*elseif.*\@/) {
501
m2c_die "use 'elsif' instead of 'elseif'\n";
503
elsif (/\@\s*else\@/) {
504
return "else" if (($endcount == 1) && ($rtnelse == 1));
506
elsif (/\@\s*elsif\s+([^\@]+)\@/) {
507
return "else" if (($endcount == 1) && ($rtnelse == 1) && (eval(process_vars($1))));
509
elsif (/\@\s*(foreach|if|while)/) {
513
print "skippart EOF\n";
514
m2c_die "unbalanced code detected in skippart: EOF when $endcount levels deep" if($endcount != 1);
521
print "close_file w/out name!\n";
524
if(!$outputs{$name}) {
525
print "no handle for $name\n";
528
$outputs{$name}->close();
529
delete $outputs{$name};
530
# print STDERR "closing $name\n" if (!$quiet);
534
foreach $name (keys(%outputs)) {
540
my $multiple = shift;
544
if ($multiple == 0) {
547
return if ($outputs{$name});
548
$outputs{$name} = new IO::File;
549
$outputs{$name}->open(">$spec") || m2c_die "failed to open $name";
550
print STDERR "writing to $name\n" if (!$quiet && !$written{$name});
551
$written{$name} = '1';
555
my ($file, $missingok, $keepvars) = (@_);
557
my $oldfile = $currentfile;
558
my $oldline = $currentline;
559
# keep old copy of @vars and just build on it.
562
%oldvars = %vars if ($keepvars != 1);
564
$file = find_conf($file,$missingok);
567
$fh = open_conf($file);
569
process("-balanced");
573
$currentfile = $oldfile;
574
$currentline = $oldline;
576
# don't keep values in replaced vars. Revert to ours.
577
%vars = %oldvars if ($keepvars != 1);
581
if ($#process_lines > -1) {
582
return $_ = shift @process_lines;
589
$stash->{'startpos'} = $fh->tell();
590
$stash->{'startline'} = $currentline;
591
@{$stash->{'lines'}} = @process_lines;
598
# save current line number
599
$currentline = $stash->{'startline'};
600
$fh->seek($stash->{'startpos'}, 0); # go to top of section.
602
# save current process_lines state.
603
@process_lines = @{$stash->{'lines'}};
605
# save state of a number of variables (references), and new assignments
606
for (my $i = 0; $i <= $#_; $i += 2) {
607
push @{$stash->{'vars'}}, $_[$i], ${$_[$i]};
608
${$_[$i]} = $_[$i+1];
614
for (my $i = 0; $i <= $#{$stash->{'vars'}}; $i += 2) {
615
${$stash->{'vars'}[$i]} = $stash->{'vars'}[$i+1];
622
my $return = process();
633
while ($arg =~ s/-(\w+)\s*//) {
634
$elseok = 1 if ($1 eq "elseok");
635
$balanced = 1 if ($1 eq "balanced");
639
$startlevel = $currentlevel;
641
$balanced = $currentlevel;
643
while(get_next_line()) {
646
# my $line = process_vars($_);
648
print "$currentfile.$currentline:P$currentlevel.$elseok:$return:$_";
651
next if (/^\s*\#\#/); # noop, it's a comment
652
if (! /^\s*\@/ ) { # output
653
my $line = process_vars($_);
654
foreach $file (values(%outputs)) {
657
} ####################################################################
658
elsif (/\@\s*exit\@/) { # EXIT
660
die "exiting at conf file ($currentfile:$currentline) request\n";
661
} elsif (/\@\s*quit\@/) { # QUIT
664
} elsif (/\@\s*debug\s+([^\@]+)\@/) { # DEBUG
671
} elsif (/\@\s*strict token\s+([^\@]+)\@/) { # STRICT
673
$strict_unk_token = 1;
676
$strict_unk_token = 0;
678
} elsif (/\@\s*balanced\@/) { # BALANCED
679
$balanced = $currentlevel;
680
} elsif (/\@\s*open\s+([^\@]+)\@/) { # OPEN
682
my ($multiple) = (0);
683
while ($arg =~ s/-(\w+)\s+//) {
684
$multiple = 1 if ($1 eq 'multiple');
686
my $spec = process_vars($arg);
687
open_file($multiple, $spec);
688
} elsif (/\@\s*close\s+([^\@]+)\@/) { # CLOSE
689
my $spec = process_vars($1);
691
} elsif (/\@\s*append\s+([^\@]+)\@/) { # APPEND
693
my ($multiple) = (0);
694
while ($arg =~ s/-(\w+)\s+//) {
695
$multiple = 1 if ($1 eq 'multiple');
697
my $spec = process_vars($arg);
699
open_file($multiple,$spec);
700
} elsif (/\@\s*define\s*(.*)\@/) { # DEFINE
703
last if (/\@\s*enddefine\s*@/);
704
push @{$defines{$it}}, $_;
706
} elsif (/\@\s*calldefine\s+(\w+)@/) {
707
if ($#{$defines{$1}} == -1) {
708
warn "called a define of $1 which didn't exist\n";
709
warn "$currentfile:$currentline [$_]\n";
711
unshift @process_lines, @{$defines{$1}};
713
} elsif (/\@\s*run (.*)\@/) { # RUN
716
while ($arg =~ s/-(\w+)\s+//) {
717
$again = 1 if ($1 eq 'again');
718
# if ($1 eq 'file') {
719
# my ($filearg) = ($arg =~ s/^(\w+)//);
722
my $spec = process_vars($arg);
723
next if (!$again && $ranalready{$spec});
724
$ranalready{$spec} = 1;
725
my %oldout = %outputs;
727
%outputs = %emptyoutputs;
728
process_file($spec,0,0);
731
} elsif (/\@\s*push\@/) { # PUSH
732
my %oldout = %outputs;
734
%outputs = %emptyoutputs;
738
} elsif (/\@\s*pop\s*\@/) { # POP
741
} elsif (/\@\s*include (.*)\@/) { # INCLUDE
743
my ($missingok) = (0);
744
while ($arg =~ s/-(\w+)\s+//) {
745
$missingok = 1 if ($1 eq 'ifexists');
747
my $spec = process_vars($arg);
748
process_file($spec,$missingok,1);
749
} elsif (/\@\s*if([a-z]*)\s+([^@]+)\@/) { # IF
750
my ($type,$arg,$ok) = ($1,$2,0);
751
# check condition based on type
753
$ok = eval(process_vars($arg));
754
} elsif ($type eq conf) {
755
my $file = find_conf(process_vars($arg),1); # missingok
757
} elsif ($type eq dir) {
760
m2c_die "unknown if modifier ($type)\n";
764
$return = process("-elseok");
766
$return = skippart("-else");
767
$return = process("-elseok") if ($return eq "else");
769
if ($return eq "next") {
770
$return = skippart();
771
m2c_die("unbalanced code detected while exiting next/2 (returned $return)") if ($return ne "end");
775
if (($return ne "end") && ($return ne "else")) {
776
m2c_die "unbalanced if / return $return\n";
778
} elsif (/\@\s*elseif.*\@/) { # bogus elseif
779
m2c_die "error: use 'elsif' instead of 'elseif'\n";
780
} elsif (/\@\s*els(e|if).*\@/) { # ELSE/ELSIF
783
m2c_die "unexpected els$1\n";
785
$return = skippart();
786
if ($return ne "end") {
787
m2c_die "unbalanced els$1 / rtn $rtn\n";
791
} elsif (/\@\s*next\s*\@/) { # NEXT
792
$return = skippart();
793
m2c_die "unbalanced code detected while exiting next/1 (returned $return)" if ($return ne "end");
796
} elsif (/\@\s*end\@/) { # END
799
} elsif (/\@\s*eval\s+\$(\w+)\s*=\s*([^\@]*)/) { # EVAL
800
my ($v, $e) = ($1, $2);
801
# print STDERR "eval: $e\n";
802
my $e = process_vars($e);
803
$vars{$v} = eval($e);
804
if (!defined($vars{$v})) {
806
warn "$currentfile:$currentline [$_]\n";
808
} elsif (/\@\s*perleval\s*(.*)\@/) { # PERLEVAL
809
# print STDERR "perleval: $1\n";
813
warn "$currentfile:$currentline [$_]\n";
815
} elsif (/\@\s*startperl\s*\@/) { # STARTPERL
817
while (get_next_line()) {
818
last if (/\@\s*endperl\s*\@/);
821
my $res = eval($text);
824
warn "$currentfile:$currentline [$_]\n";
826
# print STDERR "perleval: $1\n";
827
} elsif (/\@\s*printf\s+(\"[^\"]+\")\s*,?(.*)\@/) { # PRINTF
828
my ($f, $rest) = ($1, $2);
829
$rest = process_vars($rest);
830
my @args = split(/\s*,\s*/,$rest);
832
# print STDERR "printf: $f, ", join(", ",@args),"\n";
833
foreach $file (values(%outputs)) {
834
printf $file (eval {$f}, @args);
836
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+scalars*\s*\@/) { # SCALARS
838
my $stash = do_tell();
840
my @thekeys = keys(%scalars);
841
if ($#thekeys == -1) {
842
$return = skippart();
844
if ($havenetsnmpoid) {
846
new NetSNMP::OID($a) <=>
847
new NetSNMP::OID($b) } @thekeys;
849
foreach $scalar (@thekeys) {
850
$return = do_a_loop($stash, \$vars{$var}, $scalar,
851
\$currentscalar, $scalar,
852
\$currentvar, $scalar);
855
m2c_die("foreach did not end with \@end@") if($return ne "end");
856
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+notifications*\s*\@/) {
858
my $stash = do_tell();
860
my @thekeys = keys(%notifications);
861
if ($#thekeys == -1) {
862
$return = skippart();
864
if ($havenetsnmpoid) {
866
new NetSNMP::OID($a) <=>
867
new NetSNMP::OID($b) } @thekeys;
869
foreach $notify (@thekeys) {
870
$return = do_a_loop($stash, \$vars{$var}, $notify,
871
\$currentnotify, $notify);
874
m2c_die("foreach did not end with \@end@") if($return ne "end");
875
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+varbinds\s*\@/) {
877
my $stash = do_tell();
879
if ($#{$notifyvars{$currentnotify}} == -1) {
880
$return = skippart();
882
foreach $varbind (@{$notifyvars{$currentnotify}}) {
883
# print "looping on $var for $varbind\n";
884
$return = do_a_loop($stash, \$vars{$var}, $varbind,
885
\$currentvarbind, $varbind);
888
m2c_die("foreach did not end with \@end@") if($return ne "end");
889
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+tables*\s*\@/) {
891
my $stash = do_tell();
893
my @thekeys = keys(%tables);
894
if ($#thekeys == -1) {
895
$return = skippart();
897
if ($havenetsnmpoid) {
899
new NetSNMP::OID($a) <=>
900
new NetSNMP::OID($b) } @thekeys;
902
foreach $table (@thekeys) {
903
$return = do_a_loop($stash, \$vars{$var}, $table,
904
\$currenttable, $table);
907
m2c_die("foreach did not end with \@end@ ($return)") if($return ne "end");
908
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+stuff\s*(.*)\@/) {
911
my @stuff = split(/[,\s]+/, $stuff);
912
my $stash = do_tell();
914
$return = skippart();
916
foreach $st (@stuff) {
917
$return = do_a_loop($stash, \$vars{$var}, $st,
918
\$currentstuff, $st);
921
m2c_die("foreach did not end with \@end@ ($return)") if($return ne "end");
922
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+(column|index|internalindex|externalindex|nonindex)\s*\@/) {
923
my ($var, $type) = ($1, $2);
924
my $stash = do_tell();
926
if ($#{$tables{$currenttable}{$type}} == -1) {
927
$return = skippart();
929
foreach $column (@{$tables{$currenttable}{$type}}) {
930
# print "looping on $var for $type -> $column\n";
931
$return = do_a_loop($stash, \$vars{$var}, $column,
932
\$currentcolumn, $column,
933
\$currentvar, $column);
936
m2c_die("foreach did not end with \@end@") if($return ne "end");
937
} elsif (/\@\s*foreach\s+\$([^\@]+)\s+\$([^\@]+)\s+range\s+([^\@]+)\@/) {
938
my ($svar, $evar, $node) = ($1, $2, $3);
939
my $stash = do_tell();
941
$node = $currentcolumn if (!$node);
942
my $mibn = $SNMP::MIB{process_vars($node)};
943
die "no such mib node: $node" if (!$mibn);
944
my @ranges = @{$mibn->{'ranges'}};
946
foreach $range (@ranges) {
947
$return = do_a_loop($stash, \$vars{$svar}, $range->{'low'},
948
\$vars{$evar}, $range->{'high'});
951
$return = skippart();
953
m2c_die("foreach did not end with \@end@") if($return ne "end");
954
} elsif (/\@\s*foreach\s+\$([^\@,]+)\s*,*\s+\$([^\@]+)\s+(enums*)\s*\@/) {
955
my ($varvar, $varval, $type) = ($1, $2, $3);
956
my $stash = do_tell();
959
my @keys = sort { $SNMP::MIB{$currentvar}{'enums'}{$a} <=>
960
$SNMP::MIB{$currentvar}{'enums'}{$b} } (keys(%{$SNMP::MIB{$currentvar}{'enums'}}));
962
foreach $enum (@keys) {
963
($enum2 = $enum) =~ s/-/_/g;
964
$return = do_a_loop($stash, \$vars{$varvar}, $enum2,
966
$SNMP::MIB{$currentvar}{'enums'}{$enum});
969
$return = skippart();
971
m2c_die("foreach did not end with \@end@") if($return ne "end");
972
} elsif (/\@\s*while([a-z]*)\s+([^@]+)\@/) { # WHILE
973
my ($type,$arg,$ok) = ($1,$2,0);
974
my $stash = do_tell();
978
# check condition based on type
980
$ok = eval(process_vars($arg));
981
} elsif ($type eq conf) {
982
my $file = find_conf(process_vars($arg),1); # missingok
984
} elsif ($type eq dir) {
987
m2c_die "unknown while modifier ($type)\n";
992
$return = do_a_loop($stash, \$vars{$type}, $ok, \$vars{$args});
997
} elsif (/\@\s*prompt\s+\$(\S+)\s*(.*)\@/) { # PROMPT
998
my ($var, $prompt) = ($1, $2);
1000
my $haveit = eval { require Term::ReadLine };
1002
$term = new Term::ReadLine 'mib2c';
1006
$vars{$var} = $term->readline(process_vars($prompt));
1008
} elsif (/\@\s*print\s+([^@]*)\@/) { # PRINT
1009
my $line = process_vars($1);
1012
my $line = process_vars($_);
1013
mib2c_output($line);
1015
warn "$currentfile:$currentline contained a line that started with a @ but did not match any mib2c configuration tokens.\n";
1016
warn "(maybe missing the trailing @?)\n";
1017
warn "$currentfile:$currentline [$_]\n";
1018
m2c_die if ($strict_unk_token == 1);
1022
print "< Balanced $balanced / level $currentlevel / rtn $return / $_\n" if($debug);
1023
if((!$_) && ($return ne "eof")) {
1024
# warn "switching return of '$return' to EOF\n" if($debug);
1028
if(($balanced != $currentlevel) || ($return ne "eof")) {
1029
m2c_die "\@balanced@ specified, but processing terminated with '$return' before EOF!";
1038
foreach $file (values(%outputs)) {
1039
print $file "$line";
1046
if ($mib->{label} =~ /Table$/) {
1047
my $tablename = $mib->{label};
1048
my $entry = $mib->{children};
1049
my $columns = $entry->[0]{children};
1050
my $augments = $entry->[0]{'augments'};
1051
foreach my $col (sort { $a->{'subID'} <=> $b->{'subID'} } @$columns) {
1052
# store by numeric key so we can sort them later
1053
push @{$tables{$tablename}{'column'}}, $col->{'label'};
1056
my $mib = $SNMP::MIB{$augments} ||
1057
die "can't find info about augmented table $augments in table $tablename\n";
1058
$mib = $mib->{parent} ||
1059
die "can't find info about augmented table $augments in table $tablename\n";
1060
my $entry = $mib->{children};
1061
foreach my $index (@{$entry->[0]{'indexes'}}) {
1062
my $node = $SNMP::MIB{$index} ||
1063
die "can't find info about index $index in table $tablename\n";
1064
push @{$tables{$tablename}{'index'}}, $index;
1065
push @{$tables{$tablename}{'externalindex'}}, $index;
1067
my $columns = $entry->[0]{children};
1070
foreach my $index (@{$entry->[0]{'indexes'}}) {
1071
my $node = $SNMP::MIB{$index} ||
1072
die "can't find info about index $index in table $tablename\n";
1073
push @{$tables{$tablename}{'index'}}, $index;
1074
if("@{$tables{$tablename}{'column'}}" =~ /$index\b/ ) {
1075
# print "idx INT $index\n";
1076
push @{$tables{$tablename}{'internalindex'}}, $index;
1078
# print "idx EXT $index\n";
1079
push @{$tables{$tablename}{'externalindex'}}, $index;
1083
foreach my $col (sort { $a->{'subID'} <=> $b->{'subID'} } @$columns) {
1084
next if ( "@{$tables{$tablename}{'index'}}" =~ /$col->{'label'}\b/ );
1085
push @{$tables{$tablename}{'nonindex'}}, $col->{'label'};
1087
# print "indexes: @{$tables{$tablename}{'index'}}\n";
1088
# print "internal indexes: @{$tables{$tablename}{'internalindex'}}\n";
1089
# print "external indexes: @{$tables{$tablename}{'externalindex'}}\n";
1090
# print "non-indexes: @{$tables{$tablename}{'nonindex'}}\n";
1092
my $children = $mib->{children};
1093
if ($#children == -1 && $mib->{type}) {
1095
if ($mib->{type} eq "NOTIF" ||
1096
$mib->{type} eq "TRAP") {
1097
my $notifyname = $mib->{label};
1099
$notifications{$notifyname} = 1;
1100
$notifyvars{$notifyname} = $mib->{varbinds};
1102
$scalars{$mib->{label}} = 1 if ($mib->{'access'} ne 'Notify');
1106
for($i = 0; $i <= $#$children; $i++) {
1107
setup_data($children->[$i]);
1114
return $_[0] if ($_[0] < $_[1]);
1119
return $_[0] if ($_[0] > $_[1]);
1124
my ($configfile, $missingok) = (@_);
1126
foreach my $d (@search_dirs, @def_search_dirs) {
1127
# print STDERR "using $d/$configfile" if (-f "$d/$configfile");
1128
return "$d/$configfile" if (-f "$d/$configfile");
1130
return $configfile if (-f "$configfile");
1131
return if ($missingok);
1133
print STDERR "Can't find a configuration file called $configfile\n";
1134
print STDERR "(referenced at $currentfile:$currentline)\n" if ($currentfile);
1135
print STDERR "I looked in:\n";
1136
print " " . join("\n ", @search_dirs, @def_search_dirs), "\n";
1141
my $configfile = shift;
1142
# process .conf file
1143
if (! -f "$configfile") {
1144
print STDERR "Can't find a configuration file called $configfile\n";
1147
$currentfile = $configfile;
1148
my $fh = new IO::File;
1149
$fh->open("$configfile");
1154
my @k = keys(%scalars);
1159
my @k = keys(%tables);
1165
return $#{$tables{$table}{'column'}} + 1;
1168
sub table_is_writable {
1172
foreach $column (@{$tables{$table}{'column'}}) {
1173
if($SNMP::MIB{$column}{access} =~ /(ReadWrite|Create|WriteOnly)/) {
1181
sub table_has_create {
1185
foreach $column (@{$tables{$table}{'column'}}) {
1186
if($SNMP::MIB{$column}{access} =~ /(Create)/) {
1194
sub table_has_rowstatus {
1198
foreach $column (@{$tables{$table}{'column'}}) {
1199
if($SNMP::MIB{$column}{syntax} =~ /(RowStatus)/) {
1207
sub table_has_lastchange {
1211
foreach $column (@{$tables{$table}{'column'}}) {
1212
if(($SNMP::MIB{$column}{syntax} =~ /(TimeStamp)/) &&
1213
($SNMP::MIB{$column}{label} =~ /(LastChange)/)) {
1221
sub table_has_storagetype {
1225
foreach $column (@{$tables{$table}{'column'}}) {
1226
if($SNMP::MIB{$column}{syntax} =~ /(StorageType)/) {
1236
return $#{$tables{$table}{'index'}} + 1;
1239
sub count_external_indexes {
1241
return $#{$tables{$table}{'externalindex'}} + 1;
1244
sub count_notifications {
1245
my @k = keys(%notifications);
1249
sub count_varbinds {
1251
return $#{$notifyvars{$notify}} + 1;