diff options
Diffstat (limited to 'local')
142 files changed, 31795 insertions, 0 deletions
diff --git a/local/FAQ2HTML b/local/FAQ2HTML new file mode 100755 index 0000000..d8b1721 --- /dev/null +++ b/local/FAQ2HTML @@ -0,0 +1,113 @@ +#!/usr/bin/perl -w + +$TOCHEADER=" TABLE OF CONTENTS"; + +open(O, ">FAQ.html"); + + +# Load FAQ into memory +while(<>) { + push (@faqfile, $_); +} +my $current_line = 0; +my $version; + +# Skip header up to table of contents +while($current_line <= $#faqfile) { + $_ = $faqfile[$current_line]; + $current_line++; + + if (/Net-SNMP Version: (.*)/) { + $version = $1; + } + + last if (/$TOCHEADER/); +} + +print O '<p class="SectionTitle"> +FAQ +</p> +FAQ Maintainer: Dave Shield<br/> +Email: <a href="mailto:net-snmp-coders@lists.sourceforge.net">net-snmp-coders@list.sourceforge.net</a><br/> +'; +print O "Version: $version<br/>\n"; +print O '<hr/> +<h2>Table of Contents</h2> +'; + +# Create table of contents +while($current_line <= $#faqfile) { + $_ = $faqfile[$current_line]; + + #Skip blank lines + if (/^\s*$/) { + $current_line++; + last; + } + + chomp(); + + # Remove white space at start of line + $_ =~ s/^ *//; + + $x = $_; + + # Remove white space at start of line + $x =~ s/^ *//g; + + # Replace all non alpha characters with _ + $x =~ s/[^a-zA-Z]/_/g; + + # Save cleaned up line + $xlate{$_} = $x; + + if ( /&/ ) { $_ =~ s/&/&/g; } + if ( /</ ) { $_ =~ s/</</g; } + if ( />/ ) { $_ =~ s/>/>/g; } + if (/^[ A-Z]+$/) { + # Section header (eg: GENERAL) + print O "</ul><b>$_</b><ul>\n"; + } else { + # Question / answer - create link to it + if ($faqfile[$current_line+1] =~ /^ */) { + + # Continuation of the question. + $current_line++; + my $part2 = $faqfile[$current_line]; + + # Remove white space at start of line + $part2 =~ s/^ *//; + + print O "<li> <a href=\"#$x\">$_ $part2</a></li>\n"; + } + else { + print O "<li> <a href=\"#$x\">$_</a></li>\n"; + } + } + $current_line++; +} + +print O "</ul><hr/><pre>\n"; + +# Print contents with targets defined +while($current_line <= $#faqfile) { + $_ = $faqfile[$current_line]; + $current_line++; + + chomp(); + + $y = $_; + + if (defined($xlate{$y})) { + print O "<a name=\"$xlate{$y}\"></a>\n"; + } + if ( /&/ ) { $_ =~ s/&/&/g; } + if ( /</ ) { $_ =~ s/</</g; } + if ( />/ ) { $_ =~ s/>/>/g; } + print O "$_\n"; +} + +print O ' +</pre> +'; + diff --git a/local/Makefile.in b/local/Makefile.in new file mode 100644 index 0000000..df81db0 --- /dev/null +++ b/local/Makefile.in @@ -0,0 +1,173 @@ +# +# local (scripts) directory Makefile +# +top_builddir=.. + +VPATH = @srcdir@ + +# +# stuff to install +# +OTHERINSTALL=localinstall +OTHERUNINSTALL=localuninstall + +# +# local info +# +SNMPCONFPATH=@SNMPCONFPATH@ +PERSISTENT_DIRECTORY=@PERSISTENT_DIRECTORY@ +PERLSCRIPTS=snmpcheck tkmib mib2c fixproc ipf-mod.pl snmpconf traptoemail snmp-bridge-mib net-snmp-cert +SHELLSCRIPTS=mib2c-update +SCRIPTSMADEFORPERL=snmpcheck.made tkmib.made mib2c.made fixproc.made \ + ipf-mod.pl.made snmpconf.made traptoemail.made snmp-bridge-mib.made \ + net-snmp-cert.made +DATASRCS=mib2c.conf mib2c.iterate.conf mib2c.iterate_access.conf \ + mib2c.create-dataset.conf mib2c.mfd.conf \ + mib2c.array-user.conf mib2c.column_enums.conf \ + mib2c.column_defines.conf mib2c.column_storage.conf \ + mib2c.old-api.conf mib2c.scalar.conf \ + mib2c.check_values.conf mib2c.check_values_local.conf \ + mib2c.access_functions.conf mib2c.notify.conf \ + mib2c.int_watch.conf mib2c.genhtml.conf \ + mib2c.raw-table.conf mib2c.table_data.conf \ + mib2c.container.conf mib2c.perl.conf +MIB2CINSTALLDIR=$(snmplibdir)/mib2c-data +MIB2CDATASRC=mib2c-conf.d +MIB2CFILES=default-mfd-top.m2c details-enums.m2i details-node.m2i \ + details-table.m2i generic-ctx-copy.m2i generic-ctx-get.m2i \ + generic-ctx-set.m2i generic-data-allocate.m2i generic-data-context.m2i \ + generic-get-char.m2i generic-get-decl-bot.m2i generic-get-decl.m2i \ + generic-get-long.m2i generic-get-oid.m2i generic-get-U64.m2i \ + generic-header-bottom.m2i \ + generic-header-top.m2i generic-source-includes.m2i \ + generic-table-constants.m2c generic-table-enums.m2c \ + generic-table-indexes-from-oid.m2i generic-table-indexes-set.m2i \ + generic-table-indexes-to-oid.m2i \ + generic-table-indexes-varbind-setup.m2i generic-table-indexes.m2i \ + generic-table-oids.m2c generic-value-map-func.m2i \ + generic-value-map-reverse.m2i generic-value-map.m2i \ + m2c-internal-warning.m2i \ + m2c_setup_enum.m2i m2c_setup_node.m2i m2c_setup_table.m2i \ + m2c_table_save_defaults.m2i \ + mfd-access-container-cached-defines.m2i \ + mfd-access-unsorted-external-defines.m2i \ + mfd-data-access.m2c mfd-data-get.m2c mfd-data-set.m2c \ + mfd-doxygen.m2c mfd-interactive-setup.m2c mfd-interface.m2c \ + mfd-makefile.m2m mfd-readme.m2c mfd-top.m2c \ + mfd-persistence.m2i \ + node-get.m2i node-set.m2i node-storage.m2i \ + node-validate.m2i node-varbind-validate.m2i \ + parent-dependencies.m2i parent-set.m2i \ + subagent.m2c \ + syntax-COUNTER64-get.m2i syntax-DateAndTime-get.m2d \ + syntax-DateAndTime-get.m2i syntax-DateAndTime-readme.m2i \ + syntax-InetAddress-get.m2i syntax-InetAddress-set.m2i \ + syntax-InetAddressType-get.m2i syntax-InetAddressType-set.m2i \ + syntax-RowStatus-dependencies.m2i syntax-RowStatus-get.m2i \ + syntax-RowStatus-varbind-validate.m2i \ + syntax-StorageType-dependencies.m2i \ + syntax-TestAndIncr-get.m2i + +CONFINSTALLDIR=$(snmplibdir)/snmpconf-data +CONFDATASRC=snmpconf.dir +CONFDIRS=snmp-data snmpd-data snmptrapd-data +CONFFILES=snmpd-data/system snmpd-data/acl snmpd-data/trapsinks \ + snmpd-data/monitor snmpd-data/extending snmpd-data/operation \ + snmpd-data/basic_setup snmpd-data/snmpconf-config \ + snmp-data/authopts snmp-data/debugging snmp-data/output snmp-data/mibs \ + snmp-data/snmpconf-config \ + snmptrapd-data/formatting snmptrapd-data/traphandle \ + snmptrapd-data/authentication snmptrapd-data/logging snmptrapd-data/runtime \ + snmptrapd-data/snmpconf-config + +OTHERCLEANTARGETS=snmpcheck $(SCRIPTSMADEFORPERL) + +all: $(SCRIPTSMADEFORPERL) standardall + +snmpcheck: $(srcdir)/snmpcheck.def ../sedscript + $(SED) -f ../sedscript $(srcdir)/snmpcheck.def > snmpcheck + +snmpcheck.made: snmpcheck + if test "x$(PERL)" != "x" ; then \ + $(PERL) -p -e 's%^#!.*/perl.*%#!$(PERL)%' snmpcheck > snmpcheck.made ; \ + else \ + touch snmpcheck.made ; \ + fi + +tkmib.made: $(srcdir)/tkmib + if test "x$(PERL)" != "x" ; then \ + $(PERL) -p -e 's%^#!.*/perl.*%#!$(PERL)%' ${srcdir}/tkmib > tkmib.made; \ + else \ + touch tkmib.made; \ + fi + +mib2c.made: $(srcdir)/mib2c + if test "x$(PERL)" != "x" ; then \ + $(PERL) -p -e 's%^#!.*/perl.*%#!$(PERL)%;s#/usr/local/share/snmp#$(snmplibdir)#;' ${srcdir}/mib2c > mib2c.made; \ + else \ + touch mib2c.made; \ + fi + +net-snmp-cert.made: $(srcdir)/net-snmp-cert + if test "x$(PERL)" != "x" ; then \ + $(PERL) -p -e 's%^#!.*/perl.*%#!$(PERL)%;s#/usr/local/share/snmp#$(snmplibdir)#;' ${srcdir}/net-snmp-cert > net-snmp-cert.made; \ + else \ + touch net-snmp-cert.made; \ + fi + + +ipf-mod.pl.made: $(srcdir)/ipf-mod.pl + if test "x$(PERL)" != "x" ; then \ + $(PERL) -p -e 's%^#!.*/perl.*%#!$(PERL)%' ${srcdir}/ipf-mod.pl > ipf-mod.pl.made; \ + else \ + touch ipf-mod.pl.made; \ + fi + +fixproc.made: $(srcdir)/fixproc + if test "x$(PERL)" != "x" ; then \ + $(PERL) -p -e 's%^#!.*/perl.*%#!$(PERL)%' ${srcdir}/fixproc > fixproc.made; \ + else \ + touch fixproc.made; \ + fi + +snmpconf.made: $(srcdir)/snmpconf + if test "x$(PERL)" != "x" ; then \ + $(PERL) -p -e 's%^#!.*/perl.*%#!$(PERL)%; s#/usr/local/share#$(datadir)#g; s#/usr/local/etc/snmp#$(SNMPCONFPATH)#g; s#/var/net-snmp#$(PERSISTENT_DIRECTORY)#g' ${srcdir}/snmpconf > snmpconf.made; \ + else \ + touch snmpconf.made; \ + fi + +traptoemail.made: $(srcdir)/traptoemail + if test "x$(PERL)" != "x" ; then \ + $(PERL) -p -e 's%^#!.*/perl.*%#!$(PERL)%; s#/usr/local/share#$(datadir)#g; s#/usr/local/etc/snmp#$(TRAPTOEMAILPATH)#g' ${srcdir}/traptoemail > traptoemail.made; \ + else \ + touch traptoemail.made; \ + fi + +snmp-bridge-mib.made: $(srcdir)/snmp-bridge-mib + if test "x$(PERL)" != "x" ; then \ + $(PERL) -p -e 's%^#!.*/perl.*%#!$(PERL)%' ${srcdir}/snmp-bridge-mib > snmp-bridge-mib.made; \ + else \ + touch snmp-bridge-mib.made; \ + fi + +localinstall: $(SCRIPTSMADEFORPERL) + @if test "x$(PERL)" != "x" ; then \ + for i in $(PERLSCRIPTS) ; do $(INSTALL) $$i.made $(INSTALL_PREFIX)$(bindir)/$$i ; echo "install: installed $$i in $(INSTALL_PREFIX)$(bindir)" ; done ; \ + for i in $(SHELLSCRIPTS) ; do $(INSTALL) $(srcdir)/$$i $(INSTALL_PREFIX)$(bindir)/$$i ; echo "install: installed $$i in $(INSTALL_PREFIX)$(bindir)" ; done ; \ + $(SHELL) $(srcdir)/../mkinstalldirs $(INSTALL_PREFIX)$(snmplibdir) ; \ + for i in $(DATASRCS) ; do $(INSTALL_DATA) $(srcdir)/$$i $(INSTALL_PREFIX)$(snmplibdir)/$$i ; echo "install: installed $$i in $(INSTALL_PREFIX)$(snmplibdir)" ; done ; \ + for i in $(CONFDIRS); do $(SHELL) $(srcdir)/../mkinstalldirs $(INSTALL_PREFIX)$(CONFINSTALLDIR)/$$i ; done ; \ + for i in $(CONFFILES); do $(INSTALL_DATA) $(srcdir)/$(CONFDATASRC)/$$i $(INSTALL_PREFIX)$(CONFINSTALLDIR)/$$i; echo "install: installed $$i in $(INSTALL_PREFIX)$(CONFINSTALLDIR)"; done ; \ + $(SHELL) $(srcdir)/../mkinstalldirs $(INSTALL_PREFIX)$(MIB2CINSTALLDIR) ; \ + for i in $(MIB2CFILES); do $(INSTALL_DATA) $(srcdir)/$(MIB2CDATASRC)/$$i $(INSTALL_PREFIX)$(MIB2CINSTALLDIR)/$$i; echo "install: installed $$i in $(INSTALL_PREFIX)$(MIB2CINSTALLDIR)"; done \ + fi + +localuninstall: + @if test "x$(PERL)" != "x" ; then \ + for i in $(PERLSCRIPTS) ; do rm -f $(INSTALL_PREFIX)$(bindir)/$$i ; echo "removed $$i from $(INSTALL_PREFIX)$(bindir)" ; done ; \ + for i in $(SHELLSCRIPTS) ; do rm -f $(INSTALL_PREFIX)$(bindir)/$$i ; echo "removed $$i from $(INSTALL_PREFIX)$(bindir)" ; done ; \ + for i in $(DATASRCS) ; do rm -f $(INSTALL_PREFIX)$(snmplibdir)/$$i ; echo "removed $$i from $(INSTALL_PREFIX)$(snmplibdir)" ; done ; \ + for i in $(CONFFILES); do rm -f $(INSTALL_PREFIX)$(CONFINSTALLDIR)/$$i; echo "removed $$i from $(INSTALL_PREFIX)$(CONFINSTALLDIR)"; done ; \ + for i in $(MIB2CFILES); do rm -f $(INSTALL_PREFIX)$(MIB2CINSTALLDIR)/$$i; echo "removed $$i from $(INSTALL_PREFIX)$(MIB2CINSTALLDIR)"; done \ + fi diff --git a/local/README.mib2c b/local/README.mib2c new file mode 100644 index 0000000..6b28770 --- /dev/null +++ b/local/README.mib2c @@ -0,0 +1,224 @@ +This README describes the ./local/mib2c script. + +Author: Derek Simkowiak + dereks@kd-dev.com + http://www.kd-dev.com + (please mail questions to net-snmp-coders@lists.sourceforge.net, + not to the author directly. Thanks!) + +Date: Wed Jan 20 02:51:06 PST 1999 +----------------------------------------------------------------------- +mib2c + +OVERVIEW + + mib2c is a Perl script that takes a MIB (such as those files found +in ./mibs/ ) and converts it into C code. That C code can then be used as a +"template" to implement your MIB. Then, when you are done editing the C +code and recompiling, the UCD-SNMP agent (snmpd) will support your MIB. +mib2c takes the place of "MIB Compilers" that come with commercial SNMP +agents. + + +REQUIREMENTS/INSTALLATION + + mib2c requires the SNMP.pm Perl module. As of this writing the +latest version of the SNMP.pm module is 1.8. + + The SNMP.pm module can be downloaded from CPAN at + +http://www.cpan.org/modules/by-module/SNMP/ + + ...the file that you want is probably SNMP-1.8b5.tar.gz . +If you didn't know that already, most every Perl module can be downloaded +from CPAN (www.cpan.org). Follow the installation instructions for the +module. + + NOTE: If you are running Redhat Linux 5.2 (and perhaps other +versions), you might get the following errors during the "make test" phase +of the installation of the SNMP.pm module: + +[root@olly SNMP-1.8b5]# make test # This is the command... +PERL_DL_NONLAZY=1 /usr/bin/perl -I./blib/arch -I./blib/lib +-I/usr/lib/perl5/i386-linux/5.00404 -I/usr/lib/perl5 -e 'use Test::Harness +qw(&runtests $verbose); $verbose=0; runtests @ARGV;' t/*.t +t/mib...............ok +t/session...........FAILED tests 7-8 + Failed 2/14 tests, 85.71% okay +t/translate.........ok +Failed Test Status Wstat Total Fail Failed List of failed +------------------------------------------------------------------------------- +t/session.t 14 2 14.29% 7-8 +Failed 1/3 test scripts, 66.67% okay. 2/24 subtests failed, 91.67% okay. +make: *** [test_dynamic] Error 9 + + + If the "make" went okay, then you can ignore these test failures. +These indicate you don't have write access to the portions of the mib +tree that the test script is trying to use. Please don't email the +UCD-SNMP list with other errors regarding the SNMP.pm module. +comp.lang.perl.modules is probably the most appropriate spot to +discuss problems with the SNMP.pm perl module itself. Interelated +problems between net-snmp and SNMP could be discussed on the net-snmp +mailing lists though. + + +USAGE + + mib2c takes one argument: an OID. It then traces down that OID +and generates the template C code. Here is the documentation, from the +top of the script: + +# 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. +# +# You *must* correct the beginning of the var_XXX() function to +# correctly determine mib ownership of the incoming request. + + +FINDING YOUR MIB + + Before you can specify the OID for your enterprise/MIB on the +command line, the script needs to be able to find your MIB so that it can +read it in and generate template code. Joe Marzot (gmarzot@nortelnetworks.com) +tells us: +-------------------------------------- +you should read (man mib_api). The default behaviour for mib loading +from within the perl interface uses the environment variables described +there. You can also override these and explicitly define mibdirs and +load modules through the perl/SNMP api. + +the easiest thing to do is toss the mibs in /usr/local/share/snmp/mibs +and set the env. var., MIBS, to 'ALL'. +-------------------------------------- + + I recommend following the last two lines of advice. I simply did + +# cp /home/dereks/MY-MIB-FILE.txt /usr/local/share/snmp/mibs/ +# export MIBS=ALL + + ...on my Redhat system (with a BASH shell) and it was able to find +my MIB just fine. + + +EXAMPLES + + Here are some examples from Wes Hardaker (wjhardaker@ucdavis.edu). +He's using a C shell. Wes writes: +-------------------------------------- +Ok, in order to run the thing, you actually need to do something like +this: + +setenv MIBS MY-ITEM-MIB # assumes csh +mib2c itemNode + +Where, "itemNode" should be a node in the mib tree that you want to +generate C code for. Note, pick small pieces not large ones. Yes, it +will generate code for the entire mibII tree if you ask it to, in one +very large mib file. + +Examples: + +% mib2c interfaces +outputing to interfaces.c and interfaces.h ... + depth: 3 + Number of Lines Created: +178 interfaces.c +84 interfaces.h +262 total +Done. + +% mib2c mib-2 # Don't ever do this. +outputing to mib-2.c and mib-2.h ... + depth: 5 + Number of Lines Created: +2783 mib-2.c +617 mib-2.h +3400 total +Done. + +It may have some sorting problems with multiple level mib tree +branches being generated into one piece of code (reorder the .h file +structure to be in OID lexical order if needed). +-------------------------------------- + +WHAT TO DO WITH THE CODE THAT GETS GENERATED + + You will need to edit your generated code to work with your +hardware. For instance, if your MIB is for a refrigerator, you will need +to write the code that talks to the refridgerator (through the serial +port, maybe?) in Fridge Protocol. + + See the files in ./agent/mibgroup/examples/ and +./agent/mibgroup/dummy/ for heavily-commented example code. Don't ask me +questions about this stuff--I'm just now figuring it out myself... + + [NOTE: If anyone out there has tips about necessary options to +./configure, or re-compiling snmpd with custom MIB support, please add +them here...] + +WARNING + + As of this writing, the mib2c compiler is a bit outdated and needs +some work. Wes writes: +-------------------------------------- +It already needs changing, because the architecture has changed in the +3.6 line (though its backwards compatible, I'd prefer to generate +code from newer models than older ones). +-------------------------------------- + When I asked him to elaborate on the new 3.6 archictecture, all I +got was: +-------------------------------------- +It hopefully will be in the new documentation about mib module api +that Dave Shield is putting together (which is also currently wrong, +for that matter)... +-------------------------------------- + ...so I don't know what the hell he's talking about. + + +SOME ERRORS AND THEIR MEANING + + If you get a large number of errors that look like: + +[...] +unknown type: INTEGER for prIndex +unknown type: OCTETSTR for prNames +unknown type: INTEGER for prMin +[...] + + ...then you are trying to use an old version of the mib2c script +that does not support the SNMP.pm module version 1.8. Get the latest +version of the script. + + If you get the error + +Couldn't find mib reference: myEnterpriseOID + + ...when you know that it should be finding your MIB file(s), then +you forgot to put the word "END" at the very end of your MIB. (Uh...I'm +not speaking from experience here. Really.) + +ACKNOWLEGMENTS + + Many thanks to the people on the UCD-SNMP mailing list +(net-snmp-users@lists.sourceforge.net). In particular, many thanks to + +Wes Hardaker <wjhardaker@ucdavis.edu> +Ken McNamara <conmara@tcon.net> +Joe Marzot <gmarzot@nortelnetworks.com> + + ...since about half this document is just cut'n'pasted from emails +they sent me. + + Good luck with your project. + +Derek Simkowiak +dereks@kd-dev.com +http://www.kd-dev.com + diff --git a/local/Version-Munge.pl b/local/Version-Munge.pl new file mode 100755 index 0000000..7f6f29c --- /dev/null +++ b/local/Version-Munge.pl @@ -0,0 +1,170 @@ +#!/usr/bin/perl + +use Getopt::Std; + +sub usage { + print " +$0 [-v VERSION] -R -C -M -D -h + + -M Modify the files with a new version (-v required) + -v VERSION Use VERSION as the version string + -T TAG Use TAG as SVN tag (must being with Ext-) + -C Commit changes to the files + -R Revert changes to the files + -D Compare files (svn diff) + -f FILE Just do a particular file + -t TYPE Just do a particular type of file + -P Print resulting modified lines + -V verbose +"; + exit 1; +} + +getopts("Pv:T:RCMDhnf:t:V",\%opts) || usage(); +if ($opts{'h'}) { usage(); } + +if (!$opts{'v'} && $opts{'M'} && !$opts{'T'}) { + warn "no version (-v or -T) specified"; + usage; +} +if (!$opts{'R'} && !$opts{'M'} && !$opts{'C'} && !$opts{'D'}) { + warn "nothing to do (need -R -C -D or -M)\n"; + usage; +} + +my @exprs = ( + # documentation files + { type => 'docs', + expr => 'Version: [\.0-9a-zA-Z]+', + repl => 'Version: $VERSION', + files => [qw(README FAQ dist/net-snmp.spec)], + not_required => {'dist/net-snmp.spec' => 1} + }, + + # Makefiles + { type => 'Makefile', + expr => 'VERSION = [\.0-9a-zA-Z]+', + repl => 'VERSION = $VERSION', + files => [qw(dist/Makefile)], + not_required => {'dist/Makefile' => 1} + }, + + # perl files + { type => 'perl', + expr => 'VERSION = \'(.*)\'', + repl => 'VERSION = \'$VERSION_FLOAT\'', + files => [qw(perl/SNMP/SNMP.pm + perl/agent/agent.pm + perl/agent/Support/Support.pm + perl/agent/default_store/default_store.pm + perl/default_store/default_store.pm + perl/OID/OID.pm + perl/ASN/ASN.pm + perl/AnyData_SNMP/Storage.pm + perl/AnyData_SNMP/Format.pm + perl/TrapReceiver/TrapReceiver.pm + )], + not_required => {'perl/agent/Support/Support.pm' => 1} + }, + + # configure script files + { type => 'configure', + expr => 'AC_INIT\\(\\[Net-SNMP\\], \\[([^\\]]+)\\]', + repl => 'AC_INIT([Net-SNMP], [$VERSION]', + files => [qw(configure.ac)], + exec => 'autoconf', + exfiles => [qw(configure)], + }, + + ); + +# +# set up versioning information +# +if ($opts{'T'} && !$opts{'v'}) { + $opts{'v'} = $opts{'T'}; + die "usage error: version tag must begin with Ext-" if ($opts{'T'} !~ /^Ext-/); + $opts{'v'} =~ s/^Ext-//; + $opts{'v'} =~ s/-/./g; +} +$VERSION = $opts{'v'}; +$VERSION_FLOAT = floatize_version($VERSION); + + +# +# loop through all the expression types +# +my @files; +for ($i = 0; $i <= $#exprs; $i++) { + + # drop other file types if only one was requested. + next if ($opts{'t'} && $exprs[$i]{'type'} ne $opts{'t'}); + + # loop through each file and process + foreach my $f (@{$exprs[$i]->{'files'}}) { + + # skip files that weren't specifically in the todo list if need be + next if ($opts{'f'} && $f ne $opts{'f'}); + + # remove the changes and revert to SVN + if ($opts{'R'}) { + print "removing changes and updating $f\n" if ($opts{'V'}); + system("svn revert $f"); + } + + # make sure it exists + if (! -f $f) { + if (!exists($exprs[$i]->{'not_required'}{$f})) { + print STDERR "FAILED to find file $f\n"; + exit(1); + } else { + print STDERR "SKIPPING file $f\n"; + next; + } + } + + # modify the files with the version + if ($opts{'M'}) { + rename ($f,"$f.bak"); + open(I,"$f.bak"); + open(O,">$f"); + while (<I>) { + my $res = eval "s/$exprs[$i]->{'expr'}/$exprs[$i]->{'repl'}/"; + if ($res && $opts{'P'}) { + my $shortened = $_; + $shortened =~ s/^\s*//; + printf("%s:\n %s", $f, $shortened); + } + print O; + } + close(I); + close(O); + unlink("$f.bak"); + push @files, $f; + print "modified $f using s/$exprs[$i]->{'expr'}/$exprs[$i]->{'repl'}/\n" if ($opts{'V'}); + } + + # run diff if requested. + if ($opts{'D'}) { + print "diffing $f\n" if ($opts{'V'}); + system("svn diff $f"); + } + } + system($exprs[$i]->{'exec'}) if ($exprs[$i]->{'exec'}); + push @files, @{$exprs[$i]->{'exfiles'}} if ($exprs[$i]->{'exfiles'}); +} + +# +# commit the modified files +# +if ($opts{'C'}) { + my $files = join(" ",@files); + print "committing $files\n" if ($opts{'V'}); + $ret = system("svn commit -m \"- version tag ( $VERSION )\" $files"); + exit($ret); +} + +sub floatize_version { + my ($major, $minor, $patch, $opps) = ($_[0] =~ /^(\d+)\.(\d+)\.?(\d*)\.?(\d*)/); + return $major + $minor/100 + $patch/10000 + $opps/100000; +} diff --git a/local/certgen-test.pl b/local/certgen-test.pl new file mode 100644 index 0000000..b335428 --- /dev/null +++ b/local/certgen-test.pl @@ -0,0 +1,66 @@ +#!/usr/bin/perl + +system("rm -rf /tmp/.snmp1"); +system("rm -rf /tmp/.snmp2"); + +system("cp net-snmp-cert ~/bin"); + +$str = "\ngenca (in -C /tmp/.snmp1) : ca-snmp\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert genca -I -C /tmp/.snmp1 --cn ca-snmp --email ca\@ca.com --host host.a.b.com --san DNS:ca.a.b.com --san EMAIL:ca\@ca.com"); + +print "\nusing -C /tmp/.snmp2 for all following tests\n"; +$str = "\ngenca: ca-snmp\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert genca -I -C /tmp/.snmp2 --cn ca-snmp --email ca\@ca.com --host host.a.b.com --san DNS:ca.a.b.com --san EMAIL:ca\@ca.com"); + +$str = "\ngenca: ca-snmp-2 (signed w/ ca-snmp)\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert genca -I -C /tmp/.snmp2 --with-ca ca-snmp --cn ca-snmp-2 --email ca2\@ca.com --host host2.a.b.com --san DNS:ca2.a.b.com --san EMAIL:ca2\@ca.com"); + +$str = "\ngencsr: snmpapp\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert gencsr -I -C /tmp/.snmp2 -t snmpapp --cn 'admin' --email admin@net-snmp.org --host admin-host.net-snmp.org --san EMAIL:a\@b.com --san IP:1.2.3.4 --san DNS:admin.a.b.org"); + +$str = "\nsigncsr: snmpapp w/ca-snmp\n\n"; +print("$str"); +die("died: $str\n") if system("net-snmp-cert signcsr -I -C /tmp/.snmp2 --with-ca ca-snmp --csr snmpapp --install"); + +$str = "\nsigncsr: snmpapp w/ca-snmp-2\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert signcsr -I -C /tmp/.snmp2 --with-ca ca-snmp-2 --csr snmpapp --san EMAIL:noinstall\@b.com --san IP:5.6.7.8"); + +$str = "\ngencert: snmptrapd (self-signed)\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert gencert -I -C /tmp/.snmp2 -t snmptrapd --cn 'NOC' --email 'noc\@net-snmp.org' --host noc-host.net-snmp.org --san DNS:noc.a.b.org --san 'EMAIL:noc\@net-snmp.org'"); + +$str = "\ngencert: snmpd (signed w/ ca-snmp-2)\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert gencert -I -C /tmp/.snmp2 -t snmpd --with-ca ca-snmp-2 --email snmpd\@net-snmp.org --host snmpd-host.net-snmp.org --san DNS:snmpd.a.b.org --san EMAIL:snmpd\@net-snmp.org"); + +system("cp net-snmp-cert.conf /tmp/.snmp2"); + +$str = "\ngenca (in -C /tmp/.snmp2 -i CA-identity)\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert genca -I -C /tmp/.snmp2 -i CA-identity"); + +$str = "\ngencert (in -C /tmp/.snmp2 -i nocadm -t snmp-identity)\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert gencert -I -C /tmp/.snmp2 -t snmp-identity -i nocadm --with-ca CA-identity"); + + +$str = "\nshow CAs\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert -C /tmp/.snmp2 showca --issuer --subject"); + +$str = "show Certs\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert -C /tmp/.snmp2 showcert --issuer --subject"); + +$str = "show CAs fingerprint\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert -C /tmp/.snmp2 showca --fingerprint --brief"); + +$str = "\nshow Certs fingerprint\n\n"; +print("$str"); +die("$str\n") if system("net-snmp-cert -C /tmp/.snmp2 showcert --fingerprint --brief"); diff --git a/local/convertcode b/local/convertcode new file mode 100755 index 0000000..314ea06 --- /dev/null +++ b/local/convertcode @@ -0,0 +1,132 @@ +#!/usr/bin/perl -p -i.snmpbak + +# this script should convert header files included based on the +# ucd-snmp header file names and convert them to their new net-snmp +# names, assuming the code was originally intended to be compiled +# within the ucd-snmp source tree. + +s/include "config.h"/include <net-snmp\/net-snmp-config.h>/; +s/include "asn1.h"/include <net-snmp\/asn1.h>/; +s/include "callback.h"/include <net-snmp\/callback.h>/; +s/include "data_list.h"/include <net-snmp\/data_list.h>/; +s/include "default_store.h"/include <net-snmp\/default_store.h>/; +s/include "getopt.h"/include <net-snmp\/getopt.h>/; +s/include "int64.h"/include <net-snmp\/int64.h>/; +s/include "keytools.h"/include <net-snmp\/keytools.h>/; +s/include "lcd_time.h"/include <net-snmp\/lcd_time.h>/; +s/include "libsnmp.h"/include <net-snmp\/libsnmp.h>/; +s/include "md5.h"/include <net-snmp\/md5.h>/; +s/include "mib.h"/include <net-snmp\/mib.h>/; +s/include "mt_support.h"/include <net-snmp\/mt_support.h>/; +s/include "net-snmp-config.h"/include <net-snmp\/net-snmp-config.h>/; +s/include "net-snmp-includes.h"/include <net-snmp\/net-snmp-includes.h>/; +s/include "oid_array.h"/include <net-snmp\/oid_array.h>/; +s/include "oid_stash.h"/include <net-snmp\/oid_stash.h>/; +s/include "parse.h"/include <net-snmp\/parse.h>/; +s/include "read_config.h"/include <net-snmp\/read_config.h>/; +s/include "scapi.h"/include <net-snmp\/scapi.h>/; +s/include "snmpAAL5PVCDomain.h"/include <net-snmp\/snmpAAL5PVCDomain.h>/; +s/include "snmp_alarm.h"/include <net-snmp\/snmp_alarm.h>/; +s/include "snmp_api.h"/include <net-snmp\/snmp_api.h>/; +s/include "snmpCallbackDomain.h"/include <net-snmp\/snmpCallbackDomain.h>/; +s/include "snmp_client.h"/include <net-snmp\/snmp_client.h>/; +s/include "snmp_debug.h"/include <net-snmp\/snmp_debug.h>/; +s/include "snmp_enum.h"/include <net-snmp\/snmp_enum.h>/; +s/include "snmp.h"/include <net-snmp\/snmp.h>/; +s/include "snmp_impl.h"/include <net-snmp\/snmp_impl.h>/; +s/include "snmpIPXDomain.h"/include <net-snmp\/snmpIPXDomain.h>/; +s/include "snmpksm.h"/include <net-snmp\/snmpksm.h>/; +s/include "snmp_locking.h"/include <net-snmp\/snmp_locking.h>/; +s/include "snmp_logging.h"/include <net-snmp\/snmp_logging.h>/; +s/include "snmp_parse_args.h"/include <net-snmp\/snmp_parse_args.h>/; +s/include "snmp_secmod.h"/include <net-snmp\/snmp_secmod.h>/; +s/include "snmp-tc.h"/include <net-snmp\/snmp-tc.h>/; +s/include "snmpTCPDomain.h"/include <net-snmp\/snmpTCPDomain.h>/; +s/include "snmpTCPIPv6Domain.h"/include <net-snmp\/snmpTCPIPv6Domain.h>/; +s/include "snmp_transport.h"/include <net-snmp\/snmp_transport.h>/; +s/include "snmpUDPDomain.h"/include <net-snmp\/snmpUDPDomain.h>/; +s/include "snmpUDPIPv6Domain.h"/include <net-snmp\/snmpUDPIPv6Domain.h>/; +s/include "snmpUnixDomain.h"/include <net-snmp\/snmpUnixDomain.h>/; +s/include "snmpusm.h"/include <net-snmp\/snmpusm.h>/; +s/include "snmpusm_init.h"/include <net-snmp\/snmpusm_init.h>/; +s/include "snmpv3.h"/include <net-snmp\/snmpv3.h>/; +s/include "system.h"/include <net-snmp\/system.h>/; +s/include "tools.h"/include <net-snmp\/tools.h>/; +s/include "transform_oids.h"/include <net-snmp\/transform_oids.h>/; +s/include "vacm.h"/include <net-snmp\/vacm.h>/; +s/include <asn1.h>/include <net-snmp\/asn1.h>/; +s/include <callback.h>/include <net-snmp\/callback.h>/; +s/include <data_list.h>/include <net-snmp\/data_list.h>/; +s/include <default_store.h>/include <net-snmp\/default_store.h>/; +s/include <getopt.h>/include <net-snmp\/getopt.h>/; +s/include <int64.h>/include <net-snmp\/int64.h>/; +s/include <keytools.h>/include <net-snmp\/keytools.h>/; +s/include <lcd_time.h>/include <net-snmp\/lcd_time.h>/; +s/include <libsnmp.h>/include <net-snmp\/libsnmp.h>/; +s/include <md5.h>/include <net-snmp\/md5.h>/; +s/include <mib.h>/include <net-snmp\/mib.h>/; +s/include <mt_support.h>/include <net-snmp\/mt_support.h>/; +s/include <net-snmp-config.h>/include <net-snmp\/net-snmp-config.h>/; +s/include <net-snmp-includes.h>/include <net-snmp\/net-snmp-includes.h>/; +s/include <oid_array.h>/include <net-snmp\/oid_array.h>/; +s/include <oid_stash.h>/include <net-snmp\/oid_stash.h>/; +s/include <parse.h>/include <net-snmp\/parse.h>/; +s/include <read_config.h>/include <net-snmp\/read_config.h>/; +s/include <scapi.h>/include <net-snmp\/scapi.h>/; +s/include <snmpAAL5PVCDomain.h>/include <net-snmp\/snmpAAL5PVCDomain.h>/; +s/include <snmp_alarm.h>/include <net-snmp\/snmp_alarm.h>/; +s/include <snmp_api.h>/include <net-snmp\/snmp_api.h>/; +s/include <snmpCallbackDomain.h>/include <net-snmp\/snmpCallbackDomain.h>/; +s/include <snmp_client.h>/include <net-snmp\/snmp_client.h>/; +s/include <snmp_debug.h>/include <net-snmp\/snmp_debug.h>/; +s/include <snmp_enum.h>/include <net-snmp\/snmp_enum.h>/; +s/include <snmp.h>/include <net-snmp\/snmp.h>/; +s/include <snmp_impl.h>/include <net-snmp\/snmp_impl.h>/; +s/include <snmpIPXDomain.h>/include <net-snmp\/snmpIPXDomain.h>/; +s/include <snmpksm.h>/include <net-snmp\/snmpksm.h>/; +s/include <snmp_locking.h>/include <net-snmp\/snmp_locking.h>/; +s/include <snmp_logging.h>/include <net-snmp\/snmp_logging.h>/; +s/include <snmp_parse_args.h>/include <net-snmp\/snmp_parse_args.h>/; +s/include <snmp_secmod.h>/include <net-snmp\/snmp_secmod.h>/; +s/include <snmp-tc.h>/include <net-snmp\/snmp-tc.h>/; +s/include <snmpTCPDomain.h>/include <net-snmp\/snmpTCPDomain.h>/; +s/include <snmpTCPIPv6Domain.h>/include <net-snmp\/snmpTCPIPv6Domain.h>/; +s/include <snmp_transport.h>/include <net-snmp\/snmp_transport.h>/; +s/include <snmpUDPDomain.h>/include <net-snmp\/snmpUDPDomain.h>/; +s/include <snmpUDPIPv6Domain.h>/include <net-snmp\/snmpUDPIPv6Domain.h>/; +s/include <snmpUnixDomain.h>/include <net-snmp\/snmpUnixDomain.h>/; +s/include <snmpusm.h>/include <net-snmp\/snmpusm.h>/; +s/include <snmpusm_init.h>/include <net-snmp\/snmpusm_init.h>/; +s/include <snmpv3.h>/include <net-snmp\/snmpv3.h>/; +s/include <system.h>/include <net-snmp\/system.h>/; +s/include <tools.h>/include <net-snmp\/tools.h>/; +s/include <transform_oids.h>/include <net-snmp\/transform_oids.h>/; +s/include <vacm.h>/include <net-snmp\/vacm.h>/; +s/\"agent_read_config.h\"/<net-snmp\/agent\/agent_read_config.h>/; +s/\"agent_registry.h\"/<net-snmp\/agent\/agent_registry.h>/; +s/\"agent_index.h\"/<net-snmp\/agent\/agent_index.h>/; +s/\"agent_trap.h\"/<net-snmp\/agent\/agent_trap.h>/; +s/\"auto_nlist.h\"/<net-snmp\/agent\/auto_nlist.h>/; +s/\"ds_agent.h\"/<net-snmp\/agent\/ds_agent.h>/; +s/\"snmp_agent.h\"/<net-snmp\/agent\/snmp_agent.h>/; +s/\"snmp_vars.h\"/<net-snmp\/agent\/snmp_vars.h>/; +s/\"var_struct.h\"/<net-snmp\/agent\/var_struct.h>/; +s/\"agent_handler.h\"/<net-snmp\/agent\/agent_handler.h>/; +s/\"ucd-snmp-agent-includes.h\"/<net-snmp\/agent\/net-snmp-agent-includes.h>/; +s/\"agent_handler.h\"/<net-snmp\/agent\/agent_handler.h>/; +s/\"agent_callbacks.h\"/<net-snmp\/agent\/agent_callbacks.h>/; +s/\"mib_modules.h\"/<net-snmp\/agent\/mib_modules.h>/; +s/<agent_read_config.h>/<net-snmp\/agent\/agent_read_config.h>/; +s/<agent_registry.h>/<net-snmp\/agent\/agent_registry.h>/; +s/<agent_index.h>/<net-snmp\/agent\/agent_index.h>/; +s/<agent_trap.h>/<net-snmp\/agent\/agent_trap.h>/; +s/<auto_nlist.h>/<net-snmp\/agent\/auto_nlist.h>/; +s/<ds_agent.h>/<net-snmp\/agent\/ds_agent.h>/; +s/<snmp_agent.h>/<net-snmp\/agent\/snmp_agent.h>/; +s/<snmp_vars.h>/<net-snmp\/agent\/snmp_vars.h>/; +s/<var_struct.h>/<net-snmp\/agent\/var_struct.h>/; +s/<agent_handler.h>/<net-snmp\/agent\/agent_handler.h>/; +s/<ucd-snmp-agent-includes.h>/<net-snmp\/agent\/net-snmp-agent-includes.h>/; +s/<agent_handler.h>/<net-snmp\/agent\/agent_handler.h>/; +s/<agent_callbacks.h>/<net-snmp\/agent\/agent_callbacks.h>/; +s/<mib_modules.h>/<net-snmp\/agent\/mib_modules.h>/; diff --git a/local/fixproc b/local/fixproc new file mode 100755 index 0000000..b79630b --- /dev/null +++ b/local/fixproc @@ -0,0 +1,694 @@ +#!/usr/bin/perl +# +# fixproc [-min n] [-max n] [-check | -kill | -restart | -exist | -fix] proc ... +# +# fixproc exit code: +# 0 ok +# 1 check failed +# 2 cannot restart +# 3 cannot kill +# 4 fix failed if fix is defined as kill or restart, then +# cannot kill or cannot restart is return instead +# 10 fixproc error +# +# +# Fixes a process named "proc" by performing the specified action. The +# actions can be check, kill, restart, exist, or fix. The action is specified +# on the command line or is read from a default database, which describes +# the default action to take for each process. The database format and +# the meaning of each action are described below. +# +# database format +# --------------- +# +# name foo required +# cmd /a/b/name args required +# min number optional, defaults to 1 +# max number optional, defaults to 1 +# +# check {null, exist, shell} optional, defaults to exist if not defined +# [shell command shell commands needed only if check=shell +# ... +# shell command +# end_shell] keyword end_shell marks end of shell commands +# fix {kill, restart, shell} required +# [shell command shell commands needed only if fix=shell +# ... +# shell command +# end_shell] keyword end_shell marks end of shell commands +# +# Blank lines and lines beginning with "#" are ignored. +# +# +# Example: +# +# name test1 +# cmd nice /home/kong/z/test1 > /dev/null & +# max 2 +# fix shell +# xterm& +# nice /home/kong/z/test1 > /dev/null & +# end_shell +# +# +# actions +# ------- +# There are 5 possible actions: kill, restart, fix, exist, check. Fix is +# defined to be the kill action, the restart action, or a series of shell +# commands. Check is optionally defined in the database. If check is not +# defined, it defaults to exist. +# +# If the action is specified on the cmd line, it is executed regardless of +# check. The commands executed for each action type is as follow: +# +# switch action: +# kill: +# kill process, wait 5 seconds, kill -9 if still exist +# if still exist +# return "cannot kill" +# else +# return "ok" +# +# restart: +# execute kill +# if kill returned "cannot kill" +# return "cannot kill" +# restart by issuing cmd to shell +# if check defined +# execute check +# if check succeeds +# return "ok" +# else +# return "cannot restart" +# +# fix: +# if fix=kill +# execute kill +# else if fix=restart +# execute restart +# else +# execute shell commands +# execute check +# +# check: +# if check defined as null +# return "fixproc error" +# else +# execute check +# if check succeeds +# return (execute exist) +# return "check failed" +# +# exist: +# if proc exists in ps && (min <= num. of processes <= max) +# return "ok" +# else +# return "check failed" +# +# +# If the action is not specified on the cmd line, the default action is the +# fix action defined in the database. Fix is only executed if check fails: +# +# if fix defined +# if check is not defined as null +# execute check +# if check succeeds +# return "ok" +# execute action defined for fix +# else +# return "fixproc error" +# +# +# If proc is not specified on the command line, return "fixproc error." +# Multiple proc's can be defined on the cmd line. When an error occurs +# when multiple proc's are specified, the first error encountered halts the +# script. +# +# For check shell scripts, any non-zero exit code means the check has failed. +# +# +# Timothy Kong 3/1995 + +use File::Temp qw(tempfile); + +$database_file = '/local/etc/fixproc.conf'; + +$debug = 0; # specify debug level using -dN + # currently defined: -d1 + +$no_error = 0; +$check_failed_error = 1; +$cannot_restart_error = 2; +$cannot_kill_error = 3; +$cannot_fix_error = 4; +$fixproc_error = 10; + +$min = 1; +$max = 1; +$cmd_line_action = ''; +%min = (); +%max = (); +%cmd = (); +%check = (); +%fix = (); +$shell_lines = (); +@proc_list = (); + +$shell_header = "#!/bin/sh\n"; +$shell_end_marker = 'shell_end_marker'; + +&read_args(); +&read_database(); +# &dump_database(); # debug only + +# change the default min. and max. number of processes allowed +if ($min != 1) + { + for $name ( keys (%min) ) + { + $min{$name} = $min; + } + } +if ($max != 1) + { + for $name ( keys (%max) ) + { + $max{$name} = $max; + } + } + +# work on one process at a time +for $proc ( @proc_list ) + { + $error_code = &work_on_proc ($proc); + +############# uncomment next line when fully working ############ +# exit $error_code if ($error_code); + + die "error_code = $error_code\n" if ($error_code); + } + + +# create an executable shell script file +sub create_sh_script +{ + local ($file) = pop (@_); + local ($fh) = pop (@_); + local ($i) = pop (@_); + + printf (STDERR "create_sh_script\n") if ($debug > 0); + + $! = $fixproc_error; + while ( $shell_lines[$i] ne $shell_end_marker ) + { + printf ($fh "%s", $shell_lines[$i]); + $i++; + } + close ($fh); + chmod 0755, $file; +} + + +sub do_fix +{ + local ($proc) = pop(@_); + + printf (STDERR "do_fix\n") if ($debug > 0); + + if ($fix{$proc} eq '') + { + $! = $fixproc_error; + die "$0: internal error 4\n"; + } + if ($fix{$proc} eq 'kill') + { + return &do_kill ($proc); + } + elsif ($fix{$proc} eq 'restart') + { + return &do_restart ($proc); + } + else + { + # it must be "shell", so execute the shell script defined in database + local ($tmpfh, $tmpfile) = tempfile("fix_XXXXXXXX", DIR => "/tmp"); + + &create_sh_script ($fix{$proc}, $tmpfh, $tmpfile); + + # return code is number divided by 256 + $error_code = (system "$tmpfile") / 256; + unlink($tmpfile); + return ($fix_failed_error) if ($error_code != 0); + # sleep needed here? + return &do_exist ($proc); + } +} + + +sub do_check +{ + local ($proc) = pop(@_); + + printf (STDERR "do_check\n") if ($debug > 0); + + if ($check{$proc} eq '') + { + $! = $fixproc_error; + die "$0: internal error 2\n"; + } + + if ($check{$proc} ne 'exist') + { + # if not "exist", then it must be "shell", so execute the shell script + # defined in database + + local ($tmpfh, $tmpfile) = tempfile("check_XXXXXXXX", DIR => "/tmp"); + + &create_sh_script ($fix{$proc}, $tmpfh, $tmpfile); + + # return code is number divided by 256 + $error_code = (system "$tmpfile") / 256; + unlink($tmpfile); + return ($check_failed_error) if ($error_code != 0); + + # check passed, continue + } + return &do_exist ($proc); +} + + +sub do_exist +{ + local ($proc) = pop(@_); + + printf (STDERR "do_exist\n") if ($debug > 0); + + # do ps, check to see if min <= no. of processes <= max + $! = $fixproc_error; + open (COMMAND, "/bin/ps -e | /bin/grep $proc | /bin/wc -l |") + || die "$0: can't run ps-grep-wc command\n"; + $proc_count = <COMMAND>; + if (($proc_count < $min{$proc}) || ($proc_count > $max{$proc})) + { + return $check_failed_error; + } + return $no_error; +} + + +sub do_kill +{ + local ($proc) = pop(@_); + local ($second_kill_needed); + + printf (STDERR "do_kill\n") if ($debug > 0); + + # first try kill + $! = $fixproc_error; + open (COMMAND, "/bin/ps -e | /bin/grep $proc |") + || die "$0: can't run ps-grep-awk command\n"; + while (<COMMAND>) + { + # match the first field of ps -e + $! = $fixproc_error; + /^\s*(\d+)\s/ || die "$0: can't match ps -e output\n"; + system "kill $1"; + } + + # if process still exist, try kill -9 + sleep 2; + $! = $fixproc_error; + open (COMMAND, "/bin/ps -e | /bin/grep $proc |") + || die "$0: can't run ps-grep-awk command\n"; + $second_kill_needed = 0; + while (<COMMAND>) + { + # match the first field of ps -e + $! = $fixproc_error; + /^\s*(\d+)\s/ || die "$0: can't match ps -e output\n"; + system "kill -9 $1"; + $second_kill_needed = 1; + } + return ($no_error) if ($second_kill_needed == 0); + + # see if kill -9 worked + sleep 2; + $! = $fixproc_error; + open (COMMAND, "/bin/ps -e | /bin/grep $proc |") + || die "$0: can't run ps-grep-awk command\n"; + while (<COMMAND>) + { # a process still exist, return error + return $cannot_kill_error; + } + return $no_error; # good, all dead +} + + +sub do_restart +{ + local ($proc) = pop(@_); + local ($error_code); + + printf (STDERR "do_restart\n") if ($debug > 0); + + $error_code = &do_kill ($proc); + return $error_code if ($error_code != $no_error); + die "$0: internal error 3\n" if ($cmd{$proc} eq ''); + system "$cmd{$proc}"; + # sleep needed here? + if ($check{$proc} ne 'null') + { + return $no_error if (&do_check($proc) == $no_error); + return $cannot_restart_error; + } +} + + +sub work_on_proc +{ + local ($proc) = pop(@_); + local ($error_code); + + printf (STDERR "work_on_proc\n") if ($debug > 0); + + if ($cmd_line_action eq '') + { + # perform action from database + + if ($check{$proc} ne 'null') + { + $error_code = &do_check ($proc); + if ($error_code != $check_failed_error) + { + return $error_code; + } + } + return &do_fix ($proc); + } + else + { + # perform action from command line + + $error_code = $no_error; + if ($cmd_line_action eq 'kill') + { + $error_code = &do_kill ($proc); + } + elsif ($cmd_line_action eq 'restart') + { + $error_code = &do_restart ($proc); + } + elsif ($cmd_line_action eq 'fix') + { + $error_code = &do_fix ($proc); + } + elsif ($cmd_line_action eq 'check') + { + if ( $check{$proc} eq 'null' ) + { + exit $fixproc_error; + } + $error_code = &do_check ($proc); + } + elsif ($cmd_line_action eq 'exist') + { + $error_code = &do_exist ($proc); + } + else + { + $! = $fixproc_error; + die "$0: internal error 1\n"; + } + } +} + + +sub dump_database +{ + local ($name); + + for $name (keys(%cmd)) + { + printf ("name\t%s\n", $name); + printf ("cmd\t%s\n", $cmd{$name}); + printf ("min\t%s\n", $min{$name}); + printf ("max\t%s\n", $max{$name}); + if ( $check{$name} =~ /[0-9]+/ ) + { + printf ("check\tshell\n"); + $i = $check{$name}; + while ( $shell_lines[$i] ne $shell_end_marker ) + { + printf ("%s", $shell_lines[$i]); + $i++; + } + } + else + { + printf ("check\t%s\n", $check{$name}); + } + if ( $fix{$name} =~ /[0-9]+/ ) + { + printf ("fix\tshell\n"); + $i = $fix{$name}; + while ( $shell_lines[$i] ne $shell_end_marker ) + { + printf ("%s", $shell_lines[$i]); + $i++; + } + } + else + { + printf ("fix\t%s\n", $fix{$name}); + } + printf ("\n"); + } +} + + +sub read_database +{ + local ($in_check_shell_lines) = 0; + local ($in_fix_shell_lines) = 0; + local ($name) = ''; + local ($str1); + local ($str2); + + $! = $fixproc_error; + open (DB, $database_file) || die 'cannot open database file $database_file\n'; + while (<DB>) + { + if ((! /\S/) || (/^[ \t]*#.*$/)) + { + # ignore blank lines or lines beginning with "#" + } + elsif ($in_check_shell_lines) + { + if ( /^\s*end_shell\s*$/ ) + { + $in_check_shell_lines = 0; + push (@shell_lines, $shell_end_marker); + } + else + { + push (@shell_lines, $_); + } + } + elsif ($in_fix_shell_lines) + { + if ( /^\s*end_shell\s*$/ ) + { + $in_fix_shell_lines = 0; + push (@shell_lines, $shell_end_marker); + } + else + { + push (@shell_lines, $_); + } + } + else + { + if ( ! /^\s*(\S+)\s+(\S.*)\s*$/ ) + { + $! = $fixproc_error; + die "$0: syntax error in database\n$_"; + } + $str1 = $1; + $str2 = $2; + if ($str1 eq 'name') + { + &finish_db_entry($name); + $name = $str2; + } + elsif ($str1 eq 'cmd') + { + $! = $fixproc_error; + die "$0: cmd specified before name in database\n$_\n" + if ($name eq ''); + die "$0: cmd specified multiple times for $name in database\n" + if ($cmd{$name} ne ''); + $cmd{$name} = $str2; + } + elsif ($str1 eq 'min') + { + $! = $fixproc_error; + die "$0: min specified before name in database\n$_\n" + if ($name eq ''); + die "$0: min specified multiple times in database\n$_\n" + if ($min{$name} ne ''); + die "$0: non-numeric min value in database\n$_\n" + if ( ! ($str2 =~ /[0-9]+/ )); + $min{$name} = $str2; + } + elsif ($str1 eq 'max') + { + $! = $fixproc_error; + die "$0: max specified before name in database\n$_\n" + if ($name eq ''); + die "$0: max specified multiple times in database\n$_\n" + if ($max{$name} ne ''); + die "$0: non-numeric max value in database\n$_\n" + if ( ! ($str2 =~ /[0-9]+/ )); + $max{$name} = $str2; + } + elsif ($str1 eq 'check') + { + $! = $fixproc_error; + die "$0: check specified before name in database\n$_\n" + if ($name eq ''); + die "$0: check specified multiple times in database\n$_\n" + if ($check{$name} ne ''); + if ( $str2 eq 'shell' ) + { + # if $check{$name} is a number, it is a pointer into + # $shell_lines[] where the shell commands are kept + $shell_lines[$#shell_lines+1] = $shell_header; + $check{$name} = $#shell_lines; + $in_check_shell_lines = 1; + } + else + { + $check{$name} = $str2; + } + } + elsif ($str1 eq 'fix') + { + $! = $fixproc_error; + die "$0: fix specified before name in database\n$_\n" + if ($name eq ''); + die "$0: fix specified multiple times in database\n$_\n" + if ($fix{$name} ne ''); + if ( $str2 eq 'shell' ) + { + # if $fix{$name} is a number, it is a pointer into + # $shell_lines[] where the shell commands are kept + $shell_lines[$#shell_lines+1] = $shell_header; + $fix{$name} = $#shell_lines; + $in_fix_shell_lines = 1; + } + else + { + $fix{$name} = $str2; + } + } + } + } + &finish_db_entry($name); +} + + +sub finish_db_entry +{ + local ($name) = pop(@_); + + if ($name ne '') + { + $! = $fixproc_error; + die "$0: fix not defined for $name in database\n" + if ($fix{$name} eq ''); + die "$0: cmd not defined for $name in database\n" + if ($cmd{$name} eq ''); + $check{$name} = 'exist' if ($check{$name} eq ''); + $max{$name} = 1 if ($max{$name} eq ''); + $min{$name} = 1 if ($min{$name} eq ''); + } +} + + +sub read_args +{ + local ($i) = 0; + local ($arg); + local ($action_arg_count) = 0; + + while ( $i <= $#ARGV ) + { + $arg = $ARGV[$i]; + if (($arg eq '-min') || ($arg eq '-max')) + { + if (($i == $#ARGV - 1) || ($ARGV[$i+1] =~ /\D/)) # \D is non-numeric + { + $! = $fixproc_error; + die "$0: numeric arg missing after -min or -max\n"; + } + if ($arg eq '-min') + { + $min = $ARGV[$i+1]; + } + else + { + $max = $ARGV[$i+1]; + } + $i += 2; + } + elsif ($arg eq '-kill') + { + $cmd_line_action = 'kill'; + $action_arg_count++; + $i++; + } + elsif ($arg eq '-check') + { + $cmd_line_action = 'check'; + $action_arg_count++; + $i++; + } + elsif ($arg eq '-restart') + { + $cmd_line_action = 'restart'; + $action_arg_count++; + $i++; + } + elsif ($arg eq '-exist') + { + $cmd_line_action = 'exist'; + $action_arg_count++; + $i++; + } + elsif ($arg eq '-fix') + { + $cmd_line_action = 'fix'; + $action_arg_count++; + $i++; + } + elsif ($arg =~ /-d(\d)$/) + { + $debug = $1; + $i++; + } + elsif ($arg =~ /^-/) + { + $! = $fixproc_error; + die "$0: unknown switch $arg\n"; + } + else + { + push (@proc_list, $arg); + $i++; + } + } + $! = $fixproc_error; + die "$0: no process specified\n" if ($#proc_list == -1); + die "$0: more than one action specified\n" if ($action_arg_count > 1); + } + diff --git a/local/gittools/shell-functions b/local/gittools/shell-functions new file mode 100644 index 0000000..45e6507 --- /dev/null +++ b/local/gittools/shell-functions @@ -0,0 +1,443 @@ +# -*- shell-script -*- +# These functions are useful functions when working with a bash shell +# within a Net-SNMP git checkout. + +nsbuildroot=${nsbuildroot:=$HOME/ns-build-root} +nsecho=${nsecho:=} +nsbranches=${nsbranches:="V5-4-patches V5-5-patches V5-6-patches V5-7-patches master"} + +# set nsverbose to ':' if you don't want verbose output of what's going on +nsverbose=${nsverbose:="echo ::: Net-SNMP: "} + +_ns_maybemkdir () { + dir=$1 + if [ ! -d $dir ] ; then + $nsverbose making directory $dir + mkdir -p $dir + fi +} + +_ns_switchtobuilddir () { + dir=$1 + _ns_maybemkdir $dir + $nsverbose cd $dir + cd $dir +} + +_ns_getbuilddir() { + nssuffix=${1:-$nssuffix} + nsbranch=`git branch | egrep '^\*' | sed 's/^..//'` + NSBUILDDIR="$nsbuildroot/$nsbranch" + NSSRCDIR="$PWD" + + # add on the suffix if created + if [ "$nssuffix" != "" ]; then + NSBUILDDIR="$nsbuildroot/$nsbranch-$nssuffix" + fi +} + +_ns_setbuilddir() { + _ns_getbuilddir "$1" + _ns_switchtobuilddir $NSBUILDDIR +} + +_ns_cleanup() { + $nsverbose cd $NSSRCDIR + cd $NSSRCDIR +} + +_ns_checkclean() { + if [ `git status --porcelain --untracked-files=no | wc -l` -gt 0 ] ; then + echo "Your directory has outstanding changes!" + echo " Please either commit the changes, discard them or stash them" + NSCLEAN=0 + else + NSCLEAN=1 + fi +} + +# +# nsmake [-s suffix] +# runs 'make' in $nsbuildroot/branch[-suffix] +nsmake () { + if [ "$1" = "-s" ]; then + shift + _ns_setbuilddir "$1" + shift + else + _ns_setbuilddir + fi + + $nsverbose make "$@" + $nsecho make "$@" + + _ns_cleanup +} + +nsmakeall () { + _ns_checkclean + if [ $NSCLEAN != 1 ]; then + return + fi + + for branch in $nsbranches ; do + git checkout $branch + nsmake "$@" + done +} + +# +# nsconfigure [-s suffix] [configure arguments] +# runs 'configure' in $nsbuildroot/branch[-suffix] with passed arguments +nsconfigure () { + if [ "$1" = "-s" ]; then + shift + _ns_setbuilddir "$1" + shift + else + _ns_setbuilddir + fi + + $nsverbose running $NSSRCDIR/configure "$@" + $nsecho $NSSRCDIR/configure "$@" + + _ns_cleanup +} + +nspatchtry() { + _ns_checkclean + if [ $NSCLEAN != 1 ]; then + return + fi + + # remember these branches + nsgoodbranches="" + nsbadbranches="" + + # remember the patch args for later use + nspatchargs="$@" + + # attempt to add any missing patch arguments + + echo "x $nspatchargs x" | grep -- -p > /dev/null + if [ $? != "0" ] ; then + # they didn't specify a -p option. Try to guess at one... + nspatchfile=`echo $nspatchargs | sed 's/.* \([^ ]+\)$/\1/'` + # attempt to count the slashes before any agent/snmplib/apps/etc + patchcount=$((`grep diff $nspatchfile | head -1 | awk '{print $NF}' | sed 's/\(agent\|snmplib\|apps\|local\|perl\|python\).*//;s#[^/]##g' | wc -c` - 1)) + nspatchargs="-p $patchcount $nspatchargs" + fi + + echo "x $nspatchargs x" | grep -- "-i " > /dev/null + if [ $? != "0" ] ; then + # they didn't specify a -i option. Try to add one... + nspatchargs=`echo $nspatchargs | sed 's/\(.*\) \(..*\)$/\1 -i \2/'` + fi + + echo "Using patch args: $nspatchargs" + + for branch in $nsbranches ; do + $nsverbose checking out and applying patch in $branch + $nsecho git checkout $branch + + $nsverbose Appling patch + $nsecho patch -N --batch $nspatchargs + if [ $? = 0 ] ; then + $nsverbose Patch succeeded on $branch + nsgoodbranches="$nsgoodbranches $branch" + else + $nsverbose Patch failed on $branch + nsbadbranches="$nsbadbranches $branch" + fi + + $nsverbose cleaning source tree + git checkout . + done + + echo "" + echo "Patch application results:" + echo " Success: $nsgoodbranches" + echo " Fail: $nsbadbranches" +} + +_nspatchapplybase() { + _ns_checkclean + if [ $NSCLEAN != 1 ]; then + return + fi + + patchbranch=`echo $nsgoodbranches | sed 's/ .*//'` + + $nsverbose Checking out $patchbranch + $nsecho git checkout $patchbranch + + $nsverbose applying the patch "$nspatchargs" + $nsecho patch $nspatchargs + $nsecho git diff | cat +} + +nspatchapply() { + _nspatchapplybase + + echo "" + echo -n "commit the results to $patchbranch? [y/n]" + read ans + + if [ $ans = 'y' -o $ans = 'Y' ] ; then + $nsecho git commit -a "$@" + fi +} + +nssfpatchapply() { + _nspatchapplybase + nssfpatchcommit "$@" +} + +nssfpatchcommit() { + patch="$1" + area="$2" + + if [ "$patch" = "" ] ; then + echo "Enter patch number: " + echo -n "> " + read patch + fi + if [ "$patch" = "" ] ; then + echo "Error: A patch number is required" + return + fi + + if [ "$area" = "" ] ; then + echo "Which area does this patch affect? (agent, libnetsnmp, apps, ...)?" + echo -n "> " + read area + fi + if [ "$area" = "" ] ; then + area="unknown" + fi + + # + # get the patch's html + # + tmpfile="/tmp/nspatch.$patch" + if [ ! -f "$tmpfile" ]; then + wget -O $tmpfile "https://sourceforge.net/tracker/?func=detail&aid="${patch}"&group_id=12694&atid=312694" + fi + + echo "---------------------------------" + + username=`grep /users/ $tmpfile | head -1 | sed 's/.*.users.//;s/.".*//;'` + echo "username: $username" + + fullname=`grep /users/ $tmpfile | head -1 | sed 's/.*title=.//;s/".*//;'` + echo "fullname: $fullname" + + title=`grep "Detail: $patch" $tmpfile | head -1 | sed "s/.*$patch - //;s/<.*//" | sed 's/"//g;' | sed "s/'//g;"` + echo commit msg: $title + + commitmsg="CHANGES: $area: PATCH $patch: from $username: $title" + + patchbranch=`echo $nsgoodbranches | sed 's/ .*//'` + echo "Branch: $patchbranch" + echo "Command: " + echo " git commit -a -s \\" + echo " --author='$fullname <$username@users.sourceforge.net>'\\" + echo " -m '$commitmsg'" + echo "" + echo -n "commit the results to $patchbranch? [y/n]" + read ans + + if [ $ans = 'y' -o $ans = 'Y' ] ; then + $nsecho git commit -a -s --author="$fullname <$username@users.sourceforge.net>" -m "$commitmsg" + else + echo "if you don't wish to keep the changes, use 'git reset --hard'" + fi +} + + +nsrollup() { + fetch=1; + + if [ "$1" = "--no-fetch" ] ; then + fetch=0 + shift + fi + + if [ "$1" = "--merge" ] ; then + mergeop="merge" + elif [ "$1" = "--rebase" ] ; then + mergeop="rebase" + else + mergeop="merge" + fi + + _ns_checkclean + if [ $NSCLEAN != 1 ]; then + return + fi + + if [ $fetch == 1 ]; then + $nsverbose Pulling all upstream branches + $nsecho git fetch --all + fi + + nslastbranch="" + + nsbranchesdone="" + nsbranchesnotdone="" + + for branch in $nsbranches ; do + if [ "$nslastbranch" != "" ] ; then + $nsverbose checking out $branch + $nsecho git checkout $branch + + if [ -f dist/release ] ; then + if [ "`egrep ^$branch dist/release`" = "$branch rc" ] ; then + $nsverbose Skipping: branch is in rc phase of release + nsbranchesnotdone="$nsbranchesnotdone $branch" + + # comment this out if you want nsrollup to skip the + # branch and roll the changes in the lower branch up + # to the super-parent when the parent is in release status + nslastbranch=$branch + + continue + fi + fi + + $nsverbose rebasing our local changes on $branch + MERGE_AUTOEDIT=no git $mergeop origin/$branch + + $nsverbose merging $nslastbranch into $branch + MERGE_AUTOEDIT=no $nsecho git merge --log $nslastbranch + if [ $? != 0 ] ; then + echo "" + echo "---------------------------------------------" + echo "Merge of $nslastbranch into $branch failed!!!" + echo "" + echo "Hints for fixing this:" + echo " You're on: $branch" + echo " Steps:" + echo " 1) fix all files marked as problematic" + echo " 2) run 'git add' on each file" + if [ $mergeop = "rebase" ] ; then + echo " 3) run 'git rebase --continue' to commit the changes" + else + echo " 3) run 'git commit' to commit the changes" + fi + echo " 4) once done, re-run nsrollup" + echo " Aborting:" + echo " If instead you want to give up, run" + echo " 1) git $mergeop --abort" + echo "" + echo "Updated the following branches:" + echo " $nsbranchesdone" + if [ "$nsbranchesnotdone" != "" ] ; then + echo "Did NOT update the following branches:" + echo " $nsbranchesnotdone" + fi + echo "Failed to update this branch:" + echo " $branch" + return + fi + + _ns_checkclean + if [ $NSCLEAN != 1 ] ; then + # we really shouldn't git here. merge will either autocommit + # or it will fail + $nsverbose committing merge results + $nsecho git commit -m "nsrollup: merging $nslastbranch into $branch" -a + fi + + nsbranchesdone="$nsbranchesdone $branch" + fi + + nslastbranch=$branch + done + + echo "" + echo "-------------------------------" + echo "Updated the following branches:" + echo " $nsbranchesdone" + if [ "$nsbranchesnotdone" != "" ] ; then + echo "Did NOT update the following branches:" + echo " $nsbranchesnotdone" + fi + + echo "" + echo "Now would be a good time to run 'nssync'" +} + +nspull() { + nscurrentbranch=`git branch | egrep '^\*' | sed 's/^..//'` + _ns_checkclean + if [ $NSCLEAN != 1 ]; then + return + fi + + if [ "$1" = "--merge" ] ; then + mergeop="merge" + elif [ "$1" = "--rebase" ] ; then + mergeop="rebase" + else + mergeop="merge" + fi + + git fetch --all + for branch in $nsbranches ; do + git checkout $branch + MERGE_AUTOEDIT=no git $mergeop origin/$branch + if [ $? != 0 ] ; then + echo "git $mergeop failed; please fix first" + return + fi + done + git checkout $nscurrentbranch +} + +nspush() { + _ns_checkclean + if [ $NSCLEAN != 1 ]; then + return + fi + + git push origin $nsbranches + if [ $? != 0 ] ; then + echo "git push failed; help?" + return + fi +} + +# +# pull/pushes all the known active branches +# +nssync() { + nspull "$@" + nspush "$@" +} + +# +# runs the right version of autoconf for a given branch +# +# configure autoconf versions using --prefix=/usr/local/autoconf-VERSION +# +nsautoconf() { + if [ "$1" == "autoheader" ] ; then + tool="autoheader" + else + tool="autoconf" + fi + + COR=`cat dist/autoconf-version` + PATH=/usr/local/autoconf-${COR}/bin:$PATH autoconf --version > /tmp/autoconf.version + VER=`head -1 /tmp/autoconf.version | awk '{print $NF}'` + if [ "$VER" != "$COR" ] ; then + echo "failed to find the correct version of autoconf" + echo "please install autoconf version $COR in /usr/local/autoconf-$COR" + echo " (configure autoconf-$COR using -prefix=/usr/local/autoconf-$COR" + return + fi + + echo "RUNNING AUTOCONF $COR" + PATH=/usr/local/autoconf-${autover}/bin:$PATH $tool +} diff --git a/local/html-add-header-footer.pl b/local/html-add-header-footer.pl new file mode 100755 index 0000000..c3cb902 --- /dev/null +++ b/local/html-add-header-footer.pl @@ -0,0 +1,212 @@ +#!/usr/bin/perl -w +############################################################################## +# +# Alex Burger - Oct 28th, 2004 +# +# Purpose: Modify .html files to add a header and footer for use +# on the Net-SNMP web site. +# +# Can also be used to change the 'section' variable +# for use in the menu system. +# +# Notes: A backup of each file is made to *.old. +# +# Any DOS newlines are removed from the destination file. +# +# Permissions are maintained. +# +############################################################################## +# +use File::Copy; +use File::stat; +use Getopt::Long; + +my $tidy_options = '-f /dev/null -m -i -asxhtml -wrap 130 -quiet --quote-nbsp n'; + +my $pattern = ''; +my $section = ''; +my $tidy = 0; +my $body = 0; +my $help = 0; +my @files = (); + +GetOptions ('pattern=s' => \$pattern, + 'section=s' => \$section, + 'tidy' => \$tidy, + 'body' => \$body, + 'help' => \$help); + +if ($help == 1) +{ +$USAGE = qq/ +Usage: + add-header-footer [<options>] file1 file2 file3 ... +Options: + --section= Menu section + --tidy Run tidy on input file before processing (turns on --body) + --body Remove everything before <body> and after <\/body> + --help Display this message + +Examples: + + add-header-footer.pl --section=tutorial --body cat.html dog.html mouse.html + + find . -name '*.html' | add-header-footer.pl --section=tutorial --body + +/; + print $USAGE; + exit 0; +} + +if ($ARGV[0]) { + # Files listed on command line + foreach my $arg (@ARGV) { + chomp $arg; + push @files, $arg; + #print "$arg\n"; + } +} +else { + # No arguments, so accept STDIN + while (<STDIN>) { + chomp; + push @files, $_; + #print "$_\n"; + } +} + +if (! (@files) ) { + exit 0; +} + +#print "@files"; + +foreach my $file (@files) { + chomp $file; + print "Processing file: $file\n"; + + # Grab current permissions + my $sb = stat($file); + my $stat_permissions = sprintf ("%04o", $sb->mode & 07777); + my $stat_uid = $sb->uid; + my $stat_gid = $sb->gid; + + my @old_file = (); + my @new_file = (); + + my $body_count = 0; + + # Backup old file + if (! (copy ("$file", "$file.old"))) { + print "Could not backup existing file $file to $file.new. Aborting.\n"; + next; + } + # Set permissions on old file to match original file + chmod oct($stat_permissions), "$file.old"; + chown $stat_uid, $stat_uid, "$file.old"; + + + if ($tidy == 1) { + $body = 1; # Enable body, as tidy will add it in. + my $tidy_command = "tidy $tidy_options $file"; + `$tidy_command`; + } + + if (open (I, "<$file")) { + # Load entire file + while (<I>) { + s/\015//g; # Remove any DOS newlines + chomp; + push (@old_file, $_); + } + } + else { + print "Could not open file $file. Aborting\n"; + next; + } + + if (!@old_file) { + print "Empty file. Skipping\n"; + next; + } + + # Remove empty lines at start + while (1) { + if ($old_file[0] eq "") { + splice (@old_file, 0, 1); + } + else { + last; + } + } + + # Remove empty lines at end + while (1) { + if ($old_file[$#old_file] eq "") { + splice (@old_file, -1, 1); + } + else { + last; + } + } + + if ($body == 1) { + # Count the number of <body lines + for (my $i = 0; $i <= $#old_file; $i++) { + if ($old_file[$i] =~ /<body/) { + $body_count++; + next; + } + } + + # Remove anything before and including <body + while ($body_count > 0) { + while (! ($old_file[0] =~ /<body/)) { + splice (@old_file, 0, 1); + } + splice (@old_file, 0, 1); # <body line + $body_count--; + } + } + + # Start to build new file in memory with header + push (@new_file, "<!--#set var=\"section\" value=\"$section\" -->\n"); + push (@new_file, '<!--#include virtual="/page-top.html" -->' . "\n"); + push (@new_file, '<!-- CONTENT START -->' . "\n"); + + # Add in old file, skipping existing header and footer and stopping at <body/> + for (my $i = 0; $i <= $#old_file; $i++) { + if (!(defined($old_file[$i]))) { next; } + if ($body == 1 && ($old_file[$i] =~ /<\/body>/)) { last; } + elsif ($old_file[$i] =~ /<!--#set var="section" value=/) { next; } + elsif ($old_file[$i] =~ /<!--#include virtual="\/page-top.html" -->/) { next; } + elsif ($old_file[$i] =~ /<!-- CONTENT START -->/) { next; } + elsif ($old_file[$i] =~ /<!-- CONTENT END -->/) { next; } + elsif ($old_file[$i] =~ /<!--#include virtual="\/page-bottom.html" -->/) { next; } + + push (@new_file, $old_file[$i] . "\n"); + } + + # Finish to building new file in memory with footer + push (@new_file, '<!-- CONTENT END -->' . "\n"); + push (@new_file, '<!--#include virtual="/page-bottom.html" -->' . "\n"); + + # Save new file + if (open (O, ">$file")) { + for (my $i = 0; $i <= $#new_file; $i++) { + print O "$new_file[$i]"; + } + print O "\n"; + close O; + + # Set permissions + chmod oct($stat_permissions), $file; + chown $stat_uid, $stat_uid, $file; + } + else { + print "Could not create new file: $file.new\n" + } + close I; +} + + diff --git a/local/html-textfile-fix.pl b/local/html-textfile-fix.pl new file mode 100755 index 0000000..b72075b --- /dev/null +++ b/local/html-textfile-fix.pl @@ -0,0 +1,62 @@ +#!/usr/bin/perl +use File::Copy; +# +# This program adds some HTML entities to the text files. This will help prevent +# missing characters when including text documents in HTML. +# +# Written by: Alex Burger +# Date: December 29th, 2005 +# +@files = qw" +CHANGES +ERRATA +INSTALL +NEWS +PORTING +README +README.agent-mibs +README.agentx +README.aix +README.hpux11 +README.irix +README.krb5 +README.mib2c +README.mibs +README.osX +README.Panasonic_AM3X.txt +README.smux +README.snmpv3 +README.solaris +README.thread +README.tru64 +README.win32 +TODO +perl/AnyData_SNMP/README +perl/default_store/README +perl/OID/README +perl/SNMP/README +perl/TrapReceiver/README +"; + + +foreach my $file (@files) { + open (FILEIN, $file) || die "Could not open file \'$file\' for reading. $!"; + open (FILEOUT, ">$file.new") || die "Could not open file \'$file.new\' for writing. $!"; + + while ($line = <FILEIN>) { + $line =~ s/&(?!lt|gt|quot|amp)/\&/g; + $line =~ s/</\</g; + $line =~ s/>/\>/g; + $line =~ s/\"/\"/g; + print FILEOUT "$line"; + } + close FILE; + + if (! (move ("$file", "$file.old"))) { + die "Could not move $file to $file.old\n"; + } + if (! (move ("$file.new", "$file"))) { + die "Could not move $file.new to $file\n"; + } +} + diff --git a/local/ipf-mod.pl b/local/ipf-mod.pl new file mode 100755 index 0000000..285e779 --- /dev/null +++ b/local/ipf-mod.pl @@ -0,0 +1,227 @@ +#!/usr/bin/perl -s +## +## IP Filter UCD-SNMP pass module +## +## Allows read IP Filter's tables (In, Out, AccIn, AccOut), +## fetching rules, hits and bytes (for accounting tables only). +## +## Author: Yaroslav Terletsky <ts@polynet.lviv.ua> +## Date: $ Tue Dec 1 10:24:08 EET 1998 $ +## Version: 1.1a + +# Put this file in /usr/local/bin/ipf-mod.pl and then add the following +# line to your snmpd.conf file (without the # at the front): +# +# pass .1.3.6.1.4.1.2021.13.2 /usr/local/bin/ipf-mod.pl + +# enterprises.ucdavis.ucdExperimental.ipFilter = .1.3.6.1.4.1.2021.13.2 +# ipfInTable.ipfInEntry.ipfInIndex integer = 1.1.1 +# ipfInTable.ipfInEntry.ipfInRule string = 1.1.2 +# ipfInTable.ipfInEntry.ipfInHits counter = 1.1.3 +# ipfOutTable.ipfOutEntry.ipfOutIndex integer = 1.2.1 +# ipfOutTable.ipfOutEntry.ipfOutRule string = 1.2.2 +# ipfOutTable.ipfOutEntry.ipfOutHits counter = 1.2.3 +# ipfAccInTable.ipfAccInEntry.ipfAccInIndex integer = 1.3.1 +# ipfAccInTable.ipfAccInEntry.ipfAccInRule string = 1.3.2 +# ipfAccInTable.ipfAccInEntry.ipfAccInHits counter = 1.3.3 +# ipfAccInTable.ipfAccInEntry.ipfAccInBytes counter = 1.3.4 +# ipfAccOutTable.ipfAccOutEntry.ipfAccOutIndex integer = 1.4.1 +# ipfAccOutTable.ipfAccOutEntry.ipfAccOutRule string = 1.4.2 +# ipfAccOutTable.ipfAccOutEntry.ipfAccOutHits counter = 1.4.3 +# ipfAccOutTable.ipfAccOutEntry.ipfAccOutBytes counter = 1.4.4 + +# variables types +%type = ('1.1.1', 'integer', '1.1.2', 'string', '1.1.3', 'counter', + '2.1.1', 'integer', '2.1.2', 'string', '2.1.3', 'counter', + '3.1.1', 'integer', '3.1.2', 'string', '3.1.3', 'counter', + '3.1.4', 'counter', + '4.1.1', 'integer', '4.1.2', 'string', '4.1.3', 'counter', + '4.1.4', 'counter'); + +# getnext sequence +%next = ('1.1.1', '1.1.2', '1.1.2', '1.1.3', '1.1.3', '2.1.1', + '2.1.1', '2.1.2', '2.1.2', '2.1.3', '2.1.3', '3.1.1', + '3.1.1', '3.1.2', '3.1.2', '3.1.3', '3.1.3', '3.1.4', + '3.1.4', '4.1.1', + '4.1.1', '4.1.2', '4.1.2', '4.1.3', '4.1.3', '4.1.4'); + +# ipfilter's commands to fetch needed information +$ipfstat_comm="/sbin/ipfstat"; +$ipf_in="$ipfstat_comm -ih 2>/dev/null"; +$ipf_out="$ipfstat_comm -oh 2>/dev/null"; +$ipf_acc_in="$ipfstat_comm -aih 2>/dev/null"; +$ipf_acc_out="$ipfstat_comm -aoh 2>/dev/null"; + +$OID=$ARGV[0]; +$IPF_OID='.1.3.6.1.4.1.2021.13.2'; +$IPF_OID_NO_DOTS='\.1\.3\.6\.1\.4\.1\.2021\.13\.2'; + +# exit if OID is not one of IPF-MIB's +exit if $OID !~ /^$IPF_OID_NO_DOTS(\D|$)/; + +# get table, entry, column and row numbers +$tecr = $OID; +$tecr =~ s/^$IPF_OID_NO_DOTS(\D|$)//; +($table, $entry, $col, $row, $rest) = split(/\./, $tecr); + +# parse 'get' request +if($g) { + # exit if OID is wrong specified + if(!defined $table or !defined $entry or !defined $col or !defined $row or defined $rest) { + print "[1] NO-SUCH NAME\n" if $d; + exit; + } + + # get the OID's value + $value = &get_value($table, $entry, $col, $row); + print "value=$value\n" if $d; + + # exit if OID does not exist + print "[2] NO-SUCH NAME\n" if $d and !defined $value; + exit if !defined $value; + + # set ObjectID and reply with response + $tec = "$table.$entry.$col"; + $ObjectID = "${IPF_OID}.${tec}.${row}"; + &response; +} + +# parse 'get-next' request +if($n) { + # set values if 0 or unspecified + $table = 1, $a = 1 if !$table or !defined $table; + $entry = 1, $a = 1 if !$entry or !defined $entry; + $col = 1, $a = 1 if !$col or !defined $col; + $row = 1, $a = 1 if !$row or !defined $row; + + if($a) { + # get the OID's value + $value = &get_value($table, $entry, $col, $row); + print "value=$value\n" if $d; + + # set ObjectID and reply with response + $tec = "$table.$entry.$col"; + $ObjectID = "${IPF_OID}.${tec}.${row}"; + &response; + } + + # get next OID's value + $row++; + $value = &get_value($table, $entry, $col, $row); + + # choose new table/column if rows exceeded + if(!defined $value) { + $tec = "$table.$entry.$col"; + $tec = $next{$tec} if !$a; + $table = $tec; + $entry = $tec; + $col = $tec; + $table =~ s/\.\d\.\d$//; + $entry =~ s/^\d\.(\d)\.\d$/$1/; + $col =~ s/^\d\.\d\.//; + $row = 1; + + # get the OID's value + $value = &get_value($table, $entry, $col, $row); + print "value=$value\n" if $d; + } + + # set ObjectID and reply with response + $tec = "$table.$entry.$col"; + $ObjectID = "${IPF_OID}.${tec}.${row}"; + &response; +} + +############################################################################## + +# fetch values from 'ipfInTable' and 'ipfOutTable' tables +sub fetch_hits_n_rules { + local($row, $col, $ipf_output) = @_; + local($asdf, $i, @ipf_lines, $length); + + # create an entry if no rule exists + $ipf_output = "0 empty list for ipfilter" if !$ipf_output; + + @ipf_lines = split("\n", $ipf_output); + $length = $#ipf_lines + 1; + + for($i = 1; $i < $length + 1; $i++) { + $hits{$i} = $ipf_lines[$i-1]; + $hits{$i} =~ s/^(\d+).*$/$1/; + $rule{$i} = $ipf_lines[$i-1]; + $rule{$i} =~ s/^\d+ //; + if($i == $row) { + return $i if $col == 1; + return $rule{$i} if $col == 2; + return $hits{$i} if $col == 3; + } + } + # return undefined value + undef $asdf; + return $asdf; +} + +# fetch values from 'ipfAccInTable' and 'ipfAccOutTable' tables +sub fetch_hits_bytes_n_rules { + local($row, $col, $ipf_output) = @_; + local($asdf, $i, @ipf_lines, $length); + + # create an entry if no rule exists + $ipf_output = "0 0 empty list for ipacct" if !$ipf_output; + + @ipf_lines = split("\n", $ipf_output); + $length = $#ipf_lines + 1; + + for($i = 1; $i < $length + 1; $i++) { + $hits{$i} = $ipf_lines[$i-1]; + $hits{$i} =~ s/^(\d+) .*$/$1/; + $bytes{$i} = $ipf_lines[$i-1]; + $bytes{$i} =~ s/^\d+ (\d+) .*/$1/; + $rule{$i} = $ipf_lines[$i-1]; + $rule{$i} =~ s/^\d+ \d+ //; + if($i == $row) { + return $i if $col == 1; + return $rule{$i} if $col == 2; + return $hits{$i} if $col == 3; + return $bytes{$i} if $col == 4; + } + } + # return undefined value + undef $asdf; + return $asdf; +} + +# get the values from ipfilter's tables +sub get_value { + local($table, $entry, $col, $row) = @_; + + if($table == 1) { + # fetch ipfInTable data + $ipf_output = `$ipf_in`; + $value = &fetch_hits_n_rules($row, $col, $ipf_output); + } elsif($table == 2) { + # fetch ipfOutTable data + $ipf_output = `$ipf_out`; + $value = &fetch_hits_n_rules($row, $col, $ipf_output); + } elsif($table == 3) { + # fetch ipfAccInTable data + $ipf_output = `$ipf_acc_in`; + $value = &fetch_hits_bytes_n_rules($row, $col, $ipf_output); + } elsif($table == 4) { + # fetch ipfAccOutTable data + $ipf_output = `$ipf_acc_out`; + $value = &fetch_hits_bytes_n_rules($row, $col, $ipf_output); + } + return $value; +} + +# generate response to 'get' or 'get-next' request +sub response { + # print ObjectID, its type and the value + if(defined $ObjectID and defined $type{$tec} and defined $value) { + print "$ObjectID\n"; + print "$type{$tec}\n"; + print "$value\n"; + } + exit; +} diff --git a/local/mib2c b/local/mib2c new file mode 100755 index 0000000..2016f06 --- /dev/null +++ b/local/mib2c @@ -0,0 +1,1275 @@ +#!/usr/bin/perl +#!/usr/bin/perl -w + +# +# $Id$ +# +# 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;}; +my $havenetsnmpoid = eval {require NetSNMP::OID;}; + +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 README file there for instructions. + +"; + exit; +} + +if ($havesnmp) { + eval { import SNMP; } +} +if ($havenetsnmp) { + eval { import NetSNMP::OID; } +} +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; +$strict_unk_token = 0; +$noindent = 0; +$nosed = 0; +$currentline = 0; +$currentlevel = -1; +%assignments; +%outputs; +@def_search_dirs = ("."); +@search_dirs = (); +if($ENV{MIB2C_DIR}) { + push @def_search_dirs, split(/:/, $ENV{MIB2C_DIR}); +} +push @def_search_dirs, "/usr/local/share/snmp/"; +push @def_search_dirs, "/usr/local/share/snmp/mib2c-data"; +push @def_search_dirs, "./mib2c-conf.d"; + +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 " -I PATH\tSpecifies a path to look for configuration files in\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 " -d\t\tdebugging output (don't do it. trust me.)\n\n"; + print " -S VAR=VAL\tSet \$VAR variable to \$VAL\n\n"; + print " -i\t\tDon't run indent on the resulting code\n\n"; + print " -s\t\tDon't look for mibNode.sed and run sed on the resulting code\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"; + 1; +} + +my @origargs = @ARGV; +my $args_done = 0; +while($#ARGV >= 0) { + $_ = shift; + if (/^-/) { + if ($args_done != 0) { + warn "all argument must be specified before the mibNode!\n"; + usage; + exit 1; + } elsif (/^-c/) { + $configfile = shift; + } elsif (/^-d/) { + $debug = 1; + } elsif (/^-S/) { + my $expr = shift; + my ($var, $val) = ($expr =~ /([^=]*)=(.*)/); + die "no variable specified for -S flag." if (!$var); + $assignments{$var} = $val; + } elsif (/^-q/) { + $quiet = 1; + } elsif (/^-i/) { + $noindent = 1; + } elsif (/^-s/) { + $nosed = 1; + } elsif (/^-h/) { + usage && exit(1); + } elsif (/^-f/) { + $outputName = shift; + } elsif (/^-I/) { + my $dirs = shift; + push @search_dirs, split(/,/,$dirs); + } else { + warn "Unknown option '$_'\n"; + usage; + exit 1; + } + } else { + $args_done = 1; + warn "Replacing previous mibNode $oid with $_\n" if ($oid); + $oid = $_ ; + } +} + +# +# 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", "U64", + "COUNTER", "u_long", + "IPADDR", "in_addr_t", + "BITS", "char", + "TICKS", "u_long", + "GAUGE", "u_long", + "OPAQUE", "u_char"); +%perltolen = ("OCTETSTR", "1", + "INTEGER", "0", + "INTEGER32", "0", + "UNSIGNED32", "0", + "UINTEGER", "0", + "OBJECTID", "1", + "COUNTER64", "0", + "COUNTER", "0", + "IPADDR", "0", + "BITS", "1", + "TICKS", "0", + "GAUGE", "0", + "OPAQUE", "1"); + +my $mibnode = $SNMP::MIB{$oid}; + +if (!$mibnode) { + +print STDERR " +You didn't give mib2c a valid OID to start with. IE, I could not find +any information about the mib node \"$oid\". This could be caused +because you supplied an incorrectly node, or by the MIB that you're +trying to generate code from isn't loaded. To make sure your mib is +loaded, run mib2c using this as an example: + + env MIBS=\"+MY-PERSONAL-MIB\" mib2c " . join(" ",@origargs) . " + +You might wish to start by reading the MIB loading tutorial at: + + http://www.net-snmp.org/tutorial-5/commands/mib-options.html + +And making sure you can get snmptranslate to display information about +your MIB node. Once snmptranslate works, then come back and try mib2c +again. + +"; +exit 1; +} + +# setup +$outputName = $mibnode->{'label'} if (!defined($outputName)); +$outputName =~ s/-/_/g; +$vars{'name'} = $outputName; +$vars{'oid'} = $oid; +$vars{'example_start'} = " /*\n" . +" ***************************************************\n" . +" *** START EXAMPLE CODE ***\n" . +" ***---------------------------------------------***/"; +$vars{'example_end'} = " /*\n" . +" ***---------------------------------------------***\n" . +" *** END EXAMPLE CODE ***\n" . +" ***************************************************/"; + +# loop through mib nodes, remembering stuff. +setup_data($mibnode); + +if(($ENV{HOME}) && (-f "$ENV{HOME}/.snmp/mib2c.conf")) { + $fh = open_conf("$ENV{HOME}/.snmp/mib2c.conf"); + process("-balanced"); + $fh->close; +} + +my $defaults = find_conf("default-$configfile",1); +if (-f "$defaults" ) { + $fh = open_conf($defaults); + process("-balanced"); + $fh->close; +} + +my @theassignments = keys(%assignments); +if ($#theassignments != -1) { + foreach $var (@theassignments) { + $vars{$var} = $assignments{$var}; + } +} +$configfile = find_conf($configfile,0); +$fh = open_conf($configfile); +process("-balanced"); +$fh->close; + +if (-f "$outputName.sed" && !$nosed) { + foreach $i (keys(%written)) { + next if ($i eq "-"); + next if (!($i =~ /\.[ch]$/)); + print STDERR "running sed --in-place=.orig --file=$outputName.sed $i\n" if (!$quiet); + system("sed --in-place=.orig --file=$outputName.sed $i"); + } +} + +if (!$noindent) { + foreach $i (keys(%written)) { + next if ($i eq "-"); + next if (!($i =~ /\.[ch]$/)); + print STDERR "running indent on $i\n" if (!$quiet); + 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"); + } +} + +sub m2c_die { + warn "ERROR: ". $_[0] . "\n"; + die " at $currentfile:$currentline\n"; +} + +sub tocommas { + my $oid = $_[0]; + $oid =~ s/\./,/g; + $oid =~ s/^\s*,//; + return $oid; +} + +sub oidlength { + return (scalar split(/\./, $_[0])) - 1; +} + +# 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 +# +# NOTE: THESE ARE AUTO-EXTRACTED/PROCESSED BY ../mib2c.extract.pl for man pages +# +# Mib components, $var must first expand to a mib node name: +# +# $var.uc -- all upper case version of $var +# +# $var.objectID -- dotted, fully-qualified, and numeric OID +# $var.commaoid -- comma separated numeric OID for array initialization +# $var.oidlength -- length of the oid +# $var.subid -- last number component of oid +# $var.module -- MIB name that the object comes from +# $var.parent -- contains the label of the parent node of $var. +# +# $var.isscalar -- returns 1 if var contains the name of a scalar +# $var.iscolumn -- returns 1 if var contains the name of a column +# $var.children -- returns 1 if var has children +# +# $var.perltype -- node's perl SYNTAX ($SNMP::MIB{node}{'syntax'}) +# $var.type -- node's ASN_XXX type (Net-SNMP specific #define) +# $var.decl -- C data type (char, u_long, ...) +# +# $var.readable -- 1 if an object is readable, 0 if not +# $var.settable -- 1 if an object is writable, 0 if not +# $var.creatable -- 1 if a column object can be created as part of a new row, 0 if not +# $var.noaccess -- 1 if not-accessible, 0 if not +# $var.accessible -- 1 if accessible, 0 if not +# $var.storagetype -- 1 if an object is a StorageType object, 0 if not +# $var.rowstatus -- 1 if an object is a RowStatus object, 0 if not +# 'settable', 'creatable', 'lastchange', 'storagetype' and 'rowstatus' can +# also be used with table variables to indicate whether it contains +# writable, creatable, LastChange, StorageType or RowStatus column objects +# +# $var.hasdefval -- returns 1 if var has a DEFVAL clause +# $var.defval -- node's DEFVAL +# $var.hashint -- returns 1 if var has a HINT clause +# $var.hint -- node's HINT +# $var.ranges -- returns 1 if var has a value range defined +# $var.enums -- returns 1 if var has enums defined for it. +# $var.access -- node's access type +# $var.status -- node's status +# $var.syntax -- node's syntax +# $var.reference -- node's reference +# $var.description -- node's description + +sub process_vars { + my $it = shift; + + # mib substitutions ($var.type -> $mibnode->{'type'}) + if ( $it =~ /\$(\w+)\.(\w+)/ ) { + if ($SNMP::MIB{$vars{$1}} && defined($tables{$SNMP::MIB{$vars{$1}}{'label'}})) { + $it =~ s/\$(\w+)\.(settable)/(table_is_writable($SNMP::MIB{$vars{$1}}{label}))/eg; + $it =~ s/\$(\w+)\.(creatable)/(table_has_create($SNMP::MIB{$vars{$1}}{label}))/eg; + $it =~ s/\$(\w+)\.(rowstatus)/(table_has_rowstatus($SNMP::MIB{$vars{$1}}{label}))/eg; + $it =~ s/\$(\w+)\.(lastchange)/(table_has_lastchange($SNMP::MIB{$vars{$1}}{label}))/eg; + $it =~ s/\$(\w+)\.(storagetype)/(table_has_storagetype($SNMP::MIB{$vars{$1}}{label}))/eg; + } + $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+)\.(description)/$SNMP::MIB{$vars{$1}}{description}/g; + $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+)\.(module)/$SNMP::MIB{$vars{$1}}{moduleID}/g; + $it =~ s/\$(\w+)\.(settable)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(ReadWrite|Create|WriteOnly)\/)?1:0)/eg; + $it =~ s/\$(\w+)\.(creatable)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(Create)\/)?1:0)/eg; + $it =~ s/\$(\w+)\.(readable)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(Read|Create)\/)?1:0)/eg; + $it =~ s/\$(\w+)\.(noaccess)/(($SNMP::MIB{$vars{$1}}{access} =~ \/(NoAccess)\/)?1:0)/eg; + $it =~ s/\$(\w+)\.(accessible)/(($SNMP::MIB{$vars{$1}}{access} !~ \/(NoAccess)\/)?1:0)/eg; + $it =~ s/\$(\w+)\.(objectID|label|subID|access|status|syntax|reference)/$SNMP::MIB{$vars{$1}}{$2}/g; + $it =~ s/\$(\w+)\.(decl)/$perltodecl{$SNMP::MIB{$vars{$1}}{type}}/g; + $it =~ s/\$(\w+)\.(needlength)/$perltolen{$SNMP::MIB{$vars{$1}}{type}}/g; + $it =~ s/\$(\w+)\.(iscolumn)/($SNMP::MIB{$vars{$1}}{'parent'}{'label'} =~ \/Entry$\/) ? 1 : 0/eg; + $it =~ s/\$(\w+)\.(isscalar)/($SNMP::MIB{$vars{$1}}{'parent'}{'label'} !~ \/Entry$\/ && $SNMP::MIB{$vars{$1}}{access}) ? 1 : 0/eg; + $it =~ s/\$(\w+)\.(parent)/$SNMP::MIB{$vars{$1}}{'parent'}{'label'}/g; + $it =~ s/\$(\w+)\.(children)/($#{$SNMP::MIB{$vars{$1}}{'children'}} == 0) ? 0 : 1/eg; + $it =~ s/\$(\w+)\.(hasdefval)/(length($SNMP::MIB{$vars{$1}}{'defaultValue'}) == 0) ? 0 : 1/eg; + $it =~ s/\$(\w+)\.(defval)/$SNMP::MIB{$vars{$1}}{'defaultValue'}/g; + $it =~ s/\$(\w+)\.(hashint)/(length($SNMP::MIB{$vars{$1}}{'hint'}) == 0) ? 0 : 1/eg; + $it =~ s/\$(\w+)\.(hint)/$SNMP::MIB{$vars{$1}}{'hint'}/g; + $it =~ s/\$(\w+)\.(ranges)/($#{$SNMP::MIB{$vars{$1}}{'ranges'}} == -1) ? 0 : 1/eg; + # check for enums + $it =~ s/\$(\w+)\.(enums)/(%{$SNMP::MIB{$vars{$1}}{'enums'}} == 0) ? 0 : 1/eg; + $it =~ s/\$(\w+)\.(enumrange)/%{$SNMP::MIB{$vars{$1}}{'enums'}}/eg; + $it =~ s/\$(\w+)\.(rowstatus)/(($SNMP::MIB{$vars{$1}}{syntax} =~ \/(RowStatus)\/)?1:0)/eg; + $it =~ s/\$(\w+)\.(storagetype)/(($SNMP::MIB{$vars{$1}}{syntax} =~ \/(StorageType)\/)?1:0)/eg; + if ( $it =~ /\$(\w+)\.(\w+)/ ) { + warn "Possible unknown variable attribute \$$1.$2 at $currentfile:$currentline\n"; + } + } + # normal variable substitions + $it =~ s/\$\{(\w+)\}/$vars{$1}/g; + $it =~ s/\$(\w+)/$vars{$1}/g; + # use $@var to put literal '$var' + $it =~ s/\$\@(\w+)/\$$1/g; + return $it; +} + +# process various types of statements +# +# NOTE: THESE ARE AUTO-EXTRACTED/PROCESSED BY ../mib2c.extract.pl for man pages +# which include: +# @open FILE@ +# writes generated output to FILE +# note that for file specifications, opening '-' will print to stdout. +# @append FILE@ +# appends the given FILE +# @close FILE@ +# closes the given FILE +# @push@ +# save the current outputs, then clear outputs. Use with @open@ +# and @pop@ to write to a new file without interfering with current +# outputs. +# @pop@ +# pop up the process() stack one level. Use after a @push@ to return to +# the previous set of open files. +# @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 nonindex@ +# repeat iterate over code until @end@ setting $VAR to all known +# non-index columns within a given table. Obviously this must be called +# within a foreach-table clause. +# @foreach $VAR internalindex@ +# repeat iterate over code until @end@ setting $VAR to all known internal +# index columns within a given table. Obviously this must be called +# within a foreach-table clause. +# @foreach $VAR externalindex@ +# repeat iterate over code until @end@ setting $VAR to all known external +# index 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 $VAR notifications@ +# repeat iterate over code until @end@ setting $VAR to all known notifications +# @foreach $VAR varbinds@ +# repeat iterate over code until @end@ setting $VAR to all known varbinds +# Obviously this must be called within a foreach-notifications 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. +# @foreach $var stuff a b c d@ +# repeat iterate over values a, b, c, d as assigned generically +# (ie, the values are taken straight from the list with no +# mib-expansion, etc). +# @while expression@ +# repeat iterate over code until the expression is false +# @eval $VAR = expression@ +# evaluates expression and assigns the results to $VAR. This is +# not a full perl eval, but sort of a "psuedo" eval useful for +# simple expressions while keeping the same variable name space. +# See below for a full-blown export to perl. +# @perleval STUFF@ +# evaluates STUFF directly in perl. Note that all mib2c variables +# interpereted within .conf files are in $vars{NAME} and that +# a warning will be printed if STUFF does not return 0. (adding a +# 'return 0;' at the end of STUFF is a workaround. +# @startperl@ +# @endperl@ +# treats everything between these tags as perl code, and evaluates it. +# @next@ +# restart foreach; should only be used inside a conditional. +# skips out of current conditional, then continues to skip to +# end for the current foreach clause. +# @if expression@ +# evaluates expression, and if expression is true processes +# contained part until appropriate @end@ is reached. If the +# expression is false, the next @elsif expression@ expression +# (if it exists) will be evaluated, until an expression is +# true. If no such expression exists and an @else@ +# clause is found, it will be evaluated. +# @ifconf file@ +# If the specified file can be found in the conf file search path, +# and if found processes contained part until an appropriate @end@ is +# found. As with a regular @if expression@, @elsif expression@ and +# @else@ can be used. +# @ifdir dir@ +# If the specified directory exists, process contained part until an +# appropriate @end@ is found. As with a regular @if expression@, +# @elsif expression@ and @else@ can be used. +# @define NAME@ +# @enddefine@ +# Memorizes "stuff" between the define and enddefine tags for +# later calling as NAME by @calldefine NAME@. +# @calldefine NAME@ +# Executes stuff previously memorized as NAME. +# @printf "expression" stuff1, stuff2, ...@ +# Like all the other printf's you know and love. +# @run FILE@ +# Sources the contents of FILE as a mib2c file, +# but does not affect current files opened. +# @include FILE@ +# Sources the contents of FILE as a mib2c file and appends its +# output to the current output. +# @prompt $var QUESTION@ +# Presents the user with QUESTION, expects a response and puts it in $var +# @print STUFF@ +# Prints stuff directly to the users screen (ie, not to where +# normal mib2c output goes) +# @quit@ +# Bail out (silently) +# @exit@ +# Bail out! +# +sub skippart { + my $endcount = 1; + my $arg = shift; + my $rtnelse = 0; + while ($arg =~ s/-(\w+)\s*//) { + $rtnelse = 1 if ($1 eq "else"); + } + while(get_next_line()) { + $currentline++; + $_ = process_vars($_) if ($debug); + print "$currentfile.$currentline:P$currentlevel:S$endcount.$rtnelse:$_" if ($debug); + next if ( /^\s*\#\#/ ); # noop, it's a comment + next if (! /^\s*\@/ ); # output + if (! /^\s*\@.*\@/ ) { + warn "$currentfile:$currentline contained a line that started with a @ but did not match any mib2c configuration tokens.\n"; + warn "(maybe missing the trailing @?)\n"; + warn "$currentfile:$currentline [$_]\n"; + } + elsif (/\@\s*end\@/) { + return "end" if ($endcount == 1); + $endcount--; + } + elsif (/\@\s*elseif.*\@/) { + m2c_die "use 'elsif' instead of 'elseif'\n"; + } + elsif (/\@\s*else\@/) { + return "else" if (($endcount == 1) && ($rtnelse == 1)); + } + elsif (/\@\s*elsif\s+([^\@]+)\@/) { + return "else" if (($endcount == 1) && ($rtnelse == 1) && (eval(process_vars($1)))); + } + elsif (/\@\s*(foreach|if|while)/) { + $endcount++; + } + } + print "skippart EOF\n"; + m2c_die "unbalanced code detected in skippart: EOF when $endcount levels deep" if($endcount != 1); + return "eof"; +} + +sub close_file { + my $name = shift; + if (!$name) { + print "close_file w/out name!\n"; + return; + } + if(!$outputs{$name}) { + print "no handle for $name\n"; + return; + } + $outputs{$name}->close(); + delete $outputs{$name}; +# print STDERR "closing $name\n" if (!$quiet); +} + +sub close_files { + foreach $name (keys(%outputs)) { + close_file($name); + } +} + +sub open_file { + my $multiple = shift; + my $spec = shift; + my $name = $spec; + $name =~ s/>//; + if ($multiple == 0) { + close_files(); + } + return if ($outputs{$name}); + $outputs{$name} = new IO::File; + $outputs{$name}->open(">$spec") || m2c_die "failed to open $name"; + print STDERR "writing to $name\n" if (!$quiet && !$written{$name}); + $written{$name} = '1'; +} + +sub process_file { + my ($file, $missingok, $keepvars) = (@_); + my $oldfh = $fh; + my $oldfile = $currentfile; + my $oldline = $currentline; + # keep old copy of @vars and just build on it. + my %oldvars; + + %oldvars = %vars if ($keepvars != 1); + + $file = find_conf($file,$missingok); + return if (! $file); + + $fh = open_conf($file); + $currentline = 0; + process("-balanced"); + $fh->close(); + + $fh = $oldfh; + $currentfile = $oldfile; + $currentline = $oldline; + + # don't keep values in replaced vars. Revert to ours. + %vars = %oldvars if ($keepvars != 1); +} + +sub get_next_line { + if ($#process_lines > -1) { + return $_ = shift @process_lines; + } + return $_ = <$fh>; +} + +sub do_tell { + my $stash; + $stash->{'startpos'} = $fh->tell(); + $stash->{'startline'} = $currentline; + @{$stash->{'lines'}} = @process_lines; + return $stash; +} + +sub do_seek { + my $stash = shift; + + # save current line number + $currentline = $stash->{'startline'}; + $fh->seek($stash->{'startpos'}, 0); # go to top of section. + + # save current process_lines state. + @process_lines = @{$stash->{'lines'}}; + + # save state of a number of variables (references), and new assignments + for (my $i = 0; $i <= $#_; $i += 2) { + push @{$stash->{'vars'}}, $_[$i], ${$_[$i]}; + ${$_[$i]} = $_[$i+1]; + } +} + +sub do_unseek { + my $stash = shift; + for (my $i = 0; $i <= $#{$stash->{'vars'}}; $i += 2) { + ${$stash->{'vars'}[$i]} = $stash->{'vars'}[$i+1]; + } +} + +sub do_a_loop { + my $stash = shift; + do_seek($stash, @_); + my $return = process(); + do_unseek($stash); + return $return; +} + +sub process { + my $arg = shift; + my $elseok = 0; + my $balanced = 0; + my $startlevel; + my $return = "eof"; + while ($arg =~ s/-(\w+)\s*//) { + $elseok = 1 if ($1 eq "elseok"); + $balanced = 1 if ($1 eq "balanced"); + } + + $currentlevel++; + $startlevel = $currentlevel; + if($balanced) { + $balanced = $currentlevel; + } + while(get_next_line()) { + $currentline++; + if ($debug) { +# my $line = process_vars($_); +# chop $line; + print "$currentfile.$currentline:P$currentlevel.$elseok:$return:$_"; + } + + next if (/^\s*\#\#/); # noop, it's a comment + if (! /^\s*\@/ ) { # output + my $line = process_vars($_); + foreach $file (values(%outputs)) { + print $file "$line"; + } + } #################################################################### + elsif (/\@\s*exit\@/) { # EXIT + close_files; + die "exiting at conf file ($currentfile:$currentline) request\n"; + } elsif (/\@\s*quit\@/) { # QUIT + close_files; + exit; + } elsif (/\@\s*debug\s+([^\@]+)\@/) { # DEBUG + if ($1 eq "on") { + $debug = 1; + } + else { + $debug = 0; + } + } elsif (/\@\s*strict token\s+([^\@]+)\@/) { # STRICT + if ($1 eq "on") { + $strict_unk_token = 1; + } + else { + $strict_unk_token = 0; + } + } elsif (/\@\s*balanced\@/) { # BALANCED + $balanced = $currentlevel; + } elsif (/\@\s*open\s+([^\@]+)\@/) { # OPEN + my $arg = $1; + my ($multiple) = (0); + while ($arg =~ s/-(\w+)\s+//) { + $multiple = 1 if ($1 eq 'multiple'); + } + my $spec = process_vars($arg); + open_file($multiple, $spec); + } elsif (/\@\s*close\s+([^\@]+)\@/) { # CLOSE + my $spec = process_vars($1); + close_file($spec); + } elsif (/\@\s*append\s+([^\@]+)\@/) { # APPEND + my $arg = $1; + my ($multiple) = (0); + while ($arg =~ s/-(\w+)\s+//) { + $multiple = 1 if ($1 eq 'multiple'); + } + my $spec = process_vars($arg); + $spec=">$spec"; + open_file($multiple,$spec); + } elsif (/\@\s*define\s*(.*)\@/) { # DEFINE + my $it = $1; + while (<$fh>) { + last if (/\@\s*enddefine\s*@/); + push @{$defines{$it}}, $_; + } + } elsif (/\@\s*calldefine\s+(\w+)@/) { + if ($#{$defines{$1}} == -1) { + warn "called a define of $1 which didn't exist\n"; + warn "$currentfile:$currentline [$_]\n"; + } else { + unshift @process_lines, @{$defines{$1}}; + } + } elsif (/\@\s*run (.*)\@/) { # RUN + my $arg = $1; + my ($again) = (0); + while ($arg =~ s/-(\w+)\s+//) { + $again = 1 if ($1 eq 'again'); +# if ($1 eq 'file') { +# my ($filearg) = ($arg =~ s/^(\w+)//); +# } + } + my $spec = process_vars($arg); + next if (!$again && $ranalready{$spec}); + $ranalready{$spec} = 1; + my %oldout = %outputs; + my %emptyarray; + %outputs = %emptyoutputs; + process_file($spec,0,0); + close_files; + %outputs = %oldout; + } elsif (/\@\s*push\@/) { # PUSH + my %oldout = %outputs; + my %emptyarray; + %outputs = %emptyoutputs; + process($arg); + close_files; + %outputs = %oldout; + } elsif (/\@\s*pop\s*\@/) { # POP + $return = "pop"; + last; + } elsif (/\@\s*include (.*)\@/) { # INCLUDE + my $arg = $1; + my ($missingok) = (0); + while ($arg =~ s/-(\w+)\s+//) { + $missingok = 1 if ($1 eq 'ifexists'); + } + my $spec = process_vars($arg); + process_file($spec,$missingok,1); + } elsif (/\@\s*if([a-z]*)\s+([^@]+)\@/) { # IF + my ($type,$arg,$ok) = ($1,$2,0); + # check condition based on type + if (! $type) { + $ok = eval(process_vars($arg)); + } elsif ($type eq conf) { + my $file = find_conf(process_vars($arg),1); # missingok + $ok = (-f $file); + } elsif ($type eq dir) { + $ok = (-d $arg); + } else { + m2c_die "unknown if modifier ($type)\n"; + } + # act on condition + if ($ok) { + $return = process("-elseok"); + } else { + $return = skippart("-else"); + $return = process("-elseok") if ($return eq "else"); + } + if ($return eq "next") { + $return = skippart(); + m2c_die("unbalanced code detected while exiting next/2 (returned $return)") if ($return ne "end"); +# $return = "next"; + last; + } + if (($return ne "end") && ($return ne "else")) { + m2c_die "unbalanced if / return $return\n"; + } + } elsif (/\@\s*elseif.*\@/) { # bogus elseif + m2c_die "error: use 'elsif' instead of 'elseif'\n"; + } elsif (/\@\s*els(e|if).*\@/) { # ELSE/ELSIF + if ($elseok != 1) { + chop $_; + m2c_die "unexpected els$1\n"; + } + $return = skippart(); + if ($return ne "end") { + m2c_die "unbalanced els$1 / rtn $rtn\n"; + } + $return = "else"; + last; + } elsif (/\@\s*next\s*\@/) { # NEXT + $return = skippart(); + m2c_die "unbalanced code detected while exiting next/1 (returned $return)" if ($return ne "end"); + $return = "next"; + last; + } elsif (/\@\s*end\@/) { # END + $return = "end"; + last; + } elsif (/\@\s*eval\s+\$(\w+)\s*=\s*([^\@]*)/) { # EVAL + my ($v, $e) = ($1, $2); +# print STDERR "eval: $e\n"; + my $e = process_vars($e); + $vars{$v} = eval($e); + if (!defined($vars{$v})) { + warn "$@"; + warn "$currentfile:$currentline [$_]\n"; + } + } elsif (/\@\s*perleval\s*(.*)\@/) { # PERLEVAL +# print STDERR "perleval: $1\n"; + my $res = eval($1); + if ($res) { + warn "$@"; + warn "$currentfile:$currentline [$_]\n"; + } + } elsif (/\@\s*startperl\s*\@/) { # STARTPERL + my $text; + while (get_next_line()) { + last if (/\@\s*endperl\s*\@/); + $text .= $_; + } + my $res = eval($text); + if ($res) { + warn "$@"; + warn "$currentfile:$currentline [$_]\n"; + } +# print STDERR "perleval: $1\n"; + } elsif (/\@\s*printf\s+(\"[^\"]+\")\s*,?(.*)\@/) { # PRINTF + my ($f, $rest) = ($1, $2); + $rest = process_vars($rest); + my @args = split(/\s*,\s*/,$rest); + $f = eval $f; +# print STDERR "printf: $f, ", join(", ",@args),"\n"; + foreach $file (values(%outputs)) { + printf $file (eval {$f}, @args); + } + } elsif (/\@\s*foreach\s+\$([^\@]+)\s+scalars*\s*\@/) { # SCALARS + my $var = $1; + my $stash = do_tell(); + my $scalar; + my @thekeys = keys(%scalars); + if ($#thekeys == -1) { + $return = skippart(); + } else { + if ($havenetsnmpoid) { + @thekeys = sort { + new NetSNMP::OID($a) <=> + new NetSNMP::OID($b) } @thekeys; + } + foreach $scalar (@thekeys) { + $return = do_a_loop($stash, \$vars{$var}, $scalar, + \$currentscalar, $scalar, + \$currentvar, $scalar); + } + } + m2c_die("foreach did not end with \@end@") if($return ne "end"); + } elsif (/\@\s*foreach\s+\$([^\@]+)\s+notifications*\s*\@/) { + my $var = $1; + my $stash = do_tell(); + my $notify; + my @thekeys = keys(%notifications); + if ($#thekeys == -1) { + $return = skippart(); + } else { + if ($havenetsnmpoid) { + @thekeys = sort { + new NetSNMP::OID($a) <=> + new NetSNMP::OID($b) } @thekeys; + } + foreach $notify (@thekeys) { + $return = do_a_loop($stash, \$vars{$var}, $notify, + \$currentnotify, $notify); + } + } + m2c_die("foreach did not end with \@end@") if($return ne "end"); + } elsif (/\@\s*foreach\s+\$([^\@]+)\s+varbinds\s*\@/) { + my $var = $1; + my $stash = do_tell(); + my $varbind; + if ($#{$notifyvars{$currentnotify}} == -1) { + $return = skippart(); + } else { + foreach $varbind (@{$notifyvars{$currentnotify}}) { + # print "looping on $var for $varbind\n"; + $return = do_a_loop($stash, \$vars{$var}, $varbind, + \$currentvarbind, $varbind, + \$currentvar, $varbind); + } + } + m2c_die("foreach did not end with \@end@") if($return ne "end"); + } elsif (/\@\s*foreach\s+\$([^\@]+)\s+tables*\s*\@/) { + my $var = $1; + my $stash = do_tell(); + my $table; + my @thekeys = keys(%tables); + if ($#thekeys == -1) { + $return = skippart(); + } else { + if ($havenetsnmpoid) { + @thekeys = sort { + new NetSNMP::OID($a) <=> + new NetSNMP::OID($b) } @thekeys; + } + foreach $table (@thekeys) { + $return = do_a_loop($stash, \$vars{$var}, $table, + \$currenttable, $table); + } + } + m2c_die("foreach did not end with \@end@ ($return)") if($return ne "end"); + } elsif (/\@\s*foreach\s+\$([^\@]+)\s+stuff\s*(.*)\@/) { + my $var = $1; + my $stuff = $2; + my @stuff = split(/[,\s]+/, $stuff); + my $stash = do_tell(); + if ($#stuff == -1) { + $return = skippart(); + } else { + foreach $st (@stuff) { + $return = do_a_loop($stash, \$vars{$var}, $st, + \$currentstuff, $st); + } + } + m2c_die("foreach did not end with \@end@ ($return)") if($return ne "end"); + } elsif (/\@\s*foreach\s+\$([^\@]+)\s+(column|index|internalindex|externalindex|nonindex)\s*\@/) { + my ($var, $type) = ($1, $2); + my $stash = do_tell(); + my $column; + if ($#{$tables{$currenttable}{$type}} == -1) { + $return = skippart(); + } else { + foreach $column (@{$tables{$currenttable}{$type}}) { + # print "looping on $var for $type -> $column\n"; + $return = do_a_loop($stash, \$vars{$var}, $column, + \$currentcolumn, $column, + \$currentvar, $column); + } + } + m2c_die("foreach did not end with \@end@") if($return ne "end"); + } elsif (/\@\s*foreach\s+\$([^\@]+)\s+\$([^\@]+)\s+range\s+([^\@]+)\@/) { + my ($svar, $evar, $node) = ($1, $2, $3); + $svar =~ s/,$//; + my $stash = do_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) { + $return = do_a_loop($stash, \$vars{$svar}, $range->{'low'}, + \$vars{$evar}, $range->{'high'}); + } + } else { + $return = skippart(); + } + m2c_die("foreach did not end with \@end@") if($return ne "end"); + } elsif (/\@\s*foreach\s+\$([^\@,]+)\s*,*\s+\$([^\@]+)\s+(enums*)\s*\@/) { + my ($varvar, $varval, $type) = ($1, $2, $3); + my $stash = do_tell(); + my $enum, $enum2; + + my @keys = sort { $SNMP::MIB{$currentvar}{'enums'}{$a} <=> + $SNMP::MIB{$currentvar}{'enums'}{$b} } (keys(%{$SNMP::MIB{$currentvar}{'enums'}})); + if ($#keys > -1) { + foreach $enum (@keys) { + ($enum2 = $enum) =~ s/-/_/g; + $return = do_a_loop($stash, \$vars{$varvar}, $enum2, + \$vars{$varval}, + $SNMP::MIB{$currentvar}{'enums'}{$enum}); + } + } else { + $return = skippart(); + } + m2c_die("foreach did not end with \@end@") if($return ne "end"); + } elsif (/\@\s*while([a-z]*)\s+([^@]+)\@/) { # WHILE + my ($type,$arg,$ok) = ($1,$2,0); + my $stash = do_tell(); + my $loop = 1; + my $everlooped = 0; + + while ($loop) { + # check condition based on type + if (! $type) { + $ok = eval(process_vars($arg)); + } elsif ($type eq conf) { + my $file = find_conf(process_vars($arg),1); # missingok + $ok = (-f $file); + } elsif ($type eq dir) { + $ok = (-d $arg); + } else { + m2c_die "unknown while modifier ($type)\n"; + } + + # act on condition + if ($ok) { + $return = do_a_loop($stash, \$vars{$type}, $ok, \$vars{$args}); + $everlooped = 1; + } else { + if (!$everlooped) { + $return = skippart(); + } + $loop = 0; + } + } + } elsif (/\@\s*prompt\s+\$(\S+)\s*(.*)\@/) { # PROMPT + my ($var, $prompt) = ($1, $2); + if (!$term) { + my $haveit = eval { require Term::ReadLine }; + if ($haveit) { + $term = new Term::ReadLine 'mib2c'; + } + } + if ($term) { + $vars{$var} = $term->readline(process_vars($prompt)); + } + } elsif (/\@\s*print\s+([^@]*)\@/) { # PRINT + my $line = process_vars($1); + print "$line\n"; + } else { + my $line = process_vars($_); + mib2c_output($line); + chop $_; + warn "$currentfile:$currentline contained a line that started with a @ but did not match any mib2c configuration tokens.\n"; + warn "(maybe missing the trailing @?)\n"; + warn "$currentfile:$currentline [$_]\n"; + m2c_die if ($strict_unk_token == 1); + } +# $return = "eof"; + } + print "< Balanced $balanced / level $currentlevel / rtn $return / $_\n" if($debug); + if((!$_) && ($return ne "eof")) { +# warn "switching return of '$return' to EOF\n" if($debug); + $return = "eof"; + } + if ($balanced) { + if(($balanced != $currentlevel) || ($return ne "eof")) { + m2c_die "\@balanced@ specified, but processing terminated with '$return' before EOF!"; + } + } + $currentlevel--; + return $return; +} + +sub mib2c_output { + my $line = shift; + foreach $file (values(%outputs)) { + print $file "$line"; + } +} + + +sub setup_data { + my $mib = shift; + if ($mib->{label} =~ /Table$/ && $#{$mib->{children}} != -1) { + my $tablename = $mib->{label}; + my $entry = $mib->{children}; + my $columns = $entry->[0]{children}; + my $augments = $entry->[0]{'augments'}; + 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'}; + } + if ($augments) { + my $mib = $SNMP::MIB{$augments} || + die "can't find info about augmented table $augments in table $tablename\n"; + $mib = $mib->{parent} || + die "can't find info about augmented table $augments in table $tablename\n"; + my $entry = $mib->{children}; + 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; + push @{$tables{$tablename}{'externalindex'}}, $index; + } + my $columns = $entry->[0]{children}; + } + else { + 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; + if("@{$tables{$tablename}{'column'}}" =~ /$index\b/ ) { +# print "idx INT $index\n"; + push @{$tables{$tablename}{'internalindex'}}, $index; + } else { +# print "idx EXT $index\n"; + push @{$tables{$tablename}{'externalindex'}}, $index; + } + } + } + foreach my $col (sort { $a->{'subID'} <=> $b->{'subID'} } @$columns) { + next if ( "@{$tables{$tablename}{'index'}}" =~ /$col->{'label'}\b/ ); + push @{$tables{$tablename}{'nonindex'}}, $col->{'label'}; + } +# print "indexes: @{$tables{$tablename}{'index'}}\n"; +# print "internal indexes: @{$tables{$tablename}{'internalindex'}}\n"; +# print "external indexes: @{$tables{$tablename}{'externalindex'}}\n"; +# print "non-indexes: @{$tables{$tablename}{'nonindex'}}\n"; + } else { + my $children = $mib->{children}; + if ($#children == -1 && $mib->{type}) { + # scalar + if ($mib->{type} eq "NOTIF" || + $mib->{type} eq "TRAP") { + my $notifyname = $mib->{label}; + my @varlist = (); + $notifications{$notifyname} = 1; + $notifyvars{$notifyname} = $mib->{varbinds}; + } else { + $scalars{$mib->{label}} = 1 if ($mib->{'access'} ne 'Notify'); + } + } 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]; +} + +sub find_conf { + my ($configfile, $missingok) = (@_); + + foreach my $d (@search_dirs, @def_search_dirs) { +# print STDERR "using $d/$configfile" if (-f "$d/$configfile"); + return "$d/$configfile" if (-f "$d/$configfile"); + } + return $configfile if (-f "$configfile"); + return if ($missingok); + + print STDERR "Can't find a configuration file called $configfile\n"; + print STDERR "(referenced at $currentfile:$currentline)\n" if ($currentfile); + print STDERR "I looked in:\n"; + print " " . join("\n ", @search_dirs, @def_search_dirs), "\n"; + exit 1; +} + +sub open_conf { + my $configfile = shift; +# process .conf file + if (! -f "$configfile") { + print STDERR "Can't find a configuration file called $configfile\n"; + exit 1; + } + $currentfile = $configfile; + my $fh = new IO::File; + $fh->open("$configfile"); + return $fh; +} + +sub count_scalars { + my @k = keys(%scalars); + return $#k + 1; +} + +sub count_tables { + my @k = keys(%tables); + return $#k + 1; +} + +sub count_columns { + my $table = shift; + return $#{$tables{$table}{'column'}} + 1; +} + +sub table_is_writable { + my $table = shift; + my $column; + my $result = 0; + foreach $column (@{$tables{$table}{'column'}}) { + if($SNMP::MIB{$column}{access} =~ /(ReadWrite|Create|WriteOnly)/) { + $result = 1; + last; + } + } + return $result; +} + +sub table_has_create { + my $table = shift; + my $column; + my $result = 0; + foreach $column (@{$tables{$table}{'column'}}) { + if($SNMP::MIB{$column}{access} =~ /(Create)/) { + $result = 1; + last; + } + } + return $result; +} + +sub table_has_rowstatus { + my $table = shift; + my $column; + my $result = 0; + foreach $column (@{$tables{$table}{'column'}}) { + if($SNMP::MIB{$column}{syntax} =~ /(RowStatus)/) { + $result = 1; + last; + } + } + return $result; +} + +sub table_has_lastchange { + my $table = shift; + my $column; + my $result = 0; + foreach $column (@{$tables{$table}{'column'}}) { + if(($SNMP::MIB{$column}{syntax} =~ /(TimeStamp)/) && + ($SNMP::MIB{$column}{label} =~ /(LastChange)/)) { + $result = 1; + last; + } + } + return $result; +} + +sub table_has_storagetype { + my $table = shift; + my $column; + my $result = 0; + foreach $column (@{$tables{$table}{'column'}}) { + if($SNMP::MIB{$column}{syntax} =~ /(StorageType)/) { + $result = 1; + last; + } + } + return $result; +} + +sub count_indexes { + my $table = shift; + return $#{$tables{$table}{'index'}} + 1; +} + +sub count_external_indexes { + my $table = shift; + return $#{$tables{$table}{'externalindex'}} + 1; +} + +sub count_notifications { + my @k = keys(%notifications); + return $#k + 1; +} + +sub count_varbinds { + my $notify = shift; + return $#{$notifyvars{$notify}} + 1; +} diff --git a/local/mib2c-conf.d/default-mfd-top.m2c b/local/mib2c-conf.d/default-mfd-top.m2c new file mode 100644 index 0000000..c2e2964 --- /dev/null +++ b/local/mib2c-conf.d/default-mfd-top.m2c @@ -0,0 +1,141 @@ +######################################################################## +## +## DEFAULTS (no blank lines allowed) +## +######################################################################## +## mark boundarys +@if "x$m2c_mark_boundary" eq "x"@ +@ eval $m2c_mark_boundary = 0@ +@end@ +## +@if "x$mfd_readme_verbose" eq "x"@ +@ eval $mfd_readme_verbose = 1@ +@end@ +@if "x$m2c_create_fewer_files" eq "x"@ +@ eval $m2c_create_fewer_files = 0@ +@end@ +@if "x$mfd_processing_types" eq "x"@ +@ eval $mfd_processing_types = "#"@ +@end@ +@if "x$m2c_code_verbose" eq "x"@ +@ eval $m2c_code_verbose = 0@ +@end@ +@if "x$m2c_defaults_dir" eq "x"@ +@ eval $m2c_defaults_dir = "defaults/"@ +@end@ +######################################################################## +## enum constants upper or lower case? (NODE_NAME vs node_name) +@if "x$m2c_const_lc" eq "x"@ +@ eval $m2c_const_lc = 0@ +@end@ +## +######################################################################## +## prefix for all enums (NODE_NAME vs XYZ_NODE_NAME) +@if "x$m2c_const_pfx" eq "x"@ +@ eval $m2c_const_pfx = ""@ # or "XYZ_" +@end@ +## +######################################################################## +## use temporary values in get routines, or direct pointers? +@if "x$m2c_get_use_temp" eq "x"@ +@ eval $m2c_get_use_temp = 0@ +@end@ +## +######################################################################## +######################################################################## +## +## CODING STYLE +## +######################################################################## +######################################################################## +## allow for different style enums (#define vs const) +@if "x$m2c_const_dcl" eq "x"@ +@ eval $m2c_const_dcl = "#define"@ # or "const int" +@end@ +@if "m2c_const_del" eq "x"@ +@ eval $m2c_const_del = ""@ # or "=" +@end@ +@if "x$m2c_const_sfx" eq "x"@ +@ eval $m2c_const_sfx = ""@ # or ";" +@end@ +## +## set defaults for mfd +## +@if "x$user_mfd_default_table_access" eq "x" @ +@ eval $mfd_default_table_access = "container-cached"@ +@else@ +@ eval $mfd_default_table_access = "$user_mfd_default_table_access"@ +@end@ +## +@if "x$user_mfd_default_table_skip_mapping" eq "x" @ +@ eval $mfd_default_table_skip_mapping = 1@ +@else@ +@ eval $mfd_default_table_skip_mapping = $user_mfd_default_table_skip_mapping@ +@end@ +## +@if "x$user_mfd_default_data_context" eq "x" @ +@ eval $mfd_default_data_context = "generated"@ +@else@ +@ eval $mfd_default_data_context = "$user_mfd_default_data_context"@ +@end@ +## +@if "x$user_mfd_default_context_reg" eq "x" @ +@ eval $mfd_default_context_reg = "netsnmp_data_list"@ +@else@ +@ eval $mfd_default_context_reg = "$user_mfd_default_context_reg"@ +@end@ +## +@if "x$user_mfd_default_data_allocate" eq "x" @ +@ eval $mfd_default_data_allocate = 0@ +@else@ +@ eval $mfd_default_data_allocate = $user_mfd_default_data_allocate@ +@end@ +## +@if "x$user_mfd_default_data_cache" eq "x" @ +@ eval $mfd_default_data_cache = 1@ +@else@ +@ eval $mfd_default_data_cache = $user_mfd_default_data_cache@ +@end@ +## +@if "x$user_mfd_default_data_sparse" eq "x" @ +@ eval $mfd_default_data_sparse = 0@ +@else@ +@ eval $mfd_default_data_sparse = $user_mfd_default_data_sparse@ +@end@ +@if "x$user_mfd_default_undo_embed" eq "x" @ +@ eval $mfd_default_undo_embed = 0@ +@else@ +@ eval $mfd_default_undo_embed = $user_mfd_default_undo_embed@ +@end@ +## +@if "x$user_mfd_default_data_init" eq "x" @ +@ eval $mfd_default_data_init = 1@ +@else@ +@ eval $mfd_default_data_init = $user_mfd_default_data_init@ +@end@ +## +@if "x$user_mfd_default_data_transient" eq "x" @ +@ eval $mfd_default_data_transient = 2@ # TRANSIENT +@else@ +@ eval $mfd_default_data_transient = $user_mfd_default_data_transient@ +@end@ +## +@if "x$user_mfd_default_include_examples" eq "x" @ +@ eval $mfd_default_include_examples = 1@ +@else@ +@ eval $mfd_default_include_examples = $user_mfd_default_include_examples@ +@end@ +@if "x$m2c_data_cache" eq "x"@ +@ eval $m2c_data_cache = 0@ +@end@ +## +@if "x$user_mfd_default_generate_makefile" eq "x" @ +@ eval $mfd_default_generate_makefile = 0@ +@else@ +@ eval $mfd_default_generate_makefile = $user_mfd_default_generate_makefile@ +@end@ +@if "x$user_mfd_default_generate_subagent" eq "x" @ +@ eval $mfd_default_generate_subagent = 0@ +@else@ +@ eval $mfd_default_generate_subagent = $user_mfd_default_generate_subagent@ +@end@ diff --git a/local/mib2c-conf.d/details-enums.m2i b/local/mib2c-conf.d/details-enums.m2i new file mode 100644 index 0000000..3b0f152 --- /dev/null +++ b/local/mib2c-conf.d/details-enums.m2i @@ -0,0 +1,80 @@ +############################################################# -*- c -*- +## generic include for enums. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +@ifconf $node.syntax.m2i@ +@ include $node.syntax.m2i@ +@else@ +## +## Generating enums +## +## Examples: +## +## enums syntax perltype net-snmp type cdecl m2c_decl +## ----- -------- -------- ------------- ----- ------- +## 1 SomeTC BITS ASN_OCTET_STR char u_long +## 1 INTEGER INTEGER ASN_INTEGER long u_long +## 1 RowStatus INTEGER ASN_INTEGER long u_long +## +/************************************************************* + * constants for enums for the MIB node + * $node ($node.syntax / $node.type) + * + * since a Textual Convention may be referenced more than once in a + * MIB, protect againt redefinitions of the enum values. + */ +## +#ifndef ${m2c_de_pfx}_ENUMS +#define ${m2c_de_pfx}_ENUMS + +@ eval $m2c_mask=""@ +@ foreach $e $v enum@ +@ include m2c_setup_enum.m2i@ +@ if "$node.perltype" eq "BITS"@ +@ if $v > 31@ +@ print ** ACK! I cannot handle BITS longer than 4 bytes!@ +@ exit@ +@ end@ +@ if "x$m2c_mask" eq "x"@ +@ eval $m2c_mask="$m2c_ename"@ +@ else@ +@ eval $m2c_mask="$m2c_mask | $m2c_ename"@ +@ end@ +$m2c_const_dcl $m2c_ename $m2c_const_del (1 << (31-$v)) $m2c_const_sfx +@ else@ +$m2c_const_dcl $m2c_ename $m2c_const_del $v $m2c_const_sfx +@ end@ +@ end@ # for each + +#endif /* ${m2c_de_pfx}_ENUMS */ + +@ if "$node.perltype" eq "BITS"@ +$m2c_const_dcl $m2c_enum_mask $m2c_const_del ($m2c_mask) + +@ end@ +@ if ($m2c_node_skip_mapping != 1) && ($node.enums == 1)@ + /* + * TODO:140:o: Define your interal representation of $node enums. + * (used for value mapping; see notes at top of file) + */ +@ foreach $e $v enum@ +@ include m2c_setup_enum.m2i@ +@ if ("$node.perltype" ne "BITS")@ +$m2c_const_dcl INTERNAL_$context.uc_$m2c_iname $m2c_const_del $v $m2c_const_sfx +@ else@ +$m2c_const_dcl INTERNAL_$context.uc_$m2c_iname $m2c_const_del (0x01 << $v) $m2c_const_sfx +@ end@ +@ end@ // foreach + +@ end@ // skip mapping / enums + +@end@ # ! syntax include +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/details-node.m2i b/local/mib2c-conf.d/details-node.m2i new file mode 100644 index 0000000..0f3e2a2 --- /dev/null +++ b/local/mib2c-conf.d/details-node.m2i @@ -0,0 +1,102 @@ +############################################################# -*- c -*- +## Generic include for columns. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +/*--------------------------------------------------------------------- + * $node.module::$node.parent.$node + * $node is subid $node.subid of $node.parent. + * Its status is $node.status, and its access level is $node.access. + * OID: $node.objectID + * Description: +$node.description + * +@if $m2c_node_detail == 1@ + * node -- name $node + * node.parent -- label of the parent $node.parent + * node.objectID -- dotted full OID $node.objectID + * node.commaoid -- comma separated OID $node.commaoid + * node.subid -- last oid component $node.subid + * node.oidlength-- length of the oid $node.oidlength + * node.syntax -- node's syntax $node.syntax + * node.perltype -- node's perl type $node.perltype + * node.type -- node's ASN_XXX type $node.type + * node.decl -- C data type $m2c_decl ($node.decl) + * node.settable -- 1 if it's writable $node.settable + * node.noaccess -- 1 if not-accessible $node.noaccess + * node.access -- node's access type $node.access + * node.status -- node's status $node.status + * node.isscalar -- returns 1 if scalar $node.isscalar + * node.iscolumn -- returns 1 if column $node.iscolumn + * node.enums -- $node.enums + * +@end@ + * Attributes: + * accessible $node.accessible isscalar $node.isscalar enums $node.enums hasdefval $node.hasdefval + * readable $node.readable iscolumn $node.iscolumn ranges $node.ranges hashint $node.hashint + * settable $node.settable +@if $node.hasdefval == 1@ + * defval: $node.defval +@end@ +@if $node.hashint == 1@ + * hint: $node.hint +@end@ + * +@if $node.enums == 1@ +@ eval $m2c_evals = ""@ +@ eval $m2c_first = 1@ +@ foreach $e $v enum@ +@ if $m2c_first == 1@ +@ eval $m2c_first = 0@ +@ else@ +@ eval $m2c_evals = "$m2c_evals,"@ +@ end@ +@ eval $m2c_evals = "$m2c_evals $e($v)"@ +@ end@ + * Enum range: $node.enumrange. Values: $m2c_evals +@elsif $node.ranges == 1@ +@ eval $m2c_range_max = 0@ +@ eval $m2c_evals = ""@ +@ eval $m2c_first = 1@ +@ foreach $a $b range $node@ +@ if $m2c_first == 1@ +@ eval $m2c_first = 0@ +@ else@ +@ eval $m2c_evals = "$m2c_evals,"@ +@ end@ +@ if $a == $b@ +@ eval $m2c_evals = "$m2c_evals $a"@ +@ else@ +@ eval $m2c_evals = "$m2c_evals $a - $b"@ +@ end@ +@ eval $m2c_range_max = max($m2c_range_max,$b)@ +@ end@ + * Ranges: $m2c_evals; +@end@ #ranges + * + * Its syntax is $node.syntax (based on perltype $node.perltype) + * The net-snmp type is $node.type. The C type decl is $node.decl ($m2c_decl) +@if $node.needlength == 1@ +@ if $node.ranges == 1@ + * This data type requires a length. (Max $m2c_range_max) +@ else@ + * This data type requires a length. +@ end@ +@end@ +@if $node.noaccess@ + * + * + * + * NOTE: NODE $node IS NOT ACCESSIBLE + * + * +@end@ + */ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/details-table.m2i b/local/mib2c-conf.d/details-table.m2i new file mode 100644 index 0000000..06df7ef --- /dev/null +++ b/local/mib2c-conf.d/details-table.m2i @@ -0,0 +1,25 @@ +############################################################# -*- c -*- +## generic include for tables. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +/********************************************************************** + ********************************************************************** + *** + *** Table $context + *** + ********************************************************************** + **********************************************************************/ +/* + * $context.module::$context is subid $context.subid of $context.parent. + * Its status is $context.status. + * OID: $context.objectID, length: $context.oidlength +*/ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-ctx-copy.m2i b/local/mib2c-conf.d/generic-ctx-copy.m2i new file mode 100644 index 0000000..c959637 --- /dev/null +++ b/local/mib2c-conf.d/generic-ctx-copy.m2i @@ -0,0 +1,33 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## + /* +@if $m2c_node_needlength == 1@ + * copy $node and ${node}_len data +@else@ + * copy $node data +@end@ + * set ${m2c_ctx_lh} from ${m2c_ctx_rh} + */ +@if ($m2c_include_examples != 0) || ("$m2c_data_context" eq "generated")@ +@ if $m2c_node_needlength == 0@ + ${m2c_ctx_lh} = ${m2c_ctx_rh}; +@ else@ + memcpy( ${m2c_ctx_lh}, ${m2c_ctx_rh}, + (${m2c_ctx_rhs} * sizeof(${m2c_ctx_lh}[0]))); + ${m2c_ctx_lhs} = ${m2c_ctx_rhs}; +@ end@ # need length +@end@ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-ctx-get.m2i b/local/mib2c-conf.d/generic-ctx-get.m2i new file mode 100644 index 0000000..ef795c3 --- /dev/null +++ b/local/mib2c-conf.d/generic-ctx-get.m2i @@ -0,0 +1,106 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +##/* +## This include will generate the code needed to assign data from +## a generated data context to a parameter reference. +## +## EXAMPLE (prototype generated elsewhere) +## int +## ifName_get(ifXTable_ctx * ctx, char **ifName_ptr_ptr, +## size_t * ifName_len_ptr) { +## +## +## m2c_node_lh : temp_ifName / (*ifName_ptr_ptr) +## m2c_node_lhs: temp_ifName_len / (*ifName_len_ptr); +## m2c_ctx_rh : ctx->data. +## node : ifName +## +## if (temp_ifName_len < ctx->data.ifName_len) { +## temp_ifName = malloc(ctx->data.ifName_len); +## } +## temp_ifName_len = ctx->data.ifName_len; +## memcpy(temp_ifName, ctx->data.ifName, temp_ifName_len); +##*/ +@if "$m2c_data_context" ne "generated"@ + /** WARNING: this code might not work for $m2c_data_context */ +@end@ +##/* set up for length/copy conversions for various cases. +## length mod applies to left hand side. copy mod applies to right hand side +## +##*/ +@if ("$m2c_ctx_rhu" ne "elements") && ("$m2c_ctx_rhu" ne "bytes")@ +@ print Invalid rh units '$m2c_ctx_rhu'@ +@ exit@ +@end@ +@if ("$m2c_ctx_lhu" ne "elements") && ("$m2c_ctx_lhu" ne "bytes")@ +@ print Invalid lh units '$m2c_ctx_lhu'@ +@ exit@ +@end@ +@if "$m2c_ctx_rhu" ne "$m2c_ctx_lhu"@ +##/* elements = bytes, length mod="/sizeof", copy mult="" */ +@ if "$m2c_ctx_lhu" eq "elements"@ +@ eval $m2c_ctx_lm = "/ sizeof($m2c_ctx_rh[0])"@ +@ eval $m2c_ctx_cm = ""@ +@ else@ +##/* bytes = elements, length mod="*sizeof", copy mult="sizeof" */ +@ eval $m2c_ctx_lm = "* sizeof($m2c_ctx_rh[0])"@ +@ eval $m2c_ctx_cm = "* sizeof($m2c_ctx_rh[0])"@ +@ end@ +@else@ +##/* elements = elements, length mod="", copy mult="sizeof" */ +@ if "$m2c_ctx_lhu" eq "elements"@ +@ eval $m2c_ctx_lm = ""@ +@ eval $m2c_ctx_cm = "* sizeof($m2c_ctx_rh[0])"@ +@ else@ +##/* bytes = bytes, length mod="", copy mult="" */ +@ eval $m2c_ctx_lm = ""@ +@ eval $m2c_ctx_cm = ""@ +@ end@ +@end@ +@if $m2c_node_needlength == 1@ + /* + * make sure there is enough space for $node data + */ + if ((NULL == $m2c_ctx_lh) || + ($m2c_ctx_lhs < + ($m2c_ctx_rhs$m2c_ctx_lm))) { +@ if $m2c_node_realloc == 0@ + snmp_log(LOG_ERR,"not enough space for value ($m2c_ctx_rh)\n"); + return MFD_ERROR; +@ else@ + /* + * allocate space for $node data + */ +@ if $m2c_node_realloc == 1@ + $m2c_ctx_lh = realloc($m2c_ctx_lh, $m2c_ctx_rhs$m2c_ctx_cm ); +@ else@ + $m2c_ctx_lh = malloc($m2c_ctx_rhs$m2c_ctx_cm); +@ end@ + if(NULL == $m2c_ctx_lh) { + snmp_log(LOG_ERR,"could not allocate memory ($m2c_ctx_rh)\n"); + return MFD_ERROR; + } +@ end@ + } + $m2c_ctx_lhs = $m2c_ctx_rhs$m2c_ctx_lm; + memcpy( $m2c_ctx_lh, $m2c_ctx_rh, $m2c_ctx_rhs$m2c_ctx_cm ); +@else@ +@ if $node.decl =~ /U64/i@ # ASN_COUNTER64 + ${m2c_ctx_lh}.high = ${m2c_ctx_rh}.high; + ${m2c_ctx_lh}.low = ${m2c_ctx_rh}.low; +@ else@ + $m2c_ctx_lh = $m2c_ctx_rh; +@ end@ +@end@ # length +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-ctx-set.m2i b/local/mib2c-conf.d/generic-ctx-set.m2i new file mode 100644 index 0000000..86c99a0 --- /dev/null +++ b/local/mib2c-conf.d/generic-ctx-set.m2i @@ -0,0 +1,29 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## + /* + * TODO:461:M: |-> Set $node value. + * set $node value in $m2c_data_item_base + */ +@if ($m2c_include_examples != 0) || ("$m2c_data_context" eq "generated")@ +@ if $m2c_node_needlength == 0@ + ${m2c_data_item}$node = $m2c_node_srh; +@ else@ + memcpy( ${m2c_data_item}$node, $m2c_node_srh, $m2c_node_srhs ); + /** convert bytes to number of $m2c_decl */ + ${m2c_data_item}${node}_len = $m2c_node_srhs / sizeof(${m2c_node_srh}[0]); +@ end@ # need length +@end@ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-data-allocate.m2i b/local/mib2c-conf.d/generic-data-allocate.m2i new file mode 100644 index 0000000..556defe --- /dev/null +++ b/local/mib2c-conf.d/generic-data-allocate.m2i @@ -0,0 +1,62 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +/* + * ${context}_allocate_data + * + * Purpose: create new ${context}_data. + */ +${context}_data * +${context}_allocate_data(void) +{ +@if $m2c_gda_todo_suppress != 1@ + /* + * TODO:201:r: |-> allocate memory for the $context data context. + */ +@end@ +@if $m2c_data_context != "generated"@ + /** this might not be right for $m2c_data_context */ +@end@ + ${context}_data *rtn = SNMP_MALLOC_TYPEDEF(${context}_data); + + DEBUGMSGTL(("verbose:${context}:${context}_allocate_data","called\n")); + + if(NULL == rtn) { + snmp_log(LOG_ERR, "unable to malloc memory for new " + "${context}_data.\n"); + } + + return rtn; +} /* ${context}_allocate_data */ + +/* + * ${context}_release_data + * + * Purpose: release ${context} data. + */ +void +${context}_release_data(${context}_data *data) +{ + DEBUGMSGTL(("verbose:${context}:${context}_release_data","called\n")); + +@if $m2c_gda_todo_suppress != 1@ + /* + * TODO:202:r: |-> release memory for the $context data context. + */ +@end@ + free(data); +} /* ${context}_release_data */ + +@eval $m2c_gda_todo_suppress = 0@ # reset +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-data-context.m2i b/local/mib2c-conf.d/generic-data-context.m2i new file mode 100644 index 0000000..4152e91 --- /dev/null +++ b/local/mib2c-conf.d/generic-data-context.m2i @@ -0,0 +1,51 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +/**********************************************************************/ +/* + * TODO:110:r: |-> Review ${context} data context structure. + * This structure is used to represent the data for $context. + */ +## +@if "$m2c_data_context" eq "generated"@ +/* + * This structure contains storage for all the columns defined in the + * $context. + */ +typedef struct ${context}_data_s { + +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ + /* + * $m2c_node_summary + */ +@ if $m2c_node_needlength == 0@ + $m2c_decl $node; +@ else@ + $m2c_decl $node[$m2c_node_maxlen]; +size_t ${node}_len; /* # of $m2c_decl elements, not bytes */ +@ end@ + +@ end@ # foreach nonindex +} ${context}_data; +@elsif "$m2c_data_context" eq "unknown"@ + /* + * update typedef to correct pointer type. + * (or add @eval $@m2c_data_context = "TYPE"@ and regenerate code) */ +typedef void ${context}_data; +@else@ +typedef $m2c_data_context ${context}_data; +@end@ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-get-U64.m2i b/local/mib2c-conf.d/generic-get-U64.m2i new file mode 100644 index 0000000..791c79e --- /dev/null +++ b/local/mib2c-conf.d/generic-get-U64.m2i @@ -0,0 +1,14 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-get-char.m2i b/local/mib2c-conf.d/generic-get-char.m2i new file mode 100644 index 0000000..e63e686 --- /dev/null +++ b/local/mib2c-conf.d/generic-get-char.m2i @@ -0,0 +1,49 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +## enums first +@if $m2c_node_skip_mapping == -1@ +@ eval $m2c_node_skip_mapping = 1@ +@end@ +@if ($node.enums == 1) && ("$node.perltype" eq "BITS")@ + /* + * TODO:242:o: update or replace BITS tests (get). + * If $node data is stored in SNMP BIT order, individual + * bit tests are redundant, and you can do a straight copy. If not, then + * update each if condition to test the correct bit. + * + * NOTE WELL: setting bit '0' for: + * C 0x0000001 + * SNMP 0x8000000 + * +@ if $m2c_node_skip_mapping != 1@ + * define correct bit to test for all INTERNAL_* defines in the + * ${context} enum or contants header file. + */ +$example_start + $m2c_node_lh = 0; +@ foreach $e $v enum@ +@ include m2c_setup_enum.m2i@ + if ($m2c_ctx_rh & INTERNAL_$context.uc_$m2c_iname) { + $m2c_node_lh |= $m2c_ename; + } +@ end@ # for each +$example_end +@ else@ + * assuming generated code keeps $node BITS in SNMP order. + */ + $m2c_node_lh = @m2c_ctx_rh; +@ end@ +@end@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-get-decl-bot.m2i b/local/mib2c-conf.d/generic-get-decl-bot.m2i new file mode 100644 index 0000000..32b4e68 --- /dev/null +++ b/local/mib2c-conf.d/generic-get-decl-bot.m2i @@ -0,0 +1,22 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@if $m2c_get_use_temp == 1@ + /* copy temporary value to passed parameter */ + (* $m2c_node_param_ref_name) = $m2c_node_lh; +@ if $m2c_node_needlength == 1@ + (* $m2c_node_param_ref_lname) = $m2c_node_lhs; +@ end@ +@end@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-get-decl.m2i b/local/mib2c-conf.d/generic-get-decl.m2i new file mode 100644 index 0000000..0568e1c --- /dev/null +++ b/local/mib2c-conf.d/generic-get-decl.m2i @@ -0,0 +1,43 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@if $m2c_get_use_temp == 1@ + /* + * Define temporary variable(s). If speed/efficency is an issue, + * remove this code and deal with the pointer directly. + * (set $@m2c_get_use_temp = 0 in your conf file to turn off) + */ +@ if $m2c_node_needlength == 1@ + $m2c_decl * $m2c_node_lh; + size_t $m2c_node_lhs; +@ else@ + $m2c_decl $m2c_node_lh; +@ end@ + +@end@ +@if $m2c_node_needlength == 1@ + /** we should have a non-NULL pointer and enough storage */ + netsnmp_assert( (NULL != $m2c_node_param_ref_name) && (NULL != *$m2c_node_param_ref_name)); + netsnmp_assert( NULL != $m2c_node_param_ref_lname ); +@else@ + /** we should have a non-NULL pointer */ + netsnmp_assert( NULL != $m2c_node_param_ref_name ); +@end@ + +@if ($m2c_get_use_temp == 1) && ($m2c_node_needlength == 1)@ + $m2c_node_lh = (* $m2c_node_param_ref_name); + $m2c_node_lhs = (* $m2c_node_param_ref_lname); + +@end@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-get-long.m2i b/local/mib2c-conf.d/generic-get-long.m2i new file mode 100644 index 0000000..791c79e --- /dev/null +++ b/local/mib2c-conf.d/generic-get-long.m2i @@ -0,0 +1,14 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-get-oid.m2i b/local/mib2c-conf.d/generic-get-oid.m2i new file mode 100644 index 0000000..56dd485 --- /dev/null +++ b/local/mib2c-conf.d/generic-get-oid.m2i @@ -0,0 +1,18 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@if $m2c_node_skip_mapping == -1@ +@ eval $m2c_node_skip_mapping = 0@ +@end@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-header-bottom.m2i b/local/mib2c-conf.d/generic-header-bottom.m2i new file mode 100644 index 0000000..e00e174 --- /dev/null +++ b/local/mib2c-conf.d/generic-header-bottom.m2i @@ -0,0 +1,21 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## + +#ifdef __cplusplus +} +#endif + +#endif /* $name.uc_H */ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-header-top.m2i b/local/mib2c-conf.d/generic-header-top.m2i new file mode 100644 index 0000000..a4d3bd8 --- /dev/null +++ b/local/mib2c-conf.d/generic-header-top.m2i @@ -0,0 +1,22 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +#ifndef $name.uc_H +#define $name.uc_H + +#ifdef __cplusplus +extern "C" { +#endif + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-source-includes.m2i b/local/mib2c-conf.d/generic-source-includes.m2i new file mode 100644 index 0000000..9341cf9 --- /dev/null +++ b/local/mib2c-conf.d/generic-source-includes.m2i @@ -0,0 +1,24 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +/* standard Net-SNMP includes */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> + +/* include our parent header */ +#include "${name}.h" + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-table-constants.m2c b/local/mib2c-conf.d/generic-table-constants.m2c new file mode 100644 index 0000000..36e23d3 --- /dev/null +++ b/local/mib2c-conf.d/generic-table-constants.m2c @@ -0,0 +1,44 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_create_fewer_files != 1@ +@ foreach $table table@ +@ include m2c_setup_table.m2i@ +@ run generic-table-oids.m2c@ +@ run generic-table-enums.m2c@ +@ end@ # table +######################################################################## +@else@ +@ eval $hack = "Id"@ +@ eval $m2c_save = "$name"@ +@ eval $name = "${m2c_save}_constants"@ +@ open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + * + * $$hack:$ + */ +######################################################################## +@ if $m2c_mark_boundary == 1@ +/** START header generated by $RCSfile$ $Revision$ */ +@ end@ +######################################################################## +@ include generic-header-top.m2i@ +@ eval $name = "$m2c_save"@ +@ foreach $table table@ +@ include m2c_setup_table.m2i@ +@ include generic-table-oids.m2c@ +@ include generic-table-enums.m2c@ +@ end@ # table +@ eval $m2c_save = "$name"@ +@ eval $name = "${m2c_save}_oids"@ +@ include generic-header-bottom.m2i@ +@ eval $name = "$m2c_save"@ +######################################################################## +@ if $m2c_mark_boundary == 1@ +/** END header generated by $RCSfile$ $Revision$ */ +@ end@ +@end@ // m2c_create_fewer_files diff --git a/local/mib2c-conf.d/generic-table-enums.m2c b/local/mib2c-conf.d/generic-table-enums.m2c new file mode 100644 index 0000000..92b2132 --- /dev/null +++ b/local/mib2c-conf.d/generic-table-enums.m2c @@ -0,0 +1,63 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_create_fewer_files != 1@ +@eval $hack = "Id"@ +@eval $m2c_save = "$name"@ +@eval $name = "${name}_enums"@ +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + * + * $$hack:$ + */ +@include generic-header-top.m2i@ +@eval $name = "$m2c_save"@ +@end@ // m2c_create_fewer_files +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START header generated by $RCSfile$ $Revision$ */ +@end@ +## + /* + * NOTES on enums + * ============== + * + * Value Mapping + * ------------- + * If the values for your data type don't exactly match the + * possible values defined by the mib, you should map them + * below. For example, a boolean flag (1/0) is usually represented + * as a TruthValue in a MIB, which maps to the values (1/2). + * + */ +## +## +/************************************************************************* + ************************************************************************* + * + * enum definitions for table $context + * + ************************************************************************* + *************************************************************************/ + +@ foreach $node column@ +@ include m2c_setup_node.m2i@ +@ if $node.enums == 1@ +@ include details-enums.m2i@ +@ end@ +@ end@ # column + +@if $m2c_create_fewer_files != 1@ +@eval $m2c_save = "$name"@ +@eval $name = "${name}_enums"@ +@include generic-header-bottom.m2i@ +@eval $name = "$m2c_save"@ +@end@ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END header generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-table-indexes-from-oid.m2i b/local/mib2c-conf.d/generic-table-indexes-from-oid.m2i new file mode 100644 index 0000000..758bda2 --- /dev/null +++ b/local/mib2c-conf.d/generic-table-indexes-from-oid.m2i @@ -0,0 +1,70 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +/** + * extract ${context} indexes from a netsnmp_index + * + * @retval SNMP_ERR_NOERROR : no error + * @retval SNMP_ERR_GENERR : error + */ +int +${context}_index_from_oid(netsnmp_index *oid_idx, + ${context}_mib_index *mib_idx) +{ +@include generic-table-indexes-varbind-setup.m2i@ + + DEBUGMSGTL(("verbose:${context}:${context}_index_from_oid","called\n")); + + /* + * parse the oid into the individual index components + */ + err = parse_oid_indexes( oid_idx->oids, oid_idx->len, + &var_$m2c_dii_first ); + if (err == SNMP_ERR_NOERROR) { + /* + * copy out values + */ +@ eval $m2c_node_name = ""@ # purge node name to re-eval $m2c_node_var_name +@ foreach $node index@ +@ eval $m2c_node_var_name = "var_${node}."@ +@ include m2c_setup_node.m2i@ +@ if $m2c_node_needlength == 1@ + /* + * NOTE: val_len is in bytes, ${node}_len might not be + */ + if(var_${node}.val_len > sizeof(mib_idx->$node)) + err = SNMP_ERR_GENERR; + else { + memcpy(mib_idx->${node}, var_${node}.val.string, var_${node}.val_len); + mib_idx->${node}_len = var_${node}.val_len / sizeof(mib_idx->${node}[0]); + } +@ else@ + mib_idx->$node = $m2c_node_var_val; +@ end@ +@ end@ # foreach +@ eval $m2c_node_var_name = ""@ #reset custom name +@ eval $m2c_node_name = ""@ # purge node name to re-eval $m2c_node_var_name + + + } + + /* + * parsing may have allocated memory. free it. + */ + snmp_reset_var_buffers( &var_$m2c_dii_first ); + + return err; +} /* ${context}_index_from_oid */ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-table-indexes-set.m2i b/local/mib2c-conf.d/generic-table-indexes-set.m2i new file mode 100644 index 0000000..34f9c7d --- /dev/null +++ b/local/mib2c-conf.d/generic-table-indexes-set.m2i @@ -0,0 +1,123 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@eval $gtis_tmp=""@ +@foreach $node index@ +@ include m2c_setup_node.m2i@ +@ eval $gtis_tmp="$gtis_tmp, $m2c_node_param_val"@ +@end@ # for each index +######################################################################## +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'h'@ + +int ${context}_indexes_set_tbl_idx(${context}_mib_index *tbl_idx$gtis_tmp); +int ${context}_indexes_set(${context}_rowreq_ctx *rowreq_ctx$gtis_tmp); + +@end@ // m2c_processing_type eq 'h' +######################################################################## +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'c'@ +/** + * set mib index(es) + * + * @param tbl_idx mib index structure +@foreach $node index@ +@ if $node.needlength == 1@ + * @param ${node}_ptr + * @param ${node}_ptr_len +@else@ + * @param ${node}_val +@end@ +@end@ + * + * @retval MFD_SUCCESS : success. + * @retval MFD_ERROR : other error. + * + * @remark + * This convenience function is useful for setting all the MIB index + * components with a single function call. It is assume that the C values + * have already been mapped from their native/rawformat to the MIB format. + */ +int +${context}_indexes_set_tbl_idx(${context}_mib_index *tbl_idx$gtis_tmp) +{ + DEBUGMSGTL(("verbose:${context}:${context}_indexes_set_tbl_idx","called\n")); + +@foreach $node index@ +@ include m2c_setup_node.m2i@ +## table indexes are not allocated pointers, so do not allow realloc here +@eval $m2c_node_realloc = 0@ // fail + /* $m2c_node_summary */ +@ eval $m2c_ctx_lh = "tbl_idx->$node"@ +@ eval $m2c_ctx_lhs = "tbl_idx->${node}_len"@ +@ eval $m2c_ctx_lhu="elements"@ +@ eval $m2c_ctx_rh = "$m2c_node_param_val_name"@ +@ eval $m2c_ctx_rhs = "$m2c_node_param_val_lname"@ +@ eval $m2c_ctx_rhu="elements"@ +@ if $m2c_node_needlength == 1@ + $m2c_ctx_lhs = sizeof($m2c_ctx_lh)/sizeof($m2c_ctx_lh[0]); /* max length */ +@ end@ +## also, assume mapping already done +@ include generic-ctx-get.m2i@ +##@ include generic-value-map.m2i@ + +@end@ // for each column + + return MFD_SUCCESS; +} /* ${context}_indexes_set_tbl_idx */ + +/** + * @internal + * set row context indexes + * + * @param reqreq_ctx the row context that needs updated indexes + * + * @retval MFD_SUCCESS : success. + * @retval MFD_ERROR : other error. + * + * @remark + * This function sets the mib indexs, then updates the oid indexs + * from the mib index. + */ +int +${context}_indexes_set(${context}_rowreq_ctx *rowreq_ctx$gtis_tmp) +{ + DEBUGMSGTL(("verbose:${context}:${context}_indexes_set","called\n")); + + if(MFD_SUCCESS != ${context}_indexes_set_tbl_idx(&rowreq_ctx->tbl_idx +@foreach $node index@ +@ include m2c_setup_node.m2i@ + , $m2c_node_param_val_call +@end@ # for each index + )) + return MFD_ERROR; + + /* + * convert mib index to oid index + */ + rowreq_ctx->oid_idx.len = sizeof(rowreq_ctx->oid_tmp) / sizeof(oid); + if(0 != ${context}_index_to_oid(&rowreq_ctx->oid_idx, + &rowreq_ctx->tbl_idx)) { + return MFD_ERROR; + } + + return MFD_SUCCESS; +} /* ${context}_indexes_set */ + +@end@ // m2c_processing_type eq 'c' +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-table-indexes-to-oid.m2i b/local/mib2c-conf.d/generic-table-indexes-to-oid.m2i new file mode 100644 index 0000000..c17de4c --- /dev/null +++ b/local/mib2c-conf.d/generic-table-indexes-to-oid.m2i @@ -0,0 +1,52 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +/** + * @internal + * convert the index component stored in the context to an oid + */ +int +${context}_index_to_oid(netsnmp_index *oid_idx, + ${context}_mib_index *mib_idx) +{ +@include generic-table-indexes-varbind-setup.m2i@ + + DEBUGMSGTL(("verbose:${context}:${context}_index_to_oid","called\n")); + +@ foreach $node index@ +@ include m2c_setup_node.m2i@ + /* $m2c_node_summary */ +@ if $m2c_node_needlength == 1@ + snmp_set_var_value(&var_$node, &mib_idx->$node, + mib_idx->${node}_len * sizeof(mib_idx->${node}[0])); +@ else@ + snmp_set_var_value(&var_$node, &mib_idx->$node, sizeof(mib_idx->$node)); +@ end@ + +@ end@ # for each column + + err = build_oid_noalloc(oid_idx->oids, oid_idx->len, &oid_idx->len, + NULL, 0, &var_$m2c_dii_first); + if(err) + snmp_log(LOG_ERR,"error %d converting index to oid\n", err); + + /* + * parsing may have allocated memory. free it. + */ + snmp_reset_var_buffers( &var_$m2c_dii_first ); + + return err; +} /* ${context}_index_to_oid */ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-table-indexes-varbind-setup.m2i b/local/mib2c-conf.d/generic-table-indexes-varbind-setup.m2i new file mode 100644 index 0000000..8c0d48d --- /dev/null +++ b/local/mib2c-conf.d/generic-table-indexes-varbind-setup.m2i @@ -0,0 +1,51 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## + int err = SNMP_ERR_NOERROR; + + /* + * temp storage for parsing indexes + */ +@ eval $m2c_dii_first = ""@ +@ foreach $node index@ +@ include m2c_setup_node.m2i@ +@ if "x$m2c_dii_first" eq "x"@ +@ eval $m2c_dii_first = $node@ +@ eval $m2c_dii_tmp = "var_${node}.next_variable = "@ +@ else@ +@ eval $m2c_dii_tmp = "$m2c_dii_tmp &var_${node}; var_${node}.next_variable = "@ +@ end@ + /* + * $m2c_node_summary + */ + netsnmp_variable_list var_$node; +@ end@ +@ eval $m2c_dii_tmp = "$m2c_dii_tmp NULL;"@ + + /* + * set up varbinds + */ +@ eval $mfd_temp = "idx_vars"@ +@ foreach $node index@ +@ include m2c_setup_node.m2i@ + memset( &var_$node, 0x00, sizeof(var_$node) ); + var_${node}.type = $node.type; +@ end@ + + /* + * chain temp index varbinds together + */ + $m2c_dii_tmp + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-table-indexes.m2i b/local/mib2c-conf.d/generic-table-indexes.m2i new file mode 100644 index 0000000..c226611 --- /dev/null +++ b/local/mib2c-conf.d/generic-table-indexes.m2i @@ -0,0 +1,67 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +/* + * TODO:120:r: |-> Review $context mib index. + * This structure is used to represent the index for $context. + */ +@eval $m2c_gi_others = (count_indexes($context) - 1)@ +@eval $m2c_gi_len = 0@ +@eval $m2c_gi_warn = 0@ +typedef struct ${context}_mib_index_s { + +@foreach $node index@ +@ include m2c_setup_node.m2i@ +##@ include details-node.m2i@ + /* + * $m2c_node_summary + */ +@ if $m2c_node_needlength == 1@ +@ eval $m2c_gi_warn = 1@ +@ eval $m2c_gi_maxlen = (128 - $node.oidlength - $m2c_gi_others - 1)@ +@ if $m2c_node_maxlen > $m2c_gi_maxlen@ +@ eval $m2c_node_maxlen = $m2c_gi_maxlen@ + /** 128 - $m2c_gi_others(other indexes) - oid length($node.oidlength) = $m2c_node_maxlen */ +@ end@ +@ eval $m2c_gi_len = $m2c_gi_len + $m2c_node_maxlen + 1@ +@ elsif "$node.type" eq "ASN_IPADDRESS"@ +@ eval $m2c_gi_len = $m2c_gi_len + 4@ +@ else@ +@ eval $m2c_gi_len = $m2c_gi_len + 1@ +@ end@ # needlength +@ include node-storage.m2i@ + +@end@ # foreach + +} ${context}_mib_index; + + /* + * TODO:121:r: | |-> Review $context max index length. + * If you KNOW that your indexes will never exceed a certain + * length, update this macro to that length. +@ if $m2c_gi_warn == 1@ + * + * BE VERY CAREFUL TO TAKE INTO ACCOUNT THE MAXIMUM + * POSSIBLE LENGHT FOR EVERY VARIABLE LENGTH INDEX! + * Guessing 128 - col/entry(2) - oid len($context.oidlength) +@ if $m2c_gi_len > 126@ +@ eval $m2c_gi_len = 126 - $context.oidlength@ +@ end@ +##@ else@ +##@ eval $m2c_gi_len = count_indexes($context)@ +@ end@ +*/ +#define MAX_${context}_IDX_LEN $m2c_gi_len + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-table-oids.m2c b/local/mib2c-conf.d/generic-table-oids.m2c new file mode 100644 index 0000000..6f8ab2a --- /dev/null +++ b/local/mib2c-conf.d/generic-table-oids.m2c @@ -0,0 +1,113 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_create_fewer_files != 1@ +@eval $hack = "Id"@ +@eval $m2c_save = "$name"@ +@eval $name = "${m2c_save}_oids"@ +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + * + * $$hack:$ + */ +@include generic-header-top.m2i@ +@eval $name = "$m2c_save"@ +@end@ // m2c_create_fewer_files +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START header generated by $RCSfile$ $Revision$ */ +@end@ +## + +/* column number definitions for table $context */ +#define $context.uc_OID $context.commaoid + + @eval $minv = 0xffffffff@ + @eval $maxv = 0@ + @eval $gto_flag_req = ""@ + @eval $gto_flag_set = ""@ + @eval $m2c_tmp_gto = 0@ + + @foreach $node column@ +#define COLUMN_$node.uc $node.subid + @if $node.accessible == 1@ + @if ($node.settable == 1) || ($m2c_table_sparse == 1)@ +@ if "x$gto_flag_set" eq "x"@ +@ eval $gto_flag_set = "COLUMN_$node.uc_FLAG"@ +@ else@ +@ eval $gto_flag_set = "$gto_flag_set | COLUMN_$node.uc_FLAG"@ +@ end@ + @if $m2c_tmp_gto > 31@ +#define COLUMN_$node.uc_FLAG (((uint64_t)0x1) << $m2c_tmp_gto) + @else@ +#define COLUMN_$node.uc_FLAG (0x1 << $m2c_tmp_gto) + @end@ + @eval $m2c_tmp_gto = $m2c_tmp_gto + 1@ + @end@ + @if ($m2c_table_row_creation == 1) && ($node.settable == 1) && ("x$node.defval" eq "x")@ + @if "x$gto_flag_req" eq "x"@ + @eval $gto_flag_req = "COLUMN_$node.uc_FLAG"@ + @else@ + @eval $gto_flag_req = "$gto_flag_req | COLUMN_$node.uc_FLAG"@ + @end@ + @end@ + @if $node.subid < $minv@ + @eval $minv = $node.subid@ + @eval $minn = "COLUMN_$node.uc"@ + @end@ + @if $node.subid > $maxv@ + @eval $maxv = $node.subid@ + @eval $maxn = "COLUMN_$node.uc"@ + @end@ + @end@ + + @end@ # column + +#define $context.uc_MIN_COL $minn +#define $context.uc_MAX_COL $maxn +## +## column_set_flags and column_exist_flags are unsigned ints, to 32 is +## the limit. Could try something with a 'long long' to see if that +## can get us to 64, or do something like FD_SET, which would let us +## be pretty unlimited. +## + @if $maxn > 31@ + @ print ERROR: more than 32 columns not supported yet.@ + @ exit@ + @end@ + + +@ if $m2c_table_settable@ + @if "x$gto_flag_set" ne "x"@ + /* + * TODO:405:r: Review $context.uc_SETTABLE_COLS macro. + * OR together all the writable cols. + */ +#define $context.uc_SETTABLE_COLS ($gto_flag_set) + @end@ + @if $m2c_table_row_creation@ + @if "x$gto_flag_req" ne "x"@ + /* + * TODO:405:r: Review $context.uc_REQUIRED_COLS macro. + * OR together all the required rows for row creation. + * default is writable cols w/out defaults. + */ +#define $context.uc_REQUIRED_COLS ($gto_flag_req) + + @end@ + @end@ +@ end@ # settable +@if $m2c_create_fewer_files != 1@ +@eval $m2c_save = "$name"@ +@eval $name = "${m2c_save}_oids"@ +@include generic-header-bottom.m2i@ +@eval $name = "$m2c_save"@ +@end@ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END header generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-value-map-func.m2i b/local/mib2c-conf.d/generic-value-map-func.m2i new file mode 100644 index 0000000..b0df63b --- /dev/null +++ b/local/mib2c-conf.d/generic-value-map-func.m2i @@ -0,0 +1,104 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## } +/** + * map a value from its original native format to the MIB format. + * + * @retval MFD_SUCCESS : success + * @retval MFD_ERROR : Any other error + * + * @note parameters follow the memset convention (dest, src). + * + * @note generation and use of this function can be turned off by re-running + * mib2c after adding the following line to the file + * ${m2c_defaults_dir}node-${node}.m2d : + * @eval $@m2c_node_skip_mapping = 1@ + * + * @remark + * If the values for your data type don't exactly match the + * possible values defined by the mib, you should map them here. + * Otherwise, just do a direct copy. + */ +int +${node}_map($m2c_node_map_param) +{ +@if $m2c_node_needlength == 1@ + int converted_len; + + netsnmp_assert(NULL != raw_$m2c_node_param_val_name); + netsnmp_assert((NULL != mib_$m2c_node_param_ref_name) && (NULL != mib_$m2c_node_param_ref_lname)); +@else@ + netsnmp_assert(NULL != mib_$m2c_node_param_ref_name); +@end@ + + DEBUGMSGTL(("verbose:${context}:${node}_map","called\n")); + +@if $m2c_node_needlength == 1@ + /* + * TODO:241:r: |-> Implement $node non-integer mapping + * it is hard to autogenerate code for mapping types that are not simple + * integers, so here is an idea of what you might need to do. It will + * probably need some tweaking to get right. + */ + /* + * if the length of the raw data doesn't directly correspond with + * the length of the mib data, set converted_len to the + * space required. + */ + converted_len = raw_$m2c_node_param_val_lname; /* assume equal */ + if((NULL == *mib_$m2c_node_param_ref_name) || (*mib_$m2c_node_param_ref_lname < converted_len)) { + if(! allow_realloc) { + snmp_log(LOG_ERR,"not enough space for value mapping\n"); + return SNMP_ERR_GENERR; + } + *mib_$m2c_node_param_ref_name = realloc( *mib_$m2c_node_param_ref_name, converted_len * sizeof(**mib_$m2c_node_param_ref_name)); + if(NULL == *mib_$m2c_node_param_ref_name) { + snmp_log(LOG_ERR,"could not allocate memory\n"); + return SNMP_ERR_GENERR; + } + } + *mib_$m2c_node_param_ref_lname = converted_len; + memcpy( *mib_$m2c_node_param_ref_name, raw_$m2c_node_param_val_name, converted_len ); +## +@elsif ($node.enums == 1) && ("$node.perltype" eq "INTEGER")@ + /* + * TODO:241:o: |-> Implement $node enum mapping. + * uses INTERNAL_* macros defined in the header files + */ + switch(raw_$m2c_node_param_val_name) { +@ foreach $e $v enum@ +@ include m2c_setup_enum.m2i@ + case INTERNAL_$context.uc_$m2c_iname: + *mib_$m2c_node_param_ref_name = $m2c_ename; + break; + +@ end@ # foreach + default: + snmp_log(LOG_ERR, "couldn't map value %ld for $node\n", raw_$m2c_node_param_val_name ); + return MFD_ERROR; + } +## +@else@ + /* + * TODO:241:o: |-> Implement $node mapping. + * If the values for your data type don't exactly match the + * possible values defined by the mib, you should map them here. + */ + (*mib_$m2c_node_param_ref_name) = raw_$m2c_node_param_val_name; +@end@ + + return MFD_SUCCESS; +} /* ${node}_map */ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-value-map-reverse.m2i b/local/mib2c-conf.d/generic-value-map-reverse.m2i new file mode 100644 index 0000000..4b6eb38 --- /dev/null +++ b/local/mib2c-conf.d/generic-value-map-reverse.m2i @@ -0,0 +1,49 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +/* + * TODO:245:o: |-> Implement $node reverse mapping. + * If the values for your data type don't exactly match the + * possible values defined by the mib, you should map them here. + */ +@if ($node.enums == 1)@ +$example_start +@ if ("$node.perltype" eq "BITS")@ + $m2c_ctx_rh = 0; +@ foreach $e $v enum@ +@ include m2c_setup_enum.m2i@ + if ($m2c_node_srh & $m2c_ename) { + $m2c_ctx_rh |= INTERNAL_$context.uc_$m2c_iname; + } +@ end@ # for each +@ elsif ("$node.perltype" eq "INTEGER")@ + switch($m2c_node_srh) { +@ foreach $e $v enum@ +@ include m2c_setup_enum.m2i@ + case $m2c_ename: + $m2c_ctx_rh = INTERNAL_$context.uc_$m2c_iname; + break; + +@ end@ # foreach + default: + snmp_log(LOG_ERR, "couldn't reverse map value %ld for $node\n", $m2c_node_srh ); + return SNMP_ERR_GENERR; + } +@ end@ # integers/bits +$example_end +@else@ +@ include generic-ctx-set.m2i@ +@end@ # enums + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/generic-value-map.m2i b/local/mib2c-conf.d/generic-value-map.m2i new file mode 100644 index 0000000..0f7bd2e --- /dev/null +++ b/local/mib2c-conf.d/generic-value-map.m2i @@ -0,0 +1,46 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## } +@if $m2c_node_skip_mapping == 1@ + /** no mapping */ +@ include generic-ctx-get.m2i@ +@else@ // mapping + /* + * TODO:246:r: |-> Define $node mapping. + * Map values between raw/native values and MIB values + * +@ if $m2c_node_needlength == 1@ + * if(MFD_SUCCESS != + * ${node}_map(&$m2c_ctx_lh, &$m2c_ctx_lhs, + * $m2c_ctx_rh, $m2c_ctx_rhs, $m2c_node_realloc)) { + * return MFD_ERROR; + * } + */ +@ include generic-ctx-get.m2i@ +## +@ elsif ($node.enums == 1) && ("$node.perltype" eq "INTEGER")@ +## + * enums usually need mapping. + */ + if(MFD_SUCCESS != + ${node}_map(&${m2c_ctx_lh}, ${m2c_ctx_rh} )) { + return MFD_ERROR; + } +@ else@ // enums + * Integer based value can usually just do a direct copy. + */ +@ include generic-ctx-get.m2i@ +@ end@ +@end@ // mapping +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/m2c-internal-warning.m2i b/local/mib2c-conf.d/m2c-internal-warning.m2i new file mode 100644 index 0000000..8e685e6 --- /dev/null +++ b/local/mib2c-conf.d/m2c-internal-warning.m2i @@ -0,0 +1,21 @@ +/* + * ********************************************************************* + * ********************************************************************* + * ********************************************************************* + * *** *** + * *** NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE *** + * *** *** + * *** *** + * *** THIS FILE DOES NOT CONTAIN ANY USER EDITABLE CODE. *** + * *** *** + * *** *** + * *** THE GENERATED CODE IS INTERNAL IMPLEMENTATION, AND *** + * *** *** + * *** *** + * *** IS SUBJECT TO CHANGE WITHOUT WARNING IN FUTURE RELEASES. *** + * *** *** + * *** *** + * ********************************************************************* + * ********************************************************************* + * ********************************************************************* + */ diff --git a/local/mib2c-conf.d/m2c_setup_enum.m2i b/local/mib2c-conf.d/m2c_setup_enum.m2i new file mode 100644 index 0000000..b5bb7ae --- /dev/null +++ b/local/mib2c-conf.d/m2c_setup_enum.m2i @@ -0,0 +1,24 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +## the iname enum should be unique per column, so always use node name +## +@ if $m2c_const_lc == 1@ +@ eval $m2c_ename = "${m2c_de_pfx}_${e}${m2c_enum_sfx}"@ +@ eval $m2c_iname = "${m2c_const_pfx}${node}_${e}${m2c_enum_sfx}"@ +@ else@ +@ eval $m2c_ename = "${m2c_de_pfx}_$e.uc${m2c_enum_sfx}"@ +@ eval $m2c_iname = "${m2c_const_pfx}$node.uc_$e.uc${m2c_enum_sfx}"@ +@ end@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/m2c_setup_node.m2i b/local/mib2c-conf.d/m2c_setup_node.m2i new file mode 100644 index 0000000..67fb89d --- /dev/null +++ b/local/mib2c-conf.d/m2c_setup_node.m2i @@ -0,0 +1,260 @@ +############################################################# -*- c -*- +## Defaults +## $Id$ +######################################################################## +@if "$m2c_node_name" ne "$node"@ +## if $node.accessible != 1, might be inaccessible index node +@ eval $m2c_node_name = $node@ +@ eval $m2c_node_skip_mapping = $m2c_table_skip_mapping@ +@ eval $m2c_node_needlength = $node.needlength@ +@ eval $m2c_node_get_comments = ""@ +@ eval $m2c_node_set_comments = ""@ +@ eval $m2c_node_skip_get = 0@ +##//how should a function handle a pointer to a buffer that is to small? +@ eval $m2c_node_realloc = 0@ // 0=fail, 1=realloc, 2=malloc +######################################################################## +## fix some declarations +######################################################################## +@ if $node.enums == 1@ +## +@ if $m2c_node_skip_mapping == -1@ +@ eval $m2c_node_skip_mapping = 0@ +@ end@ +## +## validate some assumptions +## +@ if ("$node.perltype" ne "INTEGER") && ("$node.perltype" ne "BITS")@ +@ print "$node had enums, but isn't INTEGER or BITS! ($node.perltype)\n"@ +@ exit@ +@ end@ +@ if $node.ranges == 1@ +@ print "$node has enums and ranges!\n"@ +@ exit@ +@ end@ +## +## for a TC, prefix definition w/syntax to reduce collisions +## +@ if $node.syntax ne $node.perltype@ +@ eval $m2c_de_pfx = "${m2c_const_pfx}$node.syntax"@ +@ else@ +@ eval $m2c_de_pfx = "${m2c_const_pfx}$node"@ +@ end@ +@ if $m2c_const_lc == 0@ +@ eval $m2c_de_pfx = uc($m2c_de_pfx)@ +@ end@ +@ if "$node.perltype" eq "BITS"@ +@ eval $m2c_enum_sfx="_flag"@ +@ eval $m2c_enum_mask="${m2c_de_pfx}_flag"@ +@ if $m2c_const_lc == 0@ +@ eval $m2c_enum_sfx = uc($m2c_enum_sfx)@ +@ eval $m2c_enum_mask = uc($m2c_enum_mask)@ +@ end@ +@ else@ +@ eval $m2c_enum_sfx=""@ +@ end@ +## +## use longs for enums (w/out length) +## +@ eval $m2c_decl = "u_long"@ +@ eval $m2c_node_needlength = 0@ +@ if "$node.perltype" eq "BITS"@ +@ eval $m2c_node_skip_get = 1@ +@ end@ +@ else@ +@ eval $m2c_decl = $node.decl@ + @ end@ // enums +######################################################################## +## find max size +######################################################################## +@ if $node.ranges == 1@ +## +## I do not *think* you can have both... +## +@ if $node.enums == 1@ +@ print "$node has enums and ranges!\n"@ +@ exit@ +@ end@ +@ eval $m2c_node_maxlen = 0@ +@ foreach $a $b range $node@ +@ eval $m2c_node_maxlen = max($m2c_node_maxlen,$b)@ +@ end@ +@ elsif "$node.type" eq "ASN_OBJECT_ID"@ +@ eval $m2c_node_maxlen = 128@ +@ else@ #ranges +@ eval $m2c_node_maxlen = 65535@ +@ end@ #ranges +##/*#################################################################### +## set up extra params, based on if we need length +######################################################################## +## VAR_VAL : variable value. +## VAR_VAL_PTR : pointer to variable value. +## VAR_REF : variable reference. (pointer to pointer to variable value) +##*/ +@ if "x$m2c_node_var_name" eq "x"@ +@ eval $m2c_node_var_name="var->"@ +@ end@ +@ eval $m2c_node_var_val_ptr = "($m2c_decl *)${m2c_node_var_name}val.string"@ +@ if $m2c_node_needlength == 1@ +@ eval $m2c_XX = "($m2c_decl **)&${m2c_node_var_name}val.string,"@ +@ eval $m2c_node_var_ref = "$m2c_XX &${m2c_node_var_name}val_len"@ +@ eval $m2c_node_var_val = "$m2c_node_var_val_ptr, ${m2c_node_var_name}val_len"@ +@ else@ +@ eval $m2c_node_var_ref = "($m2c_decl *)${m2c_node_var_name}val.string"@ +@ eval $m2c_node_var_val = "*($m2c_node_var_val_ptr)"@ +@ end@ +## +## +@ eval $m2c_node_param_val_name = "${node}_val"@ +@ eval $m2c_node_param_val_lname = "${m2c_node_param_val_name}_len"@ +@ eval $m2c_node_param_ref_name = "${m2c_node_param_val_name}_ptr"@ +@ if $m2c_node_needlength == 1@ +@ eval $m2c_node_param_val_name = "${m2c_node_param_val_name}_ptr"@ +@ eval $m2c_node_param_val_lname = "${m2c_node_param_val_name}_len"@ +@ eval $m2c_node_param_val_call = "${m2c_node_param_val_name}, ${m2c_node_param_val_lname}"@ +@ eval $m2c_node_param_ref_name = "${m2c_node_param_ref_name}_ptr"@ +@ eval $m2c_node_param_ref_lname = "${m2c_node_param_val_lname}_ptr"@ +@ eval $m2c_XX = "$m2c_decl **$m2c_node_param_ref_name,"@ +@ eval $m2c_node_param_ref = "$m2c_XX size_t *$m2c_node_param_ref_lname"@ +@ eval $m2c_XX = "$m2c_decl *$m2c_node_param_val_name,"@ +@ eval $m2c_node_param_val = "$m2c_XX size_t $m2c_node_param_val_lname"@ +@ else@ +@ eval $m2c_node_param_ref = "$m2c_decl * $m2c_node_param_ref_name"@ +@ eval $m2c_node_param_val = "$m2c_decl $m2c_node_param_val_name"@ +@ eval $m2c_node_param_val_call = "$m2c_node_param_val_name"@ +## +@ end@ +## +######################################################################## +## include user overrides +######################################################################## +@ include -ifexists ${m2c_defaults_dir}node-${node}.m2d@ +######################################################################## +## +######################################################################## +@ if $m2c_get_use_temp == 1@ +@ eval $m2c_node_lh = "temp_$node"@ +@ eval $m2c_node_lhs = "temp_${node}_len"@ +@ else@ +@ eval $m2c_node_lh = "(* $m2c_node_param_ref_name )"@ +@ eval $m2c_node_lhs = "(* $m2c_node_param_ref_lname )"@ +@ end@ +@ eval $m2c_ctx_lh="$m2c_node_lh"@ +@ eval $m2c_ctx_lhs="$m2c_node_lhs"@ +@ eval $m2c_ctx_lhu="bytes"@ +@ eval $m2c_ctx_rh="${m2c_data_item}${node}"@ +@ eval $m2c_ctx_rhs="${m2c_data_item}${node}_len"@ +@ eval $m2c_ctx_rhu="elements"@ +######################################################################## +## +######################################################################## +@ if $m2c_node_skip_mapping != 1@ +@ if $m2c_node_needlength == 1@ +@ eval $m2c_XX="$m2c_decl **mib_$m2c_node_param_ref_name,"@ +@ eval $m2c_XX="$m2c_XX size_t *mib_$m2c_node_param_ref_lname,"@ +@ eval $m2c_XX="$m2c_XX $m2c_decl *raw_$m2c_node_param_val_name,"@ +@ eval $m2c_XX="$m2c_XX size_t raw_$m2c_node_param_val_lname,"@ +@ eval $m2c_node_map_param="$m2c_XX int allow_realloc"@ +@ else@ +@ eval $m2c_XX="$m2c_decl *mib_$m2c_node_param_ref_name,"@ +@ eval $m2c_node_map_param="$m2c_XX $m2c_decl raw_$m2c_node_param_val_name"@ +@ end@ +@ end@ +######################################################################## +## +######################################################################## +@ eval $m2c_node_srh = "$m2c_node_param_val_name"@ +@ eval $m2c_node_srhs = "$m2c_node_param_val_lname"@ +@end@ +######################################################################## +## +@eval $m2c_node_summary="$node($node.subid)/$node.syntax/$node.type/$node.decl($m2c_decl)/"@ +@if $node.needlength == 0@ +@ eval $m2c_node_summary="$m2c_node_summary/l"@ +@else@ +@ eval $m2c_node_summary="$m2c_node_summary/L"@ +@end@ +@if $node.noaccess == 0@ +@ eval $m2c_node_summary="$m2c_node_summary/A"@ +@else@ +@ eval $m2c_node_summary="$m2c_node_summary/a"@ +@end@ +@if $node.settable == 0@ +@ eval $m2c_node_summary="$m2c_node_summary/w"@ +@else@ +@ eval $m2c_node_summary="$m2c_node_summary/W"@ +@end@ +@if $node.enums == 0@ +@ eval $m2c_node_summary="$m2c_node_summary/e"@ +@else@ +@ eval $m2c_node_summary="$m2c_node_summary/E"@ +@end@ +@if $node.ranges == 0@ +@ eval $m2c_node_summary="$m2c_node_summary/r"@ +@else@ +@ eval $m2c_node_summary="$m2c_node_summary/R"@ +@end@ +@if $node.hasdefval == 0@ +@ eval $m2c_node_summary="$m2c_node_summary/d"@ +@else@ +@ eval $m2c_node_summary="$m2c_node_summary/D"@ +@end@ +@if $node.hashint == 0@ +@ eval $m2c_node_summary="$m2c_node_summary/h"@ +@else@ +@ eval $m2c_node_summary="$m2c_node_summary/H"@ +@end@ +######################################################################## +@ ifconf ${m2c_defaults_dir}node-${node}.m2d@ +@ else@ +@ push@ +@ open ${m2c_defaults_dir}node-${node}.m2d@ +@ eval $m2c_conf_comment = "##"@ +@ eval $m2c_conf_comment_divider = "########################################################################"@ +$m2c_conf_comment_divider +$m2c_conf_comment +$m2c_conf_comment mib2c node setting for $node +$m2c_conf_comment +$m2c_conf_comment Remove the '##' comment delimeter to change settings +$m2c_conf_comment +$m2c_conf_comment_divider +$m2c_conf_comment Node declaration type? This is the C type to be used when +$m2c_conf_comment declaring a variable to hold a value for this column. It +$m2c_conf_comment is strongly recommended that you do not change this value. +$m2c_conf_comment If you do, it is likely to break lots of generated code that +$m2c_conf_comment you will have to fix. +$m2c_conf_comment +$m2c_conf_comment @eval $@m2c_decl = $m2c_decl@ +$m2c_conf_comment +$m2c_conf_comment_divider +$m2c_conf_comment Generate/use mapping functions? Useful if the MIB defines +$m2c_conf_comment a different format or enumerations than you data store uses. +$m2c_conf_comment +$m2c_conf_comment @eval $@m2c_node_skip_mapping = $m2c_node_skip_mapping@ +$m2c_conf_comment +$m2c_conf_comment_divider +$m2c_conf_comment Need a length for the value? Most OCTET-STRING based values will +$m2c_conf_comment need a length, most other types will not. Do not change this one +$m2c_conf_comment unless you know what you are doing! You will almost certainly need +$m2c_conf_comment to fix lots of generated code if you do. +$m2c_conf_comment +$m2c_conf_comment @eval $@m2c_node_needlength = $m2c_node_needlength@ +$m2c_conf_comment +$m2c_conf_comment_divider +$m2c_conf_comment Skip get? Set this to 1 if you do not want to implement a value +$m2c_conf_comment for this column. +$m2c_conf_comment +$m2c_conf_comment @eval $@m2c_node_skip_get = $m2c_node_skip_get@ +$m2c_conf_comment +@ if $m2c_node_needlength == 1@ +$m2c_conf_comment_divider +$m2c_conf_comment Allow realloc when data size exceeds length? If your data +$m2c_conf_comment store for this node is a pointer allocated with one of the +$m2c_conf_comment alloc family functions, you can set this to 1 to use realloc +$m2c_conf_comment when a new value length exceeds the old lenght. If you are +$m2c_conf_comment using a fixed size buffer, this value should be 0. +$m2c_conf_comment +$m2c_conf_comment @eval $@m2c_node_realloc = $m2c_node_realloc@ +@ end@ +@ close ${m2c_defaults_dir}node-${node}.m2d@ +@ pop@ +@ end@ diff --git a/local/mib2c-conf.d/m2c_setup_table.m2i b/local/mib2c-conf.d/m2c_setup_table.m2i new file mode 100644 index 0000000..4481e39 --- /dev/null +++ b/local/mib2c-conf.d/m2c_setup_table.m2i @@ -0,0 +1,48 @@ +######################################################################## +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@if "$m2c_context_name" ne "$table"@ +@ eval $m2c_context_name = $table@ +@ eval $context = $table@ +@ if $m2c_report_progress == 1@ +@ print | +-> Processing table $context@ +@ end@ +@ eval $m2c_context_item = "rowreq_ctx->"@ +@ eval $m2c_table_external_indexes = count_external_indexes($context)@ +## +@ eval $m2c_undo_embed = $mfd_default_undo_embed@ +@ eval $m2c_gda_todo_suppress = 0@ # todo comments +## +## user override +## +@ include ${m2c_defaults_dir}table-${context}.m2d@ +## +@ eval $m2c_data_item_base = "${m2c_context_item}data"@ +@ if $m2c_data_allocate == 1@ +@ eval $m2c_data_item = "${m2c_data_item_base}->"@ +@ else@ +@ eval $m2c_data_item = "${m2c_data_item_base}."@ +@ end@ +@ if $m2c_table_settable == 0@ +@ eval $m2c_undo_embed = 1@ +@ end@ +@ eval $m2c_undo_item_base = "${m2c_context_item}undo"@ +@ if $m2c_undo_embed == 1@ +@ eval $m2c_undo_item = "${m2c_undo_item_base}."@ +@ else@ +@ eval $m2c_undo_item = "${m2c_undo_item_base}->"@ +@ end@ +@end@ +@eval $m2c_node_name = ""@ +######################################################################## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/m2c_table_save_defaults.m2i b/local/mib2c-conf.d/m2c_table_save_defaults.m2i new file mode 100644 index 0000000..e36c32e --- /dev/null +++ b/local/mib2c-conf.d/m2c_table_save_defaults.m2i @@ -0,0 +1,117 @@ +####################################################################### +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +## +## +## Note: if you add a var here, add it in mfd-interactive-setup.m2c too +## +## +@open ${m2c_defaults_dir}table-${context}.m2d@ +@eval $m2c_tmp_cc = "##"@ +@eval $tmp_cc = ""@ # hack to prevet mib2c eval +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc mib2c Table setting for $context +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc User context structure type +$m2c_tmp_cc +$tmp_cc@eval $@m2c_context_reg = "$m2c_context_reg"@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Allocate data structure in row structure? (vs embedd) +$m2c_tmp_cc +$tmp_cc@eval $@m2c_data_allocate = $m2c_data_allocate@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Generate code to cache data? +$m2c_tmp_cc +$tmp_cc@eval $@m2c_data_cache = $m2c_data_cache@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Data context structure type +$m2c_tmp_cc +$tmp_cc@eval $@m2c_data_context = "$m2c_data_context"@ [generated|NAME] +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Generate function to initialize row context when created? +$m2c_tmp_cc +$tmp_cc@eval $@m2c_data_init = $m2c_data_init@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Persistence of data context +$m2c_tmp_cc // 0:persistent, 1:semi-transient, 2:transient +$m2c_tmp_cc +$tmp_cc@eval $@m2c_data_transient = $m2c_data_transient@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Include some example code? +$m2c_tmp_cc +$tmp_cc@eval $@m2c_include_examples = $m2c_include_examples@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Generate code for irreversible_commit mode? +$m2c_tmp_cc +$tmp_cc@eval $@m2c_irreversible_commit = $m2c_irreversible_commit@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Data access method +$m2c_tmp_cc +$tmp_cc@eval $@m2c_table_access = "$m2c_table_access"@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Generate row dependency function? +$m2c_tmp_cc +$tmp_cc@eval $@m2c_table_dependencies = $m2c_table_dependencies@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Generate data store/restore functions for persistent storage? +$m2c_tmp_cc +$tmp_cc@eval $@m2c_table_persistent = $m2c_table_persistent@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Generate code for dynamic row creation? +$m2c_tmp_cc +$tmp_cc@eval $@m2c_table_row_creation = $m2c_table_row_creation@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Generate code for settable objects? +$m2c_tmp_cc +$tmp_cc@eval $@m2c_table_settable = $m2c_table_settable@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Skip mapping between data context and MIB formats? +$m2c_tmp_cc // 0:generate maps, 1:skip maps, -1:skip unless enum/oid +$m2c_tmp_cc +$tmp_cc@eval $@m2c_table_skip_mapping = $m2c_table_skip_mapping@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Generate code for sparse tables? +$m2c_tmp_cc +$tmp_cc@eval $@m2c_table_sparse = $m2c_table_sparse@ +$m2c_tmp_cc +$m2c_tmp_cc ######################################################################## +$m2c_tmp_cc +$m2c_tmp_cc Generate Makefile/AgentX code? +$m2c_tmp_cc +$tmp_cc@eval $@mfd_generate_makefile = $mfd_generate_makefile@ +$tmp_cc@eval $@mfd_generate_subagent = $mfd_generate_subagent@ +$m2c_tmp_cc +@close ${m2c_defaults_dir}table-${context}.m2d@ diff --git a/local/mib2c-conf.d/mfd-access-container-cached-defines.m2i b/local/mib2c-conf.d/mfd-access-container-cached-defines.m2i new file mode 100644 index 0000000..5f65b63 --- /dev/null +++ b/local/mib2c-conf.d/mfd-access-container-cached-defines.m2i @@ -0,0 +1,576 @@ +####################################################################### +###generic include for XXX. Do not use directly. +### +### $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'h'@ + +@ if $m2c_data_cache != 1@ +void ${context}_container_init(netsnmp_container **container_ptr_ptr); +@ else@ + /* + * TODO:180:o: Review ${context} cache timeout. + * The number of seconds before the cache times out + */ +#define $context.uc_CACHE_TIMEOUT 60 + +void ${context}_container_init(netsnmp_container **container_ptr_ptr, + netsnmp_cache *cache); +@ end@ # data cache +void ${context}_container_shutdown(netsnmp_container *container_ptr); + +int ${context}_container_load(netsnmp_container *container); +void ${context}_container_free(netsnmp_container *container); + +@ if $m2c_data_cache == 1@ +int ${context}_cache_load(netsnmp_container *container); +void ${context}_cache_free(netsnmp_container *container); + +@ end@ +@ if $m2c_include_examples == 1@ +$example_start +/* ********************************************************************* + * Since we have no idea how you really access your data, we'll go with + * a worst case example: a flat text file. + */ +#define MAX_LINE_SIZE 256 +$example_end +@ end@ // example +@end@ // m2c_processing_type eq 'h' +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'c'@ +/** + * container overview + * + */ + +/** + * container initialization + * + * @param container_ptr_ptr A pointer to a container pointer. If you + * create a custom container, use this parameter to return it + * to the MFD helper. If set to NULL, the MFD helper will + * allocate a container for you. +@ if $m2c_data_cache == 1@ + * @param cache A pointer to a cache structure. You can set the timeout + * and other cache flags using this pointer. +@ end@ + * + * This function is called at startup to allow you to customize certain + * aspects of the access method. For the most part, it is for advanced + * users. The default code should suffice for most cases. If no custom + * container is allocated, the MFD code will create one for your. + * +@ if $m2c_data_cache == 1@ + * This is also the place to set up cache behavior. The default, to + * simply set the cache timeout, will work well with the default + * container. If you are using a custom container, you may want to + * look at the cache helper documentation to see if there are any + * flags you want to set. + * +@ end@ + * @remark + * This would also be a good place to do any initialization needed + * for you data source. For example, opening a connection to another + * process that will supply the data, opening a database, etc. + */ +void +@ if $m2c_data_cache != 1@ +${context}_container_init(netsnmp_container **container_ptr_ptr) +@ else@ +${context}_container_init(netsnmp_container **container_ptr_ptr, + netsnmp_cache *cache) +@ end@ +{ + DEBUGMSGTL(("verbose:${context}:${context}_container_init","called\n")); + + if (NULL == container_ptr_ptr) { + snmp_log(LOG_ERR,"bad container param to ${context}_container_init\n"); + return; + } + + /* + * For advanced users, you can use a custom container. If you + * do not create one, one will be created for you. + */ + *container_ptr_ptr = NULL; + +@if $m2c_data_cache == 1@ + if (NULL == cache) { + snmp_log(LOG_ERR,"bad cache param to ${context}_container_init\n"); + return; + } + + /* + * TODO:345:A: Set up $context cache properties. + * + * Also for advanced users, you can set parameters for the + * cache. Do not change the magic pointer, as it is used + * by the MFD helper. To completely disable caching, set + * cache->enabled to 0. + */ + cache->timeout = $context.uc_CACHE_TIMEOUT; /* seconds */ +@end@ +} /* ${context}_container_init */ + +/** + * container shutdown + * + * @param container_ptr A pointer to the container. + * + * This function is called at shutdown to allow you to customize certain + * aspects of the access method. For the most part, it is for advanced + * users. The default code should suffice for most cases. + * + * This function is called before ${context}_container_free(). + * + * @remark + * This would also be a good place to do any cleanup needed + * for you data source. For example, closing a connection to another + * process that supplied the data, closing a database, etc. + */ +void +${context}_container_shutdown(netsnmp_container *container_ptr) +{ + DEBUGMSGTL(("verbose:${context}:${context}_container_shutdown","called\n")); + + if (NULL == container_ptr) { + snmp_log(LOG_ERR,"bad params to ${context}_container_shutdown\n"); + return; + } + +} /* ${context}_container_shutdown */ + +/** + * load initial data + * + * TODO:350:M: Implement $context data load +@ if $m2c_data_cache == 1@ + * This function will also be called by the cache helper to load + * the container again (after the container free function has been + * called to free the previous contents). +@ end@ + * + * @param container container to which items should be inserted + * + * @retval MFD_SUCCESS : success. + * @retval MFD_RESOURCE_UNAVAILABLE : Can't access data source + * @retval MFD_ERROR : other error. + * + * This function is called to load the index(es) (and data, optionally) + * for the every row in the data set. + * + * @remark + * While loading the data, the only important thing is the indexes. + * If access to your data is cheap/fast (e.g. you have a pointer to a + * structure in memory), it would make sense to update the data here. + * If, however, the accessing the data invovles more work (e.g. parsing + * some other existing data, or peforming calculations to derive the data), + * then you can limit yourself to setting the indexes and saving any + * information you will need later. Then use the saved information in + * ${context}_row_prep() for populating data. + * + * @note + * If you need consistency between rows (like you want statistics + * for each row to be from the same time frame), you should set all + * data here. + * + */ +int +${context}_container_load(netsnmp_container *container) +{ + ${context}_rowreq_ctx *rowreq_ctx; + size_t count = 0; + + /* + * temporary storage for index values + */ +@ foreach $node index@ +@ include m2c_setup_node.m2i@ + /* + * $m2c_node_summary + */ +@ if $m2c_node_needlength == 1@ +@ eval $m2c_gi_maxlen = (126 - $node.oidlength - $m2c_gi_others)@ +@ if $m2c_node_maxlen > $m2c_gi_maxlen@ +@ eval $m2c_node_maxlen = $m2c_gi_maxlen@ + /** 128 - 1(entry) - 1(col) - $m2c_gi_others(other indexes) = $m2c_node_maxlen */ +@ end@ +@ end@ # needlength +@ include node-storage.m2i@ +@ end@ // foreach + +@if $m2c_include_examples == 1@ + + /* + * this example code is based on a data source that is a + * text file to be read and parsed. + */ + FILE *filep; + char line[MAX_LINE_SIZE]; +@end@ // examples + + DEBUGMSGTL(("verbose:${context}:${context}_container_load","called\n")); + +@if $m2c_include_examples == 1@ +$example_start + /* + * open our data file. + */ + filep = fopen("/etc/dummy.conf", "r"); + if(NULL == filep) { + return MFD_RESOURCE_UNAVAILABLE; + } + +$example_end +@end@ // example + /* + * TODO:351:M: |-> Load/update data in the $context container. + * loop over your $context data, allocate a rowreq context, + * set the index(es) [and data, optionally] and insert into + * the container. + */ + while( 1 ) { +@ if $m2c_include_examples == 0@ + /* + * check for end of data; bail out if there is no more data + */ + if( 1 ) + break; +@ else@ +$example_start + /* + * get a line (skip blank lines) + */ + do { + if (!fgets(line, sizeof(line), filep)) { + /* we're done */ + fclose(filep); + filep = NULL; + } + } while (filep && (line[0] == '\n')); + + /* + * check for end of data + */ + if(NULL == filep) + break; + + /* + * parse line into variables + */ +$example_end +@ end@ # example + + /* + * TODO:352:M: | |-> set indexes in new $context rowreq context. +@ eval $m2c_tmp = ""@ +@ if ($m2c_data_allocate == 1) || ($m2c_data_init == 1)@ +@ eval $m2c_tmp = "NULL"@ +@ if ($m2c_data_allocate == 1) && ($m2c_data_init == 1)@ +@ eval $m2c_tmp = "$m2c_tmp, NULL"@ + * data context will be set from the first param (unless NULL, + * in which case a new data context will be allocated) + * the second param will be passed, with the row context, to + * ${context}rowreq_ctx_init. +@ else@ + * data context will be set from the param (unless NULL, + * in which case a new data context will be allocated) +@ @end@ +@ end@ + */ + rowreq_ctx = ${context}_allocate_rowreq_ctx($m2c_tmp); + if (NULL == rowreq_ctx) { + snmp_log(LOG_ERR, "memory allocation failed\n"); + return MFD_RESOURCE_UNAVAILABLE; + } + if(MFD_SUCCESS != ${context}_indexes_set(rowreq_ctx +@ foreach $node index@ +@ include m2c_setup_node.m2i@ +@ if $m2c_node_needlength == 1@ + , $node, ${node}_len +@ else@ + , $node +@ end@ +@ end@ # foreach index + )) { + snmp_log(LOG_ERR,"error setting index while loading " + "${context} data.\n"); + ${context}_release_rowreq_ctx(rowreq_ctx); + continue; + } + + /* + * TODO:352:r: | |-> populate $context data context. + * Populate data context here. (optionally, delay until row prep) + */ +@if $m2c_data_transient == 0@ # persistent + /* non-TRANSIENT data: no need to copy. set pointer to data */ +@else@ + /* + * TRANSIENT or semi-TRANSIENT data: + * copy data or save any info needed to do it in row_prep. + */ +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ + /* + * setup/save data for $node + * $m2c_node_summary + */ +@ if "$m2c_data_context" eq "generated"@ +@ eval $m2c_ctx_lh = "$m2c_ctx_rh"@ +@ eval $m2c_ctx_lhs = "$m2c_ctx_rhs"@ +@ eval $m2c_ctx_rh = "$node"@ +@ eval $m2c_ctx_rhs = "${node}_len"@ +@ include generic-value-map.m2i@ + +@ end@ # data_context ! generated +@ end@ // for each +@end@ # transient + + /* + * insert into table container + */ + CONTAINER_INSERT(container, rowreq_ctx); + ++count; + } +@if $m2c_include_examples == 1@ + +$example_start + if(NULL != filep) + fclose(filep); +$example_end +@end@ # example + + DEBUGMSGT(("verbose:${context}:${context}_container_load", + "inserted %d records\n", count)); + + return MFD_SUCCESS; +} /* ${context}_container_load */ + +/** + * container clean up + * + * @param container container with all current items + * + * This optional callback is called prior to all + * item's being removed from the container. If you + * need to do any processing before that, do it here. + * + * @note + * The MFD helper will take care of releasing all the row contexts. +@ if ($m2c_data_allocate == 1) && ($m2c_data_transient == 0)@ + * If you did not pass a data context pointer when allocating + * the rowreq context, the one that was allocated will be deleted. + * If you did pass one in, it will not be deleted and that memory + * is your responsibility. +@ end@ + * + */ +void +${context}_container_free(netsnmp_container *container) +{ + DEBUGMSGTL(("verbose:${context}:${context}_container_free","called\n")); + + /* + * TODO:380:M: Free $context container data. + */ +} /* ${context}_container_free */ + +@end@ // m2c_processing_type eq 'c' +######################################################################## +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'i'@ +@ if $m2c_data_cache == 1@ +static void _container_free(netsnmp_container *container); + +/** + * @internal + */ +static int +_cache_load(netsnmp_cache *cache, void *vmagic) +{ + DEBUGMSGTL(("internal:${context}:_cache_load","called\n")); + + if((NULL == cache) || (NULL == cache->magic)) { + snmp_log(LOG_ERR, "invalid cache for ${context}_cache_load\n"); + return -1; + } + + /** should only be called for an invalid or expired cache */ + netsnmp_assert((0 == cache->valid) || (1 == cache->expired)); + + /* + * call user code + */ + return ${context}_container_load((netsnmp_container*)cache->magic); +} /* _cache_load */ + +/** + * @internal + */ +static void +_cache_free(netsnmp_cache *cache, void *magic) +{ + netsnmp_container *container; + + DEBUGMSGTL(("internal:${context}:_cache_free","called\n")); + + if((NULL == cache) || (NULL == cache->magic)) { + snmp_log(LOG_ERR, "invalid cache in ${context}_cache_free\n"); + return; + } + + container = (netsnmp_container*)cache->magic; + + _container_free(container); +} /* _cache_free */ + +@ end@ # cache +/** + * @internal + */ +static void +_container_item_free(${context}_rowreq_ctx *rowreq_ctx, void *context) +{ + DEBUGMSGTL(("internal:${context}:_container_item_free","called\n")); + + if(NULL == rowreq_ctx) + return; + + ${context}_release_rowreq_ctx(rowreq_ctx); +} /* _container_item_free */ + +/** + * @internal + */ +static void +_container_free(netsnmp_container *container) +{ + DEBUGMSGTL(("internal:${context}:_container_free","called\n")); + + if (NULL == container) { + snmp_log(LOG_ERR, "invalid container in ${context}_container_free\n"); + return; + } + + /* + * call user code + */ + ${context}_container_free(container); + + /* + * free all items. inefficient, but easy. + */ + CONTAINER_CLEAR(container, + (netsnmp_container_obj_func *)_container_item_free, + NULL); +} /* _container_free */ + +/** + * @internal + * initialize the container with functions or wrappers + */ +void +_${context}_container_init(${context}_interface_ctx *if_ctx) +{ + DEBUGMSGTL(("internal:${context}:_${context}_container_init","called\n")); + +@ if $m2c_data_cache == 1@ + /* + * cache init + */ +@ if 0@ + if_ctx->cache = + netsnmp_cache_find_by_oid(PARTNER_oid, OID_LENGTH(PARTNER_oid)); +@ else@ + if_ctx->cache = netsnmp_cache_create(30, /* timeout in seconds */ + _cache_load, _cache_free, + ${context}_oid, + ${context}_oid_size); +@ end@ // shared cache + + if(NULL == if_ctx->cache) { + snmp_log(LOG_ERR, "error creating cache for ${context}\n"); + return; + } + + if_ctx->cache->flags = NETSNMP_CACHE_DONT_INVALIDATE_ON_SET; + + ${context}_container_init(&if_ctx->container, if_ctx->cache); +@ else@ + /* + * container init + */ + ${context}_container_init(&if_ctx->container); +@ end@ data cache + if(NULL == if_ctx->container) + if_ctx->container = netsnmp_container_find("${context}:table_container"); + if(NULL == if_ctx->container) { + snmp_log(LOG_ERR,"error creating container in " + "${context}_container_init\n"); + return; + } + +@ if $m2c_data_cache == 1@ + if (NULL != if_ctx->cache) + if_ctx->cache->magic = (void*)if_ctx->container; +@ end@ +} /* _${context}_container_init */ + +/** + * @internal + * shutdown the container with functions or wrappers + */ +void +_${context}_container_shutdown(${context}_interface_ctx *if_ctx) +{ + DEBUGMSGTL(("internal:${context}:_${context}_container_shutdown","called\n")); + + ${context}_container_shutdown(if_ctx->container); + + _container_free(if_ctx->container); + +} /* _${context}_container_shutdown */ + +@end@ // m2c_processing_type eq 'i' +######################################################################## +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'r'@ +## + container summary + ------------------------ + The container data access code is for cases when you want to + store your data in the agent/sub-agent. + + ... to be continued... + + +@ if $m2c_data_cache == 1@ + cache summary + ------------------------ + The container-cached data access code is for cases when you want to + cache your data in the agent/sub-agent. + + ... to be continued... + + +@ end@ +@end@ // m2c_processing_type eq 'r' +######################################################################## +##//#################################################################### +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/mfd-access-unsorted-external-defines.m2i b/local/mib2c-conf.d/mfd-access-unsorted-external-defines.m2i new file mode 100644 index 0000000..f4f3514 --- /dev/null +++ b/local/mib2c-conf.d/mfd-access-unsorted-external-defines.m2i @@ -0,0 +1,1198 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +## +@eval $mfd_aue_wrap_param = "wrap_ctx"@ +@eval $mfd_aue_wrap_param_type = "${context}_interface_ctx *"@ +@eval $mfd_aue_wrap_param_decl = "$mfd_aue_wrap_param_type $mfd_aue_wrap_param"@ +## +@eval $mfd_aue_param = "${context}_reg"@ +@eval $mfd_aue_param_type = "${context}_registration *"@ +@eval $mfd_aue_param_decl = "$mfd_aue_param_type $mfd_aue_param"@ +@eval $mfd_aue_param_cmt = "$mfd_aue_param Pointer to a $mfd_aue_param_type" +## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'h'@ +## +@if $m2c_include_examples == 1@ +$example_start +/* ********************************************************************* + * Since we have no idea how you really access your data, we'll go with + * a worst case example: a flat text file. + @ if $m2c_data_transient != 2@ + @ print Example code is for fully transient data. Either turn off@ + @ print m2c_include_examples or set m2c_data_transient to 2.@ + @ exit@ + @ end@ + */ +#define MAX_LINE_SIZE 256 +$example_end + +@end@ +/** + * loop context + * + * ToDo: + * define loop context structure + * + * Since the actual loop is in the MFD handler, a loop contex parameter + * is provided to help you keep track of where you are in between calls + * to functions that you wrote and the master MFD handler calls. The + * structure of this context is user defineable, and is defined in the + * file ${table}_data_access.h. + * + * E.G., if your data is stored in a linked list, the obvious thing you + * want to know from one function call to the next is your current + * position in the linked list. Thus the easiest context to use is a + * pointer within the linked list. For an array, the current index to + * that array would be easiest. + * + * The funtion calls are actually passed a reference to the loop + * context, to allow the loop context to be allocated memory. Here are + * some simple examples definitions for various data formats. These + * definitions are used in examples later on. + * + */ +typedef struct ${context}_loop_context_s { + /* + * temporary context used during iteration + */ + ${context}_rowreq_ctx *rowreq_ctx; +@if $m2c_include_examples == 1@ + + /* + * this example code is based on a data source that is a + * text file to be read and parsed. + */ + FILE *filep; + char line[MAX_LINE_SIZE]; +@end@ +} ${context}_loop_context; + +/* + * define a reference to the loop context + * + * NOTE: DO NOT ADD ITEMS TO THIS STRUCTURE! + */ +typedef struct ${context}_ref_loop_ctx_s { + ${context}_loop_context *loop_ctx; +} ${context}_ref_loop_ctx; + +int ${context}_loop_get_first( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, + ${context}_ref_rowreq_ctx *rowreq_ctx_ref); +int ${context}_loop_get_next( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, + ${context}_ref_rowreq_ctx *rowreq_ctx_ref); +int ${context}_loop_get_data( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, + ${context}_ref_rowreq_ctx *rowreq_ctx_ref); +int ${context}_loop_save_position($mfd_aue_param_decl, + ${context}_ref_loop_ctx *loop_ctx_ref, + ${context}_ref_loop_ctx *save_loop_ctx_ref, int reuse); +int ${context}_loop_cleanup_context( $mfd_aue_param_decl, ${context}_ref_loop_ctx *ref); + +## +@end@ // m2c_processing_type eq 'h' +######################################################################## +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'i'@ +/** + * @internal + * wrapper around clean up a loop reference + */ +static int +_${context}_loop_cleanup_context( $mfd_aue_wrap_param_decl, + ${context}_ref_loop_ctx *ref) +{ + DEBUGMSGTL(("internal:${context}:_${context}_loop_cleanup_context","called\n")); + + return ${context}_loop_cleanup_context($mfd_aue_wrap_param->user_ctx, ref); +} /* _${context}_loop_cleanup_context */ + +/** + * @internal + * wrapper around save position + */ +static int +_${context}_loop_save_position( $mfd_aue_wrap_param_decl, ${context}_ref_loop_ctx *ref, + ${context}_ref_loop_ctx *ref_copy, int reuse) +{ + DEBUGMSGTL(("internal:${context}:_${context}_loop_save_position","called\n")); + + return ${context}_loop_save_position($mfd_aue_wrap_param->user_ctx, ref, + ref_copy, reuse); +} /* _${context}_loop_save_position */ + +/** + * @internal + * wrapper around user get_first to setup the index oid + */ +static int +_${context}_loop_get_first_wrapper($mfd_aue_wrap_param_decl, + ${context}_ref_loop_ctx * loop_ctx_ref, + ${context}_ref_rowreq_ctx * rowreq_ctx_ref) +{ + int rc; + DEBUGMSGTL(("internal:${context}:_${context}_loop_get_first_wrapper","called\n")); + + rc = ${context}_loop_get_first($mfd_aue_wrap_param->user_ctx, loop_ctx_ref, + rowreq_ctx_ref); + /* + * convert index to OID + */ + if(SNMPERR_SUCCESS == rc ) { + netsnmp_assert((NULL != rowreq_ctx_ref) && + (rowreq_ctx_ref->rowreq_ctx->oid_idx.oids == rowreq_ctx_ref->rowreq_ctx->oid_tmp)); + rowreq_ctx_ref->rowreq_ctx->oid_idx.len = sizeof(rowreq_ctx_ref->rowreq_ctx->oid_tmp); + rc = ${context}_index_to_oid(&rowreq_ctx_ref->rowreq_ctx->oid_idx, + &rowreq_ctx_ref->rowreq_ctx->tbl_idx); + netsnmp_assert(rowreq_ctx_ref->rowreq_ctx->oid_idx.len != + sizeof(rowreq_ctx_ref->rowreq_ctx->oid_tmp)); + } + + return rc; +} /* _${context}_loop_get_first_wrapper */ + +/** + * @internal + * wrapper around user get_next to setup the index oid + */ +static int +_${context}_loop_get_next_wrapper($mfd_aue_wrap_param_decl, + ${context}_ref_loop_ctx * loop_ctx_ref, + ${context}_ref_rowreq_ctx * rowreq_ctx_ref) +{ + int rc; + DEBUGMSGTL(("internal:${context}:_${context}_loop_get_next_wrapper","called\n")); + + rc = ${context}_loop_get_next($mfd_aue_wrap_param->user_ctx, loop_ctx_ref, + rowreq_ctx_ref); + /* + * convert index to OID + */ + if(SNMPERR_SUCCESS == rc ) { + netsnmp_assert((NULL != rowreq_ctx_ref) && + (rowreq_ctx_ref->rowreq_ctx->oid_idx.oids == rowreq_ctx_ref->rowreq_ctx->oid_tmp)); + rowreq_ctx_ref->rowreq_ctx->oid_idx.len = sizeof(rowreq_ctx_ref->rowreq_ctx->oid_tmp); + rc = ${context}_index_to_oid(&rowreq_ctx_ref->rowreq_ctx->oid_idx, + &rowreq_ctx_ref->rowreq_ctx->tbl_idx); + netsnmp_assert(rowreq_ctx_ref->rowreq_ctx->oid_idx.len != + sizeof(rowreq_ctx_ref->rowreq_ctx->oid_tmp)); + } + + return rc; +} /* _${context}_loop_get_next_wrapper */ + +@if $m2c_data_transient != 0@ # +/** + * @internal + * get data wrapper to allocate context for the user + */ +static int +_${context}_loop_get_data_wrapper($mfd_aue_wrap_param_decl, + ${context}_ref_loop_ctx * loop_ctx_ref, + ${context}_ref_rowreq_ctx * rowreq_ctx_ref) +{ +// ${context}_rowreq_ctx *orig_ctx = rowreq_ctx_ref->rowreq_ctx; + + DEBUGMSGTL(("internal:${context}:_${context}_loop_get_data_wrapper","called\n")); + + return ${context}_loop_get_data($mfd_aue_wrap_param->user_ctx, loop_ctx_ref, rowreq_ctx_ref); +} /* _${context}_loop_get_data_wrapper */ + +@end@ // transient != 0 +/** + * @internal + * initialize the iterator container with functions or wrappers + */ +void +_${context}_container_init(${context}_interface_ctx *if_ctx) +{ + DEBUGMSGTL(("internal:${context}:_${context}_container_init","called\n")); + + if_ctx->container = netsnmp_container_iterator_get(/** registration */ + if_ctx, + /** compare */ + NULL, + /** get_first */ + (Netsnmp_Iterator_Loop_Key*)_${context}_loop_get_first_wrapper, + /** get_next */ + (Netsnmp_Iterator_Loop_Key*)_${context}_loop_get_next_wrapper, + /** get_data */ +@if $m2c_data_transient != 0@ # + (Netsnmp_Iterator_Loop_Data*)_${context}_loop_get_data_wrapper, +@else@ + NULL, +@end@ + /** save_pos */ + (Netsnmp_Iterator_Ctx_Dup*)_${context}_loop_save_position, + /** init_context */ + (Netsnmp_Iterator_Ctx*)NULL, + /** cleanup_context */ + (Netsnmp_Iterator_Ctx*)_${context}_loop_cleanup_context, + /** free_user_ctx */ + NULL, + /** sorted */ + 0); +} /* _${context}_container_init */ + +## +@end@ // m2c_processing_type eq 'i' +######################################################################## +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'c'@ +/** + * unsorted-external overview + * + * The unsorted external data access code works by calling a few simple + * functions to get the index value for each row. Once the agent determines + * which row is needed to process an incoming request, another function + * is called to retrieve the data for that row. + * + * A simplified version of the pseudo-code looks like this: + * + * ${context}_loop_get_first(loop,data) + * while( no_error ) { + * if( best_match(data, key) + * ${context}_loop_save_position(loop,pos); + * ${context}_loop_get_next(loop,data) + * } + * ${context}_loop_get_data(pos,data) + * ${context}_loop_cleanup_context(loop) + */ + +/*********************************************************************** + * + * ITERATION + * + ***********************************************************************/ + +/** + * get the first data index + * + * Summary + * ------- + * This function is called to initialize the iterator loop context for a + * new iteration loop and return the index(es) for the first + * ${context}_data in the data set. + * + * Note that during the loop, the only important thing is the indexes. + * If access to your data is cheap/fast (e.g. you have a pointer to a + * structure in memory), it would make sense to update the data here. + * If, however, the accessing the data invovles more work (e.g. parsing + * some other existing data, or peforming calculations to derive the data), + * then you should limit yourself to setting the indexes. Extracting the + * can be put off until the desired row is found. See the notes on + * ${context}_loop_get_data(). + * + * Note that this function does not correspond to a SNMP GET pdu, and + * you should return data items in whatever order they are already in. + * (In fact, if your data is already ordered in the same order as the + * SNMP indexes, you shouldn't be using the unsorted-access code). + * + * This function should update the table index (rowreq_ctx_ref->rowreq_ctx->tbl_idx) + * values for the raw data (rowreq_ctx_ref->rowreq_ctx->data). + * + * More Details + * ------------ + * If there is currently no data available, return MFD_END_OF_DATA. + * Otherwise, you should set rowreq_ctx_ref->rowreq_ctx and its indexes. + * + * rowreq_ctx_ref->rowreq_ctx will be NULL. You should allocate a new context + * for this loop. [Alternatively, you could allocate one in + * ${context}_loop_init_context, save it in your + * ${context}_ref_loop_ctx, and use it here.] + * + * Once you have your context pointer, you should set the index (or indexes) + * in rowreq_ctx_ref->rowreq_ctx->tbl_idx to the appropriate value for this row. [If you + * use your loop_ctx_ref cleverly, you might be able to put this work in + * ${context}_loop_get_next, and simply call that function.] + * + * @param $mfd_aue_param_cmt + * @param loop_ctx_ref Pointer to your loop reference. + * @param rowreq_ctx_ref Pointer to a context reference. + * + * @retval MFD_SUCCESS : success. + * @retval MFD_END_OF_DATA : no data available + * @retval MFD_ERROR : error. + */ +int +${context}_loop_get_first( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, + ${context}_ref_rowreq_ctx *rowreq_ctx_ref) +{ + DEBUGMSGTL(("verbose:${context}:${context}_loop_get_first","called\n")); + + netsnmp_assert(rowreq_ctx_ref); + netsnmp_assert(loop_ctx_ref); + + /* + * allocate memory for new structure + */ + loop_ctx_ref->loop_ctx = SNMP_MALLOC_TYPEDEF(${context}_loop_context); + if(NULL == loop_ctx_ref->loop_ctx) + return MFD_ERROR; + + /* + * allocate a temporary context to use during iteration + */ +@ eval $m2c_tmp = ""@ +@ if ($m2c_data_allocate == 1) || ($m2c_data_init == 1)@ +@ eval $m2c_tmp = "NULL"@ +@ if ($m2c_data_allocate == 1) && ($m2c_data_init == 1)@ +@ eval $m2c_tmp = "$m2c_tmp, NULL"@ +@ @end@ +@ end@ + loop_ctx_ref->loop_ctx->rowreq_ctx = ${context}_allocate_rowreq_ctx($m2c_tmp); + if(NULL == loop_ctx_ref->loop_ctx->rowreq_ctx) { + SNMP_FREE(loop_ctx_ref->loop_ctx); + return MFD_RESOURCE_UNAVAILABLE; + } + + /* + * ToDo: + * set up loop context + */ +@if $m2c_include_examples == 1@ +$example_start + /* + * open our data file. + */ + loop_ctx_ref->loop_ctx->filep = fopen("/etc/dummy.conf", "r"); + if(NULL == loop_ctx_ref->loop_ctx->filep) { + return MFD_RESOURCE_UNAVAILABLE; + } + +$example_end +@end@ + +@ifconf ${table}_update_idx.m2i@ +@ include ${table}_update_idx.m2i@ +@else@ +@ if $m2c_include_examples == 1@ +$example_start + /* + * in this example, after opening the file, get next does the same thing + * as get first, we let's just call get next... + */ + return ${context}_loop_get_next($mfd_aue_param, loop_ctx_ref, rowreq_ctx_ref); +$example_end +@ else@ + /* + * we just need the index for now. Reuse the one in the loop context's + * temporary row request context. (rowreq_ctx_ref->rowreq_ctx->tbl_idx) + */ + rowreq_ctx_ref->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; + + /* + * ToDo: + * set local vars for index from loop_ctx_ref->loop_ctx + * this can be done in one of two ways: + */ + + /* + * 1) individually + */ +@ foreach $node index@ +@ include m2c_setup_node.m2i@ + /* + * ToDo: + * set rowreq_ctx_ref->rowreq_ctx->tbl_idx->$node +@ if $m2c_node_needlength == 1@ + * and rowreq_ctx_ref->tbl_idx->${node}_len +@ end@ + */ +@ end@ #foreach + + /* + * OR + */ + + /* + * 2) by calling ${context}_indexes_set() + * ${context}_indexes_set(rowreq_ctx_ref->tbl_idx, +@ foreach $node index@ +@ include m2c_setup_node.m2i@ +@ if $m2c_node_needlength == 1@ + * ${node}_ptr, ${node}_len +@ else@ + * $node +@ end@ +@ end@ # foreach index + * ); + */ +@ end@ # example +@end@ #ifconf + + return MFD_SUCCESS; +} /* ${context}_loop_get_first */ + +/** + * get the next data index + * + * Summary + * ------- + * This function returns the next data item in the data set. The same + * caveat applies here as did above. The indexes are the important parts + * during loop processing. + * + * Note that this function does not correspond to a SNMP GET-NEXT pdu, and + * you should return data items in whatever order they are already in. + * (In fact, if your data is already ordered in the same order as the + * SNMP indexes, you shouldn't be using the unsorted-access code). + * + * More Details + * ------------ + * rowreq_ctx_ref->rowreq_ctx will have been set in ${context}_loop_get_first. + * + * If there is currently no data available, return MFD_END_OF_DATA. + * Otherwise, you should set the indexes in rowreq_ctx_ref->rowreq_ctx->tbl_idx. + * + * You should set the index (or indexes) in rowreq_ctx_ref->rowreq_ctx->tbl_idx to the + * appropriate value for this row. + * + * @param $mfd_aue_param_cmt + * @param loop_ctx_ref Pointer to your loop reference. + * @param rowreq_ctx_ref Pointer to a context reference. + * + * @retval MFD_SUCCESS : success. + * @retval MFD_END_OF_DATA : no more data available + * @retval MFD_ERROR : error. + */ +int +${context}_loop_get_next( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, + ${context}_ref_rowreq_ctx *rowreq_ctx_ref) +{ + DEBUGMSGTL(("verbose:${context}:${context}_loop_get_next","called\n")); + + netsnmp_assert(loop_ctx_ref && loop_ctx_ref->loop_ctx); + netsnmp_assert(rowreq_ctx_ref); + + /* + * we just need the index for now. Reuse the one in the loop context's + * temporary row request context. (rowreq_ctx_ref->rowreq_ctx->tbl_idx) + */ + rowreq_ctx_ref->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; + +@ if $m2c_include_examples == 1@ +$example_start + /* + * get a line (skip blank lines) + */ + do { + if (!fgets(loop_ctx_ref->loop_ctx->line, sizeof(loop_ctx_ref->loop_ctx->line), + loop_ctx_ref->loop_ctx->filep)) { + /* we're done */ + fclose(loop_ctx_ref->loop_ctx->filep); + loop_ctx_ref->loop_ctx->filep = NULL; + } + } while (loop_ctx_ref->loop_ctx->filep && (loop_ctx_ref->loop_ctx->line[0] == '\n')); + + /* + * check for end of data + */ + if(NULL == loop_ctx_ref->loop_ctx->filep) + return MFD_END_OF_DATA; + + /* + * ToDo: + * set local vars for index from loop_ctx_ref->loop_ctx + * this can be done in one of two ways: + */ + + /* + * 1) individually + */ +@ foreach $node index@ +@ include m2c_setup_node.m2i@ + /* + * ToDo: + * set rowreq_ctx_ref->rowreq_ctx->tbl_idx->$node +@ if $m2c_node_needlength == 1@ + * and rowreq_ctx_ref->tbl_idx->${node}_len +@ end@ + */ +@ end@ #foreach + + /* + * OR + */ + + /* + * 2) by calling ${context}_indexes_set() + * ${context}_indexes_set(rowreq_ctx_ref->tbl_idx, +@ foreach $node index@ +@ include m2c_setup_node.m2i@ +@ if $m2c_node_needlength == 1@ + * ${node}_ptr, ${node}_len +@ else@ + * $node +@ end@ +@ end@ # foreach index + * ); + */ +$example_end +@ end@ # example + + return MFD_SUCCESS; +} /* ${context}_loop_get_next */ + +/** + * duplicate the current loop reference + * + * Summary + * ------- + * During loop iteration, the iterator keeps track of the row that + * is the current best match. This function is called when the + * current row is a better match than any previous row. + * + * You should save any information you need to be able to locate this row + * again from the current loop context to a new loop context. + * + * At the end of the loop, when the best match has been found, the saved + * loop context will be used to get the data for the row by calling + * ${context}_loop_get_data(). +@if $m2c_data_transient != 0@ # persistent + * + * Since your data is transient, you need to make a copy of it before + * the iterator moves on to the next row. +@end@ + * +@if $m2c_data_transient != 0@ # persistent + * More Details + * ------------ +@ if $m2c_data_transient == 1@ # short term + * Since your data is semi-TRANSIENT data, you could just keep a pointer + * to the data in the loop reference. The data should then be copied in + * ${context}_loop_get_data(). +@ else@ # $m2c_data_transient == 2@ # copy immediately + * One idea would be to copy it space allocated in the loop reference + * structure. Another would be to simply have a pointer in the loop + * reference structure, and allocate memory here. + * +@ end@ +@end@ + * @param $mfd_aue_param_cmt + * @param loop_ctx_ref Reference to current loop context. + * @param save_loop_ctx_ref Reference to a loop context for saving the current + * position. If reuse is not set or + * save_loop_ctx_ref->loop_ctx is NULL, allocate + * a new one. If reuse is set, you may reuse the existing + * loop_ctx. + * @param reuse Indicates if an existing save_loop_ctx_ref->loop_ctx + * may be reused. + * + * @retval MFD_SUCCESS : success. + * @retval MFD_ERROR : error. + */ +int +${context}_loop_save_position($mfd_aue_param_decl, + ${context}_ref_loop_ctx *loop_ctx_ref, + ${context}_ref_loop_ctx *save_loop_ctx_ref, + int reuse) +{ + DEBUGMSGTL(("verbose:${context}:${context}_loop_save_position","called\n")); + + netsnmp_assert(loop_ctx_ref && save_loop_ctx_ref); + + /* + * ToDo: + * 1) allocate new loop context, unless you can reuse a previous pointer. + * 2) save information for the position of loop_ctx_ref in save_loop_ctx_ref. + */ + if((0 == reuse) || (NULL == save_loop_ctx_ref->loop_ctx)) + save_loop_ctx_ref->loop_ctx = SNMP_MALLOC_TYPEDEF(${context}_loop_context); + if(NULL == save_loop_ctx_ref->loop_ctx) { + snmp_log(LOG_ERR, "could not allocate memory\n"); + return MFD_ERROR; + } + + /* + * if you can reuse a previously saved contex, just swap + * it out with the loop iterator + */ + if(reuse && save_loop_ctx_ref->loop_ctx->rowreq_ctx) { + ${context}_rowreq_ctx * tmp_rowreq_ctx = save_loop_ctx_ref->loop_ctx->rowreq_ctx; + save_loop_ctx_ref->loop_ctx->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; + loop_ctx_ref->loop_ctx->rowreq_ctx = tmp_rowreq_ctx; + } + else { + /* + * take the current pointer + */ + save_loop_ctx_ref->loop_ctx->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; + + /* + * allocate a new context to replace the one you just took. + */ +@ eval $m2c_tmp = ""@ +@ if ($m2c_data_allocate == 1) || ($m2c_data_init == 1)@ +@ eval $m2c_tmp = "NULL"@ +@ if ($m2c_data_allocate == 1) && ($m2c_data_init == 1)@ +@ eval $m2c_tmp = "$m2c_tmp, NULL"@ +@ @end@ +@ end@ + loop_ctx_ref->loop_ctx->rowreq_ctx = ${context}_allocate_rowreq_ctx($m2c_tmp); + if(NULL == loop_ctx_ref->loop_ctx->rowreq_ctx) { + SNMP_FREE(loop_ctx_ref->loop_ctx); + return MFD_ERROR; + } + } + +@if $m2c_data_transient == 0@ # persistent + /** non-TRANSIENT data: no need to copy */ +@elsif $m2c_data_transient == 1@ # short term + /** semi-TRANSIENT data: will copy data when index found */ + /** only need to copy pertinent data from loop context */ +@elsif $m2c_data_transient == 2@ # copy immediately + /* + * TRANSIENT data: copy all the data. + */ +@end@ +@if $m2c_include_examples == 1@ +$example_start +@ if $m2c_data_transient == 1@ # short term + /** save line to do that */ + memcpy(save_loop_ctx_ref->loop_ctx->line, loop_ctx_ref->loop_ctx->line, + sizeof(loop_ctx_ref->loop_ctx->line)); +@ elsif $m2c_data_transient == 2@ # copy immediately +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ + /* + * ToDo: + * set rowreq_ctx_ref->${m2c_data_item}$node + * from the loop context + */ +@ end@ +@ end@ +$example_end +@end@ # example + + return MFD_SUCCESS; +} /* ${context}_loop_save_position */ + +@if $m2c_data_transient != 0@ # semi-transient +/** + * set ${context}_data from a data context + * + * Summary + * ------- + * At the end of the loop, when the best match has been found, the saved + * loop context will be used to get the data for the row by calling + * ${context}_loop_get_data(). + * + * You should return a fully populated row request context in + * rowreq_ctx_ref->rowreq_ctx. + * + * More Details + * ------------ + * @param $mfd_aue_param_cmt + * @param loop_ctx_ref pointer to your loop reference. + * @param rowreq_ctx_ref pointer to a context reference. + */ +int +${context}_loop_get_data( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref, + ${context}_ref_rowreq_ctx *rowreq_ctx_ref) +{ + DEBUGMSGTL(("verbose:${context}:${context}_loop_get_data","called\n")); + + netsnmp_assert((NULL != loop_ctx_ref) && (NULL != loop_ctx_ref->loop_ctx)); + netsnmp_assert(NULL != rowreq_ctx_ref); + netsnmp_assert(NULL != rowreq_ctx_ref->rowreq_ctx); + + /* + * take temporary row request context from loop context + */ + rowreq_ctx_ref->rowreq_ctx = loop_ctx_ref->loop_ctx->rowreq_ctx; + loop_ctx_ref->loop_ctx->rowreq_ctx = NULL; + + /* + * copy data to the data context (rowreq_ctx_ref->${m2c_data_item}) +@ if $m2c_include_examples == 1@ + * in loop_save_position, we saved line to do that +@ end@ + */ +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ + /* + * $m2c_node_summary + */ +@ eval $m2c_ctx_lh = "rowreq_ctx_ref->$m2c_ctx_rh"@ +@ eval $m2c_ctx_lhs = "rowreq_ctx_ref->$m2c_ctx_rhs"@ +@ eval $m2c_ctx_rh = "loop_ctx_ref->loop_ctx->$node"@ +@ eval $m2c_ctx_rhs = "loop_ctx_ref->loop_ctx->${node}_len"@ +@ include generic-value-map.m2i@ + +@ end@ + + return MFD_SUCCESS; +} /* ${context}_loop_get_data */ + +@end@ // if $m2c_data_transient != 0 + +/** + * clean up a loop reference + * + * Summary + * ------- + * This function will be called once the loop iteration has completed + * to release any memory or resources allocated for the loop context. + * + * More Details + * ------------ + * @param $mfd_aue_param_cmt + * @param loop_ctx_ref Pointer to your loop reference. + * + * @retval MFD_SUCCESS : success. + * @retval MFD_ERROR : error. + */ +int +${context}_loop_cleanup_context( $mfd_aue_param_decl, ${context}_ref_loop_ctx *loop_ctx_ref) +{ + DEBUGMSGTL(("verbose:${context}:${context}_loop_cleanup_context","called\n")); + + netsnmp_assert(loop_ctx_ref); + + if(!loop_ctx_ref->loop_ctx) + return MFD_ERROR; + + /* + * release the row request context, if it wasn't taken + */ + if(loop_ctx_ref->loop_ctx->rowreq_ctx) + ${context}_release_rowreq_ctx(loop_ctx_ref->loop_ctx->rowreq_ctx); + + /* + * ToDo: + * release resources + */ +@if $m2c_include_examples == 1@ +$example_start + /* + * close file + */ + if(loop_ctx_ref->loop_ctx->filep) + fclose(loop_ctx_ref->loop_ctx->filep); +$example_end + +@end@ + /* + * free loop context + */ + free(loop_ctx_ref->loop_ctx); + + return MFD_SUCCESS; +} /* ${context}_loop_cleanup_context */ + +@end@ // m2c_processing_type eq 'c' +######################################################################## +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'r'@ +## + unsorted-external summary + ------------------------- + The unsorted-external data access code is for cases when you data is + kept UNSORTED and EXTERNAL to the agent/sub-agent. + + This code was generated based on the following assumptions or settings: + + 1) The raw data for this table is UNSORTED. + @if $mfd_readme_verbose != 0@ + + UNSORTED data is data that is not kept in the same order as the way + SNMP expects the index(es) for the table to be kept. [It could very + well be sorted in some other order, but for the purpose of SNMP, the + order is incorrect.] If you're not sure if your data is sorted + in an SNMP compliant way, its likely not. + + Because the raw data is unsorted, to satisfy a particular request, the + entire data set must be examined to find the apropriate index. This + is done via a simple loop. The MFD handler will call your get_first + function and the call the get_next function repeatedly, until it + returns SNMPERR_NO_VARS. + @end@ + + 2) The raw data for this table is EXTERNAL. + @if $mfd_readme_verbose != 0@ + + EXTERNAL data is data that is owned by some other process, + device, file or mechanism. The agent must use some interface to + read or modify the data. An external process may modify the data + without the agent's knowledge. For example, the Net-SNMP agent + implements the interface table (ifTable), which reports on + network interfaces. The host operating system owns this data, and + Net-SNMP must use system calls to report or manipulate the data. + Examples of external data include data stored in kernel space, in + files, in another non-memory shared process, and data stored in + devices. + @end@ + + 3) The raw data for this table is TRANSIENT. + @if $mfd_readme_verbose != 0@ + + TRANSIENT data is data that may be overwritten by another funtion + or process. For example, many OS functions return data in a + static buffer that will be reused the next time the function is + called. Because of this, we will assume that you will copy the + raw data retrieved from these other sources to a generated + structure for use within the Net-SNMP agent. (Don't worry, we'll + help you) + @end@ + + +## +## this should be syncronized with master version of comments in +## mfd-access-unsorted-external-body.m2i You should be able to copy +## the comments here and replace " * " with " ". +## + The unsorted external data access code works by calling a few simple + functions to get the index value for each row. Once the agent determines + which row is needed to process an incoming request, another function + is called to retrieve the data for that row. + + A simplified version of the pseudo-code looks like this: + + ${context}_loop_init_context(loop) + ${context}_loop_get_first(loop,data) + while( no_error ) { + if( best_match(data, key) + ${context}_loop_save_position(loop,pos); + ${context}_loop_get_next(loop,data) + } + ${context}_loop_get_data(pos,data) + ${context}_loop_cleanup_context(loop) +## +## end sync +## + + We will talk about each individual step below. + + +######################################################################## + Defining context for the loop + ----------------------------- + ToDo : typedef ${context}_loop_context + WHERE: ${table}_data_access.h + + @if $mfd_readme_verbose != 0@ +## +## this should be syncronized with master version of comments in +## mfd-access-unsorted-external-body.m2i You should be able to copy +## the comments here and replace " * " with " ". +## + Since the actual loop is in the MFD handler, a loop contex parameter + is provided to help you keep track of where you are in between calls + to functions that you wrote and the master MFD handler calls. The + structure of this context is user defineable, and is defined in the + file ${table}_data_access.h. + + E.G., if your data is stored in a linked list, the obvious thing you + want to know from one function call to the next is your current + position in the linked list. Thus the easiest context to use is a + pointer within the linked list. For an array, the current index to + that array would be easiest. + + The funtion calls are actually passed a reference to the loop + context, to allow the loop context to be allocated memory. Here are + some simple examples definitions for various data formats. These + definitions are used in examples later on. +## +## end sync +## + + Linked list + ----------- + typedef list_node ${context}_loop_context; + + Array + ----- + typedef integer ${context}_loop_context; + + File + ---- + typedef struct ${context}_loop_context_s { + char * file_name; + FILE * f; + char line[128]; + } ${context}_loop_context; + + @end@ + +######################################################################## + Initialization + -------------- + ToDo : Initialization + FUNC : ${context}_loop_init_data + WHERE: ${table}_data_access.c + + @if $mfd_readme_verbose != 0@ + The ${context}_loop_init_data function will be called during startup to + allow for any initialization needed for the data access routines. + + @end@ + +######################################################################## + Preparing for the loop + ---------------------- + ToDo : initialize loop context + FUNC : ${context}_loop_init_context + WHERE: ${table}_data_access.c + + @if $mfd_readme_verbose != 0@ +## +## this should be syncronized with master version of comments in +## mfd-access-unsorted-external-body.m2i You should be able to copy +## the comments here and replace " * " with " ". +## + This function will be called before the start of a new itertion over + the data. The loop context that is initialized here will be passed to + ${context}_loop_get_first and ${context}_loop_get_next. + + Set the loop context variable ref->loop_ctx so that the iteration + functions (get_first and get_next) can locate the apropriate data + context. +## +## end sync +## + + The primary purpose of the loop_init_context call is to initialize + the loop context data (ref). Here are some simple examples, based on the + earlier example loop contexts. + + Linked list + ----------- + ref->loop_ctx = my_table_head_ptr; + + Array + ----- + /* instead of actually allocating memory, just use the pointer */ + /* as an integer */ + (integer)(ref->loop_ctx) = 0; + + File + ---- + ref->loop_ctx = SNMP_MALLOC_TYPEDEF(${context}_loop_context); + /* error checking here */ + ref->loop_ctx->file_name = (char*) reg->mfd_user_ctx; + ref->loop_ctx->f = fopen( ref->loop_ctx->file_name, "r+" ); + + @end@ + +######################################################################## + The Loop + -------- + ToDo : return raw data + FUNC : ${context}_loop_get_first + WHERE: ${table}_data_access.c + + @if $mfd_readme_verbose != 0@ +## +## this should be syncronized with master version of comments in +## mfd-access-unsorted-external-body.m2i You should be able to copy +## the comments here and replace " * " with " ". +## + This function is called to return set the index(es) for the first + ${context}_data in the data set. + + Note that during the loop, the only important thing is the indexes. + If access to your data is cheap/fast (e.g. you have a pointer to a + structure in memory), it would make sense to update the data here. + If, however, the accessing the data invovles more work (e.g. parsing + some other existing data, or peforming calculations to derive the data), + then you should limit yourslef to setting the indexes. Extracting the + can be put off until the desired row is found See the notes on + ${context}_loop_get_data(). + + Note that this function does not correspond to a SNMP GET pdu, and + you should return data items in whatever order they are already in. + (In fact, if your data is already ordered in the same order as the + SNMP indexes, you shouldn't be using the unsorted-access code). + + This function should update the table index (rowreq_ctx_ref->rowreq_ctx->tbl_idx) + values for the raw data (rowreq_ctx_ref->rowreq_ctx->data). +## +## end sync +## + + Linked list + ----------- + rowreq_ctx_ref->rowreq_ctx->data = loop_ctx_ref->loop_ctx; + + Array + ----- + /* assuming registration has array of pointers */ + rowreq_ctx_ref->rowreq_ctx->data = reg->mfd_user_ctx[(integer)(ref->loop_ctx)]; + + File + ---- + fgets(loop_ctx_ref->loop_ctx->line, sizeof(loop_ctx_ref->loop_ctx->line), + loop_ctx_ref->loop_ctx->f); + rowreq_ctx_ref->rowreq_ctx->data = loop_ctx_ref->loop_ctx->line; + + @end@ + + ToDo : return raw data + FUNC : ${context}_loop_get_next + WHERE: ${table}_data_access.c + + @if $mfd_readme_verbose != 0@ +## +## this should be syncronized with master version of comments in +## mfd-access-unsorted-external-body.m2i You should be able to copy +## the comments here and replace " * " with " ". +## + This function returns the next data item in the data set. The same + caveat applies here as did above. The indexes are the important parts + during loop processing. + + Note that this function does not correspond to a SNMP GET-NEXT pdu, and + you should return data items in whatever order they are already in. + (In fact, if your data is already ordered in the same order as the + SNMP indexes, you shouldn't be using the unsorted-access code). +## +## end sync +## + + Linked list + ----------- + loop_ctx_ref->loop_ctx = loop_ctx_ref->loop_ctx->next; + rowreq_ctx_ref->rowreq_ctx->data = loop_ctx_ref->loop_ctx; + + Array + ----- + ++((integer)(ref->loop_ctx)); + /* assuming registration has array of pointers */ + rowreq_ctx_ref->rowreq_ctx->data = reg->mfd_user_ctx[(integer)(ref->loop_ctx)]; + + File + ---- + fgets(loop_ctx_ref->loop_ctx->line, sizeof(loop_ctx_ref->loop_ctx->line), + loop_ctx_ref->loop_ctx->f); + rowreq_ctx_ref->rowreq_ctx->data = loop_ctx_ref->loop_ctx->line; + + @end@ + +######################################################################## + Updating the Index + ------------------ + ToDo : update index for the raw data + FUNC : ${context}_indexes_set + WHERE: ${table}_data_access.c + + This is a convenience function for setting the index context from + the native C data. Where necessary, value mapping should be done. + + @if $mfd_readme_verbose == 1@ + This function should update the table index values (found in + tbl_idx) for the given raw data. + + @end@ + +######################################################################## + Saving a position in the loop + ----------------------------- + ToDo : Saving a position in the loop + FUNC : ${context}_loop_save_position + WHERE: ${table}_data_access.c + + @if $mfd_readme_verbose != 0@ +## +## this should be syncronized with master version of comments in +## mfd-access-unsorted-external-body.m2i You should be able to copy +## the comments here and replace " * " with " ". +## + During loop iteration, the iterator keeps track of the row that + is the current best match. This function is called when the + current row is a better match than any previous row. + + You should save any information you need to be able to locate this row + again from the current loop context to a new loop context. + + At the end of the loop, when the best match has been found, the saved + loop context will be used to get the data for the row by calling + ${context}_loop_get_data(). +@if $m2c_data_transient != 0@ # persistent + + Since your data is transient, you need to make a copy of it before + the iterator moves on to the next row. +@end@ +## +## end sync +## + + @end@ + +######################################################################## + Returning Data For an Index + --------------------------- + ToDo : copy transient raw data to generated structure + FUNC : ${context}_loop_get_data + WHERE: ${table}_data_access.c + + @if $mfd_readme_verbose != 0@ +## +## this should be syncronized with master version of comments in +## mfd-access-unsorted-external-body.m2i You should be able to copy +## the comments here and replace " * " with " ". +## + At the end of the loop, when the best match has been found, the saved + loop context will be used to get the data for the row by calling + ${context}_loop_get_data(). +## +## end sync +## + + @end@ + +######################################################################## + Cleaning up after the loop + -------------------------- + ToDo : release any allocated memory + FUNC : ${context}_loop_cleanup_context + WHERE: ${table}_data_access.c + + @if $mfd_readme_verbose != 0@ +## +## this should be syncronized with master version of comments in +## mfd-access-unsorted-external-body.m2i You should be able to copy +## the comments here and replace " * " with " ". +## + This function will be called once the loop iteration has completed + to release any memory allocated for loop reference. +## +## end sync +## + The purpose of the loop_cleanup_context call is to release any memory + allocated for the loop context data. Here are some simple examples, based + on the earlier example loop contexts. + + Linked list + ----------- + /* nothing to do */ + + Array + ----- + /* nothing to do */ + + File + ---- + free(ref->loop_ctx); + + @end@ + +## +@end@ // m2c_processing_type eq 'r +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/mfd-data-access.m2c b/local/mib2c-conf.d/mfd-data-access.m2c new file mode 100644 index 0000000..bcff9bb --- /dev/null +++ b/local/mib2c-conf.d/mfd-data-access.m2c @@ -0,0 +1,331 @@ +##//######################################################### -*- c -*- +##//generic include for XXX. Do not use directly. +## +##//$Id$ +##//#################################################################### +##//#################################################################### +## +## lower conf files get confused with multiple processing types, so +## set single options +@eval $mfd_data_access_processing_type = "$m2c_processing_type"@ +@eval $m2c_processing_type = 'h'@ +@open ${name}_data_access.h@ +@eval $hack = "Id"@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + */ +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +@eval $m2c_save = "$name"@ +@eval $name = "${name}_DATA_ACCESS"@ +@include generic-header-top.m2i@ +@eval $name = "$m2c_save"@ + +/* ********************************************************************* + * function declarations + */ + +/* ********************************************************************* + * Table declarations + */ +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ include details-table.m2i@ + +##@ eval $m2c_tmp=""@ +##@ foreach $node index@ +##@ include m2c_setup_node.m2i@ +##@ eval $m2c_tmp="$m2c_tmp, $m2c_node_param_val"@ +##@ end@ // for each index + + int ${context}_init_data(${context}_registration * ${context}_reg); + +@ include mfd-access-${m2c_table_access}-defines.m2i@ + int ${context}_row_prep( ${context}_rowreq_ctx *rowreq_ctx); + +@ if ($m2c_table_row_creation == 1) || ($m2c_table_persistent == 1)@ +int ${context}_validate_index( ${context}_registration * ${context}_reg, + ${context}_rowreq_ctx *rowreq_ctx); +@ foreach $node externalindex@ +@ include m2c_setup_node.m2i@ + int ${context}_${node}_check_index( ${context}_rowreq_ctx *rowreq_ctx ); /* external */ +@ end@ # foreach externalindex +@ foreach $node internalindex@ +@ include m2c_setup_node.m2i@ +int ${node}_check_index( ${context}_rowreq_ctx *rowreq_ctx ); /* internal */ +@ end@ # foreach internalindex +@ end@ # row creation/persistent +@end@ + +@eval $m2c_save = "$name"@ +@eval $name = "${name}_DATA_ACCESS"@ +@include generic-header-bottom.m2i@ +@eval $name = "$m2c_save"@ +##//################################################################## +##//Do the .c file +##//################################################################## +@eval $m2c_processing_type = 'c'@ +@open ${name}_data_access.c@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + */ +@include generic-source-includes.m2i@ + +#include "${name}_data_access.h" + +/** @ingroup interface + * @addtogroup data_access data_access: Routines to access data + * + * These routines are used to locate the data used to satisfy + * requests. + * + * @{ + */ +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ include details-table.m2i@ + +/** + * initialization for ${context} data access + * + * This function is called during startup to allow you to + * allocate any resources you need for the data table. + * + * @param ${context}_reg + * Pointer to ${context}_registration + * + * @retval MFD_SUCCESS : success. + * @retval MFD_ERROR : unrecoverable error. + */ +int +${context}_init_data(${context}_registration * ${context}_reg) +{ + DEBUGMSGTL(("verbose:${context}:${context}_init_data","called\n")); + + /* + * TODO:303:o: Initialize $context data. + */ +@ifconf ${table}_init_data.m2i@ +@ include ${table}_init_data.m2i@ +@else@ +@ if $m2c_include_examples == 1@ +$example_start + /* + * if you are the sole writer for the file, you could + * open it here. However, as stated earlier, we are assuming + * the worst case, which in this case means that the file is + * written to by someone else, and might not even exist when + * we start up. So we can't do anything here. + */ +$example_end +@ end@ + + return MFD_SUCCESS; +@end@ #ifconf +} /* ${context}_init_data */ + +@ include mfd-access-${m2c_table_access}-defines.m2i@ +/** + * prepare row for processing. + * + * When the agent has located the row for a request, this function is + * called to prepare the row for processing. If you fully populated + * the data context during the index setup phase, you may not need to + * do anything. + * + * @param rowreq_ctx pointer to a context. + * + * @retval MFD_SUCCESS : success. + * @retval MFD_ERROR : other error. + */ +int +${context}_row_prep( ${context}_rowreq_ctx *rowreq_ctx) +{ + DEBUGMSGTL(("verbose:${context}:${context}_row_prep","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + /* + * TODO:390:o: Prepare row for request. + * If populating row data was delayed, this is the place to + * fill in the row for this request. + */ + + return MFD_SUCCESS; +} /* ${context}_row_prep */ + +##//#################################################################### +@ if ($m2c_table_row_creation == 1) || ($m2c_table_persistent == 1)@ +/* + * TODO:420:r: Implement $context index validation. + */ +@ foreach $node externalindex@ +@ include m2c_setup_node.m2i@ +@ if $m2c_report_progress == 1@ +@ print | | +-> Processing index $node@ +@ end@ +@ include details-node.m2i@ +/** + * check validity of ${node} external index portion + * + * NOTE: this is not the place to do any checks for the sanity + * of multiple indexes. Those types of checks should be done in the + * ${context}_validate_index() function. + * + * @retval MFD_SUCCESS : the incoming value is legal + * @retval MFD_ERROR : the incoming value is NOT legal + */ +int +${context}_${node}_check_index( ${context}_rowreq_ctx *rowreq_ctx ) +{ + DEBUGMSGTL(("verbose:${context}:${context}_${node}_check_index","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + /* + * TODO:424:M: |-> Check $context external index $node. + * check that index value in the table context (rowreq_ctx) + * for the external index $node is legal. + */ + + return MFD_SUCCESS; /* external index $node ok */ +} /* ${context}_${node}_check_index */ + +@ end@ # foreach externalindex +@ foreach $node internalindex@ +@ include m2c_setup_node.m2i@ +@ if $m2c_report_progress == 1@ +@ print | | +-> Processing index $node@ +@ end@ +@ include details-node.m2i@ +/** + * check validity of ${node} index portion + * + * @retval MFD_SUCCESS : the incoming value is legal + * @retval MFD_ERROR : the incoming value is NOT legal + * + * @note this is not the place to do any checks for the sanity + * of multiple indexes. Those types of checks should be done in the + * ${context}_validate_index() function. + * + * @note Also keep in mind that if the index refers to a row in this or + * some other table, you can't check for that row here to make + * decisions, since that row might not be created yet, but may + * be created during the processing this request. If you have + * such checks, they should be done in the check_dependencies + * function, because any new/deleted/changed rows should be + * available then. + * + * The following checks have already been done for you: +@if $node.enums == 1@ + * The value is one of $m2c_evals +@elsif $node.ranges == 1@ +@ if ("$node.decl" eq "long") || ("$node.decl" eq "u_long")@ +@ eval $m2c_tmp_ns = "value"@ +@ else@ +@ eval $m2c_tmp_ns = "length"@ +@ end@ + * The $m2c_tmp_ns is in (one of) the range set(s): $m2c_evals +@end@ + * + * If there a no other checks you need to do, simply return MFD_SUCCESS. + */ +int +${node}_check_index( ${context}_rowreq_ctx *rowreq_ctx ) +{ + DEBUGMSGTL(("verbose:${context}:${node}_check_index","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + /* + * TODO:426:M: |-> Check $context index $node. + * check that index value in the table context is legal. + * (rowreq_ctx->tbl_index.$node) + */ + + return MFD_SUCCESS; /* $node index ok */ +} /* ${node}_check_index */ + +@ end@ # foreach internalindex +/** + * verify specified index is valid. + * + * This check is independent of whether or not the values specified for + * the columns of the new row are valid. Column values and row consistency + * will be checked later. At this point, only the index values should be + * checked. + * + * All of the individual index validation functions have been called, so this + * is the place to make sure they are valid as a whole when combined. If + * you only have one index, then you probably don't need to do anything else + * here. + * + * @note Keep in mind that if the indexes refer to a row in this or + * some other table, you can't check for that row here to make + * decisions, since that row might not be created yet, but may + * be created during the processing this request. If you have + * such checks, they should be done in the check_dependencies + * function, because any new/deleted/changed rows should be + * available then. + * + * + * @param ${context}_reg + * Pointer to the user registration data + * @param ${context}_rowreq_ctx + * Pointer to the users context. + * @retval MFD_SUCCESS : success + * @retval MFD_CANNOT_CREATE_NOW : index not valid right now + * @retval MFD_CANNOT_CREATE_EVER : index never valid + */ +int +${context}_validate_index( ${context}_registration * ${context}_reg, + ${context}_rowreq_ctx *rowreq_ctx) +{ + int rc = MFD_SUCCESS; + + DEBUGMSGTL(("verbose:${context}:${context}_validate_index","called\n")); + + /** we should have a non-NULL pointer */ + netsnmp_assert( NULL != rowreq_ctx ); + + /* + * TODO:430:M: |-> Validate potential $context index. + */ + if(1) { + snmp_log(LOG_WARNING,"invalid index for a new row in the " + "${context} table.\n"); + /* + * determine failure type. + * + * If the index could not ever be created, return MFD_NOT_EVER + * If the index can not be created under the present circumstances + * (even though it could be created under other circumstances), + * return MFD_NOT_NOW. + */ + if(0) { + return MFD_CANNOT_CREATE_EVER; + } + else { + return MFD_CANNOT_CREATE_NOW; + } + } + + return rc; +} /* ${context}_validate_index */ + +@ end@ # persistent/row creation +@end@ +## +/** @} */ +##//#################################################################### +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ +@eval $m2c_processing_type = "$mfd_data_access_processing_type"@ diff --git a/local/mib2c-conf.d/mfd-data-get.m2c b/local/mib2c-conf.d/mfd-data-get.m2c new file mode 100644 index 0000000..cf1c848 --- /dev/null +++ b/local/mib2c-conf.d/mfd-data-get.m2c @@ -0,0 +1,168 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +######################################################################## +## lower conf files get confused with multiple processing types, so +## set single options +@eval $mfd_data_get_processing_type_save = "$m2c_processing_type"@ +@if "$mfd_processing_types" =~ /h/@ +@eval $m2c_processing_type = 'h'@ +@if $m2c_create_fewer_files != 1@ +@ open ${name}_data_get.h@ +@ eval $hack = "Id"@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + * + * @file ${name}_data_get.h + * + * @addtogroup get + * + * Prototypes for get functions + * + * @{ + */ +@ eval $m2c_tmp = "$name"@ +@ eval $name = "${name}_DATA_GET"@ +@ include generic-header-top.m2i@ +@ eval $name = "$m2c_tmp"@ +@end@ // m2c_create_fewer_files +@if $m2c_mark_boundary == 1@ +/** START header generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +/* ********************************************************************* + * GET function declarations + */ + +/* ********************************************************************* + * GET Table declarations + */ +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ include details-table.m2i@ + /* + * indexes + */ +@ foreach $node index@ +@ include m2c_setup_node.m2i@ +@ if $m2c_node_skip_mapping != 1@ + int ${node}_map($m2c_node_map_param); +@ end@ # // skip mapping +@ end@ # index + +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $m2c_node_skip_mapping != 1@ + int ${node}_map($m2c_node_map_param); +@ end@ # // skip mapping + int ${node}_get( ${context}_rowreq_ctx *rowreq_ctx, $m2c_node_param_ref ); +@ end@ // nonindex + +@ include generic-table-indexes-set.m2i@ + +@end@ // table + +@if $m2c_mark_boundary == 1@ +/** END header generated by $RCSfile$ $Revision$ */ +@end@ +@if $m2c_create_fewer_files != 1@ +@ eval $m2c_tmp = "$name"@ +@ eval $name = "${name}_DATA_GET"@ +@ include generic-header-bottom.m2i@ +@ eval $name = "$m2c_tmp"@ +/** @} */ +@end@ // m2c_create_fewer_files +###################################################################### +@end@ // $mfd_processing_types =~ /h/ +###################################################################### +###################################################################### +###################################################################### +@if "$mfd_processing_types" =~ /c/@ +@eval $m2c_processing_type = 'c'@ +@if $m2c_create_fewer_files != 1@ +@open ${name}_data_get.c@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + */ +@include generic-source-includes.m2i@ + +/** @defgroup data_get data_get: Routines to get data + * + * TODO:230:M: Implement $context get routines. + * TODO:240:M: Implement $context mapping routines (if any). + * + * These routine are used to get the value for individual objects. The + * row context is passed, along with a pointer to the memory where the + * value should be copied. + * + * @{ + */ +@end@ // m2c_create_fewer_files +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ include details-table.m2i@ + +/* --------------------------------------------------------------------- + * TODO:200:r: Implement $context data context functions. + */ +@ if (($m2c_data_allocate == 1) || ($m2c_undo_embed == 0)) && ("$m2c_data_context" ne "generated")@ +@ include generic-data-allocate.m2i@ +@ end@ + +## +## do nodes +## +##// internal only? how to know how to map external? +@ foreach $node index@ +@ include m2c_setup_node.m2i@ +@ if $m2c_node_skip_mapping != 0@ +@ next@ +@ end@ +@ include details-node.m2i@ +@ if $m2c_report_progress == 1@ +@ print | | +-> Processing index $node@ +@ end@ +@ include generic-value-map-func.m2i@ +@ end@ # foreach column + +@ include generic-table-indexes-set.m2i@ + +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ include details-node.m2i@ +@ if $node.noaccess == 1@ +@ next@ # skip to next column +@ end@ +@ if $m2c_report_progress == 1@ +@ print | | +-> Processing nonindex $node@ +@ end@ +@ if $m2c_node_skip_mapping == 0@ +@ include generic-value-map-func.m2i@ +@ end@ +@ include node-get.m2i@ +@ end@ # foreach column + +@end@ # foreach table + +## +/** @} */ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +@end@ // $mfd_processing_types =~ /c/ +## restore original processing types +@eval $m2c_processing_type = "$mfd_data_get_processing_type_save"@ diff --git a/local/mib2c-conf.d/mfd-data-set.m2c b/local/mib2c-conf.d/mfd-data-set.m2c new file mode 100644 index 0000000..1150551 --- /dev/null +++ b/local/mib2c-conf.d/mfd-data-set.m2c @@ -0,0 +1,142 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +## lower conf files get confused with multiple processing types, so +## set single options +@eval $mfd_data_set_processing_type_save = "$m2c_processing_type"@ +@if "$mfd_processing_types" =~ /h/@ +@eval $m2c_processing_type = 'h'@ +@if $m2c_create_fewer_files != 1@ +@ eval $hack = "Id"@ +@open ${name}_data_set.h@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + */ +@eval $m2c_save = "$name"@ +@eval $name = "${name}_DATA_SET"@ +@include generic-header-top.m2i@ +@eval $name = "$m2c_save"@ +@end@ // m2c_create_fewer_files +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START header generated by $RCSfile$ $Revision$ */ +@end@ +## +/* ********************************************************************* + * SET function declarations + */ + +/* ********************************************************************* + * SET Table declarations + */ +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ if $m2c_table_settable == 0@ +@ next@ # skip to next table +@ end@ +@ include details-table.m2i@ + +@ include parent-set.m2i@ + +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +int ${node}_check_value( ${context}_rowreq_ctx *rowreq_ctx, $m2c_node_param_val); +int ${node}_undo_setup( ${context}_rowreq_ctx *rowreq_ctx ); +int ${node}_set( ${context}_rowreq_ctx *rowreq_ctx, $m2c_node_param_val ); +int ${node}_undo( ${context}_rowreq_ctx *rowreq_ctx ); + +@ end@ # foreach nonindex + +int ${context}_check_dependencies(${context}_rowreq_ctx *ctx); +@end@ # foreach table + +@if $m2c_mark_boundary == 1@ +/** END header generated by $RCSfile$ $Revision$ */ +@end@ +@if $m2c_create_fewer_files != 1@ +@eval $m2c_save = "$name"@ +@eval $name = "${name}_DATA_SET"@ +@include generic-header-bottom.m2i@ +@eval $name = "$m2c_save"@ +@end@ // m2c_create_fewer_files +###################################################################### +@end@ // mfd_processing_types =~ /h/ +###################################################################### +###################################################################### +###################################################################### +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if "$mfd_processing_types" =~ /c/@ +@eval $m2c_processing_type = 'c'@ +@if $m2c_create_fewer_files != 1@ +@open ${name}_data_set.c@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + * + */ +@include generic-source-includes.m2i@ + +/** @defgroup data_set data_set: Routines to set data + * + * These routines are used to set the value for individual objects. The + * row context is passed, along with the new value. + * + * @{ + */ +@end@ // m2c_create_fewer_files +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ if $m2c_table_settable == 0@ +@ next@ # skip to next table +@ end@ +@ include details-table.m2i@ +######################################################################## +@ include parent-set.m2i@ +######################################################################## +######################################################################## +/* + * TODO:440:M: Implement $context node value checks. + * TODO:450:M: Implement $context undo functions. + * TODO:460:M: Implement $context set functions. + * TODO:480:M: Implement $context commit functions. + */ +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $node.settable == 0@ +@ next@ # skip to next column +@ end@ +@ if $m2c_report_progress == 1@ +@ print | | +-> Processing nonindex $node@ +@ end@ +@ include details-node.m2i@ +@ include node-set.m2i@ +@ end@ # foreach column +######################################################################## +@ if $m2c_table_dependencies == 1@ +@ include parent-dependencies.m2i@ +@ end@ +######################################################################## +@end@ # foreach table +## +######################################################################## +/** @} */ +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +@end@ // mfd_processing_type =~ /c/ +## restore original processing types +@eval $m2c_processing_type = "$mfd_data_set_processing_type_save"@ diff --git a/local/mib2c-conf.d/mfd-doxygen.m2c b/local/mib2c-conf.d/mfd-doxygen.m2c new file mode 100644 index 0000000..25406a0 --- /dev/null +++ b/local/mib2c-conf.d/mfd-doxygen.m2c @@ -0,0 +1,60 @@ +######################################################################## +@foreach $table table@ +@ ifconf ${context}_doxygen.conf +@ print "${context}_doxygen.conf exists, skipping.@ +@ else@ +@ include m2c_setup_table.m2i@ +@ open ${context}_doxygen.conf@ +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = ${context} + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.1 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +FILE_PATTERNS = *.c *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = +@ end@ # conf file exists +@end@ # foreach table diff --git a/local/mib2c-conf.d/mfd-interactive-setup.m2c b/local/mib2c-conf.d/mfd-interactive-setup.m2c new file mode 100644 index 0000000..e3d3d55 --- /dev/null +++ b/local/mib2c-conf.d/mfd-interactive-setup.m2c @@ -0,0 +1,335 @@ +####################################################################### +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +@eval $m2c_temp_writable = table_is_writable($context)@ +@eval $m2c_temp_create = table_has_create($context)@ +@eval $m2c_temp_dependencies = $m2c_temp_writable@ +@eval $m2c_temp_context_reg = "$mfd_default_context_reg"@ +@eval $m2c_temp_data_context = "$mfd_default_data_context"@ +@eval $m2c_temp_data_allocate = $mfd_default_data_allocate@ +@eval $m2c_temp_data_cache = $mfd_default_data_cache@ +@eval $m2c_temp_undo_embed = $mfd_default_undo_embed@ +@eval $m2c_temp_data_init = $mfd_default_data_init@ +@eval $m2c_temp_persistent = $m2c_temp_writable@ +@eval $m2c_temp_table_access = "$mfd_default_table_access"@ +@eval $m2c_temp_data_transient = $mfd_default_data_transient@ +@eval $m2c_temp_include_examples = $mfd_default_include_examples@ +@eval $m2c_temp_table_skip_mapping = $mfd_default_table_skip_mapping@ +@eval $m2c_temp_table_sparse = $mfd_default_data_sparse@ +@eval $m2c_temp_generate_makefile = $mfd_default_generate_makefile@ +@eval $m2c_temp_generate_subagent = $mfd_default_generate_subagent@ +## +@if $mfd_interactive_setup != 0@ +@open -@ +@ if $mfd_interactive_setup != -1@ +There are no defaults for ${context}. Would you like to + + 1) Accept hard-coded defaults + 2) Set defaults now [DEFAULT] + +@ eval $ans = 2@ +@ prompt $ans Select your choice : @ +@ else@ +@ eval $ans = 2@ +@ end@ +@ if $ans == 1@ +@ else@ + + +## --------------------------------------------------- +@ if $m2c_temp_writable == 1@ +This table has writable columns. Do you want to generate +code for writeable columns, or restrict the table to read-only? + + 1) generate code with writeable columns [DEFAULT] + 2) generate code with read-only columns + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ eval $m2c_temp_writable = 0@ +@ eval $m2c_temp_create = 0@ +@ eval $m2c_temp_dependencies = 0@ +@ eval $m2c_temp_persistent = 0@ +@ end@ + + +@ end@ # writable +## --------------------------------------------------- +@ if $m2c_temp_persistent == 1@ +@ eval $m2c_temp_persistent = 0@ +Since your table is writable, do you want to generate code to save and +restore rows in the Net-SNMP persistent store? You should only use this +option if the agent 'owns' the data, and doesn't get the data from an +external source. + + 1) do not generate persistent store code [DEFAULT] + 2) generate persistent store code + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ eval $m2c_temp_persistent = 1@ +@ end@ + + +@ end@ # persistent +## --------------------------------------------------- +@ if $m2c_temp_dependencies == 1@ +@ eval $m2c_temp_dependencies = 0@ +Writable tables sometimes have dependencies beteen columns +or with other tables. If there are no dependencies in this table, you +probably do not want the extra code. + + 1) do not generate dependency code [DEFAULT] + 2) generate dependency code + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ eval $m2c_temp_dependencies = 1@ +@ end@ + + +@ end@ # dependencies +## --------------------------------------------------- +@ if $m2c_temp_create == 1@ +This table has read-create columns. Do you want to generate +code for dynamic row creation? + + 1) generate code for row creation [DEFAULT] + 2) do not generate code for row creation + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ eval $m2c_temp_create = 0@ +@ end@ + + +@ end@ # create +## --------------------------------------------------- +Do you want to use an existing data structure for the USER context? +This would be a structure used to track data for the entire table, +(similar to a global variable) not individual rows. (Many +implementations won't need this, in which case the default is fine). + + + 1) No, use $m2c_temp_context_reg [DEFAULT] + 2) Yes, use my own structure + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ prompt $m2c_temp_context_reg Enter your USER context : @ +@ end@ + + +## --------------------------------------------------- +Do you want to use an existing data structure for the DATA context? +The DATA context holds the data for each MIB column in a given row. By +default, a new data structure will be created with an element for each +column. If you already have a structure that holds your data, select 2. + + 1) No, use $m2c_temp_data_context [DEFAULT] + 2) Yes, use my own structure + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ + + +Note: Do not enter a pointer type. Use the base structure name. For +example, use 'struct widget', not 'struct widget *'. If you will be +using pointer to the structure, select dynamic allocation in the +next question. + +@ prompt $m2c_temp_data_context Enter your DATA context : @ +@ end@ + + +## --------------------------------------------------- +@ if "x$m2c_temp_data_context" ne "x$mfd_default_data_context"@ +Do you want to allocate your '$m2c_temp_data_context' DATA context, or +embed it directly? If your data is INTERNAL (controlled by the agent), you +probably want embedded. If your data is EXTERNAL (controlled by another +process) and you have pointers to the data, you probably want allocated. + + 1) directly embed structure [DEFAULT] + 2) dynamically allocate structure + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ eval $m2c_temp_data_allocate = 1@ +@ end@ + + +@ end@ # ! default (generated) +## --------------------------------------------------- +Do you need to initialize elements in the '$m2c_temp_data_context' DATA +context when a new instance is created (eg default values, or other structures +you are going to add that might need initialization to the row request context? +(The most common reasons you might need to do this is are if you want to keep +some non-MIB data for every row, or some columns have default values.) + + 1) no, no initialization needed + 2) yes, initilization is needed [DEFAULT] + +@ prompt $ans Select your choice : @ +@ if $ans == 1@ +@ eval $m2c_temp_data_init = 0@ +@ end@ + + +## --------------------------------------------------- +Do you plan on keeping all data in the format defined by the MIB? If so, +no functions will be generated to map values. If some data will be +stored in a different format, the mapping functions will be generated. +If your MIB has integers with enumerations, mapping functions are more +likely to be needed. (e.g. A TruthValue object will hold the value +1 or 2, but a C boolean would be 1 or 0.) + + 1) All values will be stored as defined by the MIB [DEFAULT] + 2) I need to map values to the format defined by the MIB. + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ eval $m2c_temp_table_skip_mapping = -1@ +@ end@ + + +## --------------------------------------------------- +Which method would you like to use to gather data about available rows? + + 1) container : [DEFAULT] This access method uses a netsnmp_container + to store all row data in memory. This method is best for: + - Internal data (maintained by the agent) + - Access speed is important + - Sufficient memory exists to contain all rows + + 2) container-cached : This access method uses a netsnmp_container + to keep track of the indexes (and data, usually) for each + row. This method is best for: + - External data (maintained by another process/the kernel) + - Access speed is important + - Sufficient memory exists to contain all indexes + + 3) unsorted-external : This access method iterates over all of your data + to find the row with the appropriate index. This method is good for + - External data (maintained by another process/the kernel) + - Using less memory is much more important than access speed + +@ prompt $ans Select your choice : @ +@ if $ans == 3@ +@ eval $m2c_temp_table_access = "unsorted-external"@ +@ elsif $ans == 2@ +@ eval $m2c_temp_table_access = "container-cached"@ +@ eval $m2c_temp_data_cache = 1@ +@ else@ +@ eval $m2c_temp_table_access = "container-cached"@ +@ eval $m2c_temp_data_cache = 0@ +@ end@ + + +## --------------------------------------------------- +When accessing your data, is your data TRANSIENT? + + 1) Yes. My data is TRANSIENT (e.g. a pointer to a static buffer that + my be overwritten during a request) and needs to be copied during + processing. + + 2) Yes. My data is SEMI-TRANSIENT (e.g. an allocated pointer to a + copy of the data). + + 3) No, my data is PERSISTENT (e.g. an allocated pointer to the actual + data, which is under the agent's control) +## ' + +@ prompt $ans Select your choice [DEFAULT=1] : @ +@ if $ans == 3@ +@ eval $m2c_temp_data_transient = 0@ +@ elsif $ans == 2@ +@ eval $m2c_temp_data_transient = 1@ +@ else@ +@ eval $m2c_temp_data_transient = 2@ +@ end@ + + +## --------------------------------------------------- +Do you want example code to be generated? This will generate example code +for reading data from a text file. + + 1) generate example code [DEFAULT] + 2) do not generate example code + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ eval $m2c_temp_include_examples = 0@ +@ else@ +@ eval $m2c_temp_include_examples = 1@ +@ end@ + +## --------------------------------------------------- +Is your table sparse? A sparse table is a table where some +columns might not exist for all rows. Note that if your table +contains a RowStaus column and it supports createAndWait, you +will need sparse table support. + + 1) No, all columns always exist for every row [DEFAULT] + 2) Yes, my table is sparse + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ eval $m2c_temp_table_sparse = 1@ +@ end@ + +## --------------------------------------------------- +Do you want a makefile and AgentX subagent source file generated? +This will let you test your table without having to link it into +snmpd. (You can still link it in later.) + + 1) do not generate makefile/AgentX code [DEFAULT] + 2) generate makefile/AgentX code + +@ prompt $ans Select your choice : @ +@ if $ans == 2@ +@ eval $m2c_temp_generate_makefile = 1@ +@ eval $m2c_temp_generate_subagent = 1@ +@ else@ +@ eval $m2c_temp_generate_makefile = 0@ +@ eval $m2c_temp_generate_subagent = 0@ +@ end@ + +@ end@ # do not use hardcoded +@end@ # $mfd_interactive_setup == 1 +################################## +## +## save values +## Note: if you add a var here, add it in m2c_table_save_defaults.m2i too +## +@eval $m2c_context_reg = "$m2c_temp_context_reg"@ +@eval $m2c_data_allocate = $m2c_temp_data_allocate@ +@eval $m2c_data_cache = $m2c_temp_data_cache@ +@eval $m2c_data_context = "$m2c_temp_data_context"@ +@eval $m2c_data_init = $m2c_temp_data_init@ +@eval $m2c_data_transient = $m2c_temp_data_transient@ +@eval $m2c_include_examples = $m2c_temp_include_examples@ +@eval $m2c_irreversible_commit = $m2c_irreversible_commit@ +@eval $m2c_table_access = "$m2c_temp_table_access"@ +@eval $m2c_table_dependencies = $m2c_temp_dependencies@ +@eval $m2c_table_persistent = $m2c_temp_persistent@ +@eval $m2c_table_row_creation = $m2c_temp_create@ +@eval $m2c_table_settable = $m2c_temp_writable@ +@eval $m2c_table_skip_mapping = $m2c_temp_table_skip_mapping@ +@eval $m2c_table_sparse = $m2c_temp_table_sparse@ +@eval $mfd_generate_makefile = $m2c_temp_generate_makefile@ +@eval $mfd_generate_subagent = $m2c_temp_generate_subagent@ +## +## write them back +## +@include m2c_table_save_defaults.m2i@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/mfd-interface.m2c b/local/mib2c-conf.d/mfd-interface.m2c new file mode 100644 index 0000000..d666c9a --- /dev/null +++ b/local/mib2c-conf.d/mfd-interface.m2c @@ -0,0 +1,1718 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +######################################################################## +@eval $m2c_processing_type = 'h'@ +@open ${name}_interface.h@ +@eval $hack = "Id"@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + */ +/** @ingroup interface: Routines to interface to Net-SNMP + * + * \warning This code should not be modified, called directly, + * or used to interpret functionality. It is subject to + * change at any time. + * + * @{ + */ +@include m2c-internal-warning.m2i@ +## +@eval $m2c_save = "$name"@ +@eval $name = "${name}_INTERFACE"@ +@include generic-header-top.m2i@ +@eval $name = "$m2c_save"@ + +#include "${name}.h" + + +/* ******************************************************************** + * Table declarations + */ +@foreach $table table@ +@ include m2c_setup_table.m2i@ + +/* PUBLIC interface initialization routine */ +void _${context}_initialize_interface(${context}_registration * user_ctx, + u_long flags); +void _${context}_shutdown_interface(${context}_registration * user_ctx); + +${context}_registration * +${context}_registration_get( void ); + +${context}_registration * +${context}_registration_set( ${context}_registration * newreg ); + +netsnmp_container *${context}_container_get( void ); +int ${context}_container_size( void ); + +@ if $m2c_table_settable@ +u_int ${context}_dirty_get( void ); +void ${context}_dirty_set( u_int status ); + +@ end@ +@ if $m2c_data_allocate == 1@ +@ eval $m2c_tmp = "${context}_data *"@ +@ if $m2c_data_init == 1@ +@ eval $m2c_tmp = "$m2c_tmp, void *"@ +@ @end@ +@ elsif $m2c_data_init == 1@ +@ eval $m2c_tmp = "void *"@ +@ else@ +@ eval $m2c_tmp = "void"@ +@ end@ + ${context}_rowreq_ctx * ${context}_allocate_rowreq_ctx($m2c_tmp); +void ${context}_release_rowreq_ctx(${context}_rowreq_ctx *rowreq_ctx); + +int ${context}_index_to_oid(netsnmp_index *oid_idx, + ${context}_mib_index *mib_idx); +int ${context}_index_from_oid(netsnmp_index *oid_idx, + ${context}_mib_index *mib_idx); + +@ if $m2c_table_persistent == 1@ +@ include mfd-persistence.m2i@ + +@ end@ +/* + * access to certain internals. use with caution! + */ +void ${context}_valid_columns_set(netsnmp_column_info *vc); + +@end@ # for each +@eval $m2c_save = "$name"@ +@eval $name = "${name}_INTERFACE"@ +@include generic-header-bottom.m2i@ +/** @} */ +@eval $name = "$m2c_save"@ +######################################################################## +######################################################################## +######################################################################## +######################################################################## +######################################################################## +######################################################################## +######################################################################## +## +@open ${name}_interface.c@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + */ +@include m2c-internal-warning.m2i@ + +@include generic-source-includes.m2i@ + +#include <net-snmp/agent/table_container.h> +#include <net-snmp/library/container.h> + +#include "${name}_interface.h" + +netsnmp_feature_require(baby_steps) +netsnmp_feature_require(row_merge) +netsnmp_feature_require(check_all_requests_error) + +#include <ctype.h> + +@eval $m2c_processing_type = 'i'@ +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ include details-table.m2i@ +######################################################################## +typedef struct ${context}_interface_ctx_s { + + netsnmp_container *container; +@ if $m2c_data_cache == 1@ + netsnmp_cache *cache; +@ end@ + + ${context}_registration * user_ctx; + + netsnmp_table_registration_info tbl_info; + + netsnmp_baby_steps_access_methods access_multiplexer; + +@ if $m2c_table_settable@ + u_int table_dirty; + +@ end@ +} ${context}_interface_ctx; + +static ${context}_interface_ctx ${context}_if_ctx; + +static void _${context}_container_init( + ${context}_interface_ctx *if_ctx); +static void _${context}_container_shutdown( + ${context}_interface_ctx *if_ctx); + + +netsnmp_container * +${context}_container_get( void ) +{ + return ${context}_if_ctx.container; +} + +${context}_registration * +${context}_registration_get( void ) +{ + return ${context}_if_ctx.user_ctx; +} + +${context}_registration * +${context}_registration_set( ${context}_registration * newreg ) +{ + ${context}_registration * old = ${context}_if_ctx.user_ctx; + ${context}_if_ctx.user_ctx = newreg; + return old; +} + +int +${context}_container_size( void ) +{ + return CONTAINER_SIZE(${context}_if_ctx.container); +} + +@ if $m2c_table_settable@ +u_int +${context}_dirty_get( void ) +{ + return ${context}_if_ctx.table_dirty; +} + +void +${context}_dirty_set( u_int status ) +{ + DEBUGMSGTL(("${context}:${context}_dirty_set", + "called. was %d, now %d\n", + ${context}_if_ctx.table_dirty, status)); + ${context}_if_ctx.table_dirty = status; +} + +@end@ +/* + * mfd multiplexer modes + */ +static Netsnmp_Node_Handler _mfd_${context}_pre_request; +static Netsnmp_Node_Handler _mfd_${context}_post_request; +static Netsnmp_Node_Handler _mfd_${context}_object_lookup; +static Netsnmp_Node_Handler _mfd_${context}_get_values; +@ if $m2c_table_settable@ +static Netsnmp_Node_Handler _mfd_${context}_check_objects; +static Netsnmp_Node_Handler _mfd_${context}_undo_setup; +static Netsnmp_Node_Handler _mfd_${context}_set_values; +static Netsnmp_Node_Handler _mfd_${context}_undo_cleanup; +static Netsnmp_Node_Handler _mfd_${context}_undo_values; +static Netsnmp_Node_Handler _mfd_${context}_commit; +static Netsnmp_Node_Handler _mfd_${context}_undo_commit; +static Netsnmp_Node_Handler _mfd_${context}_irreversible_commit; +@ if $m2c_table_dependencies == 1@ +static Netsnmp_Node_Handler _mfd_${context}_check_dependencies; +@ end@ + +@ end@ # writable +@ if ("$m2c_data_context" eq "generated") && (($m2c_undo_embed == 0) || ($m2c_data_allocate == 1))@ +${context}_data *${context}_allocate_data(void); + +@ end@ +/** + * @internal + * Initialize the table $context + * (Define its contents and how it's structured) + */ +void +_${context}_initialize_interface(${context}_registration * reg_ptr, u_long flags) +{ + netsnmp_baby_steps_access_methods *access_multiplexer = + &${context}_if_ctx.access_multiplexer; + netsnmp_table_registration_info *tbl_info = &${context}_if_ctx.tbl_info; + netsnmp_handler_registration *reginfo; + netsnmp_mib_handler *handler; + int mfd_modes = 0; + + DEBUGMSGTL(("internal:${context}:_${context}_initialize_interface","called\n")); + + + /************************************************* + * + * save interface context for ${context} + */ + /* + * Setting up the table's definition + */ + netsnmp_table_helper_add_indexes(tbl_info, + @foreach $tabledx index@ + $tabledx.type, /** index: $tabledx */ + @end@ + 0); + + /* Define the minimum and maximum accessible columns. This + optimizes retrieval. */ + tbl_info->min_column = $context.uc_MIN_COL; + tbl_info->max_column = $context.uc_MAX_COL; + + /* + * save users context + */ + ${context}_if_ctx.user_ctx = reg_ptr; + + /* + * call data access initialization code + */ + ${context}_init_data(reg_ptr); + + /* + * set up the container + */ + _${context}_container_init(&${context}_if_ctx); + if (NULL == ${context}_if_ctx.container) { + snmp_log(LOG_ERR,"could not initialize container for ${context}\n"); + return; + } + + /* + * access_multiplexer: REQUIRED wrapper for get request handling + */ + access_multiplexer->object_lookup = _mfd_${context}_object_lookup; + access_multiplexer->get_values = _mfd_${context}_get_values; + + /* + * no wrappers yet + */ + access_multiplexer->pre_request = _mfd_${context}_pre_request; + access_multiplexer->post_request = _mfd_${context}_post_request; + +#ifndef NETSNMP_DISABLE_SET_SUPPORT +## +@ if $m2c_table_settable@ + + /* + * REQUIRED wrappers for set request handling + */ + access_multiplexer->object_syntax_checks = _mfd_${context}_check_objects; + access_multiplexer->undo_setup = _mfd_${context}_undo_setup; + access_multiplexer->undo_cleanup = _mfd_${context}_undo_cleanup; + access_multiplexer->set_values = _mfd_${context}_set_values; + access_multiplexer->undo_sets = _mfd_${context}_undo_values; + + /* + * no wrappers yet + */ + access_multiplexer->commit = _mfd_${context}_commit; + access_multiplexer->undo_commit = _mfd_${context}_undo_commit; + access_multiplexer->irreversible_commit = _mfd_${context}_irreversible_commit; +## +@ if $m2c_table_dependencies == 1@ + + /* + * REQUIRED for tables with dependencies + */ + access_multiplexer->consistency_checks = _mfd_${context}_check_dependencies; +@ end@ +@ end@ # writable +#endif + + /************************************************* + * + * Create a registration, save our reg data, register table. + */ + DEBUGMSGTL(("$name:init_$context", + "Registering $context as a mibs-for-dummies table.\n")); + handler = netsnmp_baby_steps_access_multiplexer_get(access_multiplexer); + reginfo = netsnmp_handler_registration_create("${context}", handler, + ${context}_oid, + ${context}_oid_size, + HANDLER_CAN_BABY_STEP | +#if !(defined(NETSNMP_NO_WRITE_SUPPORT) || defined(NETSNMP_DISABLE_SET_SUPPORT)) + HANDLER_CAN_RONLY +#else +@if $m2c_table_settable == 1@ + HANDLER_CAN_RWRITE +@else@ + HANDLER_CAN_RONLY +@end@ +#endif /* NETSNMP_NO_WRITE_SUPPORT || NETSNMP_DISABLE_SET_SUPPORT */ + ); + if(NULL == reginfo) { + snmp_log(LOG_ERR,"error registering table ${context}\n"); + return; + } + reginfo->my_reg_void = &${context}_if_ctx; + + /************************************************* + * + * set up baby steps handler, create it and inject it + */ + if( access_multiplexer->object_lookup ) + mfd_modes |= BABY_STEP_OBJECT_LOOKUP; + if( access_multiplexer->pre_request ) + mfd_modes |= BABY_STEP_PRE_REQUEST; + if( access_multiplexer->post_request ) + mfd_modes |= BABY_STEP_POST_REQUEST; + +#if !(defined(NETSNMP_NO_WRITE_SUPPORT) || defined(NETSNMP_DISABLE_SET_SUPPORT)) + if( access_multiplexer->set_values ) + mfd_modes |= BABY_STEP_SET_VALUES; + if( access_multiplexer->irreversible_commit ) + mfd_modes |= BABY_STEP_IRREVERSIBLE_COMMIT; + if( access_multiplexer->object_syntax_checks ) + mfd_modes |= BABY_STEP_CHECK_OBJECT; + + if( access_multiplexer->undo_setup ) + mfd_modes |= BABY_STEP_UNDO_SETUP; + if( access_multiplexer->undo_cleanup ) + mfd_modes |= BABY_STEP_UNDO_CLEANUP; + if( access_multiplexer->undo_sets ) + mfd_modes |= BABY_STEP_UNDO_SETS; + + if( access_multiplexer->row_creation ) + mfd_modes |= BABY_STEP_ROW_CREATE; + if( access_multiplexer->consistency_checks ) + mfd_modes |= BABY_STEP_CHECK_CONSISTENCY; + if( access_multiplexer->commit ) + mfd_modes |= BABY_STEP_COMMIT; + if( access_multiplexer->undo_commit ) + mfd_modes |= BABY_STEP_UNDO_COMMIT; +#endif /* NETSNMP_NO_WRITE_SUPPORT || NETSNMP_DISABLE_SET_SUPPORT */ + + handler = netsnmp_baby_steps_handler_get(mfd_modes); + netsnmp_inject_handler(reginfo, handler); + + /************************************************* + * + * inject row_merge helper with prefix rootoid_len + 2 (entry.col) + */ + handler = netsnmp_get_row_merge_handler(reginfo->rootoid_len + 2); + netsnmp_inject_handler(reginfo, handler); + + /************************************************* + * + * inject container_table helper + */ + handler = + netsnmp_container_table_handler_get(tbl_info, + ${context}_if_ctx.container, + TABLE_CONTAINER_KEY_NETSNMP_INDEX); + netsnmp_inject_handler( reginfo, handler ); + +@ if $m2c_data_cache == 1@ + /************************************************* + * + * inject cache helper + */ + if(NULL != ${context}_if_ctx.cache) { + handler = netsnmp_cache_handler_get(${context}_if_ctx.cache); + netsnmp_inject_handler( reginfo, handler ); + } + +@ end@ + /* + * register table + */ + netsnmp_register_table(reginfo, tbl_info); + +@if $m2c_table_persistent == 1@ + /* + * register config/persistence callbacks + */ + ${context}_container_init_persistence(${context}_if_ctx.container); + +@end@ +} /* _${context}_initialize_interface */ + +/** + * @internal + * Shutdown the table $context + */ +void +_${context}_shutdown_interface(${context}_registration * reg_ptr) +{ + /* + * shutdown the container + */ + _${context}_container_shutdown(&${context}_if_ctx); +} + +void +${context}_valid_columns_set(netsnmp_column_info *vc) +{ + ${context}_if_ctx.tbl_info.valid_columns = vc; +} /* ${context}_valid_columns_set */ + +@include generic-table-indexes-to-oid.m2i@ +@include generic-table-indexes-from-oid.m2i@ + +######################################################################## +## +@ if (($m2c_data_allocate == 1) || ($m2c_undo_embed == 0)) && ("$m2c_data_context" eq "generated")@ +@ eval $m2c_gda_todo_suppress = 1@ # no todo comments +@ include generic-data-allocate.m2i@ # resets suppress +@ end@ +######################################################################## +/* ********************************************************************* + * @internal + * allocate resources for a ${context}_rowreq_ctx + */ +${context}_rowreq_ctx * +@if $m2c_data_allocate == 1@ +@ eval $m2c_tmp = "${context}_data *data"@ +@ if $m2c_data_init == 1@ +@ eval $m2c_tmp = "$m2c_tmp, void *user_init_ctx"@ +@ end@ +@elsif $m2c_data_init == 1@ +@ eval $m2c_tmp = "void *user_init_ctx"@ +@else@ +@ eval $m2c_tmp = "void"@ +@end@ +${context}_allocate_rowreq_ctx($m2c_tmp) +{ + ${context}_rowreq_ctx *rowreq_ctx = + SNMP_MALLOC_TYPEDEF(${context}_rowreq_ctx); + + DEBUGMSGTL(("internal:${context}:${context}_allocate_rowreq_ctx","called\n")); + + if(NULL == rowreq_ctx) { + snmp_log(LOG_ERR,"Couldn't allocate memory for a " + "${context}_rowreq_ctx.\n"); + return NULL; + } +@if $m2c_data_allocate == 1@ + else { + if(NULL != data) { + /* + * track if we got data from user + */ + rowreq_ctx->rowreq_flags |= MFD_ROW_DATA_FROM_USER; + rowreq_ctx->data = data; + } + else if (NULL == (rowreq_ctx->data = ${context}_allocate_data())) { + SNMP_FREE(rowreq_ctx); + return NULL; + } + } + + /* + * undo context will be allocated when needed (in *_undo_setup) + */ +@end@ + + rowreq_ctx->oid_idx.oids = rowreq_ctx->oid_tmp; + + rowreq_ctx->${context}_data_list = NULL; + +@if $m2c_data_init == 1@ + /* + * if we allocated data, call init routine + */ + if (!(rowreq_ctx->rowreq_flags & MFD_ROW_DATA_FROM_USER)) { + if(SNMPERR_SUCCESS != + ${context}_rowreq_ctx_init(rowreq_ctx, user_init_ctx)) { + ${context}_release_rowreq_ctx(rowreq_ctx); + rowreq_ctx = NULL; + } + } +@end@ + + return rowreq_ctx; +} /* ${context}_allocate_rowreq_ctx */ + +/* + * @internal + * release resources for a ${context}_rowreq_ctx + */ +void +${context}_release_rowreq_ctx(${context}_rowreq_ctx *rowreq_ctx) +{ + DEBUGMSGTL(("internal:${context}:${context}_release_rowreq_ctx","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + +@if $m2c_data_init == 1@ + ${context}_rowreq_ctx_cleanup(rowreq_ctx); +@end@ + +@if $m2c_data_allocate == 1@ + /* + * for non-transient data, don't free data we got from the user + */ + if ((rowreq_ctx->data) && + !(rowreq_ctx->rowreq_flags & MFD_ROW_DATA_FROM_USER)) + ${context}_release_data(rowreq_ctx->data); + +@end@ +@if $m2c_undo_embed == 0@ + if(rowreq_ctx->undo) + ${context}_release_data(rowreq_ctx->undo); + +@end@ + /* + * free index oid pointer + */ + if(rowreq_ctx->oid_idx.oids != rowreq_ctx->oid_tmp) + free(rowreq_ctx->oid_idx.oids); + + SNMP_FREE(rowreq_ctx); +} /* ${context}_release_rowreq_ctx */ + +######################################################################## +## +/** + * @internal + * wrapper + */ +static int +_mfd_${context}_pre_request(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + int rc; + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_pre_request", + "called\n")); + + if (1 != netsnmp_row_merge_status_first(reginfo, agtreq_info)) { + DEBUGMSGTL(("internal:${context}", + "skipping additional pre_request\n")); + return SNMP_ERR_NOERROR; + } + + rc = ${context}_pre_request(${context}_if_ctx.user_ctx); + if (MFD_SUCCESS != rc) { + /* + * nothing we can do about it but log it + */ + DEBUGMSGTL(("${context}","error %d from " + "${context}_pre_request\n", rc)); + netsnmp_request_set_error_all(requests, SNMP_VALIDATE_ERR(rc)); + } + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_pre_request */ + +/** + * @internal + * wrapper + */ +static int +_mfd_${context}_post_request(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + int rc, packet_rc; + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_post_request", + "called\n")); + + /* + * release row context, if deleted + */ + if (rowreq_ctx && (rowreq_ctx->rowreq_flags & MFD_ROW_DELETED)) + ${context}_release_rowreq_ctx(rowreq_ctx); + + /* + * wait for last call before calling user + */ + if (1 != netsnmp_row_merge_status_last(reginfo, agtreq_info)) { + DEBUGMSGTL(("internal:${context}", + "waiting for last post_request\n")); + return SNMP_ERR_NOERROR; + } + + packet_rc = netsnmp_check_all_requests_error(agtreq_info->asp, 0); +@ if $m2c_table_settable@ + if ((MFD_SUCCESS != packet_rc) && ${context}_dirty_get()) { + /* + * we shouldn't get here. the undo steps should also clear + * the dirty flags. + */ + snmp_log(LOG_WARNING, "${context} dirty flag set in post_request " + "but status != SUCCESS.\n"); + } + +@ end@ + rc = ${context}_post_request(${context}_if_ctx.user_ctx,packet_rc); + if (MFD_SUCCESS != rc) { + /* + * nothing we can do about it but log it + */ + DEBUGMSGTL(("${context}","error %d from " + "${context}_post_request\n", rc)); + } + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_post_request */ + +######################################################################## +## +@if ($m2c_table_row_creation == 1) || ($m2c_table_persistent == 1)@ +NETSNMP_STATIC_INLINE int +_${context}_check_indexes(${context}_rowreq_ctx * rowreq_ctx) +{ + int rc = SNMPERR_SUCCESS; + + DEBUGMSGTL(("internal:${context}:_${context}_check_indexes","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + +## +@if $m2c_table_external_indexes != 0@ + /* + * check that the corresponding EXTERNAL row exists + */ +@ foreach $node externalindex@ +@ include m2c_setup_node.m2i@ + + /* (INDEX) $m2c_node_summary */ + rc = ${context}_${node}_check_index( rowreq_ctx ); + if(MFD_SUCCESS != rc) + return SNMP_ERR_NOCREATION; +@ end@ # for each nonindex + +@end@ # external index +@ foreach $node internalindex@ +@ include m2c_setup_node.m2i@ + + /* (INDEX) $m2c_node_summary */ +@ eval $m2c_nv_val = "rowreq_ctx->tbl_idx.$node"@ +@ eval $m2c_nv_len = "rowreq_ctx->tbl_idx.${node}_len"@ +@ eval $m2c_nv_str = "rowreq_ctx->tbl_idx.$node"@ +@ include node-validate.m2i@ + if (MFD_SUCCESS != rc) + return rc; + rc = ${node}_check_index( rowreq_ctx ); + if(MFD_SUCCESS != rc) + return SNMP_ERR_NOCREATION; +@ end@ # for each internalindex + + /* + * if individual parts look ok, check them as a whole + */ + return ${context}_validate_index( ${context}_if_ctx.user_ctx, rowreq_ctx ); +} /* _${context}_check_indexes */ +##---------------------------------------------------------------------- +/** + * @internal + * wrapper + */ +static ${table}_rowreq_ctx * +_mfd_${context}_rowreq_from_index(netsnmp_index *oid_idx, int * rc_ptr) +{ + ${context}_rowreq_ctx * rowreq_ctx; + ${context}_mib_index mib_idx; + int rc; + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_rowreq_from_index", + "called\n")); + + if (NULL == rc_ptr) + rc_ptr = &rc; + *rc_ptr = MFD_SUCCESS; + + memset(&mib_idx, 0x0, sizeof(mib_idx)); + + /* + * try to parse oid + */ + *rc_ptr = ${context}_index_from_oid(oid_idx, &mib_idx); + if(MFD_SUCCESS != *rc_ptr) { + DEBUGMSGT(("$context", "error parsing index\n")); + return NULL; + } + + /* + * allocate new context + */ +@ eval $m2c_tmp = ""@ +@ if ($m2c_data_allocate == 1) || ($m2c_data_init == 1)@ +@ eval $m2c_tmp = "NULL"@ +@ if ($m2c_data_allocate == 1) && ($m2c_data_init == 1)@ +@ eval $m2c_tmp = "$m2c_tmp, NULL"@ +@ @end@ +@ end@ + rowreq_ctx = ${context}_allocate_rowreq_ctx($m2c_tmp); + if (NULL == rowreq_ctx) { + *rc_ptr = MFD_ERROR; + return NULL; /* msg already logged */ + } + + memcpy(&rowreq_ctx->tbl_idx, &mib_idx, sizeof(mib_idx)); + + /* + * check indexes + */ + *rc_ptr = _${context}_check_indexes(rowreq_ctx); + if(MFD_SUCCESS != *rc_ptr) { + netsnmp_assert((*rc_ptr == SNMP_ERR_NOCREATION) || + (*rc_ptr == SNMP_ERR_INCONSISTENTNAME)); + ${context}_release_rowreq_ctx(rowreq_ctx); + return NULL; + } + + /* + * copy indexes + */ + rowreq_ctx->oid_idx.len = oid_idx->len; + memcpy(rowreq_ctx->oid_idx.oids, oid_idx->oids, oid_idx->len * sizeof(oid)); + + return rowreq_ctx; +} /* _mfd_${context}_rowreq_from_index */ + + +@end@ # row creation +/** + * @internal + * wrapper + */ +static int +_mfd_${context}_object_lookup(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + int rc = SNMP_ERR_NOERROR; + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_object_lookup","called\n")); + + /* + * get our context from mfd + * ${context}_interface_ctx *if_ctx = + * (${context}_interface_ctx *)reginfo->my_reg_void; + */ + + if(NULL == rowreq_ctx) { +@ if $m2c_table_row_creation == 0@ + rc = SNMP_ERR_NOCREATION; +@ else@ + netsnmp_table_request_info *tblreq_info; + netsnmp_index oid_idx; + + tblreq_info = netsnmp_extract_table_info(requests); + if(NULL == tblreq_info) { + snmp_log(LOG_ERR, "request had no table info\n"); + return MFD_ERROR; + } + + /* + * try create rowreq + */ + oid_idx.oids = tblreq_info->index_oid; + oid_idx.len = tblreq_info->index_oid_len; + + rowreq_ctx = _mfd_${context}_rowreq_from_index(&oid_idx, &rc); + if(MFD_SUCCESS == rc) { + netsnmp_assert(NULL != rowreq_ctx); + rowreq_ctx->rowreq_flags |= MFD_ROW_CREATED; + /* + * add rowreq_ctx to request data lists + */ + netsnmp_container_table_row_insert(requests, (netsnmp_index*)rowreq_ctx); + } +@ end@ // row creation + } + + if (MFD_SUCCESS != rc) + netsnmp_request_set_error_all(requests, rc); + else + ${context}_row_prep(rowreq_ctx); + + return SNMP_VALIDATE_ERR(rc); +} /* _mfd_${context}_object_lookup */ + +######################################################################## +## +/*********************************************************************** + * + * GET processing + * + ***********************************************************************/ +/* + * @internal + * Retrieve the value for a particular column + */ +NETSNMP_STATIC_INLINE int +_${context}_get_column( ${context}_rowreq_ctx *rowreq_ctx, + netsnmp_variable_list *var, int column ) +{ + int rc = SNMPERR_SUCCESS; + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_get_column", + "called for %d\n", column)); + + + netsnmp_assert(NULL != rowreq_ctx); + + switch(column) { +@ foreach $node internalindex@ +@ include m2c_setup_node.m2i@ +@ if $node.accessible == 1@ + + /* (INDEX) $m2c_node_summary */ + case COLUMN_$node.uc: + var->type = $node.type; +@ if $m2c_node_needlength == 1@ + /* + * NOTE: val_len is in bytes, ${node}_len might not be (e.g. oids) + */ + if (var->val_len < (rowreq_ctx->tbl_idx.${node}_len * + sizeof(rowreq_ctx->tbl_idx.${node}[0]))) { + var->val.string = malloc(rowreq_ctx->tbl_idx.${node}_len * + sizeof(rowreq_ctx->tbl_idx.${node}[0])); + } + var->val_len = rowreq_ctx->tbl_idx.${node}_len * sizeof(rowreq_ctx->tbl_idx.${node}[0]); + memcpy( var->val.string, rowreq_ctx->tbl_idx.$node, var->val_len ); +@ else@ + var->val_len = sizeof($m2c_decl); + (*var->val.integer) = rowreq_ctx->tbl_idx.$node; +@ end@ + break; +@ end@ ## accessible +@ end@ ## index +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $node.accessible == 1@ + + /* $m2c_node_summary */ + case COLUMN_$node.uc: +@ if $m2c_table_sparse == 1@ + if (! (COLUMN_$node.uc_FLAG & rowreq_ctx->column_exists_flags)) { + DEBUGMSGTL(("internal:${context}:_mfd_${context}_get_column", + "column %d ($node) doesn't exist\n", column)); + return MFD_SKIP; + } + +@ end@ +## use sizeof except for BITS +@ if $m2c_node_needlength == 0@ +@ if "$node.perltype" eq "BITS"@ + { + $m2c_decl mask = 0xff << ((sizeof($m2c_decl) - 1) * 8); + int idx = 0; +@ else@ + var->val_len = sizeof($m2c_decl); +@ end@ +@ end@ + var->type = $node.type; +rc = ${node}_get(rowreq_ctx, $m2c_node_var_ref ); +@ if ($m2c_node_needlength == 0) && ("$node.perltype" eq "BITS")@ + /* + * check for length of bits string + */ + var->val_len = 0; + while( 0 != mask ) { + ++idx; + if ( *(($m2c_decl *) var->val.string) & mask ) + var->val_len = idx; + mask = mask >> 8; + } + } +@ end@ + break; + @ end@ # accessible +@ end@ # for each column + + default: + if ($context.uc_MIN_COL <= column && column <= $context.uc_MAX_COL) { + DEBUGMSGTL(("internal:${context}:_mfd_${context}_get_column", + "assume column %d is reserved\n", column)); + rc = MFD_SKIP; + } else { + snmp_log(LOG_ERR, + "unknown column %d in _${context}_get_column\n", column); + } + break; + } + + return rc; +} /* _${context}_get_column */ + +######################################################################## +## +int +_mfd_${context}_get_values(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + netsnmp_table_request_info * tri; + u_char * old_string; + void (*dataFreeHook)(void *); + int rc; + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_get_values","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + +@if $m2c_table_sparse == 1@ + DEBUGMSGTL(("9:${context}:_mfd_${context}_get_values", + "exists %p\n", (void*)rowreq_ctx->column_exists_flags)); + +@end@ + for(;requests; requests = requests->next) { + /* + * save old pointer, so we can free it if replaced + */ + old_string = requests->requestvb->val.string; + dataFreeHook = requests->requestvb->dataFreeHook; + if(NULL == requests->requestvb->val.string) { + requests->requestvb->val.string = requests->requestvb->buf; + requests->requestvb->val_len = sizeof(requests->requestvb->buf); + } + else if(requests->requestvb->buf == requests->requestvb->val.string) { + if(requests->requestvb->val_len != sizeof(requests->requestvb->buf)) + requests->requestvb->val_len = sizeof(requests->requestvb->buf); + } + + /* + * get column data + */ + tri = netsnmp_extract_table_info(requests); + if(NULL == tri) + continue; + + rc = _${context}_get_column(rowreq_ctx, requests->requestvb, tri->colnum); + if(rc) { + if(MFD_SKIP == rc) { + requests->requestvb->type = SNMP_NOSUCHINSTANCE; + rc = SNMP_ERR_NOERROR; + } + } + else if (NULL == requests->requestvb->val.string) { + snmp_log(LOG_ERR,"NULL varbind data pointer!\n"); + rc = SNMP_ERR_GENERR; + } + if(rc) + netsnmp_request_set_error(requests, SNMP_VALIDATE_ERR(rc)); + + /* + * if the buffer wasn't used previously for the old data (i.e. it + * was allcoated memory) and the get routine replaced the pointer, + * we need to free the previous pointer. + */ + if(old_string && (old_string != requests->requestvb->buf) && + (requests->requestvb->val.string != old_string)) { + if(dataFreeHook) + (*dataFreeHook)(old_string); + else + free(old_string); + } + } /* for results */ + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_get_values */ + +######################################################################## +## +/*********************************************************************** + * + * SET processing + * + ***********************************************************************/ + +@if $m2c_table_settable == 0@ +/* + * SET PROCESSING NOT APPLICABLE (per MIB or user setting) + */ +@else@ +/*---------------------------------------------------------------------- + * + * SET: Syntax checks + * + *---------------------------------------------------------------------*/ +/* + * @internal + * Check the syntax for a particular column + */ +NETSNMP_STATIC_INLINE int +_${context}_check_column( ${context}_rowreq_ctx *rowreq_ctx, + netsnmp_variable_list *var, int column ) +{ + int rc = SNMPERR_SUCCESS; + + DEBUGMSGTL(("internal:${context}:_${context}_check_column", + "called for %d\n", column)); + + netsnmp_assert(NULL != rowreq_ctx); + + switch(column) { +@ eval $m2c_nvv_item = "${m2c_context_item}tbl_idx."@ +@ foreach $node internalindex@ +@ include m2c_setup_node.m2i@ + /* (INDEX) $m2c_node_summary */ + case COLUMN_$node.uc: + rc = SNMP_ERR_NOTWRITABLE; /* can not change index of active row */ + break; +@ end@ ## index +@ eval $m2c_nvv_item = "$m2c_data_item"@ +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $node.settable == 0@ + + /* $m2c_node_summary */ + case COLUMN_$node.uc: + rc = SNMP_ERR_NOTWRITABLE; + break; +@ next@ +@ end@ + + /* $m2c_node_summary */ + case COLUMN_$node.uc: +@ include node-varbind-validate.m2i@ + if(SNMPERR_SUCCESS != rc) { + DEBUGMSGTL(("${context}:_${context}_check_column:$node", + "varbind validation failed (eg bad type or size)\n")); + } + else { + rc = ${node}_check_value( rowreq_ctx, $m2c_node_var_val ); + if((MFD_SUCCESS != rc) && (MFD_NOT_VALID_EVER != rc) && + (MFD_NOT_VALID_NOW != rc)) { + snmp_log(LOG_ERR, "bad rc %d from ${node}_check_value\n", rc); + rc = SNMP_ERR_GENERR; + } + } + break; +@ end@ # for each nonindex + + default: /** We shouldn't get here */ + rc = SNMP_ERR_GENERR; + snmp_log(LOG_ERR, "unknown column %d in _${context}_check_column\n", column); + } + + return rc; +} /* _${context}_check_column */ + +##---------------------------------------------------------------------- +int +_mfd_${context}_check_objects(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + netsnmp_table_request_info * tri; + int rc; + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_check_objects","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + for(;requests; requests = requests->next) { + + /* + * get column number from table request info, and check that column + */ + tri = netsnmp_extract_table_info(requests); + if(NULL == tri) + continue; + + rc = _${context}_check_column(rowreq_ctx, requests->requestvb, tri->colnum); + if(rc) { + netsnmp_request_set_error(requests, SNMP_VALIDATE_ERR(rc)); + break; + } + + } /* for results */ + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_check_objects */ + + +@ if $m2c_table_dependencies == 1@ +/*---------------------------------------------------------------------- + * + * SET: check dependencies + * + *---------------------------------------------------------------------*/ +/* + * @internal + * Check dependencies wrapper + */ +static int +_mfd_${context}_check_dependencies(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + int rc; + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + DEBUGMSGTL(("internal:${context}:_mfd_${context}_check_dependencies","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + rc = ${context}_check_dependencies(rowreq_ctx); + if(rc){ + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_check_dependencies\n", rc)); + netsnmp_request_set_error_all(requests, SNMP_VALIDATE_ERR(rc)); + } + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_check_dependencies */ + +@end@ // dependencies +/*---------------------------------------------------------------------- + * + * SET: Undo setup + * + *---------------------------------------------------------------------*/ +/* + * @internal + * Set the value for a particular column + */ +NETSNMP_STATIC_INLINE int +_${context}_undo_setup_column( ${context}_rowreq_ctx *rowreq_ctx, int column ) +{ + int rc = SNMPERR_SUCCESS; + + DEBUGMSGTL(("internal:${context}:_${context}_undo_setup_column", + "called for %d\n", column)); + + netsnmp_assert(NULL != rowreq_ctx); + + switch(column) { +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $node.settable == 1@ + + /* $m2c_node_summary */ + case COLUMN_$node.uc: + rowreq_ctx->column_set_flags |= COLUMN_$node.uc_FLAG; + rc = ${node}_undo_setup(rowreq_ctx ); + break; + @ end@ # settable +@ end@ # for each column + + default: + snmp_log(LOG_ERR,"unknown column %d in _${context}_undo_setup_column\n", column); + break; + } + + return rc; +} /* _${context}_undo_setup_column */ + + +##---------------------------------------------------------------------- +/** + * @internal + * undo setup + */ +int +_mfd_${context}_undo_setup(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + int rc; + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_undo_setup","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + +@if $m2c_undo_embed == 0@ + /* + * allocate undo context + */ + rowreq_ctx->undo = ${context}_allocate_data(); + if(NULL == rowreq_ctx->undo) { + /** msg already logged */ + netsnmp_request_set_error_all(requests, SNMP_ERR_RESOURCEUNAVAILABLE); + return SNMP_ERR_NOERROR; + } + +@end@ + /* + * row undo setup + */ + rowreq_ctx->column_set_flags = 0; + rc = ${context}_undo_setup(rowreq_ctx); + if (MFD_SUCCESS != rc) { + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_undo_setup\n", rc)); + netsnmp_request_set_error_all(requests, SNMP_VALIDATE_ERR(rc)); + } + else { + /* + * column undo setup + */ + netsnmp_table_request_info * tri; + for(;requests; requests = requests->next) { + /* + * set column data + */ + tri = netsnmp_extract_table_info(requests); + if(NULL == tri) + continue; + + rc = _${context}_undo_setup_column(rowreq_ctx, tri->colnum); + if(MFD_SUCCESS != rc) { + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_undo_setup_column\n", rc)); + netsnmp_set_request_error(agtreq_info, requests, SNMP_VALIDATE_ERR(rc)); + } + } /* for results */ + } + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_undo_setup */ + +/** + * @internal + * undo setup + */ +int +_mfd_${context}_undo_cleanup(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + int rc; + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_undo_cleanup","called\n")); + + /* + * failed row create in early stages has no rowreq_ctx + */ + if (NULL == rowreq_ctx) + return MFD_SUCCESS; + + /* + * call user cleanup + */ + rc = ${context}_undo_cleanup(rowreq_ctx); + if (MFD_SUCCESS != rc) { + /* + * nothing we can do about it but log it + */ + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_undo_cleanup\n", rc)); + } + +@if $m2c_undo_embed == 0@ + /* + * release undo context, if needed + */ + if(rowreq_ctx->undo) { + ${context}_release_data(rowreq_ctx->undo); + rowreq_ctx->undo = NULL; + } + +@end@ + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_undo_cleanup */ + +/*---------------------------------------------------------------------- + * + * SET: Set values + * + *---------------------------------------------------------------------*/ +/* + * @internal + * Set the value for a particular column + */ +NETSNMP_STATIC_INLINE int +_${context}_set_column( ${context}_rowreq_ctx *rowreq_ctx, + netsnmp_variable_list *var, int column ) +{ + int rc = SNMPERR_SUCCESS; + + DEBUGMSGTL(("internal:${context}:_${context}_set_column", + "called for %d\n", column)); + + netsnmp_assert(NULL != rowreq_ctx); + + switch(column) { +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $node.settable == 1@ + + /* $m2c_node_summary */ + case COLUMN_$node.uc: + rowreq_ctx->column_set_flags |= COLUMN_$node.uc_FLAG; + rc = ${node}_set(rowreq_ctx, $m2c_node_var_val ); + break; + @ end@ # settable +@ end@ # for each column + + default: + snmp_log(LOG_ERR,"unknown column %d in _${context}_set_column\n", column); + rc = SNMP_ERR_GENERR; + break; + } + + return rc; +} /* _${context}_set_column */ + +######################################################################## +## +int +_mfd_${context}_set_values(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + netsnmp_table_request_info * tri; + int rc = SNMP_ERR_NOERROR; + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_set_values","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + rowreq_ctx->column_set_flags = 0; + for(;requests; requests = requests->next) { + /* + * set column data + */ + tri = netsnmp_extract_table_info(requests); + if(NULL == tri) + continue; + + rc = _${context}_set_column(rowreq_ctx, + requests->requestvb, tri->colnum); + if(MFD_SUCCESS != rc) { + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_set_column\n", rc)); + netsnmp_set_request_error(agtreq_info, requests, SNMP_VALIDATE_ERR(rc)); + } + } /* for results */ + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_set_values */ + +/*---------------------------------------------------------------------- + * + * SET: commit + * + *---------------------------------------------------------------------*/ +/** + * @internal + * commit the values + */ +int +_mfd_${context}_commit(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + int rc; + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_commit","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + rc = ${context}_commit(rowreq_ctx); + if (MFD_SUCCESS != rc) { + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_commit\n", rc)); + netsnmp_request_set_error_all(requests, SNMP_VALIDATE_ERR(rc)); + } + + if (rowreq_ctx->rowreq_flags & MFD_ROW_DIRTY) { + /* + * if we successfully commited this row, set the dirty flag. Use the + * current value + 1 (i.e. dirty = # rows changed). + * this is checked in post_request... + */ + ${context}_dirty_set( ${context}_dirty_get() + 1 ); /* set table dirty flag */ + } + + return SNMP_ERR_NOERROR; +} + +int +_mfd_${context}_undo_commit(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + int rc; + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_undo_commit","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + if (rowreq_ctx->rowreq_flags & MFD_ROW_DIRTY) { + u_int d = ${context}_dirty_get(); + + netsnmp_assert(d != 0); + if(d) + ${context}_dirty_set( d - 1 ); + } + + rc = ${context}_undo_commit(rowreq_ctx); + if (MFD_SUCCESS != rc) { + /* + * nothing we can do about it but log it + */ + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_undo_commit\n", rc)); + } + + if (rowreq_ctx->rowreq_flags & MFD_ROW_DIRTY) { + snmp_log(LOG_WARNING, "${context} row dirty flag still set after undo_commit\n"); + rowreq_ctx->rowreq_flags &= ~MFD_ROW_DIRTY; + } + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_commit */ + +/*---------------------------------------------------------------------- + * + * SET: Undo + * + *---------------------------------------------------------------------*/ +/** + * @internal + * undo the value for a particular column + */ +NETSNMP_STATIC_INLINE int +_${context}_undo_column( ${context}_rowreq_ctx *rowreq_ctx, + netsnmp_variable_list *var, int column ) +{ + int rc = SNMPERR_SUCCESS; + + DEBUGMSGTL(("internal:${context}:_${context}_undo_column", + "called for %d\n", column)); + + netsnmp_assert(NULL != rowreq_ctx); + + switch(column) { +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $node.settable == 1@ + + /* $m2c_node_summary */ + case COLUMN_$node.uc: + rc = ${node}_undo(rowreq_ctx); + break; + @ end@ # settable +@ end@ # for each column + + default: + snmp_log(LOG_ERR,"unknown column %d in _${context}_undo_column\n", column); + break; + } + + return rc; +} /* _${context}_undo_column */ + +######################################################################## +## +int +_mfd_${context}_undo_values(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ + int rc; + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + netsnmp_table_request_info * tri; + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_undo_values","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + rc = ${context}_undo(rowreq_ctx); + if (MFD_SUCCESS != rc) { + /* + * nothing we can do about it but log it + */ + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_undo\n", rc)); + } + + for(;requests; requests = requests->next) { + /* + * set column data + */ + tri = netsnmp_extract_table_info(requests); + if(NULL == tri) + continue; + + rc = _${context}_undo_column(rowreq_ctx, requests->requestvb, + tri->colnum); + if (MFD_SUCCESS != rc) { + /* + * nothing we can do about it but log it + */ + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_undo_column\n", rc)); + } + } /* for results */ + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_undo_values */ + +/*---------------------------------------------------------------------- + * + * SET: irreversible commit + * + *---------------------------------------------------------------------*/ +/** + * @internal + * commit irreversible actions + */ +int +_mfd_${context}_irreversible_commit(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *agtreq_info, + netsnmp_request_info *requests) +{ +@ if $m2c_irreversible_commit == 1@ + int rc; +@ end@ + ${context}_rowreq_ctx *rowreq_ctx = (${context}_rowreq_ctx*) + netsnmp_container_table_row_extract(requests); + + DEBUGMSGTL(("internal:${context}:_mfd_${context}_irreversible:commit","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + +@ if $m2c_irreversible_commit == 1@ + rc = ${context}_irreversible_commit(rowreq_ctx); + if (MFD_SUCCESS != rc) { + netsnmp_request_set_error_all(requests, SNMP_ERR_COMMITFAILED); + DEBUGMSGTL(("${context}:mfd","error %d from " + "${context}_irreversible_commit\n", rc)); + } + +@ end@ + /* + * check for and handle row creation/deletion + * and update column exist flags... + */ + if (rowreq_ctx->rowreq_flags & MFD_ROW_DELETED) { + if (! (rowreq_ctx->rowreq_flags & MFD_ROW_CREATED)) + CONTAINER_REMOVE(${context}_if_ctx.container, rowreq_ctx); + } + else { + if (rowreq_ctx->column_set_flags) { +@if $m2c_table_sparse == 1@ + DEBUGMSGTL(("internal:${context}:_mfd_irreversible_commit", + "updating exists (%p) w/set (%p) = %p\n", + rowreq_ctx->column_exists_flags, + rowreq_ctx->column_set_flags, + (rowreq_ctx->column_exists_flags | + rowreq_ctx->column_set_flags))); + rowreq_ctx->column_exists_flags |= + rowreq_ctx->column_set_flags; +@end@ # sparse + rowreq_ctx->column_set_flags = 0; + } +@if ($m2c_table_row_creation == 1)@ + if (rowreq_ctx->rowreq_flags & MFD_ROW_CREATED) { + rowreq_ctx->rowreq_flags &= ~MFD_ROW_CREATED; + CONTAINER_INSERT(${context}_if_ctx.container, rowreq_ctx); + } +@end@ # creation + } + + return SNMP_ERR_NOERROR; +} /* _mfd_${context}_irreversible_commit */ + +@end@ # settable +/*********************************************************************** + * + * DATA ACCESS + * + ***********************************************************************/ +@ include mfd-access-${m2c_table_access}-defines.m2i@ +@ if $m2c_table_persistent == 1@ +@ include mfd-persistence.m2i@ +@ end@ + +${context}_rowreq_ctx * +${context}_row_find_by_mib_index(${context}_mib_index *mib_idx) +{ + ${context}_rowreq_ctx *rowreq_ctx; + oid oid_tmp[MAX_OID_LEN]; + netsnmp_index oid_idx; + int rc; + + /* + * set up storage for OID + */ + oid_idx.oids = oid_tmp; + oid_idx.len = sizeof(oid_tmp)/sizeof(oid); + + /* + * convert + */ + rc = ${context}_index_to_oid(&oid_idx, mib_idx); + if (MFD_SUCCESS != rc) + return NULL; + + rowreq_ctx = (${context}_rowreq_ctx*)CONTAINER_FIND(${context}_if_ctx.container, &oid_idx); + + return rowreq_ctx; +} + +@ if $m2c_table_refcounts == 1@ +int +${context}_row_ref_increment(${context}_rowreq_ctx *rowreq_ctx) +{ + if (NULL == rowreq_ctx) + return -1; + + ++rowreq_ctx->ref_count; + DEBUGMSGTL(("${context}:${context}_row_ref_increment", + "row %p ref count is %d\n",rowreq_ctx, + rowreq_ctx->ref_count)); + + return MFD_SUCCESS; +} + +int +${context}_row_ref_decrement(${context}_rowreq_ctx *rowreq_ctx) +{ + if (NULL == rowreq_ctx) + return -1; + + if (0 == rowreq_ctx->ref_count) { + snmp_log(LOG_WARNING,"attempt to decrement ref_count below 0\n"); + return -2; + } + --rowreq_ctx->ref_count; + DEBUGMSGTL(("${context}:${context}_row_ref_decrement", + "row %p ref count is %d\n",rowreq_ctx, + rowreq_ctx->ref_count)); + + return MFD_SUCCESS; +} + +@ end@ // refcounts +@end@ # foreach table +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/mfd-makefile.m2m b/local/mib2c-conf.d/mfd-makefile.m2m new file mode 100644 index 0000000..c41d5bb --- /dev/null +++ b/local/mib2c-conf.d/mfd-makefile.m2m @@ -0,0 +1,139 @@ +####################################################### -*- Makefile -*- +## $Id$ +## +######################################################################## +## +@strict token off@ +@ifconf ${name}_Makefile@ +@ print ${name}_Makefile exists, skipping@ +@else@ +@ if "x$m2c_create_fewer_files" eq "x"@ +@ eval $m2c_create_fewer_files = 0@ +@ end@ +@ open ${name}_Makefile@ +@ ifconf ${name}_Makefile.m2m@ +@ include ${name}_Makefile.m2m@ +@ else@ +######################################################################## +@ if $m2c_mark_boundary == 1@ +# START code generated by $RCSfile$ $Revision$ +@ end@ +######################################################################## + +CC=gcc +TABLE_PREFIX=${name} + +@ if "$mfd_netsnmp_dir" ne ""@ +NETSNMPDIR=$mfd_netsnmp_dir +NETSNMPCONFIG=$(NETSNMPDIR)/net-snmp-config +@ else@ +NETSNMPCONFIG=net-snmp-config +@ end@ + +@ if "$mfd_netsnmp_dir" ne ""@ + +# Assuming we're linking against a Net-SNMP build tree (which may or +# may not be the same as the source tree) and not an installed package. + +# Note: to do this we REQUIRE gnu-make. + +NETSNMPBASECFLAGS := $(shell $(NETSNMPCONFIG) --base-cflags) +NETSNMPINCLUDES := $(shell $(NETSNMPCONFIG) --build-includes $(NETSNMPDIR)) +# base flags after build/src include, in case it has /usr/local/include +NETSNMPCFLAGS=$(NETSNMPINCLUDES) $(NETSNMPBASECFLAGS) + +NETSNMPBASELIBS := $(shell $(NETSNMPCONFIG) --base-agent-libs) +NETSNMPEXTLIBS := $(shell $(NETSNMPCONFIG) --external-agent-libs) +NETSNMPLIBDIRS := $(shell $(NETSNMPCONFIG) --build-lib-dirs $(NETSNMPDIR)) +NETSNMPLIBDEPS := $(shell $(NETSNMPCONFIG) --build-lib-deps $(NETSNMPDIR)) +LIB_DEPS=$(NETSNMPLIBDEPS) +LIBS=$(NETSNMPLIBDIRS) -Wl,-Bstatic $(NETSNMPBASELIBS) -Wl,-Bdynamic $(NETSNMPEXTLIBS) + +@ else@ + +# uncomment this if you have GNU make +#NETSNMPCFLAGS := $(shell $(NETSNMPCONFIG) --base-cflags) +#NETSNMPLIBS := $(shell $(NETSNMPCONFIG) --agent-libs) +NETSNMPCFLAGS=`$(NETSNMPCONFIG) --base-cflags` +NETSNMPLIBS=`$(NETSNMPCONFIG) --agent-libs` + +LIBS=$(NETSNMPLIBS) + +@ end@ + +STRICT_FLAGS = -Wall -Wstrict-prototypes +CFLAGS=-I. $(NETSNMPCFLAGS) $(STRICT_FLAGS) + +USER_SRCS = \ +@ if $m2c_create_fewer_files != 1@ + $(TABLE_PREFIX)_data_get.c \ + $(TABLE_PREFIX)_data_set.c \ +@ end@ + $(TABLE_PREFIX)_data_access.c + +SRCS = $(USER_SRCS) \ + $(TABLE_PREFIX).c \ + $(TABLE_PREFIX)_subagent.c \ + $(TABLE_PREFIX)_interface.c + +USER_OBJS = \ +@ if $m2c_create_fewer_files != 1@ + $(TABLE_PREFIX)_data_get.o \ + $(TABLE_PREFIX)_data_set.o \ +@ end@ + $(TABLE_PREFIX)_data_access.o + +OBJS = $(USER_OBJS) \ + $(TABLE_PREFIX).o \ + $(TABLE_PREFIX)_subagent.o \ + $(TABLE_PREFIX)_interface.o + +TARGETS=$(TABLE_PREFIX) + +.SUFFIXES: +.SUFFIXES: .c .o .deps + + +all: $(TARGETS) + +user: $(USER_OBJS) + +$(TARGETS): $(LIB_DEPS) + +$(TABLE_PREFIX): $(OBJS) $(TABLE_PREFIX)_Makefile + $(CC) -o $(TABLE_PREFIX) $(OBJS) $(LIBS) + +clean: + rm -f $(OBJS) $(TARGETS) + +@if "$mfd_netsnmp_dir" ne ""@ + +$(TABLE_PREFIX).deps $(TABLE_PREFIX)_subagent.deps $(TABLE_PREFIX)_interface.deps: $(TABLE_PREFIX)_Makefile +$(TABLE_PREFIX)_data_access.deps: $(TABLE_PREFIX)_Makefile +@if $m2c_create_fewer_files != 1@ +$(TABLE_PREFIX)_data_get.deps: $(TABLE_PREFIX)_Makefile +$(TABLE_PREFIX)_data_set.deps: $(TABLE_PREFIX)_Makefile +@end@ + +%.deps : %.c + \@echo "Generating makefile $\@ ..." + \@set -e; $(CC) -M $(COPTS) $(CFLAGS) $(CPPFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $\@ : /g' > $\@; \ + [ -s $\@ ] || $(RM) $(RMFLAGS) $\@ + +include $(TABLE_PREFIX).deps +include $(TABLE_PREFIX)_subagent.deps +include $(TABLE_PREFIX)_interface.deps +include $(TABLE_PREFIX)_data_access.deps +@ if $m2c_create_fewer_files != 1@ +include $(TABLE_PREFIX)_data_get.deps +include $(TABLE_PREFIX)_data_set.deps +@ end@ +@end@ +######################################################################## +@ if $m2c_mark_boundary == 1@ +# END code generated by $RCSfile$ $Revision$ +@ end@ +@ end@ # not including ${name}_Makefile.m2m +@ close ${name}_Makefile@ +@end@ # no existing makefile diff --git a/local/mib2c-conf.d/mfd-persistence.m2i b/local/mib2c-conf.d/mfd-persistence.m2i new file mode 100644 index 0000000..7796cf2 --- /dev/null +++ b/local/mib2c-conf.d/mfd-persistence.m2i @@ -0,0 +1,478 @@ +######################################################################## +## generic include for XXX. Do not use directly. +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +@if $m2c_processing_type eq 'h'@ +/* ********************************************************************* + * Persistent declarations + */ +/* + * persistence + */ +#define LINE_TERM_CHAR '$' + +void ${context}_container_init_persistence( netsnmp_container * container ); +int ${context}_container_should_save(${context}_rowreq_ctx * rowreq_ctx); + +@end@ // m2c_processing_type eq 'h' +###################################################################### +###################################################################### +###################################################################### +@if $m2c_processing_type eq 'c'@ +/************************************************************ + * the *_should_save routine is called to determine if a row + * should be stored persistently. + * + * Note that this is not a 'dirty' check (i.e. if a row has changed), + * but a check for volatile rows that should not be saved between + * restarts. + * + * return 1 if the row should be stored + * return 0 if the row should not be stored + */ +int +${context}_container_should_save(${context}_rowreq_ctx * rowreq_ctx) +{ +@ foreach $node column@ +@ if "$node.syntax" eq "StorageType"@ +@ include m2c_setup_node.m2i@ + if (SNMP_STORAGE_VOLATILE == $m2c_ctx_rh ) + return 0; +@ end@ +@ end@ + + return 1; /* save the row */ +} + +@end@ // m2c_processing_type eq 'h' +###################################################################### +###################################################################### +###################################################################### +@if $m2c_processing_type eq 'i'@ +/*********************************************************************** + * + * PERSISTENCE + * + ***********************************************************************/ + +static int _${context}_container_save_rows(int majorID, int minorID, void *serverarg, void *clientarg); +static void _${context}_container_row_restore(const char *token, char *buf); +static int _${context}_container_row_save( + ${context}_rowreq_ctx *rowreq_ctx, + void *type); +static char * _${context}_container_col_restore( + ${context}_rowreq_ctx *rowreq_ctx, + u_int col, char* buf); +static char * _${context}_container_col_save( + ${context}_rowreq_ctx *rowreq_ctx, + u_int col, char* buf); + +static char row_token[] = "${context}"; + +/************************************************************ + * *_init_persistence should be called from the main table + * init routine. + * + * If your table depends on rows in another table, + * you should register your callback after the other table, + * which should ensure the rows on which you depend are saved + * (and re-created) before the dependent rows. + */ +void +${context}_container_init_persistence( netsnmp_container * container ) +{ + int rc; + + register_config_handler(NULL, row_token, + _${context}_container_row_restore, NULL, NULL); + rc = snmp_register_callback( SNMP_CALLBACK_LIBRARY, + SNMP_CALLBACK_STORE_DATA, + _${context}_container_save_rows, + container); + + if( rc != SNMP_ERR_NOERROR ) + snmp_log(LOG_ERR, "error registering for STORE_DATA callback " + "in _${context}_container_init_persistence\n"); +} + +static int +_${context}_container_save_rows(int majorID, int minorID, void *serverarg, void *clientarg) +{ + char sep[] = + "##############################################################"; + char buf[] = + "#\n" + "# $context persistent data\n" + "#"; + char *type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_APPTYPE); + + read_config_store((char*)type, sep); + read_config_store((char*)type, buf); + + /* + * save all rows + */ + CONTAINER_FOR_EACH((netsnmp_container*)clientarg, + (netsnmp_container_obj_func*)_${context}_container_row_save, + type); + + read_config_store((char*)type, sep); + read_config_store((char*)type, "\n"); + + /* + * never fails + */ + return SNMPERR_SUCCESS; +} + + + +/************************************************************ + * _${context}_container_row_save + */ +static int +_${context}_container_row_save( + ${context}_rowreq_ctx *rowreq_ctx, + void *type) +{ + /* + * Allocate space for a line with all data for a row. An + * attempt is made to come up with a default maximum size, but + * there is no guarantee it will be enough. It probably will be, + * unless you are dealing with large values or you have external + * indexes. + * + * 1) allocate space for each column. Comment out columns you don't + * intend to save. You may also need to add room for any non- + * column data you want to store. Remeber, data will be stored in + * ASCII form, so you need to allow for that. Here are some + * general guidelines: + * + * Object ID : 12 * len [ASCII len of max int + 1 for .] + * Octet String: (2 * len) + 2 [2 ASCII chars per byte + "0x"] + * Integers : 12 [ASCII len for smallest negative number] + * + * 2) You also need to allocate space for the row index. This will + * be stored as an OID, which means that Octet Strings need to + * be treated a little differently. Specifically, you will need + * (4 * len) + 4 [3 ASCII chars per byte + 1 for ., + 4 for len]. + * + * 3) Also, remember to add space for the identifier and separator + * characters (for example, each column is prefixed by the + * column number and a semicolon. To allow for the maximum + * column values, 12 bytes [11 for oid + 1 for ':'] per + * column are added). + */ + /** xxx: add storage for external index(s)! */ +#define MAX_ROW_SIZE (sizeof(row_token) + 1 + \ +@ if $ext_index != 0@ + ( 12 * 128 ) + /* external interfaces - max 128 subids */ \ +@ end@ +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if ($node.settable == 1)@ +@ if "$node.type" eq "ASN_OBJECT_ID"@ + ( ( 12 * sizeof(${m2c_ctx_rh}) ) + 3 ) + /* $node.type */ \ +@ elsif "$node.type" eq "ASN_OCTET_STR"@ + ( ( 2 * sizeof(${m2c_ctx_rh}) ) + 3 ) + /* $node.type */ \ +@ else@ + ( 12 ) + /* $node.type $node */ \ +@ end@ +@ end@ +@ end@ + ( $table.uc_MAX_COL * 12 ) + /* column num prefix + : */ \ + 2 /* LINE_TERM_CHAR + \n */ ) + + char buf[MAX_ROW_SIZE], *pos = buf, *max = &buf[MAX_ROW_SIZE-1]; + char *tmp; + int i; + + if (${context}_container_should_save(rowreq_ctx) == 0) { + return SNMP_ERR_NOERROR; + } + + /* + * build the line + */ + pos += sprintf(pos, "%s ", row_token); + pos = read_config_save_objid(pos, rowreq_ctx->oid_idx.oids, + rowreq_ctx->oid_idx.len); + if(NULL == pos) { + snmp_log(LOG_ERR,"error saving ${context} row " + "to persistent file\n"); + return SNMP_ERR_GENERR; + } + *pos++ = ' '; + if(pos > max) { + snmp_log(LOG_ERR,"error saving ${context} row " + "to persistent file (too long)\n"); + return SNMP_ERR_GENERR; + } + + /* + * add each column + */ + for(i = $table.uc_MIN_COL; i <= $table.uc_MAX_COL; ++i ) { + + if ((0x1 << (i-1)) & ~$context.uc_SETTABLE_COLS) + continue; + + tmp = pos; + pos = _${context}_container_col_save(rowreq_ctx, i, pos); + if(NULL == pos) + pos = tmp; + else + *pos++ = ' '; + if(pos > max) { + snmp_log(LOG_ERR,"error saving ${context} row " + "to persistent file (too long)\n"); + return SNMP_ERR_GENERR; + } + } + + /* + * if you have non-column data, add it here + */ + + + /* + * store the line + */ + pos += sprintf(pos, "%c", LINE_TERM_CHAR); + if(pos > max) { + snmp_log(LOG_ERR,"error saving ${context} row " + "to persistent file (too long)\n"); + return SNMP_ERR_GENERR; + } + read_config_store((char*)type, buf); + + DEBUGMSGTL(("internal:${context}:_${context}_container_row_save", + "saving line '%s'\n", buf)); + + return SNMP_ERR_NOERROR; +} + +static void +_${context}_container_row_restore(const char *token, char *buf) +{ + ${context}_rowreq_ctx * rowreq_ctx; + netsnmp_index index; + oid tmp_oid[ MAX_${context}_IDX_LEN]; + u_int col = 0, found = 0; + + + if (strncmp(token, row_token, sizeof(row_token)) != 0) { + snmp_log(LOG_ERR, "unknown token in _${context}_container_row_restore\n"); + return; + } + + DEBUGMSGTL(("internal:${context}:_${context}_container_row_restore", + "parsing line '%s'\n", buf)); + + /* + * pull out index and create default row + */ + index.oids = tmp_oid; + index.len = OID_LENGTH(tmp_oid); + buf = read_config_read_objid(buf, &index.oids, + &index.len); + if (NULL == buf) { + snmp_log(LOG_ERR, "error reading row index in " + "_${context}_container_row_restore\n"); + return; + } + rowreq_ctx = _mfd_${context}_rowreq_from_index( &index, NULL ); + if (NULL == rowreq_ctx) { + snmp_log(LOG_ERR, "error creating row index in " + "_${context}_container_row_restore\n"); + return; + } + + /* + * loop through and get each column + */ + buf = skip_white(buf); + while ( (NULL != buf) && isdigit(*buf) ) { + /* + * extract column, skip ':' + */ + col = (u_int)strtol(buf, &buf, 10); + if (NULL == buf) + break; + if (*buf != ':') { + buf = NULL; + break; + } + ++buf; /* skip : */ + + /* + * parse value + */ + DEBUGMSGTL(("_${context}_container_row_restore", + "parsing column %d\n", col)); + buf = _${context}_container_col_restore( rowreq_ctx, col, buf ); + ++found; + } + if (0 == found) { + snmp_log(LOG_ERR, "error parsing ${context} row; no columns found\n"); + ${context}_release_rowreq_ctx( rowreq_ctx ); + return; + } + + /* + * if you added any non-column data, this is where + * you should handle it. + */ + + /* + * if the pointer is NULL and we didn't reach the + * end of the line, something went wrong. Log message, + * delete the row and bail. + */ + if ((buf == NULL) || (*buf != LINE_TERM_CHAR)) { + snmp_log(LOG_ERR, "error parsing ${context} row around column %d\n", + col); + ${context}_release_rowreq_ctx( rowreq_ctx ); + return; + } + + DEBUGMSGTL(("internal:${context}:_${context}_container_row_restore", + "inserting row\n")); + + /* + * copy oid index and insert row + */ + rowreq_ctx->oid_idx.len = index.len; + memcpy(rowreq_ctx->oid_idx.oids, index.oids, index.len * sizeof(oid)); + + CONTAINER_INSERT(${context}_if_ctx.container, rowreq_ctx); +} + +/************************************************************ + * _${context}_container_col_save + */ +static char * +_${context}_container_col_save( + ${context}_rowreq_ctx *rowreq_ctx, + u_int col, char* buf) +{ + if( ( NULL == rowreq_ctx ) || ( NULL == buf )) { + snmp_log(LOG_ERR, "bad parameter in " + "_${context}_container_col_save\n"); + return NULL; + } + + DEBUGMSGTL(("internal:${context}:_${context}_container_col_save", + "processing column %d\n", col)); + + /* + * prefix with column number, so we don't ever depend on + * order saved. + */ + buf += sprintf(buf, "%u:", col); + + /* + * save data for the column + */ + switch(col) { + +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ + case COLUMN_$node.uc: /** $node.syntax = $node.type */ +@ if $m2c_node_needlength == 1@ +@ if "$node.type" eq "ASN_OBJECT_ID"@ + buf = read_config_save_objid(buf, ${m2c_ctx_rh}, + ${m2c_ctx_rhs} ); +@ else@ # "$node.type" eq "ASN_OCTET_STR"@ + buf = read_config_save_octet_string(buf, ${m2c_ctx_rh}, + ${m2c_ctx_rhs} ); +@ end@ +@ elsif "$node.type" eq "ASN_INTEGER"@ + buf += sprintf(buf,"%ld",${m2c_ctx_rh}); +@ else@ + buf += sprintf(buf,"%lu",${m2c_ctx_rh}); +@ end@ + break; + +@ end@ # for each + default: /** We shouldn't get here */ + snmp_log(LOG_ERR, "unknown column %d in " + "_${context}_container_col_save\n", col); + return NULL; + } + + return buf; +} + +/************************************************************ + * _${context}_container_col_restore + */ +static char * +_${context}_container_col_restore( + ${context}_rowreq_ctx *rowreq_ctx, + u_int col, char* buf) +{ + size_t len; + if( ( NULL == rowreq_ctx ) || ( NULL == buf )) { + snmp_log(LOG_ERR, "bad parameter in " + "_${context}_container_col_restore\n"); + return NULL; + } + + DEBUGMSGTL(("verbose:${context}:_${context}_container_col_restore", + "processing column %d\n", col)); + + /* + * restore data for the column + */ + switch(col) { + +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ + case COLUMN_$node.uc: /** $node.syntax = $node.type */ +@ if $m2c_node_needlength == 1@ + ${m2c_ctx_rhs} = sizeof(${m2c_ctx_rh}); + buf = read_config_read_memory($node.type,buf, + (char*)&${m2c_ctx_rh}, + (size_t*)&${m2c_ctx_rhs} ); +@ if "$node.type" eq "ASN_OBJECT_ID"@ + ${m2c_ctx_rhs} /= sizeof(oid); +@ end@ +@ else@ + len = sizeof(${m2c_ctx_rh}); +@ if "$node.type" eq "ASN_OCTET_STR"@ # BITS +@ eval $m2c_tmp = "ASN_INTEGER"@ +@ else@ +@ eval $m2c_tmp = $node.type@ +@ end@ + buf = read_config_read_memory($m2c_tmp, buf, + (char*)&${m2c_ctx_rh}, + &len); +@ end@ +@ if $m2c_table_sparse == 1@ + if (NULL != buf) + rowreq_ctx->column_exists_flags |= COLUMN_$node.uc_FLAG; +@ end@ # table sparse + break; + +@ end@ # foreach col + default: /** We shouldn't get here */ + snmp_log(LOG_ERR, "unknown column %d in " + "_${context}_container_col_restore\n", col); + return NULL; + } + + return buf; +} + +## +@end@ // $m2c_processing_type eq 'i' +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/mfd-readme.m2c b/local/mib2c-conf.d/mfd-readme.m2c new file mode 100644 index 0000000..5ed9d59 --- /dev/null +++ b/local/mib2c-conf.d/mfd-readme.m2c @@ -0,0 +1,846 @@ +######################################################################## +## generic include for XXX. Don't use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@open ${name}-README-FIRST.txt@ +************************************************************************ +${name} README +------------------------------------------------------------------------ +This document describes the results of the mib2c code generation +system using the mfd code generation template. The resulting files +are documented both in this README file as well as per-table specific +README files. All of the files generated by this run of mib2c will +begin with the ${name} prefix. + +Quick Start +----------- +For those interested in a quick start, to get a pseudo-todo list, try +this command in directory with the generated code: + + grep -n "TODO:" *.[ch] | sed 's/\([^ ]*\) \(.*\)TODO\(.*\)/\3 (\1)/' | sort -n + +Key: + :o: Optional + :r: Recommended + :M: Mandatory + :A: Advanced users + +This will give you and ordered list of places in the code that you +may (or must) take a closer look at). + +You may also want to take a look at the on-line tutorial, found here: + + http://www.net-snmp.org/tutorial/tutorial-5/toolkit/mfd/index.html + + +MIBs For Dummies Overview +------------------------- +The MIBs For Dummies (MFD) configuration files have been written to help +SNMP novices implement SNMP MIBs. This section will be a brief +introduction to some of the general concepts you should be familar with. + + Managed Information Base (MIB) + ------------------------------ + A SNMP MIB (Managed information base) is a text file that describes the + syntax for some set of data objects. The MIB creates a correlation + between an ASCII name for an object and a number OID (Object Identifier). + The SNMP protocol communicates information using the OIDs, and the MIB + allows tools to display a name, which we humans find easier to deal with. + + To use an analogy, a MIB is much like a menu at a restaurant. If you've + ever been to a reataurant and ordered a meal, and later received a bill + that simply had '#6' on it, you get the idea. The name is easier for + the customers to remember, and the waiters and chefs use the number for + efficency. + + + Scalars + ------- + A scalar variable is a unique object in a MIB which can represent + a single value. For example, the SNMP standard MIB-II defines a + variable, sysContact.0, which is a string containing the contact + information for the person in charge of a particular agent. Note + that scalar variable always end with '.0'. + + + Rows and Tables + --------------- + When a group of related attributes occur more than once, they can be + grouped together in a table. A table has an index, which uniquely + identifies a particular row, and data columns, which contain the + attributes for that row. + + For example, the SNMP standard MIB-II defines a table, ifTable, which + contains information on the ethernet interfaces on a system. + + + Data Structures + --------------- + The code generated by the MFD configuration files has a few important + structures. + + + The Data Context + ---------------- + The data context structure should contain the necessary information + to provide the data for the columns in a given row. As long as you + can extract the data for a column for the data context, the data context + can be anything you want: a pointer to an existing structure, the + parameters needed for a function call or an actual copy of the data. + + By default, a data context structure is generated with storage for + all the data in a row. Information on changing the default is presented + later on in this help. + + + The MIB Context + --------------- + The MIB context structure is generated with storage for all the + indexes of a table. This data will be used when searching for the + correct row to process for a request. + + + The Row Request Context + ----------------------- + Each table will have a unique data structure for holding data during + the processing of a particular row. The row request context contains + the registration context (that you supply during initilization), + the data context, the MIB context, the undo context (for settable + tables) and other data. There is also a netsnmp_data_list, which can + be used to temporary storage during processing. + + + The Table Registration Pointer + ------------------------------ + During initilization, you may provide a pointer to arbitrary data for + you own use. This pointer will be saved in the row request context, + and is passed as a parameter to several functions. It is not required, + and is provided as a way for you to access table specific data in + the generated code. + + + +These files are top-level files potentially useful for all the tables: +------------------------------------------------------------------------ + + File : ${name}_Makefile + ---------------------------------------------------------------------- + Purpose : Make file for compiling a (sub)agent. This file is only + useful if you don't want to compile your code directly + into the Net-SNMP master agent. + Editable: Optional + Usage : make -f ${name}_Makefile + + + File : ${name}_subagent.c + ---------------------------------------------------------------------- + Purpose : This file contains a main() function for an agent or + sub-agent and is compiled using the Makefile above. + + + + +Table specific README files +------------------------------------------------------------------------ +Each table for which code was generated has its own README file +describing the files specifically associated with each table. You +should probably read these next: + +@foreach $table table@ +@ include m2c_setup_table.m2i@ + ${name}-README-${table}.txt +@end@ + + + +These are miscellaneous auto-generated code files you generally +shouldn't edit. They contain code that ties your code together with +the Net-SNMP agent. +------------------------------------------------------------------------ +@if $m2c_create_fewer_files == 1@ + File : ${name}.c + Purpose : Initilization for the entire module set, including the + SNMP tables. + +@end@ + File : ${name}.h + Purpose : Header file for the module set. Includes config_require + macros to auto-load the other code pieces when compiled + into the agent. + +@if $m2c_create_fewer_files != 1@ + File : ${name}_oids.h + Purpose : C #define definitions of the tables, columns, and OIDs + + File : ${name}_enums.h + Purpose : C #define definitions of the enumerated type values for + each column of each table that requires them. +@else@ + File : ${name}_constants.h + Purpose : C #define definitions of the tables, columns, OIDs, enumerated + type values for each column of each table that requires them. +@end@ + + File : ${name}_interface.c + Purpose : MFD interface to Net-SNMP. This auto-generated code ties the + functions you will fill out to the code that the agent needs. + +######################################################################## +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ open ${name}-README-${table}.txt@ +************************************************************************ +${context} README +------------------------------------------------------------------------ + This readme file describes the code generated by mib2c (using the MIBs + for Dummies (MFD) configuration file). The code generated was + generated specifically for the following SNMP table: + + ${context} + + Your code will be called when the snmp agent receives requests for + the ${context} table. The agent will start by looking for the right + row in your existing data to operate on, if one exists. + + + Configuration Variables + ------------------------------------------------------------ + Some variables used for code generation may be set to affect the code + generation. You may override these variables by setting them in the + file ${m2c_defaults_dir}table-${context}.m2d, and then re-running mib2c. + + m2c_table_settable (currently '$m2c_table_settable') + -------------------------------------------------------- + This variable determines whether or not code is generated to support + MIB object which have an access of read-write or read-create. The + default is set based on whether or not the table contains writable + objects, but can be over-ridden. + + Syntax: @eval $@m2c_table_settable = 0@ + + + m2c_table_dependencies (currently '$m2c_table_dependencies') + -------------------------------------------------------- + This variable determines whether or not code is generated to support + checking dependencies between columns, rows or tables. The default + is set based on whether or not the table contains writable objects, + but can be over-ridden. + + Syntax: @eval $@m2c_table_dependencies = 0@ + + + m2c_table_row_creation (currently '$m2c_table_row_creation') + -------------------------------------------------------- + This variable determines whether or not code is generated to support + checking creation of new rows via SNMP. The default is set based on + whether or not the table contains read-create objects, but can be + over-ridden. + + Syntax: @eval $@m2c_table_row_creation = 0@ + + + m2c_context_reg (currently '$m2c_context_reg') + -------------------------------------------------------- + This variable contains the structure name to typedef for the + ${context}_registration. + + During initilization, you will provide a pointer to a structure of + this type. This pointer is used as a parameter to many functions so + that you have access to your registration data. The default is a + netsnmp_data_list pointer, which will allow you to keep multiple + pointers tagged by a text name. If you have a new or existing structure + you would rather use, you can redefine this variable. + + + To avoid regenerating code, you may also change this typedef directly + in the ${table}.h header. + + Syntax: @eval $@m2c_context_reg = "struct my_registration_context@ + + + m2c_data_context (currently '$m2c_data_context') + -------------------------------------------------------- + This variable contains the structure name to typedef for the + ${context}_data. + + This typedef is used in the row request context structure for the table, + ${context}_rowreq_ctx. + + The typedef in the primary table context will be used for the data and + undo structure types. This structure should contain all the data + needed for all the columns in the table. The default is 'generated', + which will cuase a new data strcuture to be generated with data members + for each column. + + To avoid regenerating code, you may also change this typedef directly + in the ${table}.h header. + + Syntax: @eval $@m2c_data_context = "struct my_data_context"@ + + + m2c_data_allocate (currently '$m2c_data_allocate') + -------------------------------------------------------- + This variable determines whether or not the data context (see above) + requires memory to be allocated. The default generated data structure + does not. If you are using a custom data context which needs to + allocate memory, override this value and two additional functions + will be generated: + + ${context}_allocate_data + ${context}_release_data + + Syntax: @eval $@m2c_data_allocate = 1@ + + + m2c_data_init (currently '$m2c_data_init') + -------------------------------------------------------- + This variable determines whether or not the data context (see above) + or any other items you have added to the table context requires + initialization. The default generated data structure does not. If you + are using a custom data context or have added items needing initialization + to the table context, override this value and two additional functions + will be generated: + + ${context}_rowreq_ctx_init + ${context}_rowreq_ctx_cleanup + + Syntax: @eval $m2c_data_init = 1@ + + + m2c_table_access (currently '$m2c_table_access') + ------------------------------------------------------------------ + This variable determines which data interface will be use to generate + code for looking up data for a given index. The default is the + 'container-cached' access code, which caches the data in a netsnmp- + container (usually a sorted array). + + Available options can be determined by checking for mib2c configuration + files that begin with 'mfd-access-*'. + + Syntax: @eval $@m2c_table_access = '$m2c_table_access'@ + + + m2c_include_examples (currently '$m2c_include_examples') + ------------------------------------------------------------------ + This variable determines whether or not to generate example code. The + default is to generate example code. + + Syntax: @eval $@m2c_include_examples = 0@ + + + m2c_data_transient (currently '$m2c_data_transient') + ------------------------------------------------------------------ + This variable determines how the generated example code deals with the + data during data lookup. See the table readme file for details on how + the current table access method interprets this value. In general, + a value of 0 indicates persistent data, 1 indicates semi-transient and + 2 indicates transient data. + + Syntax: @eval $@m2c_data_transient = 0@ + + + Index(es) for the ${context} table + ------------------------------------------------------------ + The index(es) for the ${context} table are: + +@foreach $node index@ +@ include m2c_setup_node.m2i@ + $node: + Syntax: $node.syntax + DataType: $node.perltype + ASN type: $node.type + C-code type: $m2c_decl +@end@ # foreach + + You should know how to set all these values from your data context, + ${context}_data. + + +************************************************************************ +${context} File Overview +------------------------------------------------------------------------ + Several files have been generated to implement the ${context} + table. We'll go through these files, one by one, explaining each and + letting you know which you need to edit. + + +File: ${name}_data_access.[c|h] +------------------------------------------------------------------------ + The ${name}_data_access file contains the interface to your data in + its raw format. These functions are used to build the row cache or + locate the row (depending on the table access method). + + Set MIB context + ----------------- + TODO : Set MIB index values + FUNC : ${context}_indexes_set + WHERE: ${context}_data_access.c + + This is a convenience function for setting the index context from + the native C data. Where necessary, value mapping should be done. + +@if $mfd_readme_verbose == 1@ + This function should update the table index values (found in + tbl_idx) for the given raw data. + +@end@ + +@ eval $m2c_processing_type = 'r'@ +@ include mfd-access-${m2c_table_access}-defines.m2i@ + + +@if $m2c_create_fewer_files != 1@ +File: ${name}_enums.h +@else@ +File: ${name}_constants.h +@end@ +------------------------------------------------------------------------ + This file contains macros for mapping enumeration values when the + enumerated values defined by the MIB do not match the values used + internally. + + Review this file to see if any values need to be updated. + + +@if $m2c_create_fewer_files != 1@ +File: ${name}_data_get.c +@else@ +File: ${name}.c; GET support +@end@ +------------------------------------------------------------------------ +@ if ("$m2c_data_allocate" eq "yes") && ("$m2c_data_context" ne "generated")@ + Allocate data context + --------------------- + TODO : allocate memory for a data context + FUNC : ${context}_allocate_data + + This function will be called to allocate memory for a data context + when a new row request context is being created, or to create an + undo context while processing a set request. + + Release data context + ------- + TODO : release memory allocated for a data context + FUNC : ${context}_release_data + + This function will be called to release any resources held by a + data or undo context. It will be called when a row request context + is released, or during cleanup after a set request. + + +@ end@ +@ foreach $node index@ +@ include m2c_setup_node.m2i@ +@ if ($m2c_skip_mapping != 1)@ + Map native data to MIB format + ----------------------------- + TODO : convert data from its native format to the format required by the MIB + FUNC : ${node}_map + + This function should map between the native format of the node data to + the format or values required by the MIB. For example, a C boolean value + for a MIB node with the TruthValue syntax needs to map the value C + false(0) to TruthValue false(2). + +@ end@ #// skip mapping +@ end@ // foreach index +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if ($m2c_skip_mapping != 1)@ + Map native data to MIB format + ----------------------------- + TODO : convert data from its native format to the format required by the MIB + FUNC : ${node}_map + + This function should map between the native format of the node data to + the format or values required by the MIB. For example, a C boolean value + for a MIB node with the TruthValue syntax needs to map the value C + false(0) to TruthValue false(2). + +@ end@ #// skip mapping + Get data for column + ------------------- + TODO : retrieve column data from raw data + FUNC : ${node}_get + +@ ifconf syntax-$node.syntax-readme.m2i@ +@ include syntax-$node.syntax-readme.m2i@ +@ elsif ($node.enums == 1) && ("$node.perltype" eq "BITS")@ + Since this column has enumerated BITS, you should update or replace the + IS_SET_* macros to properly determine whether or not a particular bit + should be set. + +@ end@ +@ end@ + + +@if $m2c_create_fewer_files != 1@ +File: ${name}_data_set.c +@else@ +File: ${name}.c; SET support +@end@ +------------------------------------------------------------------------ + +@if $m2c_table_settable == 0@ + This table does not support set requests. +@else@ + This code was generated based on the following assumptions or settings: + +@ if $m2c_table_dependencies == 1@ + 1) None of the values for this table have DEPENDENCIES on other objects. +@ else@ + 1) Some of the values for this table have DEPENDENCIES on other objects. +@ end@ + +@ if $mfd_readme_verbose != 0@ + DEPENDENCIES on other objects complicates SET request processing. When + one or more columns in a table depend on another object (in the same + table, or in another table), a DEPENDENCY exists. For example, if you + have a table that determine a color with three columns (red, green and + blue) that define the percentage of each primary color, the total for + the three columns must equal 100 percent. So, in addition to checking + that each colums has a valid value between 0 and 100, the total of + all three columns must equal 100. + + Set $@m2c_table_dependencies = 0 in ${m2c_defaults_dir}table-${table}.m2d + and regenerate code if this assumption is incorrect. +@ end@ + +@if $m2c_table_row_creation == 1@ + 2) This table supports ROW CREATION. +@else@ + 2) This table does not support ROW CREATION. +@end@ + +@if $mfd_readme_verbose != 0@ + Supporting ROW CREATION allows new rows to be created via SNMP requests. +@end@ +@if $m2c_table_row_creation == 1@ + + To support row creation, the index component of an incoming set request must + be validated. A funciton is generated for each individual index component, + and another for validating all the index components together. + +@ foreach $node externalindex@ +@ include m2c_setup_node.m2i@ + Validate external index + ----------------------- + TODO : validate the specified external index component + FUNC : ${context}_${node}_check_index + +@ end@ # foreach externalindex + +@ foreach $node internalindex@ +@ include m2c_setup_node.m2i@ + Validate index component + ------------------------ + TODO : validate the specified index component + FUNC : ${node}_check_index + +@ end@ + + Validate index + -------------- + TODO : check that all index components are valid + FUNC : ${context}_validate_index +@end@ + + +@ if $m2c_table_dependencies == 1@ + Check dependencies + ------------------ + TODO : check that all dependencies have been satisfied + FUNC : ${context}_check_dependencies + + This function will be called after all the individual columns have been + set to their new values. Check for any dependencies between rows or + tables in this function. + +@ end@ + + Undo setup + ---------- + TODO : save data for undo + FUNC : ${context}_undo_setup + + This function will be called before the individual undo_setup functions are + called. This is where you should save any undo information which is not + directly related to a particular column. This function will only be called + once per row. After this function is called, any column which is being + set will have its individual node undo_setup function called. + + + +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $node.settable == 0@ +@ next@ # skip to next column +@ end@ + Check value for column + ---------------------- + TODO : perform additional validations on values for a set request + FUNC : ${node}_check_value + + The generated code will automatically validate incoming requests against + all the requirements specified by the syntax of the MIB. However, it is + often the case that additional requirements are specified in the + description of a MIB object. Those type of validations should be checked + in this function. + + + Undo setup for column + --------------------- + TODO : save the value for column + FUNC : ${node}_undo_setup + + After the table level undo setup function has been called, the individual + node undo setup functions will be called for columns which are being set. + + + Set value for column + -------------------- + TODO : set the value for column + FUNC : ${node}_set + + After all the validations have been passed, this function will be called to + set the new value. + + + Undo value for column + --------------------- + TODO : undo set for column + FUNC : ${node}_undo + + If an error occurs after a column has been set, this function will be called + to undo the set and restore the previous state. + +@ end@ # nonindex + + + Commit changes + -------------- + TODO : commit changes + FUNC : ${context}_commit + + After all values have been set, the commit function will be called. + + +@ if $m2c_irreversible_commit == 1@ + Commit irreversible changes + --------------------------- + FUNC: ${context}_irreversible_commit + + This special mode is reserved for committing changes which can not be undone. + (e.g. launching a rocket). It is called after all normal commits have + succeeded. +@ end@ + +@end@ # settable + + +************************************************************************ +${context} Reference +------------------------------------------------------------------------ + +Function flow +---------------------------------------------------- +To give you the general idea of how the functions flow works, this +example flow is from a complete table implementation. + +NOTE: Depending on your configuration, some of the functions used in the + examples below may not have been generated for the + ${context} table. + + Conversely, the examples below may not include some functions that + were generated for the ${context} table. + +To watch the flow of the ${context} table, use the +following debug tokens: + + snmp_agent + helper:table:req + ${context} + verbose:${context} + internal:${context} + +e.g. + snmpd -f -Le -D${context},verbose:${context},internal:${context} + + +@if $m2c_create_fewer_files == 1@ +@ eval $tmp_mfd_rm_set = "xxx.c"@ +@ eval $tmp_mfd_rm_get = "xxx.c"@ +@else@ +@ eval $tmp_mfd_rm_set = "xxx_data_set.c"@ +@ eval $tmp_mfd_rm_get = "xxx_data_get.c"@ +@end@ +Initialization +-------------------------------- +init_xxxTable: called xxx.c + initialize_table_xxxTable xxx.c + _xxxTable_initialize_interface xxx_interface.c + xxxTable_init_data xxx_data_access.c + _xxxTable_container_init xxx_interface.c + xxxTable_container_init xxx_data_access.c + + +GET Request +-------------------------------- +_cache_load xxx_interface.c + xxxTable_cache_load xxx_data_access.c + xxxTable_allocate_rowreq_ctx xxx_interface.c + xxxTable_allocate_data $tmp_mfd_rm_get + xxxTable_rowreq_ctx_init $tmp_mfd_rm_get + xxxTable_indexes_set $tmp_mfd_rm_get + xxxTable_indexes_set_tbl_idx $tmp_mfd_rm_get + +xxxTable_pre_request + +_mfd_xxxTable_object_lookup xxx_interface.c + xxxTable_row_prep xxx_data_access.c + +_mfd_xxxTable_get_values xxx_interface.c + _mfd_xxxTable_get_column xxx_interface.c + yyy_get $tmp_mfd_rm_get + +xxxTable_post_request + + +GETNEXT Request +-------------------------------- +_cache_load ... +xxxTable_pre_request ... +_mfd_xxxTable_object_lookup ... +_mfd_xxxTable_get_values ... +xxxTable_post_request ... + + +SET Request: success +-------------------------------- +_cache_load ... +xxxTable_pre_request +_mfd_xxxTable_object_lookup ... + +_mfd_xxxTable_check_objects xxx_interface.c + _xxxTable_check_column xxx_interface.c + yyy_check_value $tmp_mfd_rm_set + +_mfd_xxxTable_undo_setup xxx_interface.c + xxxTable_allocate_data ... + xxxTable_undo_setup xxx_interface.c + _xxxTable_undo_setup_column xxx_interface.c + yyy_undo_setup $tmp_mfd_rm_set + +_mfd_xxxTable_set_values xxx_interface.c + _xxxTable_set_column xxx_interface.c + yyy_set $tmp_mfd_rm_set + +_mfd_xxxTable_check_dependencies xxx_interface.c + xxxTable_check_dependencies $tmp_mfd_rm_set + +_mfd_xxxTable_commit xxx_interface.c + xxxTable_commit $tmp_mfd_rm_set + +_mfd_xxxTable_undo_cleanup xxx_interface.c + xxxTable_undo_cleanup $tmp_mfd_rm_set + xxxTable_release_data ... + +xxxTable_post_request ... + + +SET Request: row creation +-------------------------------- +_cache_load ... +xxxTable_pre_request + +_mfd_xxxTable_object_lookup ... + xxxTable_index_from_oid xxx_interface.c + xxxTable_allocate_rowreq_ctx ... + ... + _xxxTable_check_indexes xxx_interface.c + yyy_check_index $tmp_mfd_rm_set + xxxTable_validate_index $tmp_mfd_rm_set + +_mfd_xxxTable_check_objects ... + _xxxTable_check_column ... + yyy_check_value ... + _xxxTable_check_column ... + yyy_check_value ... + +_mfd_xxxTable_undo_setup ... +_mfd_xxxTable_set_values ... +_mfd_xxxTable_check_dependencies ... +_mfd_xxxTable_commit ... +_mfd_xxxTable_undo_cleanup ... +xxxTable_post_request ... + + +SET Resuest: value error +-------------------------------- +_cache_load ... +xxxTable_pre_request ... +_mfd_xxxTable_object_lookup ... + +_mfd_xxxTable_check_objects ... + _xxxTable_check_column ... + yyy_check_value ... + ERROR:"yyy value not supported" + +xxxTable_post_request ... + + +SET Request: commit failure +-------------------------------- +_cache_load ... +xxxTable_pre_request ... +_mfd_xxxTable_object_lookup ... +_mfd_xxxTable_check_objects ... +_mfd_xxxTable_undo_setup ... +_mfd_xxxTable_set_values ... +_mfd_xxxTable_check_dependencies ... + +_mfd_xxxTable_commit ... + xxxTable_commit ... + ERROR: bad rc -1 + +_mfd_xxxTable_undo_commit xxx_interface.c + xxxTable_undo_commit $tmp_mfd_rm_set + +_mfd_xxxTable_undo_values xxx_interface.c + _xxxTable_undo_column xxx_interface.c + yyy_undo $tmp_mfd_rm_set + +_mfd_xxxTable_undo_cleanup ... +xxxTable_post_request ... + + +Row release (user initiated) +-------------------------------- +xxxTable_release_rowreq_ctx xxx_interface.c + xxxTable_rowreq_ctx_cleanup $tmp_mfd_rm_get + xxxTable_release_data $tmp_mfd_rm_get + + + +Table / column details +---------------------------------------------------- +@ include details-table.m2i@ + +@ foreach $node column@ +@ include m2c_setup_node.m2i@ +@ include details-node.m2i@ +@ end@ + +@end@ # foreach table + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/mfd-top.m2c b/local/mib2c-conf.d/mfd-top.m2c new file mode 100644 index 0000000..db0f398 --- /dev/null +++ b/local/mib2c-conf.d/mfd-top.m2c @@ -0,0 +1,605 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## $Id$ +######################################################################## +######################################################################## +## +## mfd function params +## +@ifconf ${name}.m2d@ +@ include ${name}.m2d@ +@end@ +## +## set up defaults +## +@foreach $table table@ +@ include default-mfd-top.m2c@ # get defaults +@ eval $context = $table@ +## +## set up defaults +## +@ print Defaults for $table...@ +@ eval $m2c_context_reg = "$mfd_default_context_reg"@ +@ eval $m2c_data_allocate = $mfd_default_data_allocate@ +@ eval $m2c_data_cache = $mfd_default_data_cache@ +@ eval $m2c_data_context = "$mfd_default_data_context"@ +@ eval $m2c_data_init = $mfd_default_data_init@ +@ eval $m2c_data_transient = $mfd_default_data_transient@ +@ eval $m2c_include_examples = $mfd_default_include_examples@ +@ eval $m2c_irreversible_commit = 0@ +@ eval $m2c_table_access = "$mfd_default_table_access"@ +@ eval $m2c_table_dependencies = table_is_writable($context)@ +@ eval $m2c_table_persistent = 0@ +@ eval $m2c_table_row_creation = table_has_create($context)@ +@ eval $m2c_table_settable = table_is_writable($context)@ +@ eval $m2c_table_skip_mapping = -1@ # -1 = no default; based on type +@ eval $m2c_table_sparse = 0@ +@ eval $mfd_generate_makefile = $mfd_default_generate_makefile@ +@ eval $mfd_generate_subagent = $mfd_default_generate_subagent@ +## +## allow for user override, or save defaults +## +@ ifconf default-table-${context}.m2d@ +@ print Warning: using defaults in current directory. Consider moving@ +@ print them to $m2c_defaults_dir.@ +@ eval $m2c_defaults_dir = "default-"@ +@ end@ +@ ifconf ${m2c_defaults_dir}table-${context}.m2d@ +@ if $mfd_interactive_setup == 1@ +@ print There are existing defaults for $context (${m2c_defaults_dir}table-${context}.m2d).@ +@ prompt $ans r)econfigure or u)se existing [default=u] : @ +@ if "x$ans" eq "xr"@ +@ eval $mfd_interactive_setup = -1@ # already asked to overwrite +@ run -again mfd-interactive-setup.m2c@ +@ eval $mfd_interactive_setup = 1@ +@ else@ +## ## read in old, write them back (this should add any new vars +@ include ${m2c_defaults_dir}table-${context}.m2d@ +@ include m2c_table_save_defaults.m2i@ +@ end@ +@ end@ +@ else@ # no existing defaults +@ ifdir defaults@ +## NOP +@ else@ +@ perleval my $rc = mkdir(defaults,0775); return $rc != 1 @ +@ end@ +@ run mfd-interactive-setup.m2c@ +@ end@ # no conf file +@end@ # foreach table +@if $m2c_gen_table_defaults == 1@ +@ exit@ +@end@ +@print Starting MFD code generation...@ +######################################################################## +@eval $m2c_processing_type = 'h'@ +@open ${name}.h@ +@eval $hack = "Id"@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + */ +@include generic-header-top.m2i@ + +/** @addtogroup misc misc: Miscellaneous routines + * + * @{ + */ +@if $m2c_mark_boundary == 1@ +/** START header generated by $RCSfile$ $Revision$ */ +@end@ +#include <net-snmp/library/asn1.h> + +/* other required module components */ + /* *INDENT-OFF* */ +config_add_mib($name.module) +config_require($name.module/${name}/${name}_interface) +config_require($name.module/${name}/${name}_data_access) +@if $m2c_create_fewer_files != 1@ +config_require($name.module/${name}/${name}_data_get) +config_require($name.module/${name}/${name}_data_set) + /* *INDENT-ON* */ + +/* OID and column number definitions for $context */ +#include "${name}_oids.h" + +/* enum definions */ +#include "${name}_enums.h" +@else@ + /* *INDENT-ON* */ + +/* OID, column number and enum definions for $context */ +#include "${name}_constants.h" +@end@ // m2c_create_fewer_files + +/* ********************************************************************* + * function declarations + */ +void init_$name(void); +void shutdown_$context(void); + +/* ********************************************************************* + * Table declarations + */ +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ include details-table.m2i@ +/* ********************************************************************* + * When you register your mib, you get to provide a generic + * pointer that will be passed back to you for most of the + * functions calls. + * + * TODO:100:r: Review all context structures + */ + /* + * TODO:101:o: |-> Review $context registration context. + */ +@ if "x$m2c_context_reg" eq "x"@ +@ eval $m2c_context_reg = "netsnmp_data_list"@ +@ end@ +typedef $m2c_context_reg ${context}_registration; + +@ include generic-data-context.m2i@ + +@ if $m2c_table_settable@ +/* ********************************************************************* + * TODO:115:o: |-> Review $context undo context. + * We're just going to use the same data structure for our + * undo_context. If you want to do something more efficent, + * define your typedef here. + */ +typedef ${context}_data ${context}_undo_data; + +@ end@ +@ include generic-table-indexes.m2i@ + +/* ********************************************************************* + * TODO:130:o: |-> Review $context Row request (rowreq) context. + * When your functions are called, you will be passed a + * ${context}_rowreq_ctx pointer. + */ +typedef struct ${context}_rowreq_ctx_s { + + /** this must be first for container compare to work */ + netsnmp_index oid_idx; +## /* xxx-rks: shrink index oid_tmp? */ + oid oid_tmp[MAX_${context}_IDX_LEN]; + + ${context}_mib_index tbl_idx; + +@ if $m2c_data_allocate == 1@ +@ eval $mfd_tmp = "*"@ +@ else@ +@ eval $mfd_tmp = " "@ +@ end@ + ${context}_data $mfd_tmp data; +@ if $m2c_table_sparse == 1@ + unsigned int column_exists_flags; /* flags for existence */ +@ end@ +@ if $m2c_table_settable@ +@ if $m2c_undo_embed == 1@ +@ eval $mfd_tmp = " "@ +@ else@ +@ eval $mfd_tmp = "*"@ +@ end@ # embed + ${context}_undo_data $mfd_tmp undo; + unsigned int column_set_flags; /* flags for set columns */ + +@ end@ # settable + + /* + * flags per row. Currently, the first (lower) 8 bits are reserved + * for the user. See mfd.h for other flags. + */ + u_int rowreq_flags; +@ if $m2c_table_refcounts == 1@ + u_int ref_count; +@ end@ + + /* + * TODO:131:o: | |-> Add useful data to $context rowreq context. + */ + + /* + * storage for future expansion + */ + netsnmp_data_list *${context}_data_list; + +} ${context}_rowreq_ctx; + +typedef struct ${context}_ref_rowreq_ctx_s { + ${context}_rowreq_ctx *rowreq_ctx; +} ${context}_ref_rowreq_ctx; + +/* ********************************************************************* + * function prototypes + */ +## { + int ${context}_pre_request(${context}_registration * user_context); + int ${context}_post_request(${context}_registration * user_context, + int rc); + +@ if $m2c_data_init == 1@ + int ${context}_rowreq_ctx_init(${context}_rowreq_ctx *rowreq_ctx, + void *user_init_ctx); + void ${context}_rowreq_ctx_cleanup(${context}_rowreq_ctx *rowreq_ctx); + +@ end@ +@ if "$m2c_data_context" ne "generated"@ +@ if ($m2c_data_allocate == 1) || ($m2c_undo_embed == 1)@ + ${context}_data * ${context}_allocate_data(void); + void ${context}_release_data(${context}_data *data); + +@ end@ +@ end@ +@ if $m2c_table_settable@ +@ if $m2c_table_dependencies == 1@ + int ${context}_check_dependencies(${context}_rowreq_ctx * rowreq_ctx); +@ end@ + int ${context}_commit(${context}_rowreq_ctx * rowreq_ctx); +@ if $m2c_irreversible_commit == 1@ + int ${context}_irreversible_commit(${context}_rowreq_ctx * rowreq_ctx); +@ end@ +@ end@ # writable + + ${context}_rowreq_ctx * + ${context}_row_find_by_mib_index(${context}_mib_index *mib_idx); + +@ if $m2c_table_refcounts == 1@ +int ${context}_row_ref_increment(${context}_rowreq_ctx *rowreq_ctx); +int ${context}_row_ref_decrement(${context}_rowreq_ctx *rowreq_ctx); + +@ end@ +extern const oid ${context}_oid[]; +extern const int ${context}_oid_size; + +@end@ # for each + +#include "${name}_interface.h" +#include "${name}_data_access.h" +@if $m2c_create_fewer_files != 1@ +#include "${name}_data_get.h" +#include "${name}_data_set.h" +@else@ +@ eval $mfd_processing_types = "h"@ +@ include mfd-data-get.m2c@ +@ include mfd-data-set.m2c@ +@end@ // m2c_create_fewer_files + +/* + * DUMMY markers, ignore + * + * TODO:099:x: ************************************************************* + * TODO:199:x: ************************************************************* + * TODO:299:x: ************************************************************* + * TODO:399:x: ************************************************************* + * TODO:499:x: ************************************************************* + */ +@if $m2c_mark_boundary == 1@ +/** END header generated by $RCSfile$ $Revision$ */ +@end@ +@include generic-header-bottom.m2i@ +/** @} */ +###################################################################### +## Do the .c file +###################################################################### +@eval $m2c_processing_type = 'c'@ +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + * + * $$hack:$ + */ +/** \page MFD helper for ${name} + * + * \section intro Introduction + * Introductory text. + * + */ +@include generic-source-includes.m2i@ +#include <net-snmp/agent/mib_modules.h> + +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +#include "${name}_interface.h" + +@foreach $table table@ +@ include m2c_setup_table.m2i@ +const oid ${context}_oid[] = { $context.uc_OID }; +const int ${context}_oid_size = OID_LENGTH(${context}_oid); + +@ if "x$m2c_context_reg" ne "x"@ + ${context}_registration ${context}_user_context; +@ end@ + +void initialize_table_$context(void); +void shutdown_table_$context(void); + +@end@ + +/** + * Initializes the $name module + */ +void +init_$name(void) +{ + DEBUGMSGTL(("verbose:$name:init_$name","called\n")); + + /* + * TODO:300:o: Perform $name one-time module initialization. + */ + + /* + * here we initialize all the tables we're planning on supporting + */ + @foreach $table table@ + if (should_init("$context")) + initialize_table_$context(); + + @end@ +} /* init_$name */ + +/** + * Shut-down the $name module (agent is exiting) + */ +void +shutdown_$name(void) +{ + @foreach $table table@ + if (should_init("$context")) + shutdown_table_$context(); + + @end@ +} + +######################################################################## +## +@foreach $table table@ +@ include m2c_setup_table.m2i@ +/** + * Initialize the table $context + * (Define its contents and how it's structured) + */ +void +initialize_table_$context(void) +{ + ${context}_registration * user_context; + u_long flags; + + DEBUGMSGTL(("verbose:$context:initialize_table_$context","called\n")); + + /* + * TODO:301:o: Perform $context one-time table initialization. + */ + + /* + * TODO:302:o: |->Initialize $context user context + * if you'd like to pass in a pointer to some data for this + * table, allocate or set it up here. + */ +@ if "$m2c_context_reg" eq "netsnmp_data_list"@ + /* + * a netsnmp_data_list is a simple way to store void pointers. A simple + * string token is used to add, find or remove pointers. + */ + user_context = netsnmp_create_data_list("$context", NULL, NULL); +@ else@ + user_context = &${context}_user_context; +@ end@ + + /* + * No support for any flags yet, but in the future you would + * set any flags here. + */ + flags = 0; + + /* + * call interface initialization code + */ + _${context}_initialize_interface(user_context, flags); +} /* initialize_table_$context */ + +/** + * Shutdown the table $context + */ +void +shutdown_table_$context(void) +{ + /* + * call interface shutdown code + */ + _${context}_shutdown_interface(&${context}_user_context); +} + +######################################################################## +@ if $m2c_data_init == 1@ +/** + * extra context initialization (eg default values) + * + * @param rowreq_ctx : row request context + * @param user_init_ctx : void pointer for user (parameter to rowreq_ctx_allocate) + * + * @retval MFD_SUCCESS : no errors + * @retval MFD_ERROR : error (context allocate will fail) + */ +int +${context}_rowreq_ctx_init(${context}_rowreq_ctx *rowreq_ctx, + void *user_init_ctx) +{ + DEBUGMSGTL(("verbose:$context:${context}_rowreq_ctx_init","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + /* + * TODO:210:o: |-> Perform extra $context rowreq initialization. (eg DEFVALS) + */ +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $node.hasdefval == 0@ +@ next@ +@ end@ +##------------------------------------------------------ +@ if $node.needlength == 1@ + /* + * strings and oids are hard to handle automagically. + * so all we've got for you is a hint: + * + * memcpy($m2c_data_item$node, $node.defval, + * len($node.defval) * sizeof($m2c_data_itme$node[0]); + */ +@ elsif $node.enums == 1@ +@ if "$node.perltype" ne "BITS"@ +@ eval $m2c_tmp_mt = $node.defval@ +@ foreach $e $v enum@ +@ include m2c_setup_enum.m2i@ +@ if $e eq $node.defval@ +@ eval $m2c_tmp_mt = $m2c_ename@ +@ end@ +@ end@ # for each +@ end@ # ! bits + $m2c_data_item$node = $m2c_tmp_mt; +@ elsif ("$node.decl" eq "long") || ("$node.decl" eq "u_long")@ + $m2c_data_item$node = $node.defval; +@ else@ + /** $m2c_data_item$node = $node.defval; */ +@ end@ + +@ end@ foreach nonindex + + return MFD_SUCCESS; +} /* ${context}_rowreq_ctx_init */ + +/** + * extra context cleanup + * + */ +void ${context}_rowreq_ctx_cleanup(${context}_rowreq_ctx *rowreq_ctx) +{ + DEBUGMSGTL(("verbose:$context:${context}_rowreq_ctx_cleanup","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + /* + * TODO:211:o: |-> Perform extra $context rowreq cleanup. + */ +} /* ${context}_rowreq_ctx_cleanup */ + +@ end@ // data_init +######################################################################## +@if $m2c_table_persistent == 1@ +@ include mfd-persistence.m2i@ +@end@ +######################################################################## +/** + * pre-request callback + * + * + * @retval MFD_SUCCESS : success. + * @retval MFD_ERROR : other error + */ +int +${context}_pre_request(${context}_registration * user_context) +{ + DEBUGMSGTL(("verbose:${context}:${context}_pre_request","called\n")); + + /* + * TODO:510:o: Perform $context pre-request actions. + */ + + return MFD_SUCCESS; +} /* ${context}_pre_request */ + +/** + * post-request callback + * + * Note: + * New rows have been inserted into the container, and + * deleted rows have been removed from the container and + * released. + * + * @param user_context + * @param rc : MFD_SUCCESS if all requests succeeded + * + * @retval MFD_SUCCESS : success. + * @retval MFD_ERROR : other error (ignored) + */ +int +${context}_post_request(${context}_registration * user_context, int rc) +{ + DEBUGMSGTL(("verbose:${context}:${context}_post_request","called\n")); + + /* + * TODO:511:o: Perform $context post-request actions. + */ + +@ if $m2c_table_settable@ + /* + * check to set if any rows were changed. + */ + if (${context}_dirty_get()) { + /* + * check if request was successful. If so, this would be + * a good place to save data to its persistent store. + */ + if (MFD_SUCCESS == rc) { + /* + * save changed rows, if you haven't already + */ +@ if $m2c_table_persistent@ + snmp_store(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, + NETSNMP_DS_LIB_APPTYPE)); +@ end@ + } + + ${context}_dirty_set(0); /* clear table dirty flag */ + } + +@ end@ + return MFD_SUCCESS; +} /* ${context}_post_request */ + +@end@ // table + +######################################################################## +@if $m2c_create_fewer_files == 1@ +@ eval $mfd_processing_types = "c"@ +@ include mfd-data-get.m2c@ +@ include mfd-data-set.m2c@ +@else@ +@ eval $mfd_processing_types = "chi"@ +@ run mfd-data-get.m2c@ +@ run mfd-data-set.m2c@ +@end@ +######################################################################## +/** @{ */ +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ +## +######################################################################## +## +## Do support files +## +######################################################################## +@run generic-table-constants.m2c@ +@run mfd-interface.m2c@ +@run mfd-data-access.m2c@ +## +@run mfd-readme.m2c@ +## +@if $mfd_generate_doxygen == 1@ +@ run mfd-doxygen.m2c@ +@end@ +## +@if $mfd_generate_makefile == 1@ +@ run mfd-makefile.m2m@ +@end@ +## +@if $mfd_generate_subagent == 1@ +@ run subagent.m2c@ +@end@ +## diff --git a/local/mib2c-conf.d/node-get.m2i b/local/mib2c-conf.d/node-get.m2i new file mode 100644 index 0000000..1d7255c --- /dev/null +++ b/local/mib2c-conf.d/node-get.m2i @@ -0,0 +1,107 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@include m2c_setup_node.m2i@ +@eval $m2c_node_realloc = 2@ // malloc +/** + * Extract the current value of the $node data. + * + * Set a value using the data context for the row. + * +@if $m2c_node_get_comments ne ""@ +$m2c_node_get_comments +* +@end@ + * @param rowreq_ctx + * Pointer to the row request context. + * @param $m2c_node_param_ref_name + * Pointer to storage for a $node.decl variable +@if $m2c_node_needlength == 1@ + * @param $m2c_node_param_ref_lname + * Pointer to a size_t. On entry, it will contain the size (in bytes) + * pointed to by $node. + * On exit, this value should contain the data size (in bytes). +@end@ + * + * @retval MFD_SUCCESS : success + * @retval MFD_SKIP : skip this node (no value for now) + * @retval MFD_ERROR : Any other error +@if $m2c_node_needlength == 1@ +* + * @note If you need more than (*$m2c_node_param_ref_lname) bytes of memory, + * allocate it using malloc() and update $m2c_node_param_ref_name. + * <b>DO NOT</b> free the previous pointer. + * The MFD helper will release the memory you allocate. + * + * @remark If you call this function yourself, you are responsible + * for checking if the pointer changed, and freeing any + * previously allocated memory. (Not necessary if you pass + * in a pointer to static memory, obviously.) +@end@ + */ +int +${node}_get( ${context}_rowreq_ctx *rowreq_ctx, $m2c_node_param_ref ) +{ +@ifconf syntax-$node.syntax-get.m2i@ +@ include syntax-$node.syntax-get.m2i@ +@else@ +@ include generic-get-decl.m2i@ + + DEBUGMSGTL(("verbose:${context}:${node}_get","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + +/* + * TODO:231:o: |-> Extract the current value of the $node data. +@if $m2c_node_needlength == 0@ + * copy $m2c_node_lh from $m2c_data_item_base +@else@ + * copy $m2c_node_lh data and $m2c_node_lhs from $m2c_data_item_base +@end@ + */ +@ if ("$m2c_data_context" eq "generated") && ($m2c_node_skip_get != 1)@ +@ include generic-ctx-get.m2i@ +@ else@ +@ if ($m2c_node_skip_get != 1)@ + /* + * TODO:235:M: |-> Remove log message/SKIP once you've set $node data + */ + snmp_log(LOG_ERR,"${context} node $node not implemented: skipping\n"); +@ end@ + return MFD_SKIP; +@ end@ +## ------------------------------------------------------------------ +@ if $node.decl =~ /long/i@ # ASN_INTEGER ASN_COUNTER ASN_GAUGE +@ include generic-get-long.m2i@ +@ elsif $node.decl =~ /char/i@ # ASN_OCTET_STR ASN_OPAQUE +@ include generic-get-char.m2i@ +@ elsif $node.decl =~ /oid/i@ # ASN_OBJECT_ID +@ include generic-get-oid.m2i@ +@ elsif $node.decl =~ /U64/i@ # ASN_COUNTER64 +@ include generic-get-U64.m2i@ +@ else@ +@ print ERROR: unknown node.decl: $node.decl@ +@ exit@ +@ end@ +## ------------------------------------------------------------------ +@ if ($m2c_node_skip_mapping != 1) && ("$m2c_data_context" ne "generated")@ +@ include generic-value-map.m2i@ +@ end@ +@ include generic-get-decl-bot.m2i@ // copy value out +@end@ # no syntax include + + return MFD_SUCCESS; +} /* ${node}_get */ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/node-set.m2i b/local/mib2c-conf.d/node-set.m2i new file mode 100644 index 0000000..2041b59 --- /dev/null +++ b/local/mib2c-conf.d/node-set.m2i @@ -0,0 +1,236 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +##---------------------------------------------------------------------- +/** + * Check that the proposed new value is potentially valid. + * + * @param rowreq_ctx + * Pointer to the row request context. + * @param $m2c_node_param_val_name + * A $node.decl containing the new value. +@ if $m2c_node_needlength == 1@ + * @param $m2c_node_param_val_lname + * The size (in bytes) of the data pointed to by $m2c_node_param_val_name +@ end@ + * + * @retval MFD_SUCCESS : incoming value is legal + * @retval MFD_NOT_VALID_NOW : incoming value is not valid now + * @retval MFD_NOT_VALID_EVER : incoming value is never valid + * + * This is the place to check for requirements that are not + * expressed in the mib syntax (for example, a requirement that + * is detailed in the description for an object). + * +@if ("$m2c_data_context" ne "generated") && ($m2c_node_needlength == 1)@ + * Since you aren't using a generated data context, you also need to + * check the length, to make sure you don't overflow your storage space. + * +@end@ + * You should check that the requested change between the undo value and the + * new value is legal (ie, the transistion from one value to another + * is legal). + * + *@note + * This check is only to determine if the new value + * is \b potentially valid. This is the first check of many, and + * is one of the simplest ones. + * + *@note + * this is not the place to do any checks for values + * which depend on some other value in the mib. Those + * types of checks should be done in the + * ${context}_check_dependencies() function. + * + * The following checks have already been done for you: + * The syntax is $node.type +@if ("$m2c_data_context" eq "generated") && ($m2c_node_needlength == 1)@ + * The length is < sizeof($m2c_data_item$node). +@end@ +@if $node.enums == 1@ + * The value is one of $m2c_evals +@elsif $node.ranges == 1@ +@ if ("$node.decl" eq "long") || ("$node.decl" eq "u_long")@ +@ eval $m2c_tmp_ns = "value"@ +@ else@ +@ eval $m2c_tmp_ns = "length"@ +@ end@ + * The $m2c_tmp_ns is in (one of) the range set(s): $m2c_evals +@end@ + * + * If there a no other checks you need to do, simply return MFD_SUCCESS. + * +@ if $mfd_code_verbose == 1@ +@ if ("$node.decl" eq "long") || ("$node.decl" eq "u_long")@ + * For example, an object with the syntax INTEGER(0..500) will + * have already been checked for a value between 0 and 500. But + * if the description also specifies that the value must be an + * even number, you would enforce that requirement here. If and odd + * numer is set, return MFD_NOT_VALID_EVER. If the description also + * specified that changed must be made in single steps of 2, then a set + * to change the value 10 to an even value other than 8 or 12 should + * return MFD_NOT_VALID_NOW. +@ else@ + * For example, and object with the syntax DisplayString(0..40) + * will have already been checked for a length between 0 and 40. + * But if the description also specified that the value must + * be all uppercase letters, you would enforce that requirement here + * by returning MFD_NOT_VALID_EVER for a set containing lowercase + * letters. If the description also specified that the value can not + * change by more than one letter at a time, an attempt to change + * "ABBY" to "ANNIE" should return MFD_NOT_VALID_NOW. +@ end@ + * +@ end@ + */ +int +${node}_check_value( ${context}_rowreq_ctx *rowreq_ctx, $m2c_node_param_val) +{ + DEBUGMSGTL(("verbose:${context}:${node}_check_value","called\n")); + + /** should never get a NULL pointer */ + netsnmp_assert(NULL != rowreq_ctx); +@if $m2c_node_needlength == 1@ + netsnmp_assert(NULL != $m2c_node_param_val_name); +@end@ + + /* + * TODO:441:o: |-> Check for valid $node value. + */ + + return MFD_SUCCESS; /* $node value not illegal */ +} /* ${node}_check_value */ + +##---------------------------------------------------------------------- +/** + * Save old value information + * + * @param rowreq_ctx + * Pointer to the table context (${context}_rowreq_ctx) + * + * @retval MFD_SUCCESS : success + * @retval MFD_ERROR : error. set will fail. + * + * This function will be called after the table level undo setup function + * ${context}_undo_setup has been called. + * + *@note + * this function will only be called if a new value is set for this column. + * + * If there is any setup specific to a particular column (e.g. allocating + * memory for a string), you should do that setup in this function, so it + * won't be done unless it is necessary. + */ +int +${node}_undo_setup( ${context}_rowreq_ctx *rowreq_ctx) +{ + DEBUGMSGTL(("verbose:${context}:${node}_undo_setup","called\n")); + +@ifconf syntax-$node.syntax-undo-setup.m2i@ +@ include syntax-$node.syntax-undo-setup.m2i@ +@else@ + /** should never get a NULL pointer */ + netsnmp_assert(NULL != rowreq_ctx); + + /* + * TODO:455:o: |-> Setup $node undo. + */ +@ eval $m2c_ctx_lh = "${m2c_undo_item}${node}"@ +@ eval $m2c_ctx_lhs = "${m2c_undo_item}${node}_len"@ +@ eval $m2c_ctx_rh = "${m2c_data_item}${node}"@ +@ eval $m2c_ctx_rhs = "${m2c_data_item}${node}_len"@ +@ include generic-ctx-copy.m2i@ +@end@ + + return MFD_SUCCESS; +} /* ${node}_undo_setup */ + +##---------------------------------------------------------------------- +/** + * Set the new value. + * +@if $m2c_node_set_comments ne ""@ +$m2c_node_set_comments +* +@end@ + * @param rowreq_ctx + * Pointer to the users context. You should know how to + * manipulate the value from this object. + * @param $m2c_node_param_val_name + * A $node.decl containing the new value. +@ if $m2c_node_needlength == 1@ + * @param $m2c_node_param_val_lname + * The size (in bytes) of the data pointed to by $m2c_node_param_val_name +@ end@ + */ +int +${node}_set( ${context}_rowreq_ctx *rowreq_ctx, $m2c_node_param_val ) +{ +@ifconf syntax-$node.syntax-set.m2i@ +@ include syntax-$node.syntax-set.m2i@ +@else@ + + DEBUGMSGTL(("verbose:${context}:${node}_set","called\n")); + + /** should never get a NULL pointer */ + netsnmp_assert(NULL != rowreq_ctx); +@if $m2c_node_needlength == 1@ + netsnmp_assert(NULL != $m2c_node_param_val_name); +@end@ + +@ if $m2c_node_skip_mapping != 1@ +@ include generic-value-map-reverse.m2i@ +@ else@ +@ include generic-ctx-set.m2i@ +@ end@ +@end@ # no syntax include + return MFD_SUCCESS; +} /* ${node}_set */ + +##---------------------------------------------------------------------- +/** + * undo the previous set. + * +@if $m2c_node_undo_comments ne ""@ +$m2c_node_undo_comments +* +@end@ + * @param rowreq_ctx + * Pointer to the users context. + */ +int +${node}_undo( ${context}_rowreq_ctx *rowreq_ctx) +{ +@ifconf syntax-$node.syntax-undo.m2i@ +@ include syntax-$node.syntax-undo.m2i@ +@else@ + + DEBUGMSGTL(("verbose:${context}:${node}_undo","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + /* + * TODO:456:o: |-> Clean up $node undo. + */ +@ eval $m2c_ctx_rh = "${m2c_undo_item}${node}"@ +@ eval $m2c_ctx_rhs = "${m2c_undo_item}${node}_len"@ +@ eval $m2c_ctx_lh = "${m2c_data_item}${node}"@ +@ eval $m2c_ctx_lhs = "${m2c_data_item}${node}_len"@ +@ include generic-ctx-copy.m2i@ +@end@ # no syntax include + + return MFD_SUCCESS; +} /* ${node}_undo */ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/node-storage.m2i b/local/mib2c-conf.d/node-storage.m2i new file mode 100644 index 0000000..7a941f0 --- /dev/null +++ b/local/mib2c-conf.d/node-storage.m2i @@ -0,0 +1,21 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@if $m2c_node_needlength == 0@ + $m2c_decl $node; +@else@ + $m2c_decl $node[$m2c_node_maxlen]; + size_t ${node}_len; +@end@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/node-validate.m2i b/local/mib2c-conf.d/node-validate.m2i new file mode 100644 index 0000000..a63f1d7 --- /dev/null +++ b/local/mib2c-conf.d/node-validate.m2i @@ -0,0 +1,71 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +## +## assumes an integer rc is available and will be tested by caller +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +##---------------------------------------------------------------------- +## setup +##---------------------------------------------------------------------- +@if $node.enums == 1@ +##------------------------------------------------------ +@ if "$node.perltype" ne "BITS"@ + /* check that the value is one of defined enums */ + if( (SNMPERR_SUCCESS == rc) +@ foreach $e $v enum@ +@ include m2c_setup_enum.m2i@ + && ( $m2c_nv_val != $m2c_ename ) +@ end@ # for each + ) { + rc = SNMP_ERR_WRONGVALUE; + } +##------------------------------------------------------ +@ else@ # BITS +## { + if($m2c_nv_len > 4) { + snmp_log(LOG_ERR,"I can not handle BITS > 4 bytes\n"); + rc = SNMP_ERR_GENERR; + } + else if (SNMPERR_SUCCESS == rc){ + u_long bits = 0; + /* check that value is within enum mask */ + memcpy( &bits, $m2c_nv_str, $m2c_nv_len); + if( (bits | $m2c_enum_mask) != $m2c_enum_mask) + rc = SNMP_ERR_WRONGVALUE; + } +@ end@ +##---------------------------------------------------------------------- +## check RANGES +##---------------------------------------------------------------------- +@elsif $node.ranges == 1@ +@ if ("$node.decl" eq "long") || ("$node.decl" eq "u_long")@ +@ eval $m2c_nv_rc = "SNMP_ERR_WRONGVALUE"@ +@ eval $m2c_nv_tmp = "$m2c_nv_val"@ +@ else@ +@ eval $m2c_nv_rc = "SNMP_ERR_WRONGLENGTH"@ +@ eval $m2c_nv_tmp = "$m2c_nv_len"@ +@ end@ + /* check defined range(s). */ + if( (SNMPERR_SUCCESS == rc) +@ foreach $a $b range $node@ +@ if $a == $b@ + && ($m2c_nv_tmp != $a) +@ else@ + && (($m2c_nv_tmp < $a) || ($m2c_nv_tmp > $b)) +@ end@ +@ end@ + ) { + rc = $m2c_nv_rc; + } +@end@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/node-varbind-validate.m2i b/local/mib2c-conf.d/node-varbind-validate.m2i new file mode 100644 index 0000000..1b3fb37 --- /dev/null +++ b/local/mib2c-conf.d/node-varbind-validate.m2i @@ -0,0 +1,54 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +## +## Tests a netsnmp_variable_list pointer (var) against known +## contstraints. If none are found, calls the user supplied funtion +## ${node}_check_value. +## +## Sets the variable rc to a SNMP_ERR. +## +## Requirements +## ------------ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +##---------------------------------------------------------------------- +## setup +##---------------------------------------------------------------------- +@if $m2c_paranoid == 1@ +netsnmp_assert(rc == SNMP_ERR_NOERROR); /* paranoia */ +@end@ +##---------------------------------------------------------------------- +## syntax specific +##---------------------------------------------------------------------- +@ifconf syntax-$node.syntax-varbind-validate.m2i@ +@ include syntax-$node.syntax-varbind-validate.m2i@ +@else@ +##---------------------------------------------------------------------- +## Check type +##---------------------------------------------------------------------- +## if not generated code, length checks are up to user +@ if "$m2c_data_context" ne "generated"@ + rc = netsnmp_check_vb_type( var, $node.type ); +@ elsif ($m2c_node_needlength == 1) || ("$node.perltype" eq "BITS")@ + rc = netsnmp_check_vb_type_and_max_size( var, $node.type, + sizeof( $m2c_nvv_item$node ) ); +@ else@ + rc = netsnmp_check_vb_type_and_size( var, $node.type, + sizeof( $m2c_nvv_item$node ) ); +@ end@ +@ eval $m2c_nv_val = "*var->val.integer"@ +@ eval $m2c_nv_len = "var->val_len"@ +@ eval $m2c_nv_str = "var->val.string"@ +@ include node-validate.m2i@ +@end@ # not syntax specific +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/parent-dependencies.m2i b/local/mib2c-conf.d/parent-dependencies.m2i new file mode 100644 index 0000000..104f150 --- /dev/null +++ b/local/mib2c-conf.d/parent-dependencies.m2i @@ -0,0 +1,63 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ # ; +######################################################################## +## +/** + * check dependencies + * + * This is useful for for tables which have dependencies between columns + * (or rows, or tables). For example, two columns allocating a percentage + * of something add up 100%. + * + * Should you need different behavior depending on which columns were + * set, rowreq_ctx->column_set_flags will indicate which writeable columns were + * set. The definitions for the COLUMN_*_FLAG bits can be found in +@if $m2c_create_fewer_files != 1@ + * ${context}_oids.h. +@else@ + * ${context}.h. +@end@ + * A new row will have the MFD_ROW_CREATED bit set in rowreq_flags. + * + * @retval MFD_SUCCESS all the changes to the row are legal + * @retval MFD_ERROR one or more changes are not legal + * + * (see README-table-${table} if you don't have dependencies) + */ +int +${context}_check_dependencies(${context}_rowreq_ctx *rowreq_ctx) +{ + int rc = MFD_SUCCESS; + + DEBUGMSGTL(("internal:${context}:${context}_check_dependencies","called\n")); + + netsnmp_assert(NULL != rowreq_ctx); + + /* + * TODO:470:o: Check $context row dependencies. + * check that all new value are legal and consistent with each other + */ +## } +@foreach $node nonindex@ +@ ifconf syntax-$node.syntax-dependencies.m2i@ +@ include syntax-$node.syntax-dependencies.m2i@ + if ( MFD_SUCCESS != rc ) + return rc; + +@ end@ +@end@ # for each +## { + return rc; +} /* ${context}_check_dependencies */ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/parent-set.m2i b/local/mib2c-conf.d/parent-set.m2i new file mode 100644 index 0000000..02afe3d --- /dev/null +++ b/local/mib2c-conf.d/parent-set.m2i @@ -0,0 +1,417 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'h'@ + +int ${context}_undo_setup( ${context}_rowreq_ctx *rowreq_ctx); +int ${context}_undo_cleanup( ${context}_rowreq_ctx *rowreq_ctx); +int ${context}_undo( ${context}_rowreq_ctx *rowreq_ctx); +int ${context}_commit( ${context}_rowreq_ctx *rowreq_ctx); +int ${context}_undo_commit( ${context}_rowreq_ctx *rowreq_ctx); +@ if $m2c_irreversible_commit == 1@ +int ${context}_irreversible_commit( ${context}_rowreq_ctx *rowreq_ctx); +@ end@ + +@end@ // m2c_processing_type eq 'h' +######################################################################## +##//#################################################################### +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +##//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@if $m2c_processing_type eq 'c'@ +## +## MASTER COPY OF THIS FLOWCHART IS IN agent/helpers/baby_steps.c +## + /* + * NOTE: if you update this chart, please update the versions in + * local/mib2c-conf.d/parent-set.m2i + * agent/mibgroup/helpers/baby_steps.c + * while you're at it. + */ + /* + *********************************************************************** + * Baby Steps Flow Chart (2004.06.05) * + * * + * +--------------+ +================+ U = unconditional path * + * |optional state| ||required state|| S = path for success * + * +--------------+ +================+ E = path for error * + *********************************************************************** + * + * +--------------+ + * | pre | + * | request | + * +--------------+ + * | U +@if $m2c_table_row_creation == 1@ + * +-------------+ +==============+ + * | row |f|<-------|| object || + * | create |1| E || lookup || + * +-------------+ +==============+ + * E | | S | S + * | +------------------>| + * | +==============+ + * | E || check || + * |<---------------|| values || +@else@ + * +==============+ + * +----------------|| object || + * | E || lookup || + * | +==============+ + * | | S + * | +==============+ + * | E || check || + * |<---------------|| values || +@end@ # row creation + * | +==============+ + * | | S + * | +==============+ + * | +<-------|| undo || + * | | E || setup || + * | | +==============+ + * | | | S + * | | +==============+ + * | | || set ||-------------------------->+ + * | | || value || E | + * | | +==============+ | + * | | | S | + * | | +--------------+ | + * | | | check |-------------------------->| + * | | | consistency | E | + * | | +--------------+ | + * | | | S | + * | | +==============+ +==============+ | + * | | || commit ||-------->|| undo || | + * | | || || E || commit || | + * | | +==============+ +==============+ | + * | | | S U |<--------+ + * | | +--------------+ +==============+ + * | | | irreversible | || undo || + * | | | commit | || set || + * | | +--------------+ +==============+ + * | | | U U | + * | +-------------->|<------------------------+ + * | +==============+ + * | || undo || + * | || cleanup || + * | +==============+ + * +---------------------->| U +@if $m2c_table_row_creation == 1@ + * | + * (err && f1)------------------->+ + * | | + * +--------------+ +--------------+ + * | post |<--------| row | + * | request | U | release | + * +--------------+ +--------------+ +@else@ + * +--------------+ + * | post | + * | request | + * +--------------+ +@end@ # row creation + * + */ + +##---------------------------------------------------------------------- +/** + * Setup up context with information needed to undo a set request. + * + * This function will be called before the individual node undo setup + * functions are called. If you need to do any undo setup that is not + * related to a specific column, you can do it here. + * +@if $m2c_undo_embed == 0@ +@ if $m2c_data_init == 1@ + * Note that the undo context has been allocated with + * ${context}_allocate_data(), but may need extra + * initialization similar to what you may have done in + * ${context}_rowreq_ctx_init(). +@ end@ +@end@ + * Note that an individual node's undo_setup function will only be called + * if that node is being set to a new value. + * + * If there is any setup specific to a particular column (e.g. allocating + * memory for a string), you should do that setup in the node's undo_setup + * function, so it won't be done unless it is necessary. + * + * @param rowreq_ctx + * Pointer to the table context (${context}_rowreq_ctx) + * + * @retval MFD_SUCCESS : success + * @retval MFD_ERROR : error. set will fail. + */ +int +${context}_undo_setup( ${context}_rowreq_ctx *rowreq_ctx) +{ + int rc = MFD_SUCCESS; + + DEBUGMSGTL(("verbose:${context}:${context}_undo_setup","called\n")); + + /** we should have a non-NULL pointer */ + netsnmp_assert( NULL != rowreq_ctx ); + + /* + * TODO:451:M: |-> Setup $context undo. + * set up $context undo information, in preparation for a set. + * Undo storage is in ${m2c_ctx_lh}* + */ + + return rc; +} /* ${context}_undo_setup */ + +/** + * Undo a set request. + * + * This function will be called before the individual node undo + * functions are called. If you need to do any undo that is not + * related to a specific column, you can do it here. + * + * Note that an individual node's undo function will only be called + * if that node is being set to a new value. + * + * If there is anything specific to a particular column (e.g. releasing + * memory for a string), you should do that setup in the node's undo + * function, so it won't be done unless it is necessary. + * + * @param rowreq_ctx + * Pointer to the table context (${context}_rowreq_ctx) + * + * @retval MFD_SUCCESS : success + * @retval MFD_ERROR : error. set will fail. + */ +int +${context}_undo( ${context}_rowreq_ctx *rowreq_ctx) +{ + int rc = MFD_SUCCESS; + + DEBUGMSGTL(("verbose:${context}:${context}_undo","called\n")); + + /** we should have a non-NULL pointer */ + netsnmp_assert( NULL != rowreq_ctx ); + + /* + * TODO:451:M: |-> $context undo. + * $context undo information, in response to a failed set. + * Undo storage is in ${m2c_ctx_lh}* + */ + + return rc; +} /* ${context}_undo_setup */ + +/** + * Cleanup up context undo information. + * + * This function will be called after set/commit processing. If you + * allocated any resources in undo_setup, this is the place to release + * those resources. + * + * This function is called regardless of the success or failure of the set + * request. If you need to perform different steps for cleanup depending + * on success or failure, you can add a flag to the rowreq_ctx. + * + * @param rowreq_ctx + * Pointer to the table context (${context}_rowreq_ctx) + * + * @retval MFD_SUCCESS : success + * @retval MFD_ERROR : error + */ +int +${context}_undo_cleanup( ${context}_rowreq_ctx *rowreq_ctx) +{ + int rc = MFD_SUCCESS; + + DEBUGMSGTL(("verbose:${context}:${context}_undo_cleanup","called\n")); + + /** we should have a non-NULL pointer */ + netsnmp_assert( NULL != rowreq_ctx ); + + /* + * TODO:452:M: |-> Cleanup $context undo. + * Undo storage is in ${m2c_ctx_lh}* + */ + + return rc; +} /* ${context}_undo_cleanup */ + +##---------------------------------------------------------------------- +/** + * commit new values. + * + * At this point, you should have done everything you can to ensure that + * this commit will not fail. + * + * Should you need different behavior depending on which columns were + * set, rowreq_ctx->column_set_flags will indicate which writeable columns were + * set. The definitions for the COLUMN_*_FLAG bits can be found in +@if $m2c_create_fewer_files != 1@ + * ${context}_oids.h. +@else@ + * ${context}.h. +@end@ + * A new row will have the MFD_ROW_CREATED bit set in rowreq_flags. + * + * @param ${context}_rowreq_ctx + * Pointer to the users context. + * + * @retval MFD_SUCCESS : success + * @retval MFD_ERROR : error + */ +int +${context}_commit( ${context}_rowreq_ctx *rowreq_ctx) +{ + int rc = MFD_SUCCESS; + int save_flags; + + DEBUGMSGTL(("verbose:${context}:${context}_commit","called\n")); + + /** we should have a non-NULL pointer */ + netsnmp_assert( NULL != rowreq_ctx ); + + /* + * save flags, then clear until we actually do something + */ + save_flags = rowreq_ctx->column_set_flags; + rowreq_ctx->column_set_flags = 0; + + /* + * commit $context data + * 1) check the column's flag in save_flags to see if it was set. + * 2) clear the flag when you handle that column + * 3) set the column's flag in column_set_flags if it needs undo + * processing in case of a failure. + */ +@ foreach $node nonindex@ +@ include m2c_setup_node.m2i@ +@ if $node.settable == 0@ +@ next@ +@ end@ + if (save_flags & COLUMN_$node.uc_FLAG) { + save_flags &= ~COLUMN_$node.uc_FLAG; /* clear $node */ + /* + * TODO:482:o: |-> commit column $node. + */ + rc = -1; + if(-1 == rc) { + snmp_log(LOG_ERR,"$context column $node commit failed\n"); + } + else { + /* + * set flag, in case we need to undo $node + */ + rowreq_ctx->column_set_flags |= COLUMN_$node.uc_FLAG; + } + } + +@ end@ # foreach $node + /* + * if we successfully commited this row, set the dirty flag. + */ + if (MFD_SUCCESS == rc) { + rowreq_ctx->rowreq_flags |= MFD_ROW_DIRTY; + } + + if (save_flags) { + snmp_log(LOG_ERR, "unhandled columns (0x%x) in commit\n", save_flags); + return MFD_ERROR; + } + + return rc; +} /* ${context}_commit */ + +/** + * undo commit new values. + * + * Should you need different behavior depending on which columns were + * set, rowreq_ctx->column_set_flags will indicate which writeable columns were + * set. The definitions for the COLUMN_*_FLAG bits can be found in +@if $m2c_create_fewer_files != 1@ + * ${context}_oids.h. +@else@ + * ${context}.h. +@end@ + * A new row will have the MFD_ROW_CREATED bit set in rowreq_flags. + * + * @param ${context}_rowreq_ctx + * Pointer to the users context. + * + * @retval MFD_SUCCESS : success + * @retval MFD_ERROR : error + */ +int +${context}_undo_commit( ${context}_rowreq_ctx *rowreq_ctx) +{ + int rc = MFD_SUCCESS; + + DEBUGMSGTL(("verbose:${context}:${context}_undo_commit","called\n")); + + /** we should have a non-NULL pointer */ + netsnmp_assert( NULL != rowreq_ctx ); + + /* + * TODO:485:M: |-> Undo $context commit. + * check the column's flag in rowreq_ctx->column_set_flags to see + * if it was set during commit, then undo it. + * + * eg: if (rowreq_ctx->column_set_flags & COLUMN_$node.uc_FLAG) {} + */ + + + /* + * if we successfully un-commited this row, clear the dirty flag. + */ + if (MFD_SUCCESS == rc) { + rowreq_ctx->rowreq_flags &= ~MFD_ROW_DIRTY; + } + + return rc; +} /* ${context}_undo_commit */ + +@if $m2c_irreversible_commit == 1@ +##---------------------------------------------------------------------- +/** + * perform commit actions that are not reversible + * + * THERE IS NO ATTEMPT AT RECOVERY FOR ERRORS FROM THIS STATE! + * + * @param ${context}_rowreq_ctx + * Pointer to the users context. + * + * @retval MFD_SUCCESS : success + * @retval MFD_ERROR : other error + */ +int +${context}_irreversible_commit( ${context}_rowreq_ctx *rowreq_ctx) +{ + int rc; + + DEBUGMSGTL(("verbose:${context}:${context}_irreversible_commit","called\n")); + + /** we should have a non-NULL pointer */ + netsnmp_assert( NULL != rowreq_ctx ); + + /* + * TODO:495:o: Irreversible $context commit. + */ +##$example_start +##$example_end + + return MFD_SUCCESS; +} /* ${context}_irreversible_commit */ + +@end@ // irreversable commit +## +######################################################################## +@end@ // m2c_processing_type eq 'c' +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/subagent.m2c b/local/mib2c-conf.d/subagent.m2c new file mode 100644 index 0000000..61fc26d --- /dev/null +++ b/local/mib2c-conf.d/subagent.m2c @@ -0,0 +1,195 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@if 0@ +@open ${name}_subagent.h@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + */ +@include generic-header-top.m2i@ +@include generic-header-bottom.m2i@ +@end@ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}_subagent.c@ +/* + * Note: this file originally auto-generated by mib2c using + * version $Revision$ of $RCSfile$ + */ +@include generic-source-includes.m2i@ +#include <signal.h> + +/* + * If compiling within the net-snmp source code, this will trigger the feature + * detection mechansim to ensure the agent_check_and_process() function + * is left available even if --enable-minimialist is turned on. If you + * have configured net-snmp using --enable-minimialist and want to compile + * this code externally to the Net-SNMP code base, then please add + * --with-features="agent_check_and_process enable_stderrlog" to your + * configure line. + */ +netsnmp_feature_require(agent_check_and_process) +netsnmp_feature_require(enable_stderrlog) + +static int keep_running; + +static RETSIGTYPE +stop_server(int a) { + keep_running = 0; +} + +static void usage(void) { + printf("usage: $name [-D<tokens>] [-f] [-L] [-M] [-H] [LISTENING ADDRESSES]\n" + "\t-f Do not fork() from the calling shell.\n" + "\t-DTOKEN[,TOKEN,...]\n" + "\t\tTurn on debugging output for the given TOKEN(s).\n" + "\t\tWithout any tokens specified, it defaults to printing\n" + "\t\tall the tokens (which is equivalent to the keyword 'ALL').\n" + "\t\tYou might want to try ALL for extremely verbose output.\n" + "\t\tNote: You can't put a space between the -D and the TOKENs.\n" + "\t-H\tDisplay a list of configuration file directives\n" + "\t\tunderstood by the agent and then exit.\n" + "\t-M\tRun as a normal SNMP Agent instead of an AgentX sub-agent.\n" + "\t-x ADDRESS\tconnect to master agent at ADDRESS (default /var/agentx/master).\n" + "\t-L\tDo not open a log file; print all messages to stderr.\n"); + exit(0); +} + +int +main (int argc, char **argv) { + int agentx_subagent=1; /* change this if you want to be a SNMP master agent */ + /* Defs for arg-handling code: handles setting of policy-related variables */ + int ch; + extern char *optarg; + int dont_fork = 0, use_syslog = 0; + char *agentx_socket = NULL; + + while ((ch = getopt(argc, argv, "D:fHLMx:")) != EOF) + switch(ch) { + case 'D': + debug_register_tokens(optarg); + snmp_set_do_debugging(1); + break; + case 'f': + dont_fork = 1; + break; + case 'H': + netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); + init_agent("$name"); /* register our .conf handlers */ + init_$name(); + init_snmp("$name"); + fprintf(stderr, "Configuration directives understood:\n"); + read_config_print_usage(" "); + exit(0); + case 'M': + agentx_subagent = 0; + break; + case 'L': + use_syslog = 0; /* use stderr */ + break; + case 'x': + agentx_socket = optarg; + break; + default: + fprintf(stderr,"unknown option %c\n", ch); + usage(); + } + + if (optind < argc) { + int i; + /* + * There are optional transport addresses on the command line. + */ + DEBUGMSGTL(("snmpd/main", "optind %d, argc %d\n", optind, argc)); + for (i = optind; i < argc; i++) { + char *c, *astring; + if ((c = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_PORTS))) { + astring = malloc(strlen(c) + 2 + strlen(argv[i])); + if (astring == NULL) { + fprintf(stderr, "malloc failure processing argv[%d]\n", i); + exit(1); + } + sprintf(astring, "%s,%s", c, argv[i]); + netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_PORTS, astring); + SNMP_FREE(astring); + } else { + netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_PORTS, argv[i]); + } + } + DEBUGMSGTL(("snmpd/main", "port spec: %s\n", + netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_PORTS))); + } + + /* we're an agentx subagent? */ + if (agentx_subagent) { + /* make us a agentx client. */ + netsnmp_enable_subagent(); + if (NULL != agentx_socket) + netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_X_SOCKET, agentx_socket); + } + + snmp_disable_log(); + if (use_syslog) + snmp_enable_calllog(); + else + snmp_enable_stderrlog(); + + /* daemonize */ + if(!dont_fork) { + int rc = netsnmp_daemonize(1,!use_syslog); + if(rc) + exit(-1); + } + + /* initialize tcp/ip if necessary */ + SOCK_STARTUP; + + /* initialize the agent library */ + init_agent("$name"); + + /* init $name mib code */ + init_$name(); + + /* read ${name}.conf files. */ + init_snmp("$name"); + + /* If we're going to be a snmp master agent, initial the ports */ + if (!agentx_subagent) + init_master_agent(); /* open the port to listen on (defaults to udp:161) */ + + /* In case we recevie a request to stop (kill -TERM or kill -INT) */ + keep_running = 1; + signal(SIGTERM, stop_server); + signal(SIGINT, stop_server); + + /* you're main loop here... */ + while(keep_running) { + /* if you use select(), see snmp_select_info() in snmp_api(3) */ + /* --- OR --- */ + agent_check_and_process(1); /* 0 == don't block */ + } + + /* at shutdown time */ + snmp_shutdown("$name"); + SOCK_CLEANUP; + exit(0); +} + +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-COUNTER64-get.m2i b/local/mib2c-conf.d/syntax-COUNTER64-get.m2i new file mode 100644 index 0000000..7f36830 --- /dev/null +++ b/local/mib2c-conf.d/syntax-COUNTER64-get.m2i @@ -0,0 +1,35 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ + ## } +@end@ +######################################################################## +## +@include generic-get-decl.m2i@ +/* + * TODO:231:o: |-> copy $node data. + * get ${m2c_node_lh}.low and ${m2c_node_lh}.high from $m2c_data_item_base + */ +@if ("$m2c_data_context" eq "generated")@ + ${m2c_node_lh}.high = ${m2c_data_item}${node}.high; + ${m2c_node_lh}.low = ${m2c_data_item}${node}.low; +@else@ + return MFD_SKIP; /* TODO:235:M: |-> Remove SKIP once you've set $node data */ +@end@ +## spirit of @include generic-get-decl-bot.m2i@ +@if $m2c_get_use_temp == 1@ + + /* copy temporary value to passed parameter */ + ${node}_ptr->high = ${m2c_node_lh}.high; + ${node}_ptr->low = ${m2c_node_lh}.low; +@end@ + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-DateAndTime-get.m2d b/local/mib2c-conf.d/syntax-DateAndTime-get.m2d new file mode 100644 index 0000000..dfb2a21 --- /dev/null +++ b/local/mib2c-conf.d/syntax-DateAndTime-get.m2d @@ -0,0 +1,9 @@ +## +## +@eval $m2c_node_needlength = 0@ +@eval $m2c_decl = "u_char *"@ +## +@eval $m2c_node_proto_parms = "u_char * ${node}"@ +## +@eval $m2c_node_proto_comments = "$m2c_node_proto_comments * Param: ${node}\n"@ +@eval $m2c_node_proto_comments = "$m2c_node_proto_comments * Pointer to storage for a DateAndTime value\n"@ diff --git a/local/mib2c-conf.d/syntax-DateAndTime-get.m2i b/local/mib2c-conf.d/syntax-DateAndTime-get.m2i new file mode 100644 index 0000000..984d1e2 --- /dev/null +++ b/local/mib2c-conf.d/syntax-DateAndTime-get.m2i @@ -0,0 +1,54 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## + /* temporary storage for date. If you have any of this data available + directly, use it instead. */ + int year, month, day, hour, minutes, seconds, deci_seconds; + int rc, utc_offset_direction, utc_offset_hours, utc_offset_minutes; + + /** we should have a pointer and enough storage */ + netsnmp_assert( (NULL != $m2c_node_param_ref_name) && (NULL != *$m2c_node_param_ref_name)); + netsnmp_assert( (NULL != $m2c_node_param_ref_lname) && ((* $m2c_node_param_ref_lname) >= 11)); + + /* + * TODO:231:o: |-> copy $node data. + * get the date from your context pointer. + */ + return MFD_SKIP; /* TODO:234:M: |-> Remove SKIP once you've set $node data */ + + year = 0; /* 0..65536 */ + month = 0; /* 1..12 */ + day = 0; /* 1..31 */ + hour = 0; /* 0..23 */ + minutes = 0; /* 0..59 */ + seconds = 0; /* 0..60 (60 indicates a leap-second) */ + deci_seconds = 0; /* 0..9 */ + + /* setting utc offset is optional. Leave the values as is if you + want to exclude this information. */ + utc_offset_direction = 0; /* -1, +1 */ + utc_offset_hours = -1; /* 0..13 */ + utc_offset_minutes = -1; /* 0..59 */ + + /* call convenience function to set data */ + rc = netsnmp_dateandtime_set_buf_from_vars(*$m2c_node_param_ref_name, + $m2c_node_param_ref_lname, + year, month, day, + hour, minutes, seconds, deci_seconds, + utc_offset_direction, utc_offset_hours, + utc_offset_minutes ); + if(rc != SNMP_ERR_NOERROR) + return rc; + +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-DateAndTime-readme.m2i b/local/mib2c-conf.d/syntax-DateAndTime-readme.m2i new file mode 100644 index 0000000..cc678ac --- /dev/null +++ b/local/mib2c-conf.d/syntax-DateAndTime-readme.m2i @@ -0,0 +1,4 @@ + This column is a DataAndTime object. The local local variables year, + month, day, hour, minues, seconds, deci_seconds, utc_offset_direction, + utc_offset_hours and utc_offset_minutes should be set from the data + context before the netsnmp_dateandtime_set_buf_from_vars function call. diff --git a/local/mib2c-conf.d/syntax-InetAddress-get.m2i b/local/mib2c-conf.d/syntax-InetAddress-get.m2i new file mode 100644 index 0000000..253980c --- /dev/null +++ b/local/mib2c-conf.d/syntax-InetAddress-get.m2i @@ -0,0 +1,100 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## + /* + * TODO:231:M: |-> copy $node data. + * TODO:231:M: | |-> get address type from your context pointer. + */ + int addressType = -1; + size_t actual_size = 0; + + return MFD_SKIP; /* TODO:235:M: |-> Remove SKIP once you've set $node data */ + + switch (addressType) { + case INETADDRESSTYPE_UNKNOWN: + /* + * An unknown address type. This value MUST be used if the value + * of the InetAddress object is a zero-length string. It may also be + * used to indicate an IP address which is not in one of the formats + * defined below. + */ + actual_size = ${m2c_ctx_rhs}; + break; + + case INETADDRESSTYPE_IPV4: + /* + * Represents an IPv4 network address: + * octets contents encoding + * 1-4 IPv4 address network-byte order + */ + actual_size = 4; + break; + + case INETADDRESSTYPE_IPV6: + /* + * Represents an IPv6 network address: + * + * octets contents encoding + * 1-16 IPv6 address network-byte order + */ + actual_size = 16; + break; + + case INETADDRESSTYPE_IPV4Z: + /* + * Represents a non-global IPv4 network address together + * with its zone index: + * + * octets contents encoding + * 1-4 IPv4 address network-byte order + * 5-8 zone index network-byte order + */ + actual_size = 8; + break; + + case INETADDRESSTYPE_IPV6Z: + /* + * Represents a non-global IPv6 network address together + * with its zone index: + * + * octets contents encoding + * 1-16 IPv6 address network-byte order + * 17-20 zone index network-byte order + */ + actual_size = 20; + break; + + case INETADDRESSTYPE_DNS: + /* + * Represents a DNS domain name. The name SHOULD be fully + * qualified whenever possible. + */ + actual_size = ${m2c_ctx_rhs}; + break; + + default: + snmp_log(LOG_ERR, "unknown InetAddressType %d for $node\n", + addressType); + return SNMP_ERR_GENERR; + } + + if ( actual_size > ${m2c_ctx_lhs} ) { + snmp_log(LOG_ERR, "actual size %d too big for $node\n", + addressType); + return SNMP_ERR_GENERR; + } + + memcpy( ${m2c_ctx_lh}, ${m2c_ctx_rh}, actual_size); + ${m2c_ctx_lhs} = actual_size; +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-InetAddress-set.m2i b/local/mib2c-conf.d/syntax-InetAddress-set.m2i new file mode 100644 index 0000000..be3cf10 --- /dev/null +++ b/local/mib2c-conf.d/syntax-InetAddress-set.m2i @@ -0,0 +1,22 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## + /* + * Note: if there is a corresponding InetAddressType object, we don't + * know if they'll be set together, or which order they will be set. + * So we just accept the value here, and check that both values are + * consistent in ${context}_check_dependencies(). + */ +@include generic-ctx-set.m2i@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-InetAddressType-get.m2i b/local/mib2c-conf.d/syntax-InetAddressType-get.m2i new file mode 100644 index 0000000..0b89a5a --- /dev/null +++ b/local/mib2c-conf.d/syntax-InetAddressType-get.m2i @@ -0,0 +1,25 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@include generic-get-decl.m2i@ +@include generic-ctx-get.m2i@ + /* the ${context}_rowreq_ctx->data pointer should be pointer to the data you + supplied during the data lookup, so you should know how to + determine the InetAddressType from this pointer. */ + + return MFD_SKIP; /* TODO:235:M: |-> Remove SKIP once you've set $node data */ + +@include generic-value-map.m2i@ +@include generic-get-decl-bot.m2i@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-InetAddressType-set.m2i b/local/mib2c-conf.d/syntax-InetAddressType-set.m2i new file mode 100644 index 0000000..fb81efe --- /dev/null +++ b/local/mib2c-conf.d/syntax-InetAddressType-set.m2i @@ -0,0 +1,25 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## + /* + * TODO:230:o: Set $node data + * set data context from $m2c_ctx_rh + * + * Note: if there is a corresponding InetAddress object, we don't + * know if they'll be set together, or which order they will be set. + * So we just accept the value here, and check that both values are + * consistent in ${context}_check_dependencies(). + */ +@include generic-ctx-set.m2i@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-RowStatus-dependencies.m2i b/local/mib2c-conf.d/syntax-RowStatus-dependencies.m2i new file mode 100644 index 0000000..9f14baf --- /dev/null +++ b/local/mib2c-conf.d/syntax-RowStatus-dependencies.m2i @@ -0,0 +1,113 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## { + /* + * check RowStatus dependencies + */ + if (rowreq_ctx->column_set_flags & COLUMN_$node.uc_FLAG) { + /* + * check for valid RowStatus transition (old, new) + * (Note: move transition check to $node_check_value + * to catch errors earlier) + */ + rc = check_rowstatus_transition( ${m2c_undo_item}$node, + ${m2c_data_item}$node ); + if (MFD_SUCCESS != rc) + return rc; + +@if $m2c_table_row_creation == 1@ + /* + * row creation requirements + */ + if (rowreq_ctx->rowreq_flags & MFD_ROW_CREATED) { + if (ROWSTATUS_DESTROY == ${m2c_data_item}$node) { + rowreq_ctx->rowreq_flags |= MFD_ROW_DELETED; + } + else if (ROWSTATUS_CREATEANDGO == ${m2c_data_item}$node) { + if ((rowreq_ctx->column_set_flags & $context.uc_REQUIRED_COLS) + != $context.uc_REQUIRED_COLS) { + DEBUGMSGTL(("${context}", + "required columns missing (0x%0x != 0x%0x)\n", + rowreq_ctx->column_set_flags, $context.uc_REQUIRED_COLS)); + return MFD_CANNOT_CREATE_NOW; + } + ${m2c_data_item}$node = ROWSTATUS_ACTIVE; + } + } /* row creation */ + else { +@end@ + /* + * row change requirements + */ + /* + * don't allow a destroy if any other value was changed, since + * that might call data access routines with bad info. + * + * you may or may not require the row be notInService before it + * can be destroyed. + */ + if (ROWSTATUS_DESTROY == ${m2c_data_item}$node) { +@if $m2c_table_refcounts == 1@ + if (0 != rowreq_ctx->ref_count) { + DEBUGMSGTL(("$context", + "can't delete row, %d references\n", + rowreq_ctx->ref_count)); + return MFD_NOT_VALID_NOW; + } +@end@ + if (rowreq_ctx->column_set_flags & ~COLUMN_$node.uc_FLAG) { + DEBUGMSGTL(("$context", + "destroy must be only varbind for row\n")); + return MFD_NOT_VALID_NOW; + } + rowreq_ctx->rowreq_flags |= MFD_ROW_DELETED; + + } /* row destroy */ +@if $m2c_table_refcounts == 1@ + else if(ROWSTATUS_NOTINSERVICE == ${m2c_data_item}$node) { + if (0 != rowreq_ctx->ref_count) { + DEBUGMSGTL(("$context", + "can't deactivate row, %d references\n", + rowreq_ctx->ref_count)); + return MFD_NOT_VALID_NOW; + } + } /* notInService */ +@end@ +@if $m2c_table_row_creation == 1@ + } /* row change */ +@end@ + } + else { +@if $m2c_table_row_creation == 1@ + /* + * must have row status to create a row + */ + if (rowreq_ctx->rowreq_flags & MFD_ROW_CREATED) { + DEBUGMSGTL(("$context", + "must use RowStatus to create rows\n")); + return MFD_CANNOT_CREATE_NOW; + } +@else@ + /* + * row creation not supported + */ + if (rowreq_ctx->rowreq_flags & MFD_ROW_CREATED) { + DEBUGMSGTL(("$context", + "row creation not supported\n")); + return MFD_CANNOT_CREATE_EVER; + } +@end@ + } /* row status not set */ + +## } +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-RowStatus-get.m2i b/local/mib2c-conf.d/syntax-RowStatus-get.m2i new file mode 100644 index 0000000..f8e0996 --- /dev/null +++ b/local/mib2c-conf.d/syntax-RowStatus-get.m2i @@ -0,0 +1,65 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@include generic-get-decl.m2i@ +@include generic-ctx-get.m2i@ +@if $m2c_node_skip_mapping != 1@ + /* + * TODO:245:o: |-> Implement $context RowStatus mapping +@if $mfd_code_verbose == 1@ + * + * If the values for your data type don't exactly match the + * possible values defined by the mib, you should map them here. +@end@ + */ + /* + * update INTERNAL_* macros defined in the header, if neccessary + */ + switch ($m2c_node_lh) { + + /* `active', which indicates that the conceptual row is + available for use by the managed device */ + case INTERNAL_$context.uc_$node.uc_ACTIVE: + $m2c_node_lh = ROWSTATUS_ACTIVE; + break; + + /* `notInService', which indicates that the conceptual + row exists in the agent, but is unavailable for use by + the managed device (see NOTE below); 'notInService' has + no implication regarding the internal consistency of + the row, availability of resources, or consistency with + the current state of the managed device */ + case INTERNAL_$context.uc_$node.uc_NOTINSERVICE: + $m2c_node_lh = ROWSTATUS_NOTINSERVICE; + break; + + /* `notReady', which indicates that the conceptual row + exists in the agent, but is missing information + necessary in order to be available for use by the + managed device (i.e., one or more required columns in + the conceptual row have not been instanciated) */ + case INTERNAL_$context.uc_$node.uc_NOTREADY: + $m2c_node_lh = ROWSTATUS_NOTREADY; + break; + + default: + snmp_log(LOG_ERR, + "couldn't map value %ld for $node RowStatus\n", + $m2c_node_lh); + return SNMP_ERR_GENERR; + } + +@end@ +@include generic-get-decl-bot.m2i@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-RowStatus-varbind-validate.m2i b/local/mib2c-conf.d/syntax-RowStatus-varbind-validate.m2i new file mode 100644 index 0000000..dc91ac8 --- /dev/null +++ b/local/mib2c-conf.d/syntax-RowStatus-varbind-validate.m2i @@ -0,0 +1,16 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +rc = netsnmp_check_vb_rowstatus_value(var); +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-StorageType-dependencies.m2i b/local/mib2c-conf.d/syntax-StorageType-dependencies.m2i new file mode 100644 index 0000000..0419514 --- /dev/null +++ b/local/mib2c-conf.d/syntax-StorageType-dependencies.m2i @@ -0,0 +1,19 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## { + /* + * check for valid StorageType transition (old, new) + */ + rc = check_storage_transition( ${m2c_undo_item}$node, ${m2c_data_item}$node ); +## } +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-conf.d/syntax-TestAndIncr-get.m2i b/local/mib2c-conf.d/syntax-TestAndIncr-get.m2i new file mode 100644 index 0000000..7cd2f60 --- /dev/null +++ b/local/mib2c-conf.d/syntax-TestAndIncr-get.m2i @@ -0,0 +1,22 @@ +############################################################# -*- c -*- +## generic include for XXX. Do not use directly. +## +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@include generic-get-decl.m2i@ + + return MFD_SKIP; /* TODO:235:M: Remove SKIP once you've set $node data */ + +@include generic-ctx-get.m2i@ +@include generic-get-long.m2i@ +@include generic-get-decl-bot.m2i@ +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c-update b/local/mib2c-update new file mode 100755 index 0000000..42017e5 --- /dev/null +++ b/local/mib2c-update @@ -0,0 +1,366 @@ +#!/bin/sh +# +# $Id$ +# +# script to merge custom code into updated mib2c code +# +#----- example .mib2c-updaterc ----- +#UPDATE_OID=ipAddressTable +#UPDATE_CONF=mib2c.mfd.conf +#UPDATE_MIB2C_OPTS= +#UPDATE_NOPROBE=1 +#----- example .mib2c-updaterc ----- + +#---------------------------------------------------------------------- +# +# defaults +# +UPDATE_CURR=$PWD +UPDATE_ORIG=$PWD/.orig +UPDATE_NEW=$PWD/.new +UPDATE_MERGED=$PWD/.merged +UPDATE_BACKUP=$PWD/.backup +UPDATE_PATCH=$PWD/.patch + +# +# number of diff context lines / patch fuzz factor +# +FUZZ=5 +FIRST_RUN=0 + +#---------------------------------------------------------------------- +# +debug() +{ + if [ $UPDATE_DEBUG -ge 1 ]; then + echo $1 + fi +} + +error() +{ + echo "ERROR: $@" >&2 +} + +die() +{ + error "$@" + exit 99 +} + +safecd() +{ + cd $1 + if [ $? -ne 0 ]; then + die "changing to directory $1 from $PWD failed!" + fi +} + +safecp() +{ + cp $@ + if [ $? -ne 0 ]; then + die "'cp $@' failed!" + fi +} + +#---------------------------------------------------------------------- +# +check_setup() +{ + rc=1 + for d in $UPDATE_CURR $UPDATE_ORIG $UPDATE_NEW $UPDATE_MERGED $UPDATE_PATCH $UPDATE_BACKUP $UPDATE_BACKUP/curr $UPDATE_BACKUP/orig + do + if [ ! -d $d ]; then + echo "Creating missing directory $d" + mkdir -p $d + if [ $? -ne 0 ]; then + error "Could not create directory $d" + rc=0 + fi + fi + done + + if [ -z "$UPDATE_OID" ]; then + error "Environment variable missing! Set UPDATE_OID in .mib2c-updaterc" + rc=0 + fi + + if [ -z "$UPDATE_CONF" ]; then + error "Environment variable missing! Set UPDATE_CONF in .mib2c-updaterc" + rc=0 + fi + +# if [ -z "$UPDATE_" ]; then +# error "Environment variable missing! Set UPDATE_ in .mib2c-updaterc" +# rc=0 +# fi + + if [ $rc -eq 0 ] && [ $UPDATE_NOPROBE -ne 1 ]; then + mib2c -c unknown > /dev/null 2>&1 + if [ $? -eq 0 ]; then + error "WARNING: mib2c returns 0 on error conditions!" + rc=0 + fi + fi + + return $rc +} + +#---------------------------------------------------------------------- +# +do_diff() +{ + DD_ORIG=$1 + DD_CURR=$2 + DD_OUTPUT=$3 + # u | c unified | context + # r recursive + # b ignore blank lines + # w ignore white space + # p Show which C function each change is in. + # d smaller changes + # --exclude='*Makefile' --unidirectional-new-file + local rc=0 + local rcs=0 + safecd $DD_ORIG + echo " checking files in $1 ($PWD)" + files=`ls *$UPDATE_OID* 2>/dev/null` + if [ ! -z "$files" ]; then + for f in $files; do + diff -U $FUZZ -p -b -w --show-c-function \ + -I "$""Id:" $f $DD_CURR/$f >> $DD_OUTPUT + rc=$? + rcs=`expr $rcs + $rc` + if [ $rc -eq 1 ]; then + echo " $f is different" + fi + done + fi + if [ $rcs -eq 0 ]; then + rm -f $DD_OUTPUT + fi + safecd - + return $rcs +} + +#---------------------------------------------------------------------- +# +do_cp() +{ + src=$1 + dest=$2 + if [ ! -d $dest ]; then + die "dest $dest is not a directory" + fi + if [ ! -d $src ]; then + die "src $src is not a directory" + fi + safecd $src + files=`ls *$UPDATE_OID* 2>/dev/null| egrep "(file|onf|m2d|txt|\.c|\.h)$"` + if [ -z "$files" ]; then + echo " no files to copy from $src" + else + safecp $files $dest + if [ $? -ne 0 ]; then + die "error while copying files from $src to $dest in $PWD" + fi + fi + safecd - +} + +#---------------------------------------------------------------------- +# +save_diff() +{ + echo "Creating patch for your custom code" + cnt=`ls $UPDATE_CURR/*$UPDATE_OID* 2>/dev/null | egrep "(file|onf|m2d|txt|\.c|\.h)$" | wc -l` + if [ $cnt -eq 0 ]; then + echo " no custom code!" + FIRST_RUN=1 + return + fi + + do_diff $UPDATE_ORIG/ $UPDATE_CURR/ $UPDATE_PATCH/custom.$UPDATE_DATE + if [ $? -eq 0 ]; then + echo " no custom code changes found." + fi +} + +#---------------------------------------------------------------------- +# +gen_code() +{ + copy_defaults . $UPDATE_NEW + + safecd $UPDATE_NEW + files=`ls *$UPDATE_OID* 2>/dev/null | grep -v "^default"` + if [ ! -z "$files" ]; then + rm -f $files > /dev/null 2>&1 + fi + echo "mib2c $@ -c $UPDATE_CONF $UPDATE_MIB2C_OPTS $UPDATE_OID" + mib2c $@ -c $UPDATE_CONF $UPDATE_MIB2C_OPTS $UPDATE_OID + if [ $? -ne 0 ]; then + die "bad rc $rc from mib2 while generation new code." + fi + safecd - +} + +#---------------------------------------------------------------------- +# +check_new() +{ + echo "Checking for updates to generated code" + do_diff $UPDATE_ORIG/ $UPDATE_NEW/ $UPDATE_PATCH/generated.$UPDATE_DATE + if [ $? -eq 0 ]; then + echo "Generated code has not changed." + safecd $UPDATE_PATCH + files=`ls *.$UPDATE_DATE 2>/dev/null ` + if [ ! -z "$files" ]; then + rm $files + fi + exit 0 + fi +} + +#---------------------------------------------------------------------- +# +merge_code() +{ + files=`ls $UPDATE_MERGED/* 2>/dev/null ` + if [ ! -z "$files" ]; then + rm $UPDATE_MERGED/* + fi + do_cp $UPDATE_NEW $UPDATE_MERGED + + if [ -f $UPDATE_PATCH/custom.$UPDATE_DATE ]; then + touch .M2C-UPDATE-MERGE-FAILED + echo "Patching new generated code in $UPDATE_MERGED ($PWD)" + # --forward = ignore already applied + patch --forward -F $FUZZ -N -d $UPDATE_MERGED -i $UPDATE_PATCH/custom.$UPDATE_DATE + if [ $? -ne 0 ]; then + error "Could not apply custom code patch to new generated code" + die "You must fix the problem in $UPDATE_MERGED, and then re-run mib2c-update." + fi + rm .M2C-UPDATE-MERGE-FAILED + fi +} + +copy_defaults() +{ + SRC=$1 + DST=$2 + if [ -d $SRC/defaults ]; then + safecp -a $SRC/defaults $DST + else + files=`ls $SRC/default-*.m2d 2>/dev/null ` + if [ ! -z "$files" ]; then + safecp $files $DST + fi + fi + +} + +copy_merged() +{ + echo "Backing up current code to $UPDATE_BACKUP/curr" + do_cp $UPDATE_CURR $UPDATE_BACKUP/curr/ + copy_defaults . $UPDATE_BACKUP/curr/ + + echo "Copying merged code to $UPDATE_CURR" + do_cp $UPDATE_MERGED $UPDATE_CURR/ + + + echo "Backing up original code to $UPDATE_BACKUP/orig" + do_cp $UPDATE_ORIG $UPDATE_BACKUP/orig/ + echo "Saving new original code to $UPDATE_ORIG" + do_cp $UPDATE_NEW $UPDATE_ORIG/ +} + +copy_new() +{ + echo "Copying code to $UPDATE_CURR" + do_cp $UPDATE_NEW $UPDATE_CURR/ + # copy defaults back to current dir (which may not be UPDATE_CURR) + copy_defaults $UPDATE_NEW . + + echo "Saving original code to $UPDATE_ORIG" + do_cp $UPDATE_NEW $UPDATE_ORIG/ +} + +copy_code() +{ + if [ $FIRST_RUN -ne 1 ]; then + copy_merged + else + copy_new + fi + + # always get defaults from UPDATE_NEW, since those are what were used. + copy_defaults $UPDATE_NEW . +} + + +#---------------------------------------------------------------------- +UPDATE_NOPROBE=0 + +if [ -f $HOME/.mib2c-updaterc ]; then + . $HOME/.mib2c-updaterc +fi + +if [ -f $PWD/.mib2c-updaterc ]; then + . $PWD/.mib2c-updaterc +else + echo "creating example .mib2c-udpaterc. edit as needed and re-run " + echo "mib2c-update." + + echo "UPDATE_OID=ipAddressTable" >> .mib2c-updaterc + echo "UPDATE_CONF=mib2c.mfd.conf" >> .mib2c-updaterc + echo "UPDATE_MIB2C_OPTS=" >> .mib2c-updaterc + echo "#UPDATE_NOPROBE=1" >> .mib2c-updaterc +fi + +check_setup +if [ $? -ne 1 ]; then + exit 1 +fi + +UPDATE_DATE=`date "+%F_%I.%M"` +echo "Starting regneration of $UPDATE_OID using $UPDATE_CONF at $UPDATE_DATE" + +if [ -f .M2C-UPDATE-MERGE-FAILED ]; then + echo "It appears that the last run of mib2c-update was not able to merge" + echo "your changes automatically. Do you want to:" + echo + while : ; do + echo "[c)opy merged files to $UPDATE_CURR]" + echo "[r)e-run from scratch]" + echo "[q)uit]" + echo "(c|r|q) ?" + read ans + if [ "x$ans" = "xr" ]; then + rm .M2C-UPDATE-MERGE-FAILED + break + elif [ "x$ans" = "xc" ]; then + echo "Have you have manually merged all the" + echo "changes into the merged directory?" + echo "(y|n)" + read ans + if [ "x$ans" != "xy" ]; then + echo "Ok. Try again after you've done that." + exit 1 + fi + rm .M2C-UPDATE-MERGE-FAILED + copy_code + exit 0 + fi + done +fi + +save_diff +gen_code $@ +if [ $FIRST_RUN -ne 1 ]; then + check_new + merge_code +fi +copy_code diff --git a/local/mib2c.access_functions.conf b/local/mib2c.access_functions.conf new file mode 100644 index 0000000..9d54f14 --- /dev/null +++ b/local/mib2c.access_functions.conf @@ -0,0 +1,183 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}_access.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_ACCESS_H +#define $name.uc_ACCESS_H + +@foreach $t table@ +/** User-defined data access functions for data in table $t */ +/** row level accessors */ +Netsnmp_First_Data_Point ${t}_get_first_data_point; +Netsnmp_Next_Data_Point ${t}_get_next_data_point; +int ${t}_commit_row(void **my_data_context, int new_or_del); +void * ${t}_create_data_context(netsnmp_variable_list *index_data, int column); + +/** column accessors */ + @foreach $c column@ + @if $c.access =~ /(Read|Create)/@ + $c.decl *get_$c(void *data_context, size_t *ret_len); + @end@ + @if $c.access =~ /(Write|Create)/@ + int set_$c(void *data_context, $c.decl *val, size_t val_len); + @end@ + @end@ +@end@ + +#endif /* $name.uc_ACCESS_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}_access.c@ + +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}_access.h" +#include "${name}_enums.h" + +@foreach $t table@ + +/** returns the first data point within the $t table data. + + Set the my_loop_context variable to the first data point structure + of your choice (from which you can find the next one). This could + be anything from the first node in a linked list, to an integer + pointer containing the beginning of an array variable. + + Set the my_data_context variable to something to be returned to + you later that will provide you with the data to return in a given + row. This could be the same pointer as what my_loop_context is + set to, or something different. + + The put_index_data variable contains a list of snmp variable + bindings, one for each index in your table. Set the values of + each appropriately according to the data matching the first row + and return the put_index_data variable at the end of the function. +*/ +netsnmp_variable_list * +${t}_get_first_data_point(void **my_loop_context, void **my_data_context, + netsnmp_variable_list *put_index_data, + netsnmp_iterator_info *mydata) +{ + + netsnmp_variable_list *vptr; + + *my_loop_context = /** XXX */; + *my_data_context = /** XXX */; + + vptr = put_index_data; + + @foreach $idx index@ + snmp_set_var_value(vptr, /** XXX: $idx data */, /** XXX: length of $idx data */); + vptr = vptr->next_variable; + @end@ + + return put_index_data; +} + +/** functionally the same as ${t}_get_first_data_point, but + my_loop_context has already been set to a previous value and should + be updated to the next in the list. For example, if it was a + linked list, you might want to cast it to your local data type and + then return my_loop_context->next. The my_data_context pointer + should be set to something you need later and the indexes in + put_index_data updated again. */ +netsnmp_variable_list * +${t}_get_next_data_point(void **my_loop_context, void **my_data_context, + netsnmp_variable_list *put_index_data, + netsnmp_iterator_info *mydata) +{ + + netsnmp_variable_list *vptr; + + *my_loop_context = /** XXX */; + *my_data_context = /** XXX */; + + vptr = put_index_data; + + @foreach $idx index@ + snmp_set_var_value(vptr, /** XXX: $idx data */, /** XXX: length of $idx data */); + vptr = vptr->next_variable; + @end@ + + return put_index_data; +} + +/** Create a data_context for non-existent rows that SETs are performed on. + * return a void * pointer which will be passed to subsequent get_XXX + * and set_XXX functions for data retrieval and modification during + * this SET request. + * + * The indexes are encoded (in order) into the index_data pointer, + * and the column object which triggered the row creation is available + * via the column parameter, if it would be helpful to use that information. + */ +void * +${t}_create_data_context(netsnmp_variable_list *index_data, int column) { + return NULL; /* XXX: you likely want to return a real pointer */ +} + +/** If the implemented set_* functions don't operate directly on the + real-live data (which is actually recommended), then this function + can be used to take a given my_data_context pointer and "commit" it + to whereever the modified data needs to be put back to. For + example, if this was a routing table you could publish the modified + routes back into the kernel at this point. + + new_or_del will be set to 1 if new, or -1 if it should be deleted + or 0 if it is just a modification of an existing row. + + If you free the data yourself, make sure to *my_data_context = NULL */ +int +${t}_commit_row(void **my_data_context, int new_or_del) +{ + /** Add any necessary commit code here */ + /* */ + + /* return no errors. And there shouldn't be any!!! Ever!!! You + should have checked the values long before this. */ + return SNMP_ERR_NOERROR; +} + + +/* User-defined data access functions (per column) for data in table $t */ +/* + * NOTE: + * - these get_ routines MUST return data that will not be freed (ie, + * use static variables or persistent data). It will be copied, if + * needed, immediately after the get_ routine has been called. + * - these SET routines must copy the incoming data and can not take + * ownership of the memory passed in by the val pointer. + */ + @foreach $c column@ + @if $c.access =~ /(Read|Create)/@ +/** XXX: return a data pointer to the data for the $c column and set + ret_len to its proper size in bytes. */ + $c.decl *get_$c(void *data_context, size_t *ret_len) { + return NULL; /** XXX: replace this with a pointer to a real value */ + } + @end@ + @if $c.access =~ /(Write|Create)/@ +/** XXX: Set the value of the $c column and return + SNMP_ERR_NOERROR on success + SNMP_ERR_XXX for SNMP deterministic error codes + SNMP_ERR_GENERR on generic failures (a last result response). */ + int set_$c(void *data_context, $c.decl *val, size_t val_len) { + return SNMP_ERR_NOERROR; /** XXX: change if an error occurs */ + } + @end@ + @end@ + +@end@ + diff --git a/local/mib2c.array-user.conf b/local/mib2c.array-user.conf new file mode 100644 index 0000000..31559ac --- /dev/null +++ b/local/mib2c.array-user.conf @@ -0,0 +1,1305 @@ +## -*- c -*- +## +## For documentation on the code generated by this configuration file, +## see the file agent/helpers/table_array.c. +## +###################################################################### +## Do the .h file +## @perleval $vars{shortname} =~ s/([A-Z])[a-z]+/$1/g@ +###################################################################### +@foreach $i table@ +@open ${i}.h@ +@eval $hack = "Id" +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + * + * $$hack:$ + * + * Yes, there is lots of code here that you might not use. But it is much + * easier to remove code than to add it! + */ +#ifndef $i.uc_H +#define $i.uc_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/library/container.h> +#include <net-snmp/agent/table_array.h> + + @eval $ext_index = 0@ + @foreach $idx index@ + @if "$idx" ne ""@ + @eval $found = "external"@ + @foreach $c column@ + @if "$idx" eq "$c"@ + @eval $found = "internal"@ + @end@ + @end@ + /** Index $idx is $found */ + @if "$found" eq "external"@ + @eval $ext_index = 1@ + @end@ + @end@ + @end@ + +typedef struct ${i}_context_s { + netsnmp_index index; /** THIS MUST BE FIRST!!! */ + + /************************************************************* + * You can store data internally in this structure. + * + * TODO: You will probably have to fix a few types here... + */ + @if $ext_index != 0@ + /** TODO: add storage for external index(s)! */ + @end@ + @foreach $c column@ + /** $c.syntax = $c.type */ + @eval $have_type = 0@ + @if "$c.type" eq "ASN_OCTET_STR"@ + @eval $have_type = 1@ + @eval $o_len = "65535"@ + @if "$c.syntax" eq "DisplayString"@ + @eval $o_len = "255"@ + @end@ + @if "$c.syntax" eq "SnmpAdminString"@ + @eval $o_len = "255"@ + @end@ + unsigned char $c[$o_len]; + long ${c}_len; + @end@ + @if "$c.type" eq "ASN_OBJECT_ID"@ + @eval $have_type = 1@ + oid $c[MAX_OID_LEN]; + long ${c}_len; + @end@ + @if "$c.type" eq "ASN_UNSIGNED"@ + @eval $have_type = 1@ + unsigned long $c; + @end@ + @if "$c.type" eq "ASN_TIMETICKS"@ + @eval $have_type = 1@ + unsigned long $c; + @end@ + @if "$c.type" eq "ASN_IPADDRESS"@ + @eval $have_type = 1@ + unsigned long $c; + @end@ + @if "$c.type" eq "ASN_UINTEGER"@ + @eval $have_type = 1@ + unsigned long $c; + @end@ + @if "$c.type" eq "ASN_INTEGER"@ + @eval $have_type = 1@ + long $c; + @end@ + @if "$c.type" eq "ASN_COUNTER"@ + @eval $have_type = 1@ + unsigned long $c; + @end@ + @if $have_type == 0@ + /** TODO: Is this type correct? */ + long $c; + @end@ + + @end@ + + /* + * OR + * + * Keep a pointer to your data + */ + void * data; + + /* + *add anything else you want here + */ + +} ${i}_context; + +/************************************************************* + * function declarations + */ +void init_$i(void); +void initialize_table_$i(void); +const ${i}_context * ${i}_get_by_idx(netsnmp_index *); +const ${i}_context * ${i}_get_by_idx_rs(netsnmp_index *, + int row_status); +int ${i}_get_value(netsnmp_request_info *, netsnmp_index *, netsnmp_table_request_info *); + + +/************************************************************* + * oid declarations + */ +extern const oid ${i}_oid[]; +extern const size_t ${i}_oid_len; + +#define ${i}_TABLE_OID $i.commaoid + +/************************************************************* + * column number definitions for table $i + */ +@eval $minv = 0xffffffff@ +@eval $maxv = 0@ +@foreach $c column@ +#define COLUMN_$c.uc $c.subid +@if ! $c.noaccess@ +@eval $minv = min($minv, $c.subid)@ +@eval $maxv = max($maxv, $c.subid)@ +@end@ +@if "$c.syntax" eq "RowStatus"@ + @eval $rs_name = "$c"@ +@end@ +@if "$c.syntax" eq "StorageType"@ + @eval $st_name = "$c"@ +@end@ +@end@ +#define ${i}_COL_MIN $minv +#define ${i}_COL_MAX $maxv + +/* comment out the following line if you don't want a custom sort */ +#define ${i}_CUSTOM_SORT + +@if "$rs_name" ne ""@ +/* uncommend the following line if you allow modifications to an + * active row */ +/** define ${i}_CAN_MODIFY_ACTIVE_ROW */ + +@end@ +@if $i.settable@ +int ${i}_extract_index( ${i}_context * ctx, netsnmp_index * hdr ); + +void ${i}_set_reserve1( netsnmp_request_group * ); +void ${i}_set_reserve2( netsnmp_request_group * ); +void ${i}_set_action( netsnmp_request_group * ); +void ${i}_set_commit( netsnmp_request_group * ); +void ${i}_set_free( netsnmp_request_group * ); +void ${i}_set_undo( netsnmp_request_group * ); + +${i}_context * ${i}_duplicate_row( ${i}_context* ); +netsnmp_index * ${i}_delete_row( ${i}_context* ); + +@if "$rs_name" ne ""@ +int ${i}_can_activate(${i}_context *undo_ctx, + ${i}_context *row_ctx, + netsnmp_request_group * rg); +int ${i}_can_deactivate(${i}_context *undo_ctx, + ${i}_context *row_ctx, + netsnmp_request_group * rg); +@end@ +int ${i}_can_delete(${i}_context *undo_ctx, + ${i}_context *row_ctx, + netsnmp_request_group * rg); + + +@if $i.creatable@ +${i}_context * ${i}_create_row( netsnmp_index* ); +@end@ +@end@ + +#ifdef ${i}_CUSTOM_SORT +${i}_context * ${i}_get( const char *name, int len ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /** $i.uc_H */ +@end@ +###################################################################### +## Do the .c file +###################################################################### +@foreach $i table@ +@open ${i}.c@ +@eval $hack = "Id"@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + * + * $$hack:$ + * + * + * For help understanding NET-SNMP in general, please check the + * documentation and FAQ at: + * + * http://www.net-snmp.org/ + * + * + * For help understanding this code, the agent and how it processes + * requests, please check the following references. + * + * http://www.net-snmp.org/tutorial-5/ + * + * + * You can also join the #net-snmp channel on irc.freenode.net + * and ask for help there. + * + * + * And if all else fails, send a detailed message to the developers + * describing the problem you are having to: + * + * net-snmp-coders@lists.sourceforge.net + * + * + * Yes, there is lots of code here that you might not use. But it is much + * easier to remove code than to add it! + */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#include <net-snmp/library/snmp_assert.h> + +#include "${i}.h" + +static netsnmp_handler_registration *my_handler = NULL; +static netsnmp_table_array_callbacks cb; + +const oid ${i}_oid[] = { ${i}_TABLE_OID }; +const size_t ${i}_oid_len = OID_LENGTH(${i}_oid); + + +#ifdef ${i}_CUSTOM_SORT +/************************************************************ + * keep binary tree to find context by name + */ +static int ${i}_cmp( const void *lhs, const void *rhs ); + +/************************************************************ + * compare two context pointers here. Return -1 if lhs < rhs, + * 0 if lhs == rhs, and 1 if lhs > rhs. + */ +static int +${i}_cmp( const void *lhs, const void *rhs ) +{ + ${i}_context *context_l = + (${i}_context *)lhs; + ${i}_context *context_r = + (${i}_context *)rhs; + + /* + * check primary key, then secondary. Add your own code if + * there are more than 2 keys + */ + int rc; + + /* + * TODO: implement compare. Remove this ifdef code and + * add your own code here. + */ +#ifdef TABLE_CONTAINER_TODO + snmp_log(LOG_ERR, + "${i}_compare not implemented! Container order undefined\n" ); + return 0; +#endif + + /* + * EXAMPLE (assuming you want to sort on a name): + * + * rc = strcmp( context_l->xxName, context_r->xxName ); + * + * if(rc) + * return rc; + * + * TODO: fix secondary keys (or delete if there are none) + * + * if(context_l->yy < context_r->yy) + * return -1; + * + * return (context_l->yy == context_r->yy) ? 0 : 1; + */ +} + +/************************************************************ + * search tree + */ +/** TODO: set additional indexes as parameters */ +${i}_context * +${i}_get( const char *name, int len ) +{ + ${i}_context tmp; + + /** we should have a custom container */ + netsnmp_assert(cb.container->next != NULL); + + /* + * TODO: implement compare. Remove this ifdef code and + * add your own code here. + */ +#ifdef TABLE_CONTAINER_TODO + snmp_log(LOG_ERR, "${i}_get not implemented!\n" ); + return NULL; +#endif + + /* + * EXAMPLE: + * + * if(len > sizeof(tmp.xxName)) + * return NULL; + * + * strncpy( tmp.xxName, name, sizeof(tmp.xxName) ); + * tmp.xxName_len = len; + * + * return CONTAINER_FIND(cb.container->next, &tmp); + */ +} +#endif + + +/************************************************************ + * Initializes the $i module + */ +void +init_$i(void) +{ + initialize_table_$i(); + + /* + * TODO: perform any startup stuff here, such as + * populating the table with initial data. + * + * ${i}_context * new_row = create_row(index); + * CONTAINER_INSERT(cb.container,new_row); + */ +} + +@if $i.settable@ +/************************************************************ + * the *_row_copy routine + */ +static int ${i}_row_copy(${i}_context * dst, + ${i}_context * src) +{ + if(!dst||!src) + return 1; + + /* + * copy index, if provided + */ + if(dst->index.oids) + free(dst->index.oids); + if(snmp_clone_mem( (void*)&dst->index.oids, src->index.oids, + src->index.len * sizeof(oid) )) { + dst->index.oids = NULL; + return 1; + } + dst->index.len = src->index.len; + + + /* + * copy components into the context structure + */ + @if $ext_index != 0@ + /** TODO: add code for external index(s)! */ + @end@ + @foreach $c column@ + @eval $have_type = 0@ + @if "$c.type" eq "ASN_OCTET_STR"@ + @eval $have_type = 1@ + memcpy( dst->$c, src->$c, src->${c}_len ); + dst->${c}_len = src->${c}_len; + @end@ + @if "$c.type" eq "ASN_OBJECT_ID"@ + @eval $have_type = 1@ + memcpy( dst->$c, src->$c, src->${c}_len ); + dst->${c}_len = src->${c}_len; + @end@ + @if $have_type == 0@ + dst->$c = src->$c; + @end@ + + @end@ + return 0; +} + +/** + * the *_extract_index routine + * + * This routine is called when a set request is received for an index + * that was not found in the table container. Here, we parse the oid + * in the the individual index components and copy those indexes to the + * context. Then we make sure the indexes for the new row are valid. + */ +int +${i}_extract_index( ${i}_context * ctx, netsnmp_index * hdr ) +{ + /* + * temporary local storage for extracting oid index + * + * extract index uses varbinds (netsnmp_variable_list) to parse + * the index OID into the individual components for each index part. + */ + @if $ext_index != 0@ + /** TODO: add storage for external index(s)! */ + @end@ + @eval $first_idx = ""@ + @foreach $idx index@ + @if "$first_idx" eq ""@ + @eval $first_idx = $idx@ + @end@ + netsnmp_variable_list var_$idx; + @end@ + int err; + + /* + * copy index, if provided + */ + if(hdr) { + netsnmp_assert(ctx->index.oids == NULL); + if((hdr->len > MAX_OID_LEN) || + snmp_clone_mem( (void*)&ctx->index.oids, hdr->oids, + hdr->len * sizeof(oid) )) { + return -1; + } + ctx->index.len = hdr->len; + } + + /* + * initialize variable that will hold each component of the index. + * If there are multiple indexes for the table, the variable_lists + * need to be linked together, in order. + */ + @if $ext_index != 0@ + /** TODO: add code for external index(s)! */ + @end@ + @foreach $idx index@ + memset( &var_$idx, 0x00, sizeof(var_$idx) ); + var_${idx}.type = $idx.type; /* type hint for parse_oid_indexes */ + /** TODO: link this index to the next, or NULL for the last one */ +#ifdef TABLE_CONTAINER_TODO + snmp_log(LOG_ERR, "${i}_extract_index index list not implemented!\n" ); + return 0; +#else + var_${idx}.next_variable = &var_XX; +#endif + + @end@ + + /* + * parse the oid into the individual index components + */ + err = parse_oid_indexes( hdr->oids, hdr->len, &var_$first_idx ); + if (err == SNMP_ERR_NOERROR) { + /* + * copy index components into the context structure + */ + @foreach $idx index@ + @eval $found = "external"@ + @foreach $c column@ + @if "$idx" eq "$c"@ + @eval $found = "internal"@ + @end@ + @end@ + @if "$found" eq "external"@ + /** skipping external index $idx */ + @end@ + @if "$found" eq "internal"@ + @eval $have_type = 0@ + @if "$idx.type" eq "ASN_OCTET_STR"@ + @eval $have_type = 1@ + if(var_${idx}.val_len > sizeof(ctx->$idx)) + err = -1; + else + memcpy( ctx->$idx, var_${idx}.val.string, var_${idx}.val_len ); + ctx->${idx}_len = var_${idx}.val_len; + @end@ + @if "$idx.type" eq "ASN_OBJECT_ID"@ + @eval $have_type = 1@ + memcpy( ctx->$idx, var_${idx}.val.string, var_${idx}.val_len ); + ctx->${idx}_len = var_${idx}.val_len; + @end@ + @if $have_type == 0@ + ctx->$idx = *var_${idx}.val.integer; + @end@ + @end@ + + @end@ + + @foreach $c index@ + /* + * TODO: check index for valid values. For EXAMPLE: + * + @eval $have_check = 0@ + @if "$c.type" eq "ASN_IPADDRESS"@ + @eval $have_check = 1@ + * if ( XXX_check_ip( *var_${c}.val.integer ) ) { + @end@ + @if "$c.type" eq "ASN_OBJECT_ID"@ + @eval $have_check = 1@ + * if ( XXX_check_oid( var_${c}.val.objid, var_${c}.val_len / + sizeof(oid) ) ) { + @end@ + @if "$c.type" eq "ASN_OCTET_STR"@ + @eval $have_check = 1@ + * if ( XXX_check_value( var_${c}.val.string, XXX ) ) { + @end@ + @if $have_check != 1@ + * if ( *var_${c}.val.integer != XXX ) { + @end@ + * err = -1; + * } + */ + @end@ + } + + /* + * parsing may have allocated memory. free it. + */ + snmp_reset_var_buffers( &var_$first_idx ); + + return err; +} + +@if "$rs_name" ne ""@ +/************************************************************ + * the *_can_activate routine is called + * when a row is changed to determine if all the values + * set are consistent with the row's rules for a row status + * of ACTIVE. + * + * return 1 if the row could be ACTIVE + * return 0 if the row is not ready for the ACTIVE state + */ +int ${i}_can_activate(${i}_context *undo_ctx, + ${i}_context *row_ctx, + netsnmp_request_group * rg) +{ + /* + * TODO: check for activation requirements here + */ + + + /* + * be optimistic. + */ + return 1; +} + +/************************************************************ + * the *_can_deactivate routine is called when a row that is + * currently ACTIVE is set to a state other than ACTIVE. If + * there are conditions in which a row should not be allowed + * to transition out of the ACTIVE state (such as the row being + * referred to by another row or table), check for them here. + * + * return 1 if the row can be set to a non-ACTIVE state + * return 0 if the row must remain in the ACTIVE state + */ +int ${i}_can_deactivate(${i}_context *undo_ctx, + ${i}_context *row_ctx, + netsnmp_request_group * rg) +{ + /* + * TODO: check for deactivation requirements here + */ + return 1; +} + +@end@ +/************************************************************ + * the *_can_delete routine is called to determine if a row + * can be deleted. + * + * return 1 if the row can be deleted + * return 0 if the row cannot be deleted + */ +int ${i}_can_delete(${i}_context *undo_ctx, + ${i}_context *row_ctx, + netsnmp_request_group * rg) +{ +@if "$rs_name" ne ""@ + /* + * probably shouldn't delete a row that we can't + * deactivate. + */ + if(${i}_can_deactivate(undo_ctx,row_ctx,rg) != 1) + return 0; +@end@ + + /* + * TODO: check for other deletion requirements here + */ + return 1; +} + +@if $i.creatable@ +/************************************************************ + * the *_create_row routine is called by the table handler + * to create a new row for a given index. If you need more + * information (such as column values) to make a decision + * on creating rows, you must create an initial row here + * (to hold the column values), and you can examine the + * situation in more detail in the *_set_reserve1 or later + * states of set processing. Simple check for a NULL undo_ctx + * in those states and do detailed creation checking there. + * + * returns a newly allocated ${i}_context + * structure if the specified indexes are not illegal + * returns NULL for errors or illegal index values. + */ +${i}_context * +${i}_create_row( netsnmp_index* hdr) +{ + ${i}_context * ctx = + SNMP_MALLOC_TYPEDEF(${i}_context); + if(!ctx) + return NULL; + + /* + * TODO: check indexes, if necessary. + */ + if(${i}_extract_index( ctx, hdr )) { + if (NULL != ctx->index.oids) + free(ctx->index.oids); + free(ctx); + return NULL; + } + + /* netsnmp_mutex_init(ctx->lock); + netsnmp_mutex_lock(ctx->lock); */ + + /* + * TODO: initialize any default values here. This is also + * first place you really should allocate any memory for + * yourself to use. If you allocated memory earlier, + * make sure you free it for earlier error cases! + */ + /** + @foreach $c column@ + @if $c.settable@ + ctx->$c = 0; + @end@ + @end@ + */ + + return ctx; +} +@end@ + +/************************************************************ + * the *_duplicate row routine + */ +${i}_context * +${i}_duplicate_row( ${i}_context * row_ctx) +{ + ${i}_context * dup; + + if(!row_ctx) + return NULL; + + dup = SNMP_MALLOC_TYPEDEF(${i}_context); + if(!dup) + return NULL; + + if(${i}_row_copy(dup,row_ctx)) { + free(dup); + dup = NULL; + } + + return dup; +} + +/************************************************************ + * the *_delete_row method is called to delete a row. + */ +netsnmp_index * ${i}_delete_row( ${i}_context * ctx ) +{ + /* netsnmp_mutex_destroy(ctx->lock); */ + + if(ctx->index.oids) + free(ctx->index.oids); + + /* + * TODO: release any memory you allocated here... + */ + + /* + * release header + */ + free( ctx ); + + return NULL; +} + + +/************************************************************ + * RESERVE is used to check the syntax of all the variables + * provided, that the values being set are sensible and consistent, + * and to allocate any resources required for performing the SET. + * After this stage, the expectation is that the set ought to + * succeed, though this is not guaranteed. (In fact, with the UCD + * agent, this is done in two passes - RESERVE1, and + * RESERVE2, to allow for dependancies between variables). + * + * BEFORE calling this routine, the agent will call duplicate_row + * to create a copy of the row (unless this is a new row; i.e. + * row_created == 1). + * + * next state -> SET_RESERVE2 || SET_FREE + */ +void ${i}_set_reserve1( netsnmp_request_group *rg ) +{ + ${i}_context *row_ctx = + (${i}_context *)rg->existing_row; + ${i}_context *undo_ctx = + (${i}_context *)rg->undo_info; + netsnmp_variable_list *var; + netsnmp_request_group_item *current; + int rc; + + @if "$st_name" ne ""@ + /* + * Block all attempts to modify a readOnly row + */ + if( row_ctx && (row_ctx->$st_name == SNMP_STORAGE_READONLY) ) { + netsnmp_set_mode_request_error(MODE_SET_BEGIN, rg->list->ri, + SNMP_ERR_NOTWRITABLE); + return; + } + @end@ + + /* + * TODO: loop through columns, check syntax and lengths. For + * columns which have no dependencies, you could also move + * the value/range checking here to attempt to catch error + * cases as early as possible. + */ + for( current = rg->list; current; current = current->next ) { + + var = current->ri->requestvb; + rc = SNMP_ERR_NOERROR; + + switch(current->tri->colnum) { + + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + /** $c.syntax = $c.type */ + @if $c.needlength@ + /* or possibly 'netsnmp_check_vb_type_and_size' */ + rc = netsnmp_check_vb_type_and_max_size(var, $c.type, + sizeof(row_ctx->$c)); + @else@ + /* or possibly 'netsnmp_check_vb_int_range' */ + rc = netsnmp_check_vb_int( var ); + @end@ + break; + + @end@ + @end@ + default: /** We shouldn't get here */ + rc = SNMP_ERR_GENERR; + snmp_log(LOG_ERR, "unknown column in " + "${i}_set_reserve1\n"); + } + + if (rc) + netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc ); + rg->status = SNMP_MAX( rg->status, current->ri->status ); + } + + /* + * done with all the columns. Could check row related + * requirements here. + */ +} + +void ${i}_set_reserve2( netsnmp_request_group *rg ) +{ + ${i}_context *row_ctx = (${i}_context *)rg->existing_row; + ${i}_context *undo_ctx = (${i}_context *)rg->undo_info; + netsnmp_request_group_item *current; + netsnmp_variable_list *var; + int rc; + + rg->rg_void = rg->list->ri; + + /* + * TODO: loop through columns, check for valid + * values and any range constraints. + */ + for( current = rg->list; current; current = current->next ) { + + var = current->ri->requestvb; + rc = SNMP_ERR_NOERROR; + + switch(current->tri->colnum) { + + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + /** $c.syntax = $c.type */ + @eval $have_check = 0@ + @if "$c" eq "$st_name"@ + @eval $have_check = 1@ + rc = netsnmp_check_vb_storagetype(current->ri->requestvb, + undo_ctx ? + undo_ctx->$c:0); + @end@ + @if "$c" eq "$rs_name"@ + @eval $have_check = 1@ + rc = netsnmp_check_vb_rowstatus(current->ri->requestvb, + undo_ctx ? + undo_ctx->$c:0); + rg->rg_void = current->ri; + @end@ + @if "$c.syntax" eq "TruthValue"@ + @eval $have_check = 1@ + rc = netsnmp_check_vb_truthvalue(current->ri->requestvb); + @end@ + @if $have_check == 0@ + /* + * TODO: routine to check valid values + * + * EXAMPLE: + * + @if "$c.type" eq "ASN_IPADDRESS"@ + @eval $have_check = 1@ + * if ( XXX_check_ip( *var->val.integer ) ) { + @end@ + @if "$c.type" eq "ASN_OBJECT_ID"@ + @eval $have_check = 1@ + * if ( XXX_check_oid( var ) ) { + @end@ + @if "$c.type" eq "ASN_OCTET_STR"@ + @eval $have_check = 1@ + * if ( XXX_check_value( var->val.string, XXX ) ) { + @end@ + @if $have_check != 1@ + * if ( *var->val.integer != XXX ) { + @end@ + * rc = SNMP_ERR_INCONSISTENTVALUE; + * rc = SNMP_ERR_BADVALUE; + * } + */ + @end@ + break; + + @end@ + @end@ + default: /** We shouldn't get here */ + netsnmp_assert(0); /** why wasn't this caught in reserve1? */ + } + + if (rc) + netsnmp_set_mode_request_error(MODE_SET_BEGIN, current->ri, rc); + } + + /* + * done with all the columns. Could check row related + * requirements here. + */ +} + +/************************************************************ + * Assuming that the RESERVE phases were successful, the next + * stage is indicated by the action value ACTION. This is used + * to actually implement the set operation. However, this must + * either be done into temporary (persistent) storage, or the + * previous value stored similarly, in case any of the subsequent + * ACTION calls fail. + * + * In your case, changes should be made to row_ctx. A copy of + * the original row is in undo_ctx. + */ +void ${i}_set_action( netsnmp_request_group *rg ) +{ + netsnmp_variable_list *var; + ${i}_context *row_ctx = (${i}_context *)rg->existing_row; + ${i}_context *undo_ctx = (${i}_context *)rg->undo_info; + netsnmp_request_group_item *current; + + @if "$rs_name" ne ""@ + int row_err = 0; + @end@ + + /* + * TODO: loop through columns, copy varbind values + * to context structure for the row. + */ + for( current = rg->list; current; current = current->next ) { + + var = current->ri->requestvb; + + switch(current->tri->colnum) { + + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + /** $c.syntax = $c.type */ + @eval $have_type = 0@ + @if "$c.type" eq "ASN_OCTET_STR"@ + @eval $have_type = 1@ + memcpy(row_ctx->$c,var->val.string,var->val_len); + row_ctx->${c}_len = var->val_len; + @end@ + @if "$c.type" eq "ASN_OBJECT_ID"@ + @eval $have_type = 1@ + memcpy(row_ctx->$c,var->val.objid,var->val_len); + row_ctx->${c}_len = var->val_len; + @end@ + @if $have_type == 0@ + row_ctx->$c = *var->val.integer; + @end@ + break; + + @end@ + @end@ + default: /** We shouldn't get here */ + netsnmp_assert(0); /** why wasn't this caught in reserve1? */ + } + } + + /* + * done with all the columns. Could check row related + * requirements here. + */ + @if "$rs_name" ne ""@ +#ifndef ${i}_CAN_MODIFY_ACTIVE_ROW + if( undo_ctx && RS_IS_ACTIVE(undo_ctx->$rs_name) && + row_ctx && RS_IS_ACTIVE(row_ctx->$rs_name) ) { + row_err = 1; + } +#endif + + /* + * check activation/deactivation + */ + row_err = netsnmp_table_array_check_row_status(&cb, rg, + row_ctx ? &row_ctx->$rs_name : NULL, + undo_ctx ? &undo_ctx->$rs_name : NULL); + if(row_err) { + netsnmp_set_mode_request_error(MODE_SET_BEGIN, + (netsnmp_request_info*)rg->rg_void, + row_err); + return; + } + + @end@ + /* + * TODO: if you have dependencies on other tables, this would be + * a good place to check those, too. + */ +} + +/************************************************************ + * Only once the ACTION phase has completed successfully, can + * the final COMMIT phase be run. This is used to complete any + * writes that were done into temporary storage, and then release + * any allocated resources. Note that all the code in this phase + * should be "safe" code that cannot possibly fail (cue + * hysterical laughter). The whole intent of the ACTION/COMMIT + * division is that all of the fallible code should be done in + * the ACTION phase, so that it can be backed out if necessary. + * + * BEFORE calling this routine, the agent will update the + * container (inserting a row if row_created == 1, or removing + * the row if row_deleted == 1). + * + * AFTER calling this routine, the agent will delete the + * undo_info. + */ +void ${i}_set_commit( netsnmp_request_group *rg ) +{ + netsnmp_variable_list *var; + ${i}_context *row_ctx = (${i}_context *)rg->existing_row; + ${i}_context *undo_ctx = (${i}_context *)rg->undo_info; + netsnmp_request_group_item *current; + + /* + * loop through columns + */ + for( current = rg->list; current; current = current->next ) { + + var = current->ri->requestvb; + + switch(current->tri->colnum) { + + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + /** $c.syntax = $c.type */ + break; + + @end@ + @end@ + default: /** We shouldn't get here */ + netsnmp_assert(0); /** why wasn't this caught in reserve1? */ + } + } + + /* + * done with all the columns. Could check row related + * requirements here. + */ +} + +/************************************************************ + * If either of the RESERVE calls fail, the write routines + * are called again with the FREE action, to release any resources + * that have been allocated. The agent will then return a failure + * response to the requesting application. + * + * AFTER calling this routine, the agent will delete undo_info. + */ +void ${i}_set_free( netsnmp_request_group *rg ) +{ + netsnmp_variable_list *var; + ${i}_context *row_ctx = (${i}_context *)rg->existing_row; + ${i}_context *undo_ctx = (${i}_context *)rg->undo_info; + netsnmp_request_group_item *current; + + /* + * loop through columns + */ + for( current = rg->list; current; current = current->next ) { + + var = current->ri->requestvb; + + switch(current->tri->colnum) { + + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + /** $c.syntax = $c.type */ + break; + + @end@ + @end@ + default: /** We shouldn't get here */ + /** should have been logged in reserve1 */ + } + } + + /* + * done with all the columns. Could check row related + * requirements here. + */ +} + +/************************************************************ + * If the ACTION phase does fail (for example due to an apparently + * valid, but unacceptable value, or an unforeseen problem), then + * the list of write routines are called again, with the UNDO + * action. This requires the routine to reset the value that was + * changed to its previous value (assuming it was actually changed), + * and then to release any resources that had been allocated. As + * with the FREE phase, the agent will then return an indication + * of the error to the requesting application. + * + * BEFORE calling this routine, the agent will update the container + * (remove any newly inserted row, re-insert any removed row). + * + * AFTER calling this routing, the agent will call row_copy + * to restore the data in existing_row from the date in undo_info. + * Then undo_info will be deleted (or existing row, if row_created + * == 1). + */ +void ${i}_set_undo( netsnmp_request_group *rg ) +{ + netsnmp_variable_list *var; + ${i}_context *row_ctx = (${i}_context *)rg->existing_row; + ${i}_context *undo_ctx = (${i}_context *)rg->undo_info; + netsnmp_request_group_item *current; + + /* + * loop through columns + */ + for( current = rg->list; current; current = current->next ) { + + var = current->ri->requestvb; + + switch(current->tri->colnum) { + + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + /** $c.syntax = $c.type */ + break; + + @end@ + @end@ + default: /** We shouldn't get here */ + netsnmp_assert(0); /** why wasn't this caught in reserve1? */ + } + } + + /* + * done with all the columns. Could check row related + * requirements here. + */ +} + +@end@ + + +/************************************************************ + * + * Initialize the $i table by defining its contents and how it's structured + */ +void +initialize_table_$i(void) +{ + netsnmp_table_registration_info *table_info; + + if(my_handler) { + snmp_log(LOG_ERR, "initialize_table_${i}_handler called again\n"); + return; + } + + memset(&cb, 0x00, sizeof(cb)); + + /** create the table structure itself */ + table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); + + my_handler = netsnmp_create_handler_registration("$i", + netsnmp_table_array_helper_handler, + ${i}_oid, + ${i}_oid_len, +@if $i.settable@ + HANDLER_CAN_RWRITE +@else@ + HANDLER_CAN_RONLY +@end@ + ); + + if (!my_handler || !table_info) { + snmp_log(LOG_ERR, "malloc failed in " + "initialize_table_${i}_handler\n"); + return; /** mallocs failed */ + } + + /*************************************************** + * Setting up the table's definition + */ + /* + * TODO: add any external indexes here. + */ + @if $ext_index != 0@ + /** TODO: add code for external index(s)! */ + @end@ + + /* + * internal indexes + */ + @foreach $idx index@ + /** index: $idx */ + netsnmp_table_helper_add_index(table_info, $idx.type); + @end@ + + table_info->min_column = ${i}_COL_MIN; + table_info->max_column = ${i}_COL_MAX; + + /*************************************************** + * registering the table with the master agent + */ + cb.get_value = ${i}_get_value; + cb.container = netsnmp_container_find("${i}_primary:" + "${i}:" + "table_container"); +#ifdef ${i}_CUSTOM_SORT + netsnmp_container_add_index(cb.container, + netsnmp_container_find("${i}_custom:" + "${i}:" + "table_container")); + cb.container->next->compare = ${i}_cmp; +#endif +@if $i.settable@ + cb.can_set = 1; +@if $i.creatable@ + cb.create_row = (UserRowMethod*)${i}_create_row; +@end@ + cb.duplicate_row = (UserRowMethod*)${i}_duplicate_row; + cb.delete_row = (UserRowMethod*)${i}_delete_row; + cb.row_copy = (Netsnmp_User_Row_Operation *)${i}_row_copy; + +@if "$rs_name" ne ""@ + cb.can_activate = (Netsnmp_User_Row_Action *)${i}_can_activate; + cb.can_deactivate = (Netsnmp_User_Row_Action *)${i}_can_deactivate; +@end@ + cb.can_delete = (Netsnmp_User_Row_Action *)${i}_can_delete; + + cb.set_reserve1 = ${i}_set_reserve1; + cb.set_reserve2 = ${i}_set_reserve2; + cb.set_action = ${i}_set_action; + cb.set_commit = ${i}_set_commit; + cb.set_free = ${i}_set_free; + cb.set_undo = ${i}_set_undo; +@end@ + DEBUGMSGTL(("initialize_table_$i", + "Registering table $i " + "as a table array\n")); + netsnmp_table_container_register(my_handler, table_info, &cb, + cb.container, 1); +} + +/************************************************************ + * ${i}_get_value + * + * This routine is called for get requests to copy the data + * from the context to the varbind for the request. If the + * context has been properly maintained, you don't need to + * change in code in this fuction. + */ +int ${i}_get_value( + netsnmp_request_info *request, + netsnmp_index *item, + netsnmp_table_request_info *table_info ) +{ + netsnmp_variable_list *var = request->requestvb; + ${i}_context *context = (${i}_context *)item; + + switch(table_info->colnum) { + + @foreach $c column@ + @if $c.readable@ + @eval $have_type = 0@ + case COLUMN_$c.uc: + /** $c.syntax = $c.type */ + @if "$c.type" eq "ASN_OBJECT_ID"@ + @eval $have_type = 1@ + snmp_set_var_typed_value(var, $c.type, + (char*)&context->$c, + context->${c}_len ); + @end@ + @if "$c.type" eq "ASN_OCTET_STR"@ + @eval $have_type = 1@ + snmp_set_var_typed_value(var, $c.type, + (char*)&context->$c, + context->${c}_len ); + @end@ + @if $have_type == 0@ + snmp_set_var_typed_value(var, $c.type, + (char*)&context->$c, + sizeof(context->$c) ); + @end@ + break; + + @end@ + @end@ + default: /** We shouldn't get here */ + snmp_log(LOG_ERR, "unknown column in " + "${i}_get_value\n"); + return SNMP_ERR_GENERR; + } + return SNMP_ERR_NOERROR; +} + +/************************************************************ + * ${i}_get_by_idx + */ +const ${i}_context * +${i}_get_by_idx(netsnmp_index * hdr) +{ + return (const ${i}_context *) + CONTAINER_FIND(cb.container, hdr ); +} + + +@end@ diff --git a/local/mib2c.check_values.conf b/local/mib2c.check_values.conf new file mode 100644 index 0000000..259ac1f --- /dev/null +++ b/local/mib2c.check_values.conf @@ -0,0 +1,154 @@ +@run mib2c.check_values_local.conf@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}_checkfns.h@ +/* + * Note: this file originally auto-generated by mib2c using + * : mib2c.iterate.conf,v 5.6 2003/02/20 00:52:07 hardaker Exp $ + */ + +/*********************************************************************** + * This file is auto-generated and SHOULD NOT BE EDITED by hand. + * Modify the ${name}_checkfns_local.[ch] files insead. + * (so that you can regenerate this one as mib2c improvements are made) + ***********************************************************************/ +#ifndef $name.uc_CHECKFNS_H +#define $name.uc_CHECKFNS_H + +/** make sure we load the functions that you can modify */ +config_require(${name}_checkfns_local) + +@foreach $t table@ +/* these functions are designed to check incoming values for +columns in the $t table for legality with respect to +datatype and value. + */ + + @foreach $i column@ + @if $i.access =~ /(Write|Create)/@ + int check_${i}(int type, $i.decl *val, size_t val_len, $i.decl *old_val, size_t old_val_len); + @end@ + @end@ +@end@ + +#endif /* $name.uc_CHECKFNS_H */ + +###################################################################### +## Do the .c file +###################################################################### +@open ${name}_checkfns.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +/******************************************************************** + * NOTE NOTE NOTE + * This file is auto-generated and SHOULD NOT BE EDITED by hand. + * Modify the ${name}_checkfns_local.[ch] files insead so that you + * can regenerate this one as mib2c improvements are made. + ********************************************************************/ + +/* standard headers */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include "${name}_checkfns.h" +#include "${name}_checkfns_local.h" +#include "${name}_enums.h" +@run mib2c.column_enums.conf@ + +@foreach $t table@ + @foreach $i column@ + @if $i.access =~ /(Write|Create)/@ +/** Decides if an incoming value for the $i mib node is legal. + * @param type The incoming data type. + * @param val The value to be checked. + * @param val_len The length of data stored in val (in bytes). + * @return 0 if the incoming value is legal, an SNMP error code otherwise. + */ + int + check_${i}(int type, $i.decl *val, size_t val_len, + $i.decl *old_val, size_t old_val_len) { + + int ret; + + /** Check to see that we were called legally */ + if (!val) + return SNMP_ERR_GENERR; + + /** Check the incoming type for correctness */ + if (type != $i.type) + return SNMP_ERR_WRONGTYPE; + + @if $i.type =~ /int/i@ + @eval $x = 0@ + @foreach $l, $v enum@ + @if $x == 0@ + /** Check the enums. Legal values will continue, others return error. */ + switch (*val) { + @eval $x = 1@ + @end@ + case $i.uc_$l.uc: + @end@ + @if $x == 1@ + break; + + /** not a legal enum value. return an error */ + default: + return SNMP_ERR_INCONSISTENTVALUE; + } + @end@ + ret = SNMP_ERR_NOERROR; + @eval $x = 0@ + @foreach $min $max range $i@ + @if $x == 0@ + /** Check the ranges of the passed value for legality */ + @eval $x = 1@ + if ( + @else@ + && + @end@ + !(*val >= $min && *val <= $max) + @end@ + @if $x != 0@ + ) { + return SNMP_ERR_WRONGVALUE; + } + @end@ + @end@ + @if $i.type =~ /str/i@ + @eval $x = 0@ + @foreach $min $max range $i@ + @if $x == 0@ + /** Check the ranges of the passed value for legality */ + @eval $x = 1@ + if ( + @else@ + && + @end@ + !(val_len >= $min && val_len <= $max) + @end@ + @if $x == 1@ + ) { + return SNMP_ERR_WRONGVALUE; + } + @end@ + @end@ + + @if "$i.syntax" eq "RowStatus"@ + if (ret = check_rowstatus_transition((old_val) ? *old_val : RS_NONEXISTENT, *val)) + return ret; + @end@ + @if "$i.syntax" eq "StorageType"@ + if (ret = check_storage_transition((old_val) ? *old_val : SNMP_STORAGE_NONE, *val)) + return ret; + @end@ + + /** looks ok, call the local version of the same function. */ + return check_${i}_local(type, val, val_len, old_val, old_val_len); + } + @end@ + @end@ +@end@ diff --git a/local/mib2c.check_values_local.conf b/local/mib2c.check_values_local.conf new file mode 100644 index 0000000..13c1e60 --- /dev/null +++ b/local/mib2c.check_values_local.conf @@ -0,0 +1,72 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}_checkfns_local.h@ +/* + * Note: this file originally auto-generated by mib2c using + * : $Id$ + * + */ +#ifndef $name.uc_CHECKFNS_H +#define $name.uc_CHECKFNS_H + +@foreach $t table@ +/* these functions are designed to check incoming values for +columns in the $t table for legality with respect to +datatype and value according to local conventions. You should modify +them as appropriate. They will be called from parent check_value +functions that are auto-generated using mib2c and the parent functions +should NOT be modified. + */ + + @foreach $i column@ + @if $i.access =~ /(Write|Create)/@ + int check_${i}_local(int type, $i.decl *val, size_t val_len, $i.decl *old_val, size_t old_val_len); + @end@ + @end@ +@end@ + +#endif /* $name.uc_CHECKFNS_H */ + +###################################################################### +## Do the .c file +###################################################################### +@open ${name}_checkfns_local.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +/* standard headers */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include "${name}_checkfns.h" +#include "${name}_enums.h" +@run mib2c.column_enums.conf@ + +@foreach $t table@ + @foreach $i column@ + @if $i.access =~ /(Write|Create)/@ +/** Decides if an incoming value for the $i mib node is legal, from a local implementation specific viewpoint. + * @param type The incoming data type. + * @param val The value to be checked. + * @param val_len The length of data stored in val (in bytes). + * @return 0 if the incoming value is legal, an SNMP error code otherwise. + */ + int + check_${i}_local(int type, $i.decl *val, size_t val_len, $i.decl *old_val, size_t old_val_len) { + + /** XXX: you may want to check aspects of the new value that + were not covered by the automatic checks by the parent function. */ + + /** XXX: you make want to check that the requested change from + the old value to the new value is legal (ie, the transistion + from one value to another is legal */ + + /** if everything looks ok, return SNMP_ERR_NOERROR */ + return SNMP_ERR_NOERROR; + } + @end@ + @end@ +@end@ diff --git a/local/mib2c.column_defines.conf b/local/mib2c.column_defines.conf new file mode 100644 index 0000000..5b8328f --- /dev/null +++ b/local/mib2c.column_defines.conf @@ -0,0 +1,15 @@ +@open ${name}_columns.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_COLUMNS_H +#define $name.uc_COLUMNS_H +@foreach $i table@ + +/* column number definitions for table $i */ + @foreach $c column@ + #define COLUMN_$c.uc $c.subid + @end@ +@end@ +#endif /* $name.uc_COLUMNS_H */ diff --git a/local/mib2c.column_enums.conf b/local/mib2c.column_enums.conf new file mode 100644 index 0000000..4dba4fa --- /dev/null +++ b/local/mib2c.column_enums.conf @@ -0,0 +1,34 @@ +@open ${name}_enums.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_ENUMS_H +#define $name.uc_ENUMS_H +@foreach $i table@ + @foreach $c column@ + @eval $x = 0@ + @foreach $e $v enum@ + @if $x == 0@ + +/* enums for column $c */ + @eval $x = 1@ + @end@ + #define $c.uc_$e.uc $v + @end@ + @end@ +@end@ + +@foreach $s scalar@ + @eval $x = 0@ + @foreach $e $v enum@ + @if $x == 0@ + +/* enums for scalar $s */ + @eval $x = 1@ + @end@ +#define $s.uc_$e.uc $v + @end@ +@end@ + +#endif /* $name.uc_ENUMS_H */ diff --git a/local/mib2c.column_storage.conf b/local/mib2c.column_storage.conf new file mode 100644 index 0000000..9f3ba1f --- /dev/null +++ b/local/mib2c.column_storage.conf @@ -0,0 +1,23 @@ +############################################################# -*- c -*- +## top level mfd conf file +## $Id$ +######################################################################## +@ open ${name}_storage.h@ +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +## +## +@foreach $table table@ +@ include m2c_setup_table.m2i@ +@ eval $m2c_data_context = "generated"@ +@ include generic-table-indexes.m2i@ +@ include generic-data-context.m2i@ +@end@ # table +## +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c.conf b/local/mib2c.conf new file mode 100644 index 0000000..c71968c --- /dev/null +++ b/local/mib2c.conf @@ -0,0 +1,308 @@ +@open -@ +mib2c has multiple configuration files depending on the type of +code you need to write. You must pick one depending on your need. + +You requested mib2c to be run on the following part of the MIB tree: + OID: $name + numeric translation: $name.objectID +@eval $numS = count_scalars@ + number of scalars within: $numS +@eval $numT = count_tables@ + number of tables within: $numT +@eval $numN = count_notifications@ + number of notifications within: $numN + +First, do you want to generate code that is compatible with the +ucd-snmp 4.X line of code, or code for the newer Net-SNMP 5.X code +base (which provides a much greater choice of APIs to pick from): + + 1) ucd-snmp style code + 2) Net-SNMP style code + +@prompt $ans Select your choice : @ +@if $ans == 1@ +********************************************************************** + GENERATING CODE FOR THE 4.X LINE OF CODE (THE OLDER API) +********************************************************************** + + using the mib2c.old-api.conf configuration file to generate your code. + @run mib2c.old-api.conf@ + +@elsif $ans != 2@ +Invalid answer. +@else@ + @if $numS > 0 && $numT > 0@ +********************************************************************** + MIXED MIB TEMPLATE +********************************************************************** +The portion of the MIB tree that you have selected contains both +scalar objects and MIB tables. The automatically generated Net-SNMP +style code cannot handle both of these simultaneously (though you +could generate the two files separately, and then merge the two). + +Which code do you want to generate: + + 1) Scalar objects + 2) MIB tables + + @prompt $ans Select your choice : @ + @if $ans == 1 @ + @eval $numT = 0@ + @elsif $ans == 2@ + @eval $numS = 0@ + @else@ +Invalid answer + @eval $numS = 0@ + @eval $numT = 0@ + @end@ + @end@ +@if $numS > 0@ + +********************************************************************** + GENERATING CODE FOR SCALAR OBJECTS: +********************************************************************** + + It looks like you have some scalars in the mib you requested, so I + will now generate code for them if you wish. You have two choices + for scalar API styles currently. Pick between them, or choose not + to generate any code for the scalars: + + 1) If you're writing code for some generic scalars + (by hand use: "mib2c -c mib2c.scalar.conf $name") + + 2) If you want to magically "tie" integer variables to integer + scalars + (by hand use: "mib2c -c mib2c.int_watch.conf $name") + + 3) Don't generate any code for the scalars + + @prompt $ans Select your choice: @ + @if $ans == 1@ + using the mib2c.scalar.conf configuration file to generate your code. + @run mib2c.scalar.conf@ + @elsif $ans == 2@ + using the mib2c.int_watch.conf configuration file to generate your code. + @run mib2c.int_watch.conf@ + @elsif $ans != 3@ + WARNING: Unknown response. Skipping code generation for scalars. + @end@ +@end@ # scalars + +@if $numT > 0@ +********************************************************************** + GENERATING CODE FOR TABLES: +********************************************************************** + + The Net-SNMP agent API is extremely extensive and, in fact, lets + each programmer write agent code according to the style that works + best for them based on their experience and their preference. We're + going to ask you a serious of questions that will help mib2c + generate code that best suits *your* needs, as the programmer that + will be responsible for taking the code and further refining it. If + you don't like how the results look, you are always welcome to + re-run mib2c and select a different set of options. + + There are essentially two tasks involved in processing requests + for OIDs within a MIB table - firstly identifying the relevant row + of the table for a given request, and then returning (or updating) + the appropriate column value within that row. Many MIB tables model + the state of some external system (the kernel, a device, processes, + etc), and the MIB implementation module (the code we're about to + produce a template for) acts as an interface between this underlying + system and the SNMP side. Other tables hold data internally that is + only available within the agent itself, or at least the master copy + of the data is held within the agent. + + There are a number of different code templates that can be used to + implement MIB tables, using various approaches to these two tasks. + + There are three basic approaches to identifying the relevant row: + + 1) Pass the request through to the table-specific code, and + identify the requested row there (for both GET and GETNEXT + requests). This is typically the most efficient way to get + up-to-date information, but relies on suitable + (programmer-provided) code within the MIB handler. + Most importantly, you should be an expert to use this choice. + + This will produce code based on the table_dataset handler. + + 2) Have table-specific code to provide information about which + rows exist in the table (by iterating through them in turn), + but utilise standard helper code to select the appropriate + row for a given request. This is particularly suitable for + tables where the data is naturally stored in a "random" order + (or differently to the MIB table index), or where rows are + frequently added to or removed from the table. + + However searching for the requested row is not very efficient, + and performance can be slow - particularly for large tables with + many rows. + + 3) Hold a locally cached copy of the contents of the table (or at + least a cache of which rows are valid), and utilise standard + helper code to select the appropriate row. This is + significantly faster than the iterator-based approach, but + cached data is inevitably slightly "stale" with respect to the + data from the underlying system being managed. This approach, + since it relies on caching of data, is also results in a larger + memory footprint. It is less appropriate for tables where rows + are frequently added or removed externally to the agent (i.e., + not via SNMP requests). + + This approach can also be used where _all_ use of the table is + via SNMP, and there is no external "underlying system". In + this case, the local cache is the canonical version of the + table. + + 4) Do not generate code for the tables. + + @prompt $ans1 Select the option that best fits your requirements: @ + + @if ($ans1 == 2) || ($ans1 == 3)@ + + Having identified the appropriate row for a given request, there are + three basic styles of code for returning (or updating) the requested + column value from within this row: + + 1) A single handler routine, which contains all the code needed to + handle GET and SET requests for each of the column objects. + +@if $ans1 == 2@ + The code typically looks like a single function with a large 'case' + statement covering each of the columns. + + This will produce code based on the 'iterator' hepler. +@end@ + + 2) A set of individual routines, each of which is concerned + with a particular aspect of processing the request. + @if $ans1 == 2 @ + Each column object within the table has one routine for + retrieving the current value, and another for setting a new one. + + This will produce code based on the 'iterate_access' hepler. + @else@ + There is one routine for reporting values for GET requests, + and one routine for each stage of processing a SET request. + @end@ + + 3) A (different) set of individual routines, each of which is + smaller and more tightly focused than the code generated by + style 2. The aim here is to reduce the amount of SNMP specific + knowledge required to implement a module, and hide much of the + SNMP terminology and processing within standard generated code + (which can simply be used sight unseen). +@if $name !~ /Table$/i@ + However this style of code can only be generated when mib2c + is run on an individual MIB table. To use this approach, you + will need to re-invoke mib2c with the name of a single MIB table. +@end@ + + This will produce code based on the 'mfd' hepler ('MIB for Dummies'). + + 4) Do not generate code for the tables. + + (In all cases, GETNEXT requests are automatically converted + into the equivalent GET request, so the MIB specific code + need only be concerned with GET and SET requests.). + + @prompt $ans2 Select the code style you wish to use: @ + @end@ + + @eval $template = NONE@ + @if $ans1 == 1@ + @eval $template = "create-dataset"@ + + @elsif $ans1 == 2@ + @if $ans2 == 1@ + @eval $template = iterate@ + @elsif $ans2 == 2@ + @eval $template = iterate_access@ + @elsif $ans2 == 3@ + @eval $template = mfd@ + @elsif $ans2 != 4@ + WARNING: Unknown response. Skipping code generation for tables. + @end@ + + @elsif $ans1 == 3@ + @if $ans2 == 1@ + There are actually two alternative templates that use this + style of code - differing primarily in the data structure + used for representing a row of the table + + 1) The first is well suited for situations where there is a + natural existing data structure, or where the contents of + the table may need to be interpreted for some additional + purpose, other than simply implementing the table in SNMP. + + This will produce code based on the 'table_data' hepler. + + 2) The second is slightly more efficient, but introduces some + minor constraints on the form of the per-row data structure. + + This will produce code based on the 'container' hepler. + + @prompt $ans3 Select the row representation you wish to use: @ + + @if $ans3 == 1@ + @eval $template = table_data@ + @elsif $ans3 == 2@ + @eval $template = container@ + @else@ + WARNING: Unknown response. Skipping code generation for tables. + @end@ + @elsif $ans2 == 2@ + @eval $template = "array-user"@ + @elsif $ans2 == 3@ + @eval $template = mfd@ + @else@ + WARNING: Unknown response. Skipping code generation for tables. + @end@ + + @elsif $ans1 != 4@ + WARNING: Unknown response. Skipping code generation for tables. + @end@ + + @if $template ne NONE@ + The same template code can be generated using + mib2c -c mib2c.${template}.conf $name + @run mib2c.${template}.conf@ + @end@ +@end@ # tables + +@if $numN > 0@ +********************************************************************** + GENERATING CODE FOR NOTIFICATIONS: +********************************************************************** + +Would you like to generate code for sending notifications from within +the agent? + + @prompt $ans "y" or "n": @ + @if ("$ans" eq "y") or ("$ans" eq "yes")@ + using mib2c.notify.conf to generate code for sending notifications + @run mib2c.notify.conf@ + @end@ + +# GENERATING HEADER FILE DEFINITIONS +# +# To generate just a header with a define for each column number in +# your table: +# +# mib2c -c mib2c.column_defines.conf ${name} +# +# To generate just a header with a define for each enum for any +# column containing enums: +# +# mib2c -c mib2c.column_enums.conf ${name} + +@end@ # notifications +@end@ # new style code + +********************************************************************** +* NOTE WELL: The code generated by mib2c is only a template. *YOU* * +* must fill in the code before it'll work most of the time. In many * +* cases, spots that MUST be edited within the files are marked with * +* /* XXX */ or /* TODO */ comments. * +********************************************************************** diff --git a/local/mib2c.container.conf b/local/mib2c.container.conf new file mode 100644 index 0000000..e76621f --- /dev/null +++ b/local/mib2c.container.conf @@ -0,0 +1,864 @@ +## -*- c -*- +## Portions of this file ar Copyright (C) Apple, Inc. +## Use is subject to license terms specified in the COPYING file +## distributed with the Net-SNMP package. +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/* function declarations */ +void init_$name(void); +@foreach $i table@ +void initialize_table_$i(void); +Netsnmp_Node_Handler ${i}_handler; +@end@ +@foreach $i table@ + +/* column number definitions for table $i */ + @foreach $c column@ + #define COLUMN_$c.uc $c.subid + @end@ +@end@ +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <net-snmp/agent/table_container.h> +#include "${name}.h" + +#ifdef ${i.uc}_USE_CACHE +static void _cache_free(netsnmp_cache * cache, void *magic); +static int _cache_load(netsnmp_cache * cache, void *vmagic); +#endif + +/** Initializes the $name module */ +void +init_$name(void) +{ + /* here we initialize all the tables we're planning on supporting */ + @foreach $i table@ + initialize_table_$i(); + @end@ +} +## +@foreach $i table@ +## Determine the first/last column names + @eval $first_column = "-"@ + @eval $last_column = "-"@ + @foreach $c column@ + @if $c.readable@ + @if "$first_column" eq "-"@ + @eval $first_column = $c@ + @end@ + @eval $last_column = $c@ + @end@ + @end@ + +/** Initialize the $i table by defining its contents and how it's structured */ +void +initialize_table_$i(void) +{ + const oid ${i}_oid[] = {$i.commaoid}; + const size_t ${i}_oid_len = OID_LENGTH(${i}_oid); + netsnmp_handler_registration *reg = NULL; + netsnmp_mib_handler *handler = NULL; + netsnmp_container *container = NULL; + netsnmp_table_registration_info *table_info = NULL; +#ifdef ${i.uc}_USE_CACHE + netsnmp_cache *cache = NULL; +#endif + + DEBUGMSGTL(("${name}:init", "initializing table $i\n")); + + reg = netsnmp_create_handler_registration( + "$i", ${i}_handler, + ${i}_oid, ${i}_oid_len, +@if $i.settable@ + HANDLER_CAN_RWRITE +@else@ + HANDLER_CAN_RONLY +@end@ + ); + if (NULL == reg) { + snmp_log(LOG_ERR,"error creating handler registration for $i\n"); + goto bail; + } +@if $i.settable@ + /** should a set on a non-existent row create a new one? */ + /** reg->modes |= HANDLER_CAN_NOT_CREATE; */ +@end@ + + container = netsnmp_container_find( "$i:table_container" ); + if (NULL == container) { + snmp_log(LOG_ERR,"error creating container for $i\n"); + goto bail; + } + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (NULL == table_info) { + snmp_log(LOG_ERR,"error allocating table registration for $i\n"); + goto bail; + } + netsnmp_table_helper_add_indexes(table_info, + @foreach $idx index@ + $idx.type, /* index: $idx */ + @end@ + 0); + table_info->min_column = COLUMN_$first_column.uc; + table_info->max_column = COLUMN_$last_column.uc; + + /************************************************* + * + * inject container_table helper + */ + handler = netsnmp_container_table_handler_get(table_info, container, + TABLE_CONTAINER_KEY_NETSNMP_INDEX); + if (NULL == handler) { + snmp_log(LOG_ERR,"error allocating table registration for $i\n"); + goto bail; + } + if (SNMPERR_SUCCESS != netsnmp_inject_handler(reg, handler)) { + snmp_log(LOG_ERR,"error injecting container_table handler for $i\n"); + goto bail; + } + handler = NULL; /* reg has it, will reuse below */ + +#ifdef ${i.uc}_USE_CACHE + /************************************************* + * + * inject cache helper + */ + cache = netsnmp_cache_create(30, /* timeout in seconds */ + _cache_load, _cache_free, + ${i}_oid, ${i}_oid_len); + + if (NULL == cache) { + snmp_log(LOG_ERR, "error creating cache for $i\n"); + goto bail; + } + cache->flags = NETSNMP_CACHE_DONT_INVALIDATE_ON_SET; + cache->magic = container; + + handler = netsnmp_cache_handler_get(cache); + if (NULL == handler) { + snmp_log(LOG_ERR, "error creating cache handler for $i\n"); + goto bail; + } + + if (SNMPERR_SUCCESS != netsnmp_inject_handler(reg, handler)) { + snmp_log(LOG_ERR,"error injecting cache handler for $i\n"); + goto bail; + } + handler = NULL; /* reg has it*/ +#endif + + /* + * register the table + */ + if (SNMPERR_SUCCESS != netsnmp_register_table(reg, table_info)) { + snmp_log(LOG_ERR,"error registering table handler for $i\n"); + reg = NULL; /* it was freed inside netsnmp_register_table */ + goto bail; + } + + /* + * Initialise the contents of the table here + */ + + + return; /* ok */ + + /* + * Some error occurred during registration. Clean up and bail. + */ + bail: /* not ok */ + + if (handler) + netsnmp_handler_free(handler); + +#ifdef ${i.uc}_USE_CACHE + if (cache) + netsnmp_cache_free(cache); +#endif + + if (table_info) + netsnmp_table_registration_info_free(table_info); + + if (container) + CONTAINER_FREE(container); + + if (reg) + netsnmp_handler_registration_free(reg); +} + +/** Typical data structure for a row entry */ +typedef struct ${i}_entry_s { + netsnmp_index oid_index; + + /* Index values */ + @foreach $idx index@ + @if $idx.needlength@ + $idx.decl $idx[NNN]; + size_t ${idx}_len; + @else@ + $idx.decl $idx; + @end@ + @end@ + + /* Column values */ + @foreach $c column@ + @if $c.readable@ + @if $c.needlength@ + $c.decl $c[NNN]; + size_t ${c}_len; + @else@ + $c.decl $c; + @end@ + @if $c.settable@ + @if !$c.rowstatus@ + @if $c.needlength@ + $c.decl old_$c[NNN]; + size_t old_${c}_len; + @else@ + $c.decl old_$c; + @end@ + @end@ + @end@ + @end@ + @end@ + + int valid; +} ${i}_entry; + +/** create a new row in the table */ +${i}_entry * +${i}_createEntry(netsnmp_container *container, + @foreach $idx index@ + @if $idx.needlength@ + , $idx.decl* $idx + , size_t ${idx}_len + @else@ + , $idx.decl $idx + @end@ + @end@ + ) { + ${i}_entry *entry; + + entry = SNMP_MALLOC_TYPEDEF(${i}_entry); + if (!entry) + return NULL; + + @foreach $idx index@ + @if $idx.needlength@ + memcpy(entry->$idx, $idx, ${idx}_len); + entry->${idx}_len = ${idx}_len; + @else@ + entry->$idx = $idx; + @end@ + @end@ + entry->oid_index.len = XXX; + entry->oid_index.oids = YYY; + CONTAINER_INSERT( container, entry ); + return entry; +} + +/** remove a row from the table */ +void +${i}_removeEntry(netsnmp_container *container, + ${i}_entry *entry) { + + if (!entry) + return; /* Nothing to remove */ + CONTAINER_REMOVE( container, entry ); + if (entry) + SNMP_FREE( entry ); /* XXX - release any other internal resources */ +} + +/** remove a row from the table */ +void +${i}_freeEntry(${i}_entry *entry) { + + if (!entry) + return; /* Nothing to remove */ + SNMP_FREE( entry ); /* XXX - release any other internal resources */ +} + +/** handles requests for the $i table */ +int +${i}_handler( + netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) { + + netsnmp_request_info *request; + netsnmp_table_request_info *table_info; + netsnmp_container *container; + ${i}_entry *table_entry; +@if $i.settable@ + int ret; +@end@ + + DEBUGMSGTL(("${name}:handler", "Processing request (%d)\n", reqinfo->mode)); + + switch (reqinfo->mode) { + /* + * Read-support (also covers GetNext requests) + */ + case MODE_GET: + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + table_entry = (${i}_entry *) + netsnmp_container_table_extract_context(request); + table_info = netsnmp_extract_table_info(request); + if ((NULL == table_entry) || (NULL == table_info)) { + snmp_log(LOG_ERR, + "could not extract table entry or info for $i\n"); + snmp_set_var_typed_value(request->requestvb, SNMP_ERR_GENERR, + NULL, 0); + continue; + } + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.readable@ + case COLUMN_$c.uc: + if ( !table_entry ) { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + @if $c.needlength@ + snmp_set_var_typed_value( request->requestvb, $c.type, + table_entry->$c, + table_entry->${c}_len); + @else@ + snmp_set_var_typed_integer( request->requestvb, $c.type, + table_entry->$c); + @end@ + break; + @end@ + @end@ + default: + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHOBJECT); + break; + } + } + break; + +@if $i.settable@ + /* + * Write-support + */ + case MODE_SET_RESERVE1: + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + table_entry = (${i}_entry *) + netsnmp_container_table_extract_context(request); + table_info = netsnmp_extract_table_info(request); + if ((NULL == table_entry) || (NULL == table_info)) { + snmp_log(LOG_ERR, + "could not extract table entry or info for $i\n"); + snmp_set_var_typed_value(request->requestvb, SNMP_ERR_GENERR, + NULL, 0); + continue; + } + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + @if $c.rowstatus@ + ret = netsnmp_check_vb_rowstatus(request->requestvb, + (table_entry ? RS_ACTIVE : RS_NONEXISTENT )); + @else@ + @if $c.needlength@ + /* or possibly 'netsnmp_check_vb_type_and_size' */ + ret = netsnmp_check_vb_type_and_max_size( + request->requestvb, $c.type, sizeof(table_entry->$c)); + @else@ + /* or possibly 'netsnmp_check_vb_int_range' */ + ret = netsnmp_check_vb_int( request->requestvb ); + @end@ + @end@ + if ( ret != SNMP_ERR_NOERROR ) { + netsnmp_set_request_error( reqinfo, request, ret ); + return SNMP_ERR_NOERROR; + } + break; + @end@ + @end@ + default: + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_NOTWRITABLE ); + return SNMP_ERR_NOERROR; + } + } + break; + + case MODE_SET_RESERVE2: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + table_entry = (${i}_entry *) + netsnmp_container_table_extract_context(request); + table_info = netsnmp_extract_table_info(request); + if ((NULL == table_entry) || (NULL == table_info)) { + snmp_log(LOG_ERR, + "could not extract table entry or info for $i\n"); + snmp_set_var_typed_value(request->requestvb, SNMP_ERR_GENERR, + NULL, 0); + continue; + } + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + container = netsnmp_container_table_extract(request); + if (NULL == container) { + snmp_log(LOG_ERR, + "could not extract table container for $i\n"); + snmp_set_var_typed_value(request->requestvb, + SNMP_ERR_GENERR, NULL, 0); + continue; + } + table_entry = ${i}_createEntry(container + @foreach $idx index@ + @if $idx.needlength@ + , table_info->indexes->val.string + , table_info->indexes->val_len + @else@ + , *table_info->indexes->val.integer + @end@ + @end@ + ); + if (table_entry) { + netsnmp_container_table_insert_row( request, table_entry ); + } else { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_RESOURCEUNAVAILABLE ); + return SNMP_ERR_NOERROR; + } + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( !table_entry ) { + container = netsnmp_container_table_extract(request); + if (NULL == container) { + snmp_log(LOG_ERR, + "could not extract table container for $i\n"); + snmp_set_var_typed_value(request->requestvb, + SNMP_ERR_GENERR, NULL, 0); + continue; + } + table_entry = ${i}_createEntry(container + @foreach $idx index@ + @if $idx.needlength@ + , table_info->indexes->val.string + , table_info->indexes->val_len + @else@ + , *table_info->indexes->val.integer + @end@ + @end@ + ); + if (table_entry) { + netsnmp_container_table_insert_row( request, table_entry ); + } else { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_RESOURCEUNAVAILABLE ); + return SNMP_ERR_NOERROR; + } + } + break; +@end@ + } + } +@end@ + break; + + case MODE_SET_FREE: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + container = netsnmp_container_table_extract(request); + if (NULL == container) { + snmp_log(LOG_ERR, + "could not extract table container for $i\n"); + snmp_set_var_typed_value(request->requestvb, + SNMP_ERR_GENERR, NULL, 0); + continue; + } + table_entry = (${i}_entry *) + netsnmp_container_table_extract_context(request); + table_info = netsnmp_extract_table_info(request); + if ((NULL == table_entry) || (NULL == table_info)) { + snmp_log(LOG_ERR, + "could not extract table entry or info for $i\n"); + snmp_set_var_typed_value(request->requestvb, SNMP_ERR_GENERR, + NULL, 0); + continue; + } + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + if (table_entry && !table_entry->valid) { + ${i}_removeEntry(container, table_entry ); + } + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( table_entry && !table_entry->valid ) { + ${i}_removeEntry(container, table_entry ); + } + break; +@end@ + } + } +@end@ + break; + + case MODE_SET_ACTION: + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + table_entry = (${i}_entry *) + netsnmp_container_table_extract_context(request); + table_info = netsnmp_extract_table_info(request); + if ((NULL == table_entry) || (NULL == table_info)) { + snmp_log(LOG_ERR, + "could not extract table entry or info for $i\n"); + snmp_set_var_typed_value(request->requestvb, SNMP_ERR_GENERR, + NULL, 0); + continue; + } + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + @if !$c.rowstatus@ + case COLUMN_$c.uc: + @if $c.needlength@ + memcpy( table_entry->old_$c, + table_entry->$c, + sizeof(table_entry->$c)); + table_entry->old_${c}_len = + table_entry->${c}_len; + memset( table_entry->$c, 0, + sizeof(table_entry->$c)); + memcpy( table_entry->$c, + request->requestvb->val.string, + request->requestvb->val_len); + table_entry->${c}_len = + request->requestvb->val_len; + @else@ + table_entry->old_$c = table_entry->$c; + table_entry->$c = *request->requestvb->val.integer; + @end@ + break; + @end@ + @end@ + @end@ + } + } +@if $i.rowstatus@ + /* Check the internal consistency of an active row */ + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + table_entry = (${i}_entry *) + netsnmp_container_table_extract_context(request); + table_info = netsnmp_extract_table_info(request); + if ((NULL == table_entry) || (NULL == table_info)) { + snmp_log(LOG_ERR, + "could not extract table entry or info for $i\n"); + snmp_set_var_typed_value(request->requestvb, SNMP_ERR_GENERR, + NULL, 0); + continue; + } + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_ACTIVE: + case RS_CREATEANDGO: + if (/* XXX */) { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_INCONSISTENTVALUE ); + return SNMP_ERR_NOERROR; + } + } + @end@ + @end@ + } + } +@end@ + break; + + case MODE_SET_UNDO: + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + container = netsnmp_container_table_extract(request); + if (NULL == container) { + snmp_log(LOG_ERR, + "could not extract table container for $i\n"); + snmp_set_var_typed_value(request->requestvb, + SNMP_ERR_GENERR, NULL, 0); + continue; + } + table_entry = (${i}_entry *) + netsnmp_container_table_extract_context(request); + table_info = netsnmp_extract_table_info(request); + if ((NULL == table_entry) || (NULL == table_info)) { + snmp_log(LOG_ERR, + "could not extract table entry or info for $i\n"); + snmp_set_var_typed_value(request->requestvb, SNMP_ERR_GENERR, + NULL, 0); + continue; + } + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: +@if $i.rowstatus@ + @if $c.rowstatus@ + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + if (table_entry && !table_entry->valid) { + ${i}_removeEntry(container, table_entry ); + } + } + @else@ + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + @end@ +@else@ + @if $c.creatable@ + if ( table_entry && !table_entry->valid ) { + ${i}_removeEntry(container, table_row ); + } else { + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + } + @else@ + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + @end@ +@end@ + break; + @end@ + @end@ + } + } + break; + + case MODE_SET_COMMIT: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + table_entry = (${i}_entry *) + netsnmp_container_table_extract_context(request); + table_info = netsnmp_extract_table_info(request); + if ((NULL == table_entry) || (NULL == table_info)) { + snmp_log(LOG_ERR, + "could not extract table entry or info for $i\n"); + snmp_set_var_typed_value(request->requestvb, SNMP_ERR_GENERR, + NULL, 0); + continue; + } + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + table_entry->valid = 1; + /* Fall-through */ + case RS_ACTIVE: + table_entry->$c = RS_ACTIVE; + break; + + case RS_CREATEANDWAIT: + table_entry->valid = 1; + /* Fall-through */ + case RS_NOTINSERVICE: + table_entry->$c = RS_NOTINSERVICE; + break; + + case RS_DESTROY: + container = netsnmp_container_table_extract(request); + if (NULL == container) { + snmp_log(LOG_ERR, + "could not extract table container for $i\n"); + snmp_set_var_typed_value(request->requestvb, + SNMP_ERR_GENERR, NULL, 0); + continue; + } + ${i}_removeEntry(container, table_entry ); + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( table_entry && !table_entry->valid ) { + table_entry->valid = 1; + } +@end@ + } + } +@end@ + break; +@end@ + } + return SNMP_ERR_NOERROR; +} + +#ifdef ${i.uc}_USE_CACHE +/** + * @internal + */ +static int +_cache_load(netsnmp_cache * cache, void *vmagic) +{ + netsnmp_container *container; + + DEBUGMSGTL(("internal:${i}:_cache_load", "called\n")); + + if ((NULL == cache) || (NULL == cache->magic)) { + snmp_log(LOG_ERR, "invalid cache for ${i}_cache_load\n"); + return -1; + } + container = (netsnmp_container *)cache->magic; + + /** should only be called for an invalid or expired cache */ + netsnmp_assert((0 == cache->valid) || (1 == cache->expired)); + + /* + * load cache here (or call function to do it) + */ + /** CONTAINER_INSERT(container, record); */ + + return 0; +} /* _cache_load */ + +/** + * @Internal + */ +/** remove a row from the table */ +static void +${i}_freeEntry_cb(${i}_entry *entry, void *magic) { + + ${i}_freeEntry(entry); +} + +/** + * @internal + */ +static void +_cache_free(netsnmp_cache * cache, void *magic) +{ + netsnmp_container *container; + + DEBUGMSGTL(("internal:${i}:_cache_free", "called\n")); + + if ((NULL == cache) || (NULL == cache->magic)) { + snmp_log(LOG_ERR, "invalid cache in ${i}_cache_free\n"); + return; + } + container = (netsnmp_container *) cache->magic; + + /* + * empty (but don't free) cache here + */ + CONTAINER_CLEAR(container, + (netsnmp_container_obj_func*)${i}_freeEntry_cb, + NULL); +} /* _cache_free */ +#endif /* ${i.uc}_USE_CACHE */ +@end@ diff --git a/local/mib2c.create-dataset.conf b/local/mib2c.create-dataset.conf new file mode 100644 index 0000000..9cf75fd --- /dev/null +++ b/local/mib2c.create-dataset.conf @@ -0,0 +1,111 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/* function declarations */ +void init_$name(void); +@foreach $i table@ +void initialize_table_$i(void); +Netsnmp_Node_Handler ${i}_handler; +@end@ +@foreach $i table@ + +/* column number definitions for table $i */ + @foreach $c column@ + #define COLUMN_$c.uc $c.subid + @end@ +@end@ +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" + +@foreach $i table@ +/** Initialize the $i table by defining its contents and how it's structured */ +void +initialize_table_$i(void) +{ + const oid ${i}_oid[] = {$i.commaoid}; + netsnmp_table_data_set *table_set; + + /* create the table structure itself */ + table_set = netsnmp_create_table_data_set("$i"); + + /* comment this out or delete if you don't support creation of new rows */ + table_set->allow_creation = 1; + + /*************************************************** + * Adding indexes + */ + DEBUGMSGTL(("initialize_table_$i", + "adding indexes to table $i\n")); + netsnmp_table_set_add_indexes(table_set, + @foreach $idx index@ + $idx.type, /* index: $idx */ + @end@ + 0); + + DEBUGMSGTL(("initialize_table_$i", + "adding column types to table $i\n")); + netsnmp_table_set_multi_add_default_row(table_set, + @foreach $c column@ + COLUMN_$c.uc, $c.type, $c.settable, + NULL, 0, + @end@ + 0); + + /* registering the table with the master agent */ + /* note: if you don't need a subhandler to deal with any aspects + of the request, change ${i}_handler to "NULL" */ + netsnmp_register_table_data_set(netsnmp_create_handler_registration("$i", ${i}_handler, + ${i}_oid, + OID_LENGTH(${i}_oid), + HANDLER_CAN_RWRITE), + table_set, NULL); +} +@end@ + +/** Initializes the $name module */ +void +init_$name(void) +{ + + /* here we initialize all the tables we're planning on supporting */ + @foreach $i table@ + initialize_table_$i(); + @end@ +} +@foreach $i table@ + +/** handles requests for the $i table, if anything else needs to be done */ +int +${i}_handler( + netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) { + /* perform anything here that you need to do. The requests have + already been processed by the master table_dataset handler, but + this gives you chance to act on the request in some other way + if need be. */ + return SNMP_ERR_NOERROR; +} +@end@ diff --git a/local/mib2c.emulation.conf b/local/mib2c.emulation.conf new file mode 100644 index 0000000..20d2e7b --- /dev/null +++ b/local/mib2c.emulation.conf @@ -0,0 +1,246 @@ +############################################## -*- Mib2c -*- +## +## File : mib2c.emulation.conf +## Author : Robert Story <rstory@freesnmp.com> +## Purpose: A mib2c conf file to generate snmpd.conf configuration to +## provide basic/simplistic emulation for a particular MIB. +## +## +## $Id$ +###################################################################### +@open ${name}-emulation.conf@ +# +# $name.module MIB emulation +# Base OID: $name.objectID +# +# Requirements: Net-SNMP 5.2 or greater +# +# This file can be used as (part of) a Net-SNMP snmpd.conf +# configuration file to emulate (in a minimal fashion) the objects defined +# in the $name MIB. +# +# It is important to note that this file merely creates dummy values +# and variables and does not implement any of the functionality which +# the mib objects are actually supposed to implement! +# +# Also, it just defaults to 0, '' or 0.0 for initial values, so you'll need +# to tweak all the vaules. For any tables, you'll have set any indexes. +# +# The configuration tokens used in this file are documented in the +# snmpd.conf manual page, with the exception of the first line, which +# is documented in the snmp.conf and snmp_config manual pages. +# +# +# To use this file, you have several options: +# +# 1) add the contents to your existing snmpd.conf file +# 2) copy it to a new snmpd.conf, somewhere in SNMPCONFPATH +# 3) copy it to snmpd.conf.local, somewhere in SNMPCONFPATH +# +# The command 'net-snmp-config --snmpconfpath' will give your the +# SNMPCONFPATH (5.3.x and later). + + +# Here we tell the agent to load the MIB, so we can use names instead +# of OIDs. You may need to add other MIBs that this MIB depends on. +# Also, you might want to consider adding these to the global snmp.conf, +# so all users can reference them. +# +# See the Net-SNMP FAQ for more information on MIB loading. +# +[snmp] mibs +$name.module + +# +# $name Scalar variables +# + +@foreach $node scalar@ +@ print Scalar: $node@ +# $node.module::$node.parent.$node($node.subid) [$node.syntax = $node.type] +@ if "$node.status" eq "Deprecated"@ +# **** WARNING: deprecated object **** +@ end@ +@if $node.enums == 1@ +@ eval $m2c_evals = ""@ +@ eval $m2c_first = 1@ +@ foreach $e $v enum@ +@ if $m2c_first == 1@ +@ eval $m2c_first = 0@ +@ else@ +@ eval $m2c_evals = "$m2c_evals,"@ +@ end@ +@ eval $m2c_evals = "$m2c_evals $e($v)"@ +@ if $node.hasdefval == 1@ +@ if (("$node.defval" eq "$e") || ("$node.defval" eq "$v"))@ +@ eval $m2c_evals = "${m2c_evals}*"@ +@ end@ +@ end@ +@ end@ +# Enum range: $node.enumrange. Values: $m2c_evals +@elsif $node.ranges == 1@ +@ eval $m2c_range_max = 0@ +@ eval $m2c_evals = ""@ +@ eval $m2c_first = 1@ +@ foreach $a $b range $node@ +@ if $m2c_first == 1@ +@ eval $m2c_first = 0@ +@ else@ +@ eval $m2c_evals = "$m2c_evals,"@ +@ end@ +@ if $a == $b@ +@ eval $m2c_evals = "$m2c_evals $a"@ +@ else@ +@ eval $m2c_evals = "$m2c_evals $a - $b"@ +@ end@ +@ eval $m2c_range_max = max($m2c_range_max,$b)@ +@ end@ +# Ranges: $m2c_evals; +@end@ #ranges +@ if $node.hasdefval == 1@ +@ eval $m2c_emu_def = "$node.defval"@ +@ else@ +# (no default value) +@ if "$node.type" eq "ASN_OCTET_STR"@ +@ eval $m2c_emu_def = "''" @ +@ elsif "$node.type" eq "ASN_OBJECT_ID"@ +@ eval $m2c_emu_def = "0.0" @ +@ else@ +@ eval $m2c_emu_def = "0" @ +@ end@ +@ end@ +## uggh.. try to convert type into something 5.3.x override understands +@ eval $m2c_emu_type = lc($node.type)@ +@ perleval $vars{'m2c_emu_type'} =~ s/asn_//; 0;@ +@ if $node.settable@ +#override -rw ${node}.0 $m2c_emu_type $m2c_emu_def +@ else@ +#override ${node}.0 $m2c_emu_type $m2c_emu_def +@ end@ + +@end@ + + +##====================================================================== +# +# Note that in tables, all indices are shown first, then each +# readable column. This results in indices being given twice. +# +@foreach $node table@ +@ print Table: $node@ +# $node.module::$node.parent.$node($node.subid) +table $node +##============================================================ +@ eval $m2c_idx_def = ""@ +@ eval $m2c_add_def = ""@ +@ foreach $col index@ +# [$col] ($node.accessible) $col.syntax / $col.type / $m2c_decl ($col.decl) +@ if $col.enums == 1@ +@ eval $m2c_evals = ""@ +@ eval $m2c_first = 1@ +@ foreach $e $v enum@ +@ if $m2c_first == 1@ +@ eval $m2c_first = 0@ +@ else@ +@ eval $m2c_evals = "$m2c_evals,"@ +@ end@ +@ eval $m2c_evals = "$m2c_evals $e($v)"@ +@ if $col.hasdefval == 1@ +@ if (("$col.defval" eq "$e") || ("$col.defval" eq "$v"))@ +@ eval $m2c_evals = "${m2c_evals}*"@ +@ end@ +@ end@ +@ end@ +# enum range: $col.enumrange. Values: $m2c_evals +@ elsif $col.ranges == 1@ +@ eval $m2c_range_max = 0@ +@ eval $m2c_evals = ""@ +@ eval $m2c_first = 1@ +@ foreach $a $b range $col@ +@ if $m2c_first == 1@ +@ eval $m2c_first = 0@ +@ else@ +@ eval $m2c_evals = "$m2c_evals,"@ +@ end@ +@ if $a == $b@ +@ eval $m2c_evals = "$m2c_evals $a"@ +@ else@ +@ eval $m2c_evals = "$m2c_evals $a - $b"@ +@ end@ +@ eval $m2c_range_max = max($m2c_range_max,$b)@ +@ end@ +# ranges: $m2c_evals; +@ end@ #ranges +@ if $col.hasdefval == 1@ +@ eval $m2c_col_def = '$col.defval'@ +@ else@ +@ if "$col.type" eq "ASN_OCTET_STR"@ +@ eval $m2c_col_def = "''" @ +@ elsif "$col.type" eq "ASN_OBJECT_ID"@ +@ eval $m2c_col_def = "0.0" @ +@ else@ +@ eval $m2c_col_def = "0" @ +@ end@ +@ end@ +@ eval $m2c_add_def = "$m2c_add_def $m2c_col_def"@ +##@ if $node.accessible == 1@ +@ eval $m2c_idx_def = "$m2c_idx_def $m2c_col_def"@ +##@ end@ +@ end@ ## col +##=================================================== +@ foreach $col nonindex@ +# $col [$col.syntax = $col.type] +@ if "$col.status" eq "Deprecated"@ +# **** WARNING: deprecated object **** +@ end@ +@ if $col.enums == 1@ +@ eval $m2c_evals = ""@ +@ eval $m2c_first = 1@ +@ foreach $e $v enum@ +@ if $m2c_first == 1@ +@ eval $m2c_first = 0@ +@ else@ +@ eval $m2c_evals = "$m2c_evals,"@ +@ end@ +@ eval $m2c_evals = "$m2c_evals $e($v)"@ +@ if $col.hasdefval == 1@ +@ if (("$col.defval" eq "$e") || ("$col.defval" eq "$v"))@ +@ eval $m2c_evals = "${m2c_evals}*"@ +@ end@ +@ end@ +@ end@ +# enum range: $col.enumrange. Values: $m2c_evals +@ elsif $col.ranges == 1@ +@ eval $m2c_range_max = 0@ +@ eval $m2c_evals = ""@ +@ eval $m2c_first = 1@ +@ foreach $a $b range $col@ +@ if $m2c_first == 1@ +@ eval $m2c_first = 0@ +@ else@ +@ eval $m2c_evals = "$m2c_evals,"@ +@ end@ +@ if $a == $b@ +@ eval $m2c_evals = "$m2c_evals $a"@ +@ else@ +@ eval $m2c_evals = "$m2c_evals $a - $b"@ +@ end@ +@ eval $m2c_range_max = max($m2c_range_max,$b)@ +@ end@ +# ranges: $m2c_evals; +@ end@ #ranges +@ if $col.hasdefval == 1@ +@ eval $m2c_col_def = '$col.defval'@ +@ else@ +@ if "$col.type" eq "ASN_OCTET_STR"@ +@ eval $m2c_col_def = "''" @ +@ elsif "$col.type" eq "ASN_OBJECT_ID"@ +@ eval $m2c_col_def = "0.0" @ +@ else@ +@ eval $m2c_col_def = "0" @ +@ end@ +@ end@ +@ eval $m2c_add_def = "$m2c_add_def $m2c_col_def"@ +@ end@ ## col +#add_row $node $m2c_idx_def $m2c_add_def + +@end@ ## table diff --git a/local/mib2c.genhtml.conf b/local/mib2c.genhtml.conf new file mode 100644 index 0000000..b0f6284 --- /dev/null +++ b/local/mib2c.genhtml.conf @@ -0,0 +1,370 @@ +## +## USAGE: +## mib2c -c mib2c.genhtml.conf STARTINGNODE +## +## Optional extra arguments: +## +## -S cssfile=file.css (specifies an alternate CSS style +## file to include in the output) +## +## +## try to strip leading white space from text +## +@define DO_FORMATED_TEXT@ +@startperl@ + my ($s) = ($vars{'x'} =~ /\n(\s+)/); + $vars{'x'} =~ s/^$s//gm; + 0; +@endperl@ +<pre> +$x +</pre> +@enddefine@ +## +## print a description clause (include TC info and references +## +@define DO_DESCR@ +<td> +@if "$i.perltype" ne "$i.syntax"@ +<p> +@eval $tmpsyn = "$i.syntax"@ +@perleval if (!defined($TCS{$vars{'tmpsyn'}})) { $TCS{$vars{'tmpsyn'}} = $vars{'i'}; }; 0;@ +Note: this object is based on the <a href="#$i.syntax"> $i.syntax TEXTUAL-CONVENTION</a>. +</p> +@end@ +@eval $x = "$i.description"@ +@calldefine DO_FORMATED_TEXT@ +##@startperl@ +## my ($s) = ($vars{'x'} =~ /\n(\s+)/); +## $vars{'x'} =~ s/^$s//gm; +## 0; +##@endperl@ +##<pre> +##$x +##</pre> +@if "$i.reference" ne ""@ +@eval $x = "$i.reference"@ +<p><i>Also see Reference: +@calldefine DO_FORMATED_TEXT@ +</i></p> +@end@ +</td> +@enddefine@ +## +## print information (a row) about a given node +## +@define NODE_INFO@ +@if ("$i.status" eq "Current")@ + <td> +@else@ + <td class="deprecated"> +@end@ +<a name="$i" /> +@eval $tmpi = "$i.parent"@ +@if "$doindexstuff" ne "" && "$tmpi.parent" ne "$t"@ +(external from $tmpi.parent) +@else@ +$i.subid +@end@ +<br /><b>$i</b> + +</td><td> +## +## print the data type +## +@if ("$i.status" ne "Current")@ +<font color="red">DEPRECATED</font><br /> +@end@ + $i.perltype +## +## print range information +## + @if "$i.ranges"@ + @if "$i.perltype" eq 'OCTETSTR' || "$i.perltype" eq 'OBJECTID'@ + <br />Legal Lengths: + @else@ + <br />Legal values: + @end@ + @eval $tmpdidit = 0@ + @foreach $st $end range $i@ + @if "$tmpdidit" == "1"@ + , + @end@ + @if "$st" eq "$end"@ + $st + @else@ + $st .. $end + @end@ + @eval $tmpdidit = 1@ + @end@ + @end@ +## +## check for TC vs non-TC cases +## + @if "$i.perltype" ne "$i.syntax"@ + <br> + @eval $tmpsyn = "$i.syntax"@ + @perleval if (!defined($TCS{$vars{'tmpsyn'}})) { $TCS{$vars{'tmpsyn'}} = $vars{'i'}; }; 0;@ + <a href="#$i.syntax">$i.syntax</a> + @if $i.enums@ + <br>(ENUM list below) + @end@ + @else@ + @if $i.enums@ + <table class="enum"> + <tr><th>Value</th><th>Label/Meaning</th></tr> + @foreach $e $v enum@ + <tr><td>$v</td><td>$e</td></tr> + @end@ + </table> + @end@ + @end@ + + </td><td>$i.access</td> + @if !"$dont_do_oids"@ + <td>$i.objectID</td> + @end@ + @calldefine DO_DESCR@ +@enddefine@ +@open ${name}.html@ +<head> + <title>MIB information for $name</title> +<style type="text/css"> +@if "$cssfile" ne ""@ +@startperl@ +open(CSS,$vars{'cssfile'}); +while(<CSS>) { + mib2c_output($_); +} +close(CSS); +0; +@endperl@ +@else@ +<!-- +h2{background:#bbeebb} +h3{background:#ccccee} +table { + border: 1px; + background: #dddddd; + style: outset; +} +th { + border: 1px; + border: solid; + border-style: outset; + background: #bbbbbb; +} +td { + border: 1px; + border: solid; + border-style: inset; +} +table.enums { + background: #cccccc; +} +table.deprecated { + background: #ffdddd; +} +td.deprecated { + background: #ffdddd; +} +.label { + border-style: outset; + background: #bbbbbb; +} +--> +@end@ +</style> +</head> +<body bgcolor="#ffffff"> +<h2>INTRODUCTION</h2> +<ul> +<p> +This is a summary of information regarding objects below the <b>$name</b> +MIB object, which is defined within the <b>$name.module</b> MIB +document as <b>$name.objectID</b>. +</p> +</ul> +## +## Table of contents +## +<h2>TABLE OF CONTENTS</h2> +<ul> +@foreach $Current stuff Current Deprecated@ +<h3><a href="#objects_$Current">$Current Objects</a></h3> +<ul> +@if "$Current" eq "Current"@ +<li><a href="#scalar_current">Scalars</a></li> +@else@ +<li><a href="#scalar_notcurrent">Deprecated Scalars</a></li> +@end@ +@foreach $t table@ +@if ("$Current" eq "Current" && "$t.status" eq "Current") || ("$Current" ne "Current" && "$t.status" ne "Current")@ + <li><a href="#$t">$t</a></li> +@end@ +@end@ +</ul> +@end@ +<h3><a href="#notifications">Notifications</a></h3> +<h3><a href="#textconventions">Textual Conventions</a></h3> +<h3><a href="#treeview">Tree-based view</a></h3> +</ul> +## +## Start of definitions +## +@foreach $Current stuff Current Deprecated@ +<a name="objects_$Current" /> +@if "$Current" ne "Current"@ + @eval $namestring = "notcurrent"@ + <hr /> + <h1><font color="red">DEPRECATED OR OBSOLETE OR HISTORIC OBJECTS</font></h1> + <br> + <table class="deprecated"><tr><td> +@else@ + @eval $namestring = "current"@ +@end@ +<a name="scalar_$namestring" /> +<h2>SCALAR OBJECTS</h2> +<ul> +<table> +<tr><th>Name</th><th>Type</th><th>Access</th><th>OID</th><th>Description</th></tr> +@foreach $i scalar@ + @if ("$Current" eq "Current" && "$i.status" eq "Current") || ("$Current" ne "Current" && "$i.status" ne "Current")@ + <tr> + @calldefine NODE_INFO@ + </tr> + @end@ +@end@ +</table> +</ul> + +<h2>TABLE OBJECTS</h2> +@eval $dont_do_oids = 1@ +@foreach $t table@ +@if ("$Current" eq "Current" && "$t.status" eq "Current") || ("$Current" ne "Current" && "$t.status" ne "Current")@ +<a name="$t" /><h3>Table $t</h3> +<ul> + <table> + <tr><td class="label">Table Name</td><td>$t</td></tr> + <tr><td class="label">In MIB</td><td>$t.module</td></tr> + <tr><td class="label">Registered at OID</td><td>$t.objectID</td></tr> + <tr><td class="label">Table Description</td> +@eval $i ="$t"@ +@calldefine DO_DESCR@ +</tr> + @eval $tmpx = $t@ + @perleval $vars{'tmpx'} =~ s/Table/Entry/; 0;@ +@eval $i = "$tmpx"@ +<tr><td class="label"><a name="$tmpx" />Row Description</td> +@calldefine DO_DESCR@ +</tr> + </table> + + <h4>$t Indexes:</h4> + +<table> +<tr class="label"><th>Name</th><th>Type</th><th>Access</th><th>Description</th></tr> +@foreach $i index@ + <tr> + @calldefine NODE_INFO@ + </tr> +@end@ +</table> + + <h4>Other $t Columns:</h4> +<table> +<tr class="label"><th>Name</th><th>Type</th><th>Access</th><th>Description</th></tr> +@foreach $i nonindex@ + <tr> + @calldefine NODE_INFO@ + </tr> +@end@ +</table> +</ul> +@end@ +@end@ +@end@ + +<br> + </table> +<hr /> +<a name="notifications" /> +<h2>NOTIFICATIONS</h2> +<ul> +<p> +@foreach $i notifications@ +<a name="$i" /><h3>Notification: $i</h3> + + <table> + <tr><td class="label">Notification Name</td><td>$i</td></tr> + <tr><td class="label">In MIB</td><td>$i.module</td></tr> + <tr><td class="label">Registered at OID</td><td>$i.objectID</td></tr> + <tr><td class="label">Notification Description</td> +@calldefine DO_DESCR@ + </tr> + <tr> <td class="label">Mandatory<br />Objects</td><td> + <table> + @foreach $v varbinds@ + <tr><td><a href="#$v">$v</a></td></tr> + @end@ + </table></td></tr> + </table> +@end@ +</ul> + + +<br> +<hr /> +<a name="textconventions" /> +<h2>TEXTUAL CONVENTIONS</h2> +<ul> +<p> +These TEXTUAL-CONVENTIONS are used in other parts of the document +above. They are SNMP's way of defining a datatype that is used +repeatedly by other MIB objects. Any implementation implementing +objects that use one of these definitions must follow its DESCRIPTION +clause as well as the DESCRIPTION clause of the object itself. +</p> +@startperl@ +mib2c_output("<table>"); +mib2c_output("<tr class=\"label\"><th>Name</th><th>Type</th><th>Description</th></tr>\n"); +map { + my $desc = $SNMP::MIB{$TCS{$_}}{'TCDescription'}; + my ($s) = ($desc =~ /\n(\s+)/); + $desc =~ s/^$s//gm; + mib2c_output("<tr><td><a name=\"$_\">$_</td><td>"); + mib2c_output($SNMP::MIB{$TCS{$_}}{'type'}); + my @enumkeys = keys(%{$SNMP::MIB{$TCS{$_}}{'enums'}}); + if ($#enumkeys > -1) { + mib2c_output("<table class=\"enums\">"); + mib2c_output("<tr><th>Value</th><th>Label/Meaning</th></tr>"); + foreach $k (sort { $SNMP::MIB{$TCS{$_}}{'enums'}{$a} <=> + $SNMP::MIB{$TCS{$_}}{'enums'}{$b} } @enumkeys) { + mib2c_output("<tr><td>$SNMP::MIB{$TCS{$_}}{'enums'}{$k}</td><td>$k</td></tr>"); + } + mib2c_output("</table>"); + } + mib2c_output("</td><td><pre>$desc</pre></td></tr>\n"); +} keys(%TCS); +mib2c_output("</table>"); +0; +@endperl@ +</ul> + +<a name="treeview" /> +<h2>TREE VIEW</h2> +@eval $mod = "$name.module"@ +<p>Tree view generated by running: <b>snmptranslate -Tp $mod::$name</b></p> +<pre> +@startperl@ +open(TREE,"snmptranslate -Tp $vars{mod}::$vars{name}|"); +while(<TREE>) { + s/(\+-- .*\s)(\w+)(\(\d+\))$/$1<a href="#$2">$2<\/a>$3/; + s/\+--(\w+)/+--<a href="#$1">$1<\/a>/; + s/Textual Convention: (\w+)/Textual Convention: <a href="#$1">$1<\/a>/; + mib2c_output($_); +} +close(TREE); +return 0; +@endperl@ +</pre> diff --git a/local/mib2c.int_watch.conf b/local/mib2c.int_watch.conf new file mode 100644 index 0000000..ecc5d10 --- /dev/null +++ b/local/mib2c.int_watch.conf @@ -0,0 +1,106 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open -@ +*** Warning: only generating code for nodes of MIB type INTEGER +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/* function declarations */ +void init_$name(void); + +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" + +/* + * The variables we want to tie the relevant OIDs to. + * The agent will handle all GET and (if applicable) SET requests + * to these variables automatically, changing the values as needed. + */ + +@foreach $i scalar@ + @if !$i.needlength@ +$i.decl $i = 0; /* XXX: set default value */ + @end@ +@end@ + +/* + * Our initialization routine, called automatically by the agent + * (Note that the function name must match init_FILENAME()) + */ +void +init_${name}(void) +{ + netsnmp_handler_registration *reg; + + @foreach $i scalar@ + @if !$i.needlength@ + const oid ${i}_oid[] = { $i.commaoid }; + @end@ + static netsnmp_watcher_info ${i}_winfo; + @end@ + + /* + * a debugging statement. Run the agent with -D$name to see + * the output of this debugging statement. + */ + DEBUGMSGTL(("$name", "Initializing the $name module\n")); + + + /* + * Register scalar watchers for each of the MIB objects. + * The ASN type and RO/RW status are taken from the MIB definition, + * but can be adjusted if needed. + * + * In most circumstances, the scalar watcher will handle all + * of the necessary processing. But the NULL parameter in the + * netsnmp_create_handler_registration() call can be used to + * supply a user-provided handler if necessary. + * + * This approach can also be used to handle Counter64, string- + * and OID-based watched scalars (although variable-sized writeable + * objects will need some more specialised initialisation). + */ + @foreach $i scalar@ + @if !$i.needlength@ + DEBUGMSGTL(("$name", + "Initializing $i scalar integer. Default value = %d\n", + $i)); + reg = netsnmp_create_handler_registration( + "$i", NULL, + ${i}_oid, OID_LENGTH(${i}_oid), + @if $i.settable@ + HANDLER_CAN_RWRITE); + @else@ + HANDLER_CAN_RONLY); + @end@ + netsnmp_init_watcher_info(&${i}_winfo, &$i, sizeof($i.decl), + $i.type, WATCHER_FIXED_SIZE); +if (netsnmp_register_watched_scalar( reg, &${i}_winfo ) < 0 ) { + snmp_log( LOG_ERR, "Failed to register watched $i" ); + } + + @end@ + @end@ + + DEBUGMSGTL(("$name", + "Done initalizing $name module\n")); +} diff --git a/local/mib2c.iterate.conf b/local/mib2c.iterate.conf new file mode 100644 index 0000000..3a2b5f7 --- /dev/null +++ b/local/mib2c.iterate.conf @@ -0,0 +1,716 @@ +## -*- c -*- +# +@if "$cache" eq "" @ +@open -@ +This framework can work in one of two ways: + + 1) Hold a local cached copy of some external data + which is then used to service incoming requests. + + 2) Query the external data directly for each request. + +The first is typically more efficient, but results in +slightly "stale" data (depending on the expiration timeout +for the cache) and greater memory usage. The second can +provide more up-to-date information, but at the cost of +higher processing overheads. + +Which is more appropriate for your needs? + @prompt $ans Select your choice : @ + @if $ans == 1 @ + @eval $cache = 1@ + @elsif $ans == 2@ + @eval $cache = 0@ + @else@ +Invalid answer - generating caching code + @eval $cache = 1@ + @end@ +@else@ # -S cache={something} + @if "$cache" ne "1" && "$cache" ne "0" @ +@open -@ +Invalid value for 'cache' ($cache) - generating caching code + @eval $cache = 1@ + @end@ +@end@ + +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/* function declarations */ +void init_$name(void); +@foreach $i table@ +void initialize_table_$i(void); +Netsnmp_Node_Handler ${i}_handler; +Netsnmp_First_Data_Point ${i}_get_first_data_point; +Netsnmp_Next_Data_Point ${i}_get_next_data_point; +@if "$cache" == 1 @ +NetsnmpCacheLoad ${i}_load; +NetsnmpCacheFree ${i}_free; +#define $i.uc_TIMEOUT 60 +@end@ +@end@ +@foreach $i table@ + +/* column number definitions for table $i */ + @foreach $c column@ + #define COLUMN_$c.uc $c.subid + @end@ +@end@ +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" + +/** Initializes the $name module */ +void +init_$name(void) +{ + /* here we initialize all the tables we're planning on supporting */ + @foreach $i table@ + initialize_table_$i(); + @end@ +} + +@foreach $i table@ + # Determine the first/last column names + @eval $first_column = "-"@ + @eval $last_column = "-"@ + @foreach $c column@ + @if $c.readable@ + @if "$first_column" eq "-"@ + @eval $first_column = $c@ + @end@ + @eval $last_column = $c@ + @end@ + @end@ + +/** Initialize the $i table by defining its contents and how it's structured */ +void +initialize_table_$i(void) +{ + const oid ${i}_oid[] = {$i.commaoid}; + const size_t ${i}_oid_len = OID_LENGTH(${i}_oid); + netsnmp_handler_registration *reg; + netsnmp_iterator_info *iinfo; + netsnmp_table_registration_info *table_info; + + DEBUGMSGTL(("${name}:init", "initializing table $i\n")); + + reg = netsnmp_create_handler_registration( + "$i", ${i}_handler, + ${i}_oid, ${i}_oid_len, +@if $i.settable@ + HANDLER_CAN_RWRITE +@else@ + HANDLER_CAN_RONLY +@end@ + ); + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + netsnmp_table_helper_add_indexes(table_info, + @foreach $idx index@ + $idx.type, /* index: $idx */ + @end@ + 0); + table_info->min_column = COLUMN_$first_column.uc; + table_info->max_column = COLUMN_$last_column.uc; + + iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info ); + iinfo->get_first_data_point = ${i}_get_first_data_point; + iinfo->get_next_data_point = ${i}_get_next_data_point; + iinfo->table_reginfo = table_info; + + netsnmp_register_table_iterator( reg, iinfo ); +@if "$cache" == 1 @ + netsnmp_inject_handler_before( reg, + netsnmp_get_cache_handler($i.uc_TIMEOUT, + ${i}_load, ${i}_free, + ${i}_oid, ${i}_oid_len), + TABLE_ITERATOR_NAME); +@end@ + + /* Initialise the contents of the table here */ +} + + /* Typical data structure for a row entry */ +struct ${i}_entry { + /* Index values */ + @foreach $idx index@ + @if $idx.needlength@ + $idx.decl $idx[NNN]; + size_t ${idx}_len; + @else@ + $idx.decl $idx; + @end@ + @end@ + + /* Column values */ + @foreach $c column@ + @if $c.readable@ + @if $c.needlength@ + $c.decl $c[NNN]; + size_t ${c}_len; + @else@ + $c.decl $c; + @end@ + @if $c.settable@ + @if !$c.rowstatus@ + @if $c.needlength@ + $c.decl old_$c[NNN]; + size_t old_${c}_len; + @else@ + $c.decl old_$c; + @end@ + @end@ + @end@ + @end@ + @end@ + + /* Illustrate using a simple linked list */ + int valid; + struct ${i}_entry *next; +}; + +struct ${i}_entry *${i}_head; + +/* create a new row in the (unsorted) table */ +struct ${i}_entry * +${i}_createEntry( + @foreach $idx index@ + @if $idx.needlength@ + $idx.decl* $idx, + size_t ${idx}_len, + @else@ + $idx.decl $idx, + @end@ + @end@ + ) { + struct ${i}_entry *entry; + + entry = SNMP_MALLOC_TYPEDEF(struct ${i}_entry); + if (!entry) + return NULL; + + @foreach $idx index@ + @if $idx.needlength@ + memcpy(entry->$idx, $idx, ${idx}_len); + entry->${idx}_len = ${idx}_len; + @else@ + entry->$idx = $idx; + @end@ + @end@ + entry->next = ${i}_head; + ${i}_head = entry; + return entry; +} + +/* remove a row from the table */ +void +${i}_removeEntry( struct ${i}_entry *entry ) { + struct ${i}_entry *ptr, *prev; + + if (!entry) + return; /* Nothing to remove */ + + for ( ptr = ${i}_head, prev = NULL; + ptr != NULL; + prev = ptr, ptr = ptr->next ) { + if ( ptr == entry ) + break; + } + if ( !ptr ) + return; /* Can't find it */ + + if ( prev == NULL ) + ${i}_head = ptr->next; + else + prev->next = ptr->next; + + SNMP_FREE( entry ); /* XXX - release any other internal resources */ +} + +@if "$cache" == 1 @ +/* Example cache handling - set up linked list from a suitable file */ +int +${i}_load( netsnmp_cache *cache, void *vmagic ) { + FILE *fp; + struct ${i}_entry *this; + char buf[STRMAX]; + + /* The basic load routine template assumes that the data to + be reported is held in a file - with one row of the file + for each row of the table. + If your data is available via a different API, you + should amend this initial block (and the control of the + 'while' loop) accordingly. + 'XXX' marks where the template is incomplete and + code will definitely need to be added. */ + + fp = fopen( "/data/for/${i}", "r" ); + if ( !fp ) { + return -1; + } + while ( fgets( buf, STRMAX, fp )) { + this = SNMP_MALLOC_TYPEDEF( struct ${i}_entry ); + /* XXX - Unpick 'buf' to extract the individual field values + and then populate the 'this' data structure with them */ + + this->next = ${i}_head; + ${i}_head = this; /* Iterate helper is fine with unordered lists! */ + } + fclose(fp); + return 0; /* OK */ +} + +void +${i}_free( netsnmp_cache *cache, void *vmagic ) { + struct ${i}_entry *this, *that; + + for ( this = ${i}_head; this; this=that ) { + that = this->next; + SNMP_FREE( this ); /* XXX - release any other internal resources */ + } + ${i}_head = NULL; +} +@end@ + +/* Example iterator hook routines - using 'get_next' to do most of the work */ +netsnmp_variable_list * +${i}_get_first_data_point(void **my_loop_context, + void **my_data_context, + netsnmp_variable_list *put_index_data, + netsnmp_iterator_info *mydata) +{ + *my_loop_context = ${i}_head; + return ${i}_get_next_data_point(my_loop_context, my_data_context, + put_index_data, mydata ); +} + +netsnmp_variable_list * +${i}_get_next_data_point(void **my_loop_context, + void **my_data_context, + netsnmp_variable_list *put_index_data, + netsnmp_iterator_info *mydata) +{ + struct ${i}_entry *entry = (struct ${i}_entry *)*my_loop_context; + netsnmp_variable_list *idx = put_index_data; + + if ( entry ) { + @foreach $idx index@ + @if $idx.needlength@ + snmp_set_var_value( idx, entry->${idx}, sizeof(entry->${idx}) ); + @else@ + snmp_set_var_typed_integer( idx, $idx.type, entry->${idx} ); + @end@ + idx = idx->next_variable; + @end@ + *my_data_context = (void *)entry; + *my_loop_context = (void *)entry->next; + return put_index_data; + } else { + return NULL; + } +} + + +/** handles requests for the $i table */ +int +${i}_handler( + netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) { + + netsnmp_request_info *request; + netsnmp_table_request_info *table_info; + struct ${i}_entry *table_entry; + + DEBUGMSGTL(("${name}:handler", "Processing request (%d)\n", reqinfo->mode)); + + switch (reqinfo->mode) { + /* + * Read-support (also covers GetNext requests) + */ + case MODE_GET: + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_extract_iterator_context(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.readable@ + case COLUMN_$c.uc: + if ( !table_entry ) { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + @if $c.needlength@ + snmp_set_var_typed_value( request->requestvb, $c.type, + table_entry->$c, + table_entry->${c}_len); + @else@ + snmp_set_var_typed_integer( request->requestvb, $c.type, + table_entry->$c); + @end@ + break; + @end@ + @end@ + default: + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHOBJECT); + break; + } + } + break; + +@if $i.settable@ + /* + * Write-support + */ + case MODE_SET_RESERVE1: + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_extract_iterator_context(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + @if $c.rowstatus@ + ret = netsnmp_check_vb_rowstatus(request->requestvb, + (table_entry ? RS_ACTIVE : RS_NONEXISTENT )); + @else@ + @if $c.needlength@ + /* or possibly 'netsnmp_check_vb_type_and_size' */ + ret = netsnmp_check_vb_type_and_max_size( + request->requestvb, $c.type, sizeof(table_entry->$c)); + @else@ + /* or possibly 'netsnmp_check_vb_int_range' */ + ret = netsnmp_check_vb_int( request->requestvb ); + @end@ + @end@ + if ( ret != SNMP_ERR_NOERROR ) { + netsnmp_set_request_error( reqinfo, request, ret ); + return SNMP_ERR_NOERROR; + } + break; + @end@ + @end@ + default: + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_NOTWRITABLE ); + return SNMP_ERR_NOERROR; + } + } + break; + + case MODE_SET_RESERVE2: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_extract_iterator_context(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + table_row = ${i}_createEntry( + @foreach $idx index@ + @if $idx.needlength@ + , table_info->indexes->val.string + , table_info->indexes->val_len + @else@ + , *table_info->indexes->val.integer + @end@ + @end@ + ); + if (table_row) { + netsnmp_insert_iterator_context( request, table_row ); + } else { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_RESOURCEUNAVAILABLE ); + return SNMP_ERR_NOERROR; + } + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( !table_row ) { + table_row = ${i}_createEntry( + @foreach $idx index@ + @if $idx.needlength@ + , table_info->indexes->val.string + , table_info->indexes->val_len + @else@ + , *table_info->indexes->val.integer + @end@ + @end@ + ); + if (table_row) { + netsnmp_insert_iterator_context( request, table_row ); + } else { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_RESOURCEUNAVAILABLE ); + return SNMP_ERR_NOERROR; + } + } + break; +@end@ + } + } +@end@ + break; + + case MODE_SET_FREE: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_extract_iterator_context(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + if (table_entry && !table_entry->valid) { + ${i}_removeEntry(table_data, table_row ); + } + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( table_entry && !table_entry->valid ) { + ${i}_removeEntry(table_data, table_row ); + } + break; +@end@ + } + } +@end@ + break; + + case MODE_SET_ACTION: + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_extract_iterator_context(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + @if !$c.rowstatus@ + case COLUMN_$c.uc: + @if $c.needlength@ + memcpy( table_entry->old_$c, + table_entry->$c, + sizeof(table_entry->$c)); + table_entry->old_${c}_len = + table_entry->${c}_len; + memset( table_entry->$c, 0, + sizeof(table_entry->$c)); + memcpy( table_entry->$c, + request->requestvb->val.string, + request->requestvb->val_len); + table_entry->${c}_len = + request->requestvb->val_len; + @else@ + table_entry->old_$c = table_entry->$c; + table_entry->$c = *request->requestvb->val.integer; + @end@ + break; + @end@ + @end@ + @end@ + } + } +@if $i.rowstatus@ + /* Check the internal consistency of an active row */ + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_extract_iterator_context(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_ACTIVE: + case RS_CREATEANDGO: + if (/* XXX */) { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_INCONSISTENTVALUE ); + return SNMP_ERR_NOERROR; + } + } + @end@ + @end@ + } + } +@end@ + break; + + case MODE_SET_UNDO: + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_extract_iterator_context(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: +@if $i.rowstatus@ + @if $c.rowstatus@ + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + if (table_entry && !table_entry->valid) { + ${i}_removeEntry(table_data, table_row ); + } + } + @else@ + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + @end@ +@else@ + @if $c.creatable@ + if ( table_entry && !table_entry->valid ) { + ${i}_removeEntry(table_data, table_row ); + } else { + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + } + @else@ + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + @end@ +@end@ + break; + @end@ + @end@ + } + } + break; + + case MODE_SET_COMMIT: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_extract_iterator_context(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + table_entry->valid = 1; + /* Fall-through */ + case RS_ACTIVE: + table_entry->$c = RS_ACTIVE; + break; + + case RS_CREATEANDWAIT: + table_entry->valid = 1; + /* Fall-through */ + case RS_NOTINSERVICE: + table_entry->$c = RS_NOTINSERVICE; + break; + + case RS_DESTROY: + ${i}_removeEntry(table_data, table_row ); + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( table_entry && !table_entry->valid ) { + table_entry->valid = 1; + } +@end@ + } + } +@end@ + break; +@end@ + } + return SNMP_ERR_NOERROR; +} +@end@ diff --git a/local/mib2c.iterate_access.conf b/local/mib2c.iterate_access.conf new file mode 100644 index 0000000..c211fd3 --- /dev/null +++ b/local/mib2c.iterate_access.conf @@ -0,0 +1,424 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/** other required module components */ +config_require(${name}_access) +config_require(${name}_checkfns) + +/* function declarations */ +void init_$name(void); +@foreach $i table@ +void initialize_table_$i(void); +Netsnmp_Node_Handler ${i}_handler; + +@end@ +@foreach $i table@ + +/* column number definitions for table $i */ +#include "${name}_columns.h" +@run mib2c.column_defines.conf@ + +/* enum definions */ +#include "${name}_enums.h" +@run mib2c.column_enums.conf@ + +@end@ +#endif /** $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" +#include "${name}_checkfns.h" +#include "${name}_access.h" + +static netsnmp_oid_stash_node *undoStorage = NULL; +static netsnmp_oid_stash_node *commitStorage = NULL; + +struct undoInfo { + void *ptr; + size_t len; +}; + +struct commitInfo { + void *data_context; + int have_committed; + int new_row; +}; + +void +${name}_free_undoInfo(void *vptr) { + struct undoInfo *ui = vptr; + if (!ui) + return; + SNMP_FREE(ui->ptr); + SNMP_FREE(ui); +} + +@foreach $i table@ +/** Initialize the $i table by defining its contents and how it's structured */ +void +initialize_table_$i(void) +{ + const oid ${i}_oid[] = {$i.commaoid}; + netsnmp_table_registration_info *table_info; + netsnmp_handler_registration *my_handler; + netsnmp_iterator_info *iinfo; + + DEBUGMSGTL(("${name}:init", "initializing table $i\n")); + + /** create the table registration information structures */ + table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); + iinfo = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info); + + my_handler = netsnmp_create_handler_registration("$i", + ${i}_handler, + ${i}_oid, + OID_LENGTH(${i}_oid), +@if $i.settable@ + HANDLER_CAN_RWRITE +@else@ + HANDLER_CAN_RONLY +@end@ + ); + + if (!my_handler || !table_info || !iinfo) { + snmp_log(LOG_ERR, "malloc failed in initialize_table_$i"); + return; /** Serious error. */ + } + + /*************************************************** + * Setting up the table's definition + */ + netsnmp_table_helper_add_indexes(table_info, + @foreach $idx index@ + $idx.type, /** index: $idx */ + @end@ + 0); + + /** Define the minimum and maximum accessible columns. This + optimizes retrieval. */ + @eval $minv = 0xffffffff@ + @eval $maxv = 0@ + @foreach $c column@ + @if $c.access =~ /(Read|Create)/@ + @eval $minv = min($minv, $c.subid)@ + @eval $maxv = max($maxv, $c.subid)@ + @end@ + @end@ + table_info->min_column = $minv; + table_info->max_column = $maxv; + + /** iterator access routines */ + iinfo->get_first_data_point = ${i}_get_first_data_point; + iinfo->get_next_data_point = ${i}_get_next_data_point; + + /** you may wish to set these as well */ +#ifdef MAYBE_USE_THESE + iinfo->make_data_context = ${i}_context_convert_function; + iinfo->free_data_context = ${i}_data_free; + + /** pick *only* one of these if you use them */ + iinfo->free_loop_context = ${i}_loop_free; + iinfo->free_loop_context_at_end = ${i}_loop_free; +#endif + + /** tie the two structures together */ + iinfo->table_reginfo = table_info; + + /*************************************************** + * registering the table with the master agent + */ + DEBUGMSGTL(("initialize_table_$i", + "Registering table $i as a table iterator\n")); + netsnmp_register_table_iterator(my_handler, iinfo); +} +@end@ + +/** Initializes the $name module */ +void +init_$name(void) +{ + + /** here we initialize all the tables we're planning on supporting */ + @foreach $i table@ + initialize_table_$i(); + @end@ +} +@foreach $i table@ + +/** handles requests for the $i table, if anything else needs to be done */ +int +${i}_handler( + netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) { + + netsnmp_request_info *request; + netsnmp_table_request_info *table_info; + netsnmp_variable_list *var; + struct commitInfo *ci = NULL; + + void *data_context = NULL; + + /** column and row index encoded portion */ + const oid * const suffix = + requests->requestvb->name + reginfo->rootoid_len + 1; + const size_t suffix_len = requests->requestvb->name_length - + (reginfo->rootoid_len + 1); + + DEBUGMSGTL(("${name}:handler", "Processing request (%d)\n", reqinfo->mode)); + + for(request = requests; request; request = request->next) { + var = request->requestvb; + if (request->processed != 0) + continue; + + switch (reqinfo->mode) { + case MODE_GET: + data_context = netsnmp_extract_iterator_context(request); + if (data_context == NULL) { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + break; + +@if $i.settable@ + case MODE_SET_RESERVE1: + data_context = netsnmp_extract_iterator_context(request); +@if !$i.creatable@ + if (data_context == NULL) { + netsnmp_set_request_error(reqinfo, request, + SNMP_ERR_NOCREATION); + continue; + } +@end@ + break; + + default: /* == the other SET modes */ + ci = netsnmp_oid_stash_get_data(commitStorage, + suffix+1, suffix_len-1); + break; +@end@ + } + + /** extracts the information about the table from the request */ + table_info = netsnmp_extract_table_info(request); + /** table_info->colnum contains the column number requested */ + /** table_info->indexes contains a linked list of snmp variable + bindings for the indexes of the table. Values in the list + have been set corresponding to the indexes of the + request */ + if (table_info == NULL) { + continue; + } + + switch(reqinfo->mode) { + case MODE_GET: + switch(table_info->colnum) { + @foreach $c column@ + @if $c.access =~ /(Read|Create)/@ + case COLUMN_$c.uc: + { + $c.decl *retval; + size_t retval_len = 0; + retval = get_$c(data_context, &retval_len); + if (retval) + snmp_set_var_typed_value(var, $c.type, + retval, + retval_len); + } + break; + + @end@ + @end@ + default: + /** We shouldn't get here */ + snmp_log(LOG_ERR, "problem encountered in ${i}_handler: unknown column\n"); + } + break; + +@if $i.settable@ + case MODE_SET_RESERVE1: + ci = netsnmp_oid_stash_get_data(commitStorage, + suffix+1, suffix_len-1); + + if (!ci) { + /** create the commit storage info */ + ci = SNMP_MALLOC_STRUCT(commitInfo); + if (!data_context) { + ci->data_context = ${i}_create_data_context(table_info->indexes, table_info->colnum); + ci->new_row = 1; + } else { + ci->data_context = data_context; + } + netsnmp_oid_stash_add_data(&commitStorage, + suffix+1, suffix_len-1, ci); + } + break; + + case MODE_SET_RESERVE2: + switch(table_info->colnum) { + @foreach $c column@ + @if $c.access =~ /(Write|Create)/@ + case COLUMN_$c.uc: + { + $c.decl *retval; + size_t retval_len = 0; + struct undoInfo *ui = NULL; + int ret; + + /** first, get the old value */ + retval = get_$c(ci->data_context, &retval_len); + if (retval) { + ui = SNMP_MALLOC_STRUCT(undoInfo); + ui->len = retval_len; + memdup((u_char **) &ui->ptr, + retval, ui->len); + } + + /** check the new value, possibly against the + older value for a valid state transition */ + ret = check_$c(request->requestvb->type, + ($c.decl *) request->requestvb->val.string, + request->requestvb->val_len, + retval, retval_len); + if (ret != 0) { + netsnmp_set_request_error(reqinfo, request, + ret); + ${name}_free_undoInfo(ui); + } else if (ui) { + /** remember information for undo purposes later */ + netsnmp_oid_stash_add_data(&undoStorage, + suffix, + suffix_len, + ui); + } + + } + break; + @end@ + @end@ + default: + netsnmp_set_request_error(reqinfo, request, + SNMP_ERR_NOTWRITABLE); + break; + } + break; + + case MODE_SET_ACTION: + /** save a variable copy */ + switch(table_info->colnum) { + @foreach $c column@ + @if $c.access =~ /(Write|Create)/@ + case COLUMN_$c.uc: + { + int ret; + ret = set_$c(ci->data_context, + ($c.decl *) request->requestvb->val.string, + request->requestvb->val_len); + if (ret) { + netsnmp_set_request_error(reqinfo, request, + ret); + } + @if $c.syntax eq "RowStatus"@ + if (*request->requestvb->val.integer == + RS_DESTROY) { + ci->new_row = -1; + } + @end@ + } + break; + @end@ + @end@ + } + break; + + case MODE_SET_COMMIT: + if (!ci->have_committed) { + /** do this once per row only */ + ${i}_commit_row(&ci->data_context, ci->new_row); + ci->have_committed = 1; + } + break; + + case MODE_SET_UNDO: + /** save a variable copy */ + switch(table_info->colnum) { + @foreach $c column@ + @if $c.access =~ /(Write|Create)/@ + case COLUMN_$c.uc: + { + int retval; + struct undoInfo *ui; + ui = netsnmp_oid_stash_get_data(undoStorage, + suffix, + suffix_len); + retval = set_$c(ci->data_context, ui->ptr, + ui->len); + if (retval) { + netsnmp_set_request_error(reqinfo, request, + SNMP_ERR_UNDOFAILED); + } + } + break; + @end@ + @end@ + } + break; + + case MODE_SET_FREE: + break; +@end@ + + default: + snmp_log(LOG_ERR, "problem encountered in ${i}_handler: unsupported mode\n"); + } + } + +@if $i.settable@ + /** clean up after all requset processing has ended */ + switch(reqinfo->mode) { + case MODE_SET_UNDO: + case MODE_SET_FREE: + case MODE_SET_COMMIT: + /** clear out the undo cache */ + netsnmp_oid_stash_free(&undoStorage, ${name}_free_undoInfo); + netsnmp_oid_stash_free(&commitStorage, netsnmp_oid_stash_no_free); + } +@end@ + + return SNMP_ERR_NOERROR; +} +@end@ +@run mib2c.check_values.conf@ +@run mib2c.access_functions.conf@ +@open -@ + +********************************************************************** +NOTE: The only files you MUST modify should be the following: + ${name}_access.c + ${name}_access.h + ${name}_checkfns_local.h + ${name}_checkfns_local.c +********************************************************************** + diff --git a/local/mib2c.mfd.conf b/local/mib2c.mfd.conf new file mode 100644 index 0000000..d309ea5 --- /dev/null +++ b/local/mib2c.mfd.conf @@ -0,0 +1,32 @@ +############################################################# -*- c -*- +## top level mfd conf file +## $Id$ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** START code generated by $RCSfile$ $Revision$ */ +@end@ +######################################################################## +## +@if "x$mfd_interactive_setup" == "x"@ +@ eval $mfd_interactive_setup = 1@ +@end@ +@if "x$m2c_report_progress" == "x"@ +@ eval $m2c_report_progress = 1@ +@end@ +## +@ifconf default-mfd-top.m2c@ +@ include default-mfd-top.m2c@ +@end@ +## +@if $name =~ /Table$/i@ +@else@ +@ print This module can only be used with tables, not branches or entire MIBs.@ +@ print Please specify and OID that is a table. (OID: $name)@ +@ quit@ +@end@ +## +@run mfd-top.m2c@ +######################################################################## +@if $m2c_mark_boundary == 1@ +/** END code generated by $RCSfile$ $Revision$ */ +@end@ diff --git a/local/mib2c.notify.conf b/local/mib2c.notify.conf new file mode 100644 index 0000000..77f3330 --- /dev/null +++ b/local/mib2c.notify.conf @@ -0,0 +1,85 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/* function declarations */ +@foreach $i notifications@ +int send_${i}_trap(void); +@end@ + +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" + +extern const oid snmptrap_oid[]; +extern const size_t snmptrap_oid_len; + +@foreach $i notifications@ +int +send_${i}_trap( void ) +{ + netsnmp_variable_list *var_list = NULL; + const oid ${i}_oid[] = { $i.commaoid }; + @foreach $v varbinds@ + @if $v.isscalar@ + const oid ${v}_oid[] = { $v.commaoid, 0 }; + @end@ + @if !$v.isscalar@ + const oid ${v}_oid[] = { $v.commaoid, /* insert index here */ }; + @end@ + @end@ + + /* + * Set the snmpTrapOid.0 value + */ + snmp_varlist_add_variable(&var_list, + snmptrap_oid, snmptrap_oid_len, + ASN_OBJECT_ID, + ${i}_oid, sizeof(${i}_oid)); + + @if count_varbinds($i) > 0@ + /* + * Add any objects from the trap definition + */ + @end@ + @foreach $v varbinds@ + snmp_varlist_add_variable(&var_list, + ${v}_oid, OID_LENGTH(${v}_oid), + $v.type, + /* Set an appropriate value for $v */ + NULL, 0); + @end@ + + /* + * Add any extra (optional) objects here + */ + + /* + * Send the trap to the list of configured destinations + * and clean up + */ + send_v2trap( var_list ); + snmp_free_varbind( var_list ); + + return SNMP_ERR_NOERROR; +} +@end@ diff --git a/local/mib2c.old-api.conf b/local/mib2c.old-api.conf new file mode 100644 index 0000000..8fa270b --- /dev/null +++ b/local/mib2c.old-api.conf @@ -0,0 +1,349 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/* function declarations */ +void init_$name(void); +FindVarMethod var_$name; +@foreach $i table@ +FindVarMethod var_${i}; +@end@ +@foreach $i scalar@ + @if $i.settable@ + WriteMethod write_${i}; + @end@ +@end@ +@foreach $i table@ + @foreach $c column@ + @if $c.settable@ + WriteMethod write_${c}; + @end@ + @end@ +@end@ + +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" + +/* + * ${name}_variables_oid: + * this is the top level oid that we want to register under. This + * is essentially a prefix, with the suffix appearing in the + * variable below. + */ + +oid ${name}_variables_oid[] = { $name.commaoid }; + +/* + * variable4 ${name}_variables: + * this variable defines function callbacks and type return information + * for the $name mib section + */ + +struct variable4 ${name}_variables[] = { +/* magic number , variable type , ro/rw , callback fn , L, oidsuffix */ +@eval $magic = 0@ +@eval $namelen = length("$name.commaoid")@ +@foreach $i scalar@ + @eval $magic = $magic + 1@ + @eval $suffix = substr("$i.commaoid", $namelen + 1)@ + @eval $suffixlen = $i.oidlength - $name.oidlength@ +#define $i.uc $magic + @if $i.settable@ +{$i.uc, $i.type, NETSNMP_OLDAPI_RWRITE, + var_${name}, $suffixlen, { $suffix }}, + @end@ + @if !$i.settable@ +{$i.uc, $i.type, NETSNMP_OLDAPI_RONLY, + var_${name}, $suffixlen, { $suffix }}, + @end@ +@end@ + +@foreach $i table@ + @eval $magic = 0@ + @eval $nlen2 = length("$i.commaoid")@ + @if $nlen2 > $namelen@ + @eval $suffix = substr("$i.commaoid", $namelen + 1)@ + @eval $ctmp = ","@ + @else@ + @eval $suffix = ""@ + @eval $ctmp = ""@ + @end@ + @eval $suffixlen = $i.oidlength - $name.oidlength + 2@ + @foreach $c column@ + @eval $magic = $magic + 1@ +#define $c.uc $magic + @if $c.settable@ +{$c.uc, $c.type, NETSNMP_OLDAPI_RWRITE, + var_${i}, $suffixlen, { $suffix $ctmp 1, $c.subid }}, + @end@ + @if !$c.settable@ +{$c.uc, $c.type, NETSNMP_OLDAPI_RONLY, + var_${i}, $suffixlen, { $suffix $ctmp 1, $c.subid }}, + @end@ + @end@ +@end@ +}; +/* (L = length of the oidsuffix) */ + + +/** Initializes the $name module */ +void +init_$name(void) +{ + + DEBUGMSGTL(("$name", "Initializing\n")); + + /* register ourselves with the agent to handle our mib tree */ + REGISTER_MIB("$name", ${name}_variables, variable4, + ${name}_variables_oid); + + /* place any other initialization junk you need here */ +} + +/* + * var_$name(): + * This function is called every time the agent gets a request for + * a scalar variable that might be found within your mib section + * registered above. It is up to you to do the right thing and + * return the correct value. + * You should also correct the value of "var_len" if necessary. + * + * Please see the documentation for more information about writing + * module extensions, and check out the examples in the examples + * and mibII directories. + */ +unsigned char * +var_$name(struct variable *vp, + oid *name, + size_t *length, + int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* variables we may use later */ + static long long_ret; + static u_long ulong_ret; + static unsigned char string[SPRINT_MAX_LEN]; + static oid objid[MAX_OID_LEN]; + static struct counter64 c64; + + if (header_generic(vp,name,length,exact,var_len,write_method) + == MATCH_FAILED ) + return NULL; + + /* + * this is where we do the value assignments for the mib results. + */ + switch(vp->magic) { +@foreach $i scalar@ + case $i.uc: + @if $i.settable@ + *write_method = write_${i}; + @end@ + VAR = VALUE; /* XXX */ + return (u_char*) &VAR; +@end@ + default: + ERROR_MSG(""); + } + return NULL; +} + + +@foreach $i table@ +/* + * var_$i(): + * Handle this table separately from the scalar value case. + * The workings of this are basically the same as for var_$name above. + */ +unsigned char * +var_$i(struct variable *vp, + oid *name, + size_t *length, + int exact, + size_t *var_len, + WriteMethod **write_method) +{ + /* variables we may use later */ + static long long_ret; + static u_long ulong_ret; + static unsigned char string[SPRINT_MAX_LEN]; + static oid objid[MAX_OID_LEN]; + static struct counter64 c64; + + /* + * This assumes that the table is a 'simple' table. + * See the implementation documentation for the meaning of this. + * You will need to provide the correct value for the TABLE_SIZE parameter + * + * If this table does not meet the requirements for a simple table, + * you will need to provide the replacement code yourself. + * Mib2c is not smart enough to write this for you. + * Again, see the implementation documentation for what is required. + */ + if (header_simple_table(vp,name,length,exact,var_len,write_method, TABLE_SIZE) + == MATCH_FAILED ) + return NULL; + + /* + * this is where we do the value assignments for the mib results. + */ + switch(vp->magic) { +@foreach $c column@ + case $c.uc: + @if $c.settable@ + *write_method = write_${c}; + @end@ + VAR = VALUE; /* XXX */ + return (u_char*) &VAR; +@end@ + default: + ERROR_MSG(""); + } + return NULL; +} +@end@ + +@foreach $i scalar@ +@if $i.settable@ + + +int +write_$i(int action, + u_char *var_val, + u_char var_val_type, + size_t var_val_len, + u_char *statP, + oid *name, + size_t name_len) +{ + $i.decl value; + int size; + + switch ( action ) { + case RESERVE1: + if (var_val_type != $i.type) { + fprintf(stderr, "write to $name not $i.type\n"); + return SNMP_ERR_WRONGTYPE; + } + if (var_val_len > sizeof($i.decl)) { + fprintf(stderr,"write to $name: bad length\n"); + return SNMP_ERR_WRONGLENGTH; + } + break; + + case RESERVE2: + size = var_val_len; + value = * ($i.decl *) var_val; + + break; + + case FREE: + /* Release any resources that have been allocated */ + break; + + case ACTION: + /* + * The variable has been stored in 'value' for you to use, + * and you have just been asked to do something with it. + * Note that anything done here must be reversable in the UNDO case + */ + break; + + case UNDO: + /* Back out any changes made in the ACTION case */ + break; + + case COMMIT: + /* + * Things are working well, so it's now safe to make the change + * permanently. Make sure that anything done here can't fail! + */ + break; + } + return SNMP_ERR_NOERROR; +} +@end@ +@end@ + +@foreach $i table@ +@foreach $c column@ +@if $c.settable@ +int +write_$c(int action, + u_char *var_val, + u_char var_val_type, + size_t var_val_len, + u_char *statP, + oid *name, + size_t name_len) +{ + $c.decl value; + int size; + + switch ( action ) { + case RESERVE1: + if (var_val_type != $c.type) { + fprintf(stderr, "write to $name not $c.type\n"); + return SNMP_ERR_WRONGTYPE; + } + if (var_val_len > sizeof($c.decl)) { + fprintf(stderr,"write to $name: bad length\n"); + return SNMP_ERR_WRONGLENGTH; + } + break; + + case RESERVE2: + size = var_val_len; + value = * ($c.decl *) var_val; + + break; + + case FREE: + /* Release any resources that have been allocated */ + break; + + case ACTION: + /* + * The variable has been stored in 'value' for you to use, + * and you have just been asked to do something with it. + * Note that anything done here must be reversable in the UNDO case + */ + break; + + case UNDO: + /* Back out any changes made in the ACTION case */ + break; + + case COMMIT: + /* + * Things are working well, so it's now safe to make the change + * permanently. Make sure that anything done here can't fail! + */ + break; + } + return SNMP_ERR_NOERROR; +} +@end@ +@end@ +@end@ diff --git a/local/mib2c.perl.conf b/local/mib2c.perl.conf new file mode 100755 index 0000000..3c83a18 --- /dev/null +++ b/local/mib2c.perl.conf @@ -0,0 +1,314 @@ +## ######################################################################## +## +## Config for generating modules for use in the embedded perl environmentg +## +## Copyright Tripleplay Services Limited 2005 +## All rights reserved. +## +## Use is subject to license terms specified in the COPYING file +## distributed with the Net-SNMP package. +## +## ######################################################################## +## +## Gotchas. +## Any $ signs in the output will be snaffled and disappear. For this reason +## lines that need to output perl variables use a printf line instead +## +## Comments that are for the use of documenting this config file +## need to be double hashes +## +## Operation +## 1. Creates a file called output.tmp which contains the main data structures +## and a call to the agent startup function. +## 2. Creates a file called skel.tmp with skeleton accessor functions. +## This should be copied to functions.pl and edited to actually +## do the work required for each leaf. +## 3. Creates an output file with the OID name. This contains the bolierplate +## and the data structures +## This is the file to reference in the snmpd.conf file +## +## The user must fill in the functions.pl code as requried. This file is then +## included at run time at the top of the generated perl code +## (do 'functions.pl) +## +## The generated file needs the NetSNMP::agent::Support.pm module in the +## system. This module contains the run-time support for the agent. +## +## The oidtable is a hash of hashes with the top level key an OID +## There are two types of entry +## 1. Scalars have the full OID plus the .0 index string +## 2. Columnar data has the index fields set to 0. The NetSNMP::agent::Support +## code will use zeros to locate the table specific handlers. +## +## ######################################################################### + + +## ######################################################################### +## Define the 'macros' used later in this config file +## ######################################################################### +@define EMIT_INDEX_VARS@ + ## + ## Calculate the number of index identifiers and then + ## for each identifier work out the offset in the oid + ## + @eval $numindex=0@ + @eval $idxoffset = $c.oidlength@ + # The values of the oid elements for the indexes + @foreach $i index@ +## my $$idx_$i = getOidElement($$idx, $idxoffset); + @printf " my %sidx_$vars{'i'} = getOidElement(%soid, $vars{'idxoffset'});\n",$,$@ + @eval $idxoffset = $idxoffset + 1@ + @end@ +@enddefine@ + +@define EMIT_GETARGS@ + ## + ## Output the code the get the args for a function + ## + # The OID is passed as a NetSNMP::OID object + @printf " my (%soid) = shift;\n",$@ +@enddefine@ + +@define EMIT_LOAD_DATA@ + ## + ## Emit the code to load a data table + ## + # Load the $t table data + load_$t(); +@enddefine@ + +@define EMIT_INDEX_INFO@ +## +## Emit a list of indexes for a table as perl comments +## To be used when generating the comment fields for a handler +## +# In Table: $t +@foreach $i index@ +# Index: $i +@end@ +@enddefine@ + +@define EMIT_INDEX_WALKER@ +## +## Output a skeleton index walker and index checker +## for the table if it has not been done already +## +@if $needwalker@ +## Output skeleton index validator for table +# ------------------------------------------------------- +# Index validation for table $t +# Checks the supplied OID is in range +# Returns 1 if it is and 0 if out of range +@calldefine EMIT_INDEX_INFO@ +# ------------------------------------------------------- +sub check_$t { + @calldefine EMIT_GETARGS@ + + @calldefine EMIT_INDEX_VARS@ + + @calldefine EMIT_LOAD_DATA@ + + # Check the index is in range and valid + return 1; +} + +# ------------------------------------------------------- +# Index walker for table $t +# Given an OID for a table, returns the next OID in range, +# or if no more OIDs it returns 0. +@calldefine EMIT_INDEX_INFO@ +# ------------------------------------------------------- +sub next_$t { + @calldefine EMIT_GETARGS@ + + @calldefine EMIT_INDEX_VARS@ + + @calldefine EMIT_LOAD_DATA@ + + # Return the next OID if there is one + # or return 0 if no more OIDs in this table + return 0; +} +@eval $needwalker = 0@ ## Dont need this again for the current table +@end@ +@enddefine@ + +@define EMIT_TABLE_LOAD@ +# ------------------------------------------------------- +# Loader for table $t +# Edit this function to load the data needed for $t +# This function gets called for every request to columnar +# data in the $t table +# ------------------------------------------------------- +sub load_$t { + +} +@enddefine@ + +@eval $date=scalar localtime; @ + +## Open the output file and emit the perl startup bolierplate +@open output.tmp@ +#!/usr/bin/perl -w +# +# +# WARNING: DO NOT EDIT THIS FILE BY HAND. +# +# This file has been generated by mib2c using the mib2c.perl.conf file +# This is intended to be used by the net-snmp agent with embedded perl +# support. See perldoc NetSNMP::agent +# +# Created on $date +# +# To load this into a running agent with embedded perl support turned +# on, simply put the following line (without the leading # mark) your +# snmpd.conf file: +# +@printf "# perl do 'path/to/agent_%s.pl'\n",$oid@ +# +# You will need a copy of NetSNMP installed. This has been developed using +# NetSNMP version 5.2.2 +# + + + +##use strict; +use NetSNMP::agent::Support; +use NetSNMP::ASN (':all'); + +# Include the functions to handle the nodes +do 'functions.pl'; + +## Create the skeleton file ready for the skeleton handlers later on +@push@ +@open skel.tmp@ +# Skeleton accessor functions. +# DO NOT EDIT +# This file will be overwritten next time mib2c is run. +# Copy this file to functions.pl and then edit it. +@close skel.tmp@ +@pop@ + +## Generate the hash of hashes with the oids and handlers for the tables +# Hash for all OIDs +@printf "my %soidtable={\n", $@ +# Table objects +@foreach $t table@ + @print Processing table $t@ + @push@ + @append skel.tmp@ +## Output skeleton loader for this table + @calldefine EMIT_TABLE_LOAD@ + @close skel.tmp@ + @pop@ + ## + @eval $needwalker = 1@ ## Need the walker and checker once this table + @foreach $c nonindex@ + @if $c.accessible @ + ## + ## Generate the entry for the hash table + ## We first calculate the number of index items for this table + @eval $numindex = 0@ + @eval $idxelem = ""@ + @foreach $i index@ + @perleval $vars{'idxelem'} .= '.0'; 0; @ + @eval $numindex = $numindex+1@ + @end@ + "$c.objectID$idxelem"=>{func=>\&get_$c,type=>$c.type, check=>\&check_$t, nextoid=>\&next_$t, istable=>'1', next=>"", numindex=>$numindex}, + ## Output skeleton handlers for this column object + @push@ + @append skel.tmp@ +@calldefine EMIT_INDEX_WALKER@ +# ------------------------------------------------------- +# Handler for columnar object '$c' +# OID: $c.objectID +# Syntax: $c.type +# From: $c.module +@calldefine EMIT_INDEX_INFO@ +# ------------------------------------------------------- +sub get_$c { + @calldefine EMIT_GETARGS@ + + @calldefine EMIT_INDEX_VARS@ + + @calldefine EMIT_LOAD_DATA@ + + # Code here to read the required variable from the loaded table + # using whatever indexing you need. + # The index has already been checked and found to be valid + + ## Add further types as required. + @if $c.type eq "ASN_INTEGER"@ + return 32; + @end@ + @if $c.type eq "ASN_OCTET_STR"@ + return "STR"; + @end@ + @if $c.type eq "ASN_COUNTER64"@ + return 64; + @end@ +} + @close skel.tmp@ + @pop@ + @end@ + @end@ +@end@ +@print Processing scalars@ +## output the hash with the OIDs and handlers +## Scalars have a single index element +# Scalars +@foreach $s scalar@ + @if $s.accessible@ + '$s.objectID.0'=>{func=>\&get_$s,type=>$s.type,next=>"", numindex=>1}, + @end@ +@end@ +##End of the OID hash +}; + +## Emit code to register the top level oid with the agent +## The $oid variable comes from mib2c as the last non option arg +# Register the top oid with the agent +@printf "registerAgent(%sagent, '$oid', %soidtable);",$,$@ + +## Output skeleton handlers for the scalars +@push@ +@append skel.tmp@ +@foreach $s scalar@ + @if $s.accessible@ +# ------------------------------------------------------- +# Handler for scalar object $s +# OID: $s.objectID +# Syntax: $s.type +# From: $s.module +# ------------------------------------------------------- +sub get_$s { + + # Add code here to read the value required and return it + + ## Add further types as required. + @if $s.type eq "ASN_INTEGER"@ + return 32; + @end@ + @if $s.type eq "ASN_OCTET_STR"@ + return "STR"; + @end@ + @if $s.type eq "ASN_COUNTER64"@ + return 64; + @end@ +} + @end@ +@end@ +@close skel.tmp@ +@pop@ + +@close output.tmp@ +## +## Now create the code file from the outputfile +## +@startperl@ +my $oidname = $vars{'oid'}; +my $out = "agent_" . $oidname .".pl"; +system("cat output.tmp > $out"); +@endperl@ +@print Output code generated.@ + diff --git a/local/mib2c.raw-table.conf b/local/mib2c.raw-table.conf new file mode 100644 index 0000000..bd88fdb --- /dev/null +++ b/local/mib2c.raw-table.conf @@ -0,0 +1,612 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id: mib2c.raw-table.conf 17436 2009-03-31 15:12:19Z dts12 $ + */ +#ifndef $name.uc_H +#define $name.uc_H + +void init_$name(void); + +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id: mib2c.raw-table.conf 17436 2009-03-31 15:12:19Z dts12 $ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" + +@foreach $i table@ + ## Determine the first/last column names + @eval $first_column = "-"@ + @eval $last_column = "-"@ + @foreach $c column@ + @if $c.readable@ + @if "$first_column" eq "-"@ + @eval $first_column = $c@ + @end@ + @eval $last_column = $c@ + @end@ + @end@ + +/* column number definitions for table $i */ + @foreach $c column@ +#define COLUMN_$c.uc $c.subid + @end@ + + /* Typical data structure for a row entry */ +struct ${i}_entry { + /* Index values */ + @foreach $idx index@ + @if $idx.needlength@ + $idx.decl $idx[NNN]; + size_t ${idx}_len; + @else@ + $idx.decl $idx; + @end@ + @end@ + + /* Column values */ + @foreach $c nonindex@ + @if $c.readable@ + @if $c.needlength@ + $c.decl $c[NNN]; + size_t ${c}_len; + @else@ + $c.decl $c; + @end@ + @if $c.settable@ + @if !$c.rowstatus@ + @if $c.needlength@ + $c.decl old_$c[NNN]; + size_t old_${c}_len; + @else@ + $c.decl old_$c; + @end@ + @end@ + @end@ + @end@ + @end@ + + int valid; +}; + +/* create a new row in the table */ +static struct ${i}_entry * +${i}_createEntry( int dummy + @foreach $idx index@ + @if $idx.needlength@ + , $idx.decl* $idx + , size_t ${idx}_len + @else@ + , $idx.decl $idx + @end@ + @end@ + ) { + struct ${i}_entry *entry; + + entry = SNMP_MALLOC_TYPEDEF(struct ${i}_entry); + if (!entry) + return NULL; + + /* XXX - insert entry into local data structure */ + return entry; +} + +/* remove a row from the table */ +static void +${i}_removeEntry(struct ${i}_entry *entry) { + if (!entry) + return; /* Nothing to remove */ + + /* XXX - remove entry from local data structure */ + + if (entry) + SNMP_FREE( entry ); /* XXX - release any other internal resources */ +} + + +/** determine the appropriate row for an exact request */ +static struct ${i}_entry * +${i}_get_entry( netsnmp_variable_list *indexes ) { + struct ${i}_entry *row = NULL; + + /* XXX - Use the 'indexes' parameter to retrieve the data + structure for the requested row, and return this. */ + return row; +} + +/** determine the appropriate row for an fuzzy request */ +static struct ${i}_entry * +${i}_get_next_entry( netsnmp_handler_registration *reginfo, + netsnmp_request_info *request, + int column, + netsnmp_variable_list *indexes ) { + struct ${i}_entry *row = NULL; + oid build_space[MAX_OID_LEN]; + size_t build_space_len = 0; + size_t index_oid_len = 0; + + /* XXX - Use the 'indexes' parameter to identify the + next row in the table.... */ + + /* XXX .... update the 'indexes' parameter with the + appropriate index values ... */ + + /* ... and update the requested OID to match this instance */ + memcpy(build_space, reginfo->rootoid, /* registered oid */ + reginfo->rootoid_len * sizeof(oid)); + build_space_len = reginfo->rootoid_len; + build_space[build_space_len++] = 1; /* entry */ + build_space[build_space_len++] = column; /* column */ + build_oid_noalloc(build_space + build_space_len, + MAX_OID_LEN - build_space_len, &index_oid_len, + NULL, 0, indexes); + snmp_set_var_objid(request->requestvb, build_space, + build_space_len + index_oid_len); + + /* Finally, return the data structure for this row */ + return row; +} + + +/** handles requests for the $i table */ +static int +${i}_handler( + netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) { + + netsnmp_request_info *request; + netsnmp_table_request_info *table_info; + struct ${i}_entry *table_entry; + @if $i.settable@ + int ret; + @end@ + + switch (reqinfo->mode) { + /* + * Read-support + */ + case MODE_GET: + for (request=requests; request; request=request->next) { + table_info = netsnmp_extract_table_info( request); + table_entry = ${i}_get_entry( table_info->indexes ); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.readable@ + case COLUMN_$c.uc: + if ( !table_entry ) { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + @if $c.needlength@ + snmp_set_var_typed_value( request->requestvb, $c.type, + table_entry->$c, + table_entry->${c}_len); + @else@ + snmp_set_var_typed_integer( request->requestvb, $c.type, + table_entry->$c); + @end@ + break; + @end@ + @end@ + default: + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHOBJECT); + break; + } + } + break; + + case MODE_GETNEXT: + for (request=requests; request; request=request->next) { + table_info = netsnmp_extract_table_info( request); + table_entry = ${i}_get_next_entry( reginfo, request, + table_info->colnum, table_info->indexes ); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.readable@ + case COLUMN_$c.uc: + if ( !table_entry ) { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + @if $c.needlength@ + snmp_set_var_typed_value( request->requestvb, $c.type, + table_entry->$c, + table_entry->${c}_len); + @else@ + snmp_set_var_typed_integer( request->requestvb, $c.type, + table_entry->$c); + @end@ + break; + @end@ + @end@ + default: + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHOBJECT); + break; + } + } + break; + +@if $i.settable@ + /* + * Write-support + */ + case MODE_SET_RESERVE1: + for (request=requests; request; request=request->next) { + table_info = netsnmp_extract_table_info( request); + table_entry = ${i}_get_entry( table_info->indexes ); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + @if $c.rowstatus@ + ret = netsnmp_check_vb_rowstatus(request->requestvb, + (table_entry ? RS_ACTIVE : RS_NONEXISTENT )); + @else@ + @if $c.needlength@ + /* or possibly 'netsnmp_check_vb_type_and_size' */ + ret = netsnmp_check_vb_type_and_max_size( + request->requestvb, $c.type, sizeof(table_entry->$c)); + @else@ + /* or possibly 'netsnmp_check_vb_int_range' */ + ret = netsnmp_check_vb_int( request->requestvb ); + @end@ + @end@ + if ( ret != SNMP_ERR_NOERROR ) { + netsnmp_set_request_error( reqinfo, request, ret ); + return SNMP_ERR_NOERROR; + } + break; + @end@ + @end@ + default: + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_NOTWRITABLE ); + return SNMP_ERR_NOERROR; + } + } + break; + + case MODE_SET_RESERVE2: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + table_info = netsnmp_extract_table_info( request); + table_entry = ${i}_get_entry( table_info->indexes ); + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + table_entry = ${i}_createEntry( 0 + @foreach $idx index@ + @if $idx.needlength@ + , table_info->indexes->val.string + , table_info->indexes->val_len + @else@ + , *table_info->indexes->val.integer + @end@ + @end@ + ); + if ( !table_entry ) { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_RESOURCEUNAVAILABLE ); + return SNMP_ERR_NOERROR; + } + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( !table_entry ) { + table_entry = ${i}_createEntry( 0 + @foreach $idx index@ + @if $idx.needlength@ + , table_info->indexes->val.string + , table_info->indexes->val_len + @else@ + , *table_info->indexes->val.integer + @end@ + @end@ + ); + if ( !table_entry ) { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_RESOURCEUNAVAILABLE ); + return SNMP_ERR_NOERROR; + } + } + break; +@end@ + } + } +@end@ + break; + + case MODE_SET_FREE: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + table_info = netsnmp_extract_table_info( request); + table_entry = ${i}_get_entry( table_info->indexes ); + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + if (table_entry && !table_entry->valid) { + ${i}_removeEntry(table_entry); + } + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( table_entry && !table_entry->valid ) { + ${i}_removeEntry(table_entry); + } + break; +@end@ + } + } +@end@ + break; + + case MODE_SET_ACTION: + for (request=requests; request; request=request->next) { + table_info = netsnmp_extract_table_info( request); + table_entry = ${i}_get_entry( table_info->indexes ); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + @if !$c.rowstatus@ + case COLUMN_$c.uc: + @if $c.needlength@ + memcpy( table_entry->old_$c, + table_entry->$c, + sizeof(table_entry->$c)); + table_entry->old_${c}_len = + table_entry->${c}_len; + memset( table_entry->$c, 0, + sizeof(table_entry->$c)); + memcpy( table_entry->$c, + request->requestvb->val.string, + request->requestvb->val_len); + table_entry->${c}_len = + request->requestvb->val_len; + @else@ + table_entry->old_$c = table_entry->$c; + table_entry->$c = *request->requestvb->val.integer; + @end@ + break; + @end@ + @end@ + @end@ + } + } +@if $i.rowstatus@ + /* Check the internal consistency of an active row */ + for (request=requests; request; request=request->next) { + table_info = netsnmp_extract_table_info( request); + table_entry = ${i}_get_entry( table_info->indexes ); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_ACTIVE: + case RS_CREATEANDGO: + if (/* XXX */) { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_INCONSISTENTVALUE ); + return SNMP_ERR_NOERROR; + } + } + @end@ + @end@ + } + } +@end@ + break; + + case MODE_SET_UNDO: + for (request=requests; request; request=request->next) { + table_info = netsnmp_extract_table_info( request); + table_entry = ${i}_get_entry( table_info->indexes ); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: +@if $i.rowstatus@ + @if $c.rowstatus@ + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + if (table_entry && !table_entry->valid) { + ${i}_removeEntry(table_entry); + } + } + @else@ + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + @end@ +@else@ + @if $c.creatable@ + if ( table_entry && !table_entry->valid ) { + ${i}_removeEntry(table_entry); + } else { + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + } + @else@ + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + @end@ +@end@ + break; + @end@ + @end@ + } + } + break; + + case MODE_SET_COMMIT: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + table_info = netsnmp_extract_table_info( request); + table_entry = ${i}_get_entry( table_info->indexes ); + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + table_entry->valid = 1; + /* Fall-through */ + case RS_ACTIVE: + table_entry->$c = RS_ACTIVE; + break; + + case RS_CREATEANDWAIT: + table_entry->valid = 1; + /* Fall-through */ + case RS_NOTINSERVICE: + table_entry->$c = RS_NOTINSERVICE; + break; + + case RS_DESTROY: + ${i}_removeEntry(table_entry); + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( table_entry && !table_entry->valid ) { + table_entry->valid = 1; + } +@end@ + } + } +@end@ + break; +@end@ + } + return SNMP_ERR_NOERROR; +} + +/** Initialize the $i table by defining its contents and how it's structured */ +static void +initialize_table_$i(void) +{ + const oid ${i}_oid[] = {$i.commaoid}; + netsnmp_handler_registration *reg; + netsnmp_table_registration_info *table_info; + + reg = netsnmp_create_handler_registration( + "$i", ${i}_handler, + ${i}_oid, OID_LENGTH(${i}_oid), +@if $i.settable@ + HANDLER_CAN_RWRITE +@else@ + HANDLER_CAN_RONLY +@end@ + ); + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + netsnmp_table_helper_add_indexes(table_info, + @foreach $idx index@ + $idx.type, /* index: $idx */ + @end@ + 0); + + table_info->min_column = COLUMN_$first_column.uc; + table_info->max_column = COLUMN_$last_column.uc; + + netsnmp_register_table( reg, table_info ); + + /* Initialise the contents of the table here */ +} + +@end@ +/** Initializes the $name module */ +void +init_$name(void) +{ + /* here we initialize all the tables we're planning on supporting */ + @foreach $i table@ + initialize_table_$i(); + @end@ +} diff --git a/local/mib2c.row.conf b/local/mib2c.row.conf new file mode 100755 index 0000000..f736baa --- /dev/null +++ b/local/mib2c.row.conf @@ -0,0 +1,284 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/* function declarations */ +void init_$name(void); +@foreach $i table@ +void initialize_table_$i(void); +Netsnmp_Node_Handler ${i}_handler; +netsnmp_variable_list *${i}_buildIndexList(struct ${i}_entry *row); +struct ${i}_entry * +${i}_createEntry( + @foreach $idx index@ + @if $idx.needlength@ + , $idx.decl* $idx + , size_t ${idx}_len + @else@ + , $idx.decl $idx + @end@ + @end@ + ); +@end@ +@foreach $i table@ + +/* column number definitions for table $i */ + @foreach $c column@ + #define COLUMN_$c.uc $c.subid + @end@ + + +/* Typical data structure for a row entry */ +struct ${i}_entry { + netsnmp_index oid_index; + + /* Index values */ + @foreach $idx index@ + @if $idx.needlength@ + $idx.decl $idx[NNN]; + size_t ${idx}_len; + @else@ + $idx.decl $idx; + @end@ + @end@ + + /* Column values */ + @foreach $c column@ + @if $c.readable@ + @if $c.needlength@ + $c.decl $c[NNN]; + size_t ${c}_len; + @else@ + $c.decl $c; + @end@ + @if $c.settable@ + @if !$c.rowstatus@ + @if $c.needlength@ + $c.decl old_$c[NNN]; + size_t old_${c}_len; + @else@ + $c.decl old_$c; + @end@ + @end@ + @end@ + @end@ + @end@ + + int valid; +}; + +@end@ +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" + +/** Initializes the $name module */ +void +init_$name(void) +{ + /* here we initialize all the table rows we're planning on supporting */ + @foreach $i table@ + initialize_table_$i(); + @end@ +} + +@foreach $i table@ + @eval $first_column = "-"@ + @eval $last_column = "-"@ + @foreach $c column@ + @if $c.readable@ + @if "$first_column" eq "-"@ + @eval $first_column = $c@ + @end@ + @eval $last_column = $c@ + @end@ + @end@ + +/** Initialize an entry in the $i table, including how the table is structured */ +void +initialize_table_$i(void) +{ + const oid ${i}_oid[] = {$i.commaoid}; + netsnmp_handler_registration *reg; + struct ${i}_entry *row; + netsnmp_variable_list *idxs; + netsnmp_table_registration_info *table_info; + + DEBUGMSGTL(("${name}:init", "initializing table $i\n")); + + reg = netsnmp_create_handler_registration( + "$i", ${i}_handler, + ${i}_oid, OID_LENGTH(${i}_oid), +@if $i.settable@ + HANDLER_CAN_RWRITE +@else@ + HANDLER_CAN_RONLY +@end@ + ); + + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + netsnmp_table_helper_add_indexes(table_info, + @foreach $idx index@ + $idx.type, /* index: $idx */ + @end@ + 0); + table_info->min_column = COLUMN_$first_column.uc; + table_info->max_column = COLUMN_$last_column.uc; + + /* + * Create the row to be registered + */ + row = ${i}_createEntry( + @foreach $idx index@ + @if $idx.needlength@ + ,/* $idx value */ , /* $idx length */ + @else@ + ,/* $idx value */ + @end@ + @end@ + ); + /* + * XXX: Set any other readable column values here + */ + idxs = ${i}_buildIndexList(row); + netsnmp_table_row_register( reg, table_info, row, idxs ); + + /* Repeat for any other rows to be registered */ +} + +/* create a new row in the table */ +struct ${i}_entry * +${i}_createEntry( + @foreach $idx index@ + @if $idx.needlength@ + , $idx.decl* $idx + , size_t ${idx}_len + @else@ + , $idx.decl $idx + @end@ + @end@ + ) { + struct ${i}_entry *entry; + + entry = SNMP_MALLOC_TYPEDEF(struct ${i}_entry); + if (!entry) + return NULL; + + @foreach $idx index@ + @if $idx.needlength@ + memcpy(entry->$idx, $idx, ${idx}_len); + entry->${idx}_len = ${idx}_len; + @else@ + entry->$idx = $idx; + @end@ + @end@ + + return entry; +} + +netsnmp_variable_list * +${i}_buildIndexList(struct ${i}_entry *row) +{ + netsnmp_variable_list *v1 = NULL, *v2; + + if (!row) + return NULL; + + @eval $first=1@ + @foreach $idx index@ + @if $first==1@ + v1 = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); + v2 = v1; + @else@ + v2->next_variable = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); + v2 = v2->next_variable; + @end@ + @if $idx.needlength@ + snmp_set_var_typed_value( v2, $idx.type, row->$idx, row->${idx}_len); + @else@ + snmp_set_var_typed_integer( v2, $idx.type, row->$idx); + @end@ + @eval $first=0@ + @end@ + + return v1; +} + +/** handles requests for a row of the $i table */ +int +${i}_handler( + netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) { + + netsnmp_request_info *request; + netsnmp_table_request_info *table_info; + netsnmp_table_data *table_data; + struct ${i}_entry *table_entry; + int ret; + + DEBUGMSGTL(("${name}:handler", "Processing request (%d)\n", reqinfo->mode)); + + switch (reqinfo->mode) { + /* + * Read-support (also covers GetNext requests) + */ + case MODE_GET: + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_table_row_extract(request); + + switch (request->requestvb->name[$i.oidlength+1]) { + @foreach $c column@ + @if $c.readable@ + case COLUMN_$c.uc: + @if $c.needlength@ + snmp_set_var_typed_value( request->requestvb, $c.type, + table_entry->$c, + table_entry->${c}_len); + @else@ + snmp_set_var_typed_integer( request->requestvb, $c.type, + table_entry->$c); + @end@ + break; + @end@ + @end@ + default: + /* An unsupported/unreadable column (if applicable) */ + snmp_set_var_typed_value( request->requestvb, SNMP_NOSUCHOBJECT, + NULL, 0 ); + } + } + break; + +@if $i.settable@ + /* + * Write-support - TODO + */ +@end@ + break; +@end@ + } + return SNMP_ERR_NOERROR; +} +@end@ diff --git a/local/mib2c.scalar.conf b/local/mib2c.scalar.conf new file mode 100644 index 0000000..3024fa4 --- /dev/null +++ b/local/mib2c.scalar.conf @@ -0,0 +1,142 @@ +## -*- c -*- +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/* function declarations */ +void init_$name(void); +@foreach $i scalar@ +Netsnmp_Node_Handler handle_${i}; +@end@ + +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" + +/** Initializes the $name module */ +void +init_$name(void) +{ + @foreach $i scalar@ + const oid ${i}_oid[] = { $i.commaoid }; + @end@ + + DEBUGMSGTL(("$name", "Initializing\n")); + + @foreach $i scalar@ + netsnmp_register_scalar( + netsnmp_create_handler_registration("$i", handle_$i, + ${i}_oid, OID_LENGTH(${i}_oid), + @if !$i.settable@ + HANDLER_CAN_RONLY + @end@ + @if $i.settable@ + HANDLER_CAN_RWRITE + @end@ + )); + @end@ +} + +@foreach $i scalar@ +int +handle_$i(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + @if $i.settable@ + int ret; + @end@ + /* We are never called for a GETNEXT if it's registered as a + "instance", as it's "magically" handled for us. */ + + /* a instance handler also only hands us one request at a time, so + we don't need to loop over a list of requests; we'll only get one. */ + + switch(reqinfo->mode) { + + case MODE_GET: + snmp_set_var_typed_value(requests->requestvb, $i.type, + /* XXX: a pointer to the scalar's data */, + /* XXX: the length of the data in bytes */); + break; + + @if $i.settable@ + /* + * SET REQUEST + * + * multiple states in the transaction. See: + * http://www.net-snmp.org/tutorial-5/toolkit/mib_module/set-actions.jpg + */ + case MODE_SET_RESERVE1: + /* or you could use netsnmp_check_vb_type_and_size instead */ + ret = netsnmp_check_vb_type(requests->requestvb, $i.type); + if ( ret != SNMP_ERR_NOERROR ) { + netsnmp_set_request_error(reqinfo, requests, ret ); + } + break; + + case MODE_SET_RESERVE2: + /* XXX malloc "undo" storage buffer */ + if (/* XXX if malloc, or whatever, failed: */) { + netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); + } + break; + + case MODE_SET_FREE: + /* XXX: free resources allocated in RESERVE1 and/or + RESERVE2. Something failed somewhere, and the states + below won't be called. */ + break; + + case MODE_SET_ACTION: + /* XXX: perform the value change here */ + if (/* XXX: error? */) { + netsnmp_set_request_error(reqinfo, requests, /* some error */); + } + break; + + case MODE_SET_COMMIT: + /* XXX: delete temporary storage */ + if (/* XXX: error? */) { + /* try _really_really_ hard to never get to this point */ + netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_COMMITFAILED); + } + break; + + case MODE_SET_UNDO: + /* XXX: UNDO and return to previous value for the object */ + if (/* XXX: error? */) { + /* try _really_really_ hard to never get to this point */ + netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED); + } + break; + @end@ + + default: + /* we should never get here, so this is a really bad error */ + snmp_log(LOG_ERR, "unknown mode (%d) in handle_${i}\n", reqinfo->mode ); + return SNMP_ERR_GENERR; + } + + return SNMP_ERR_NOERROR; +} +@end@ diff --git a/local/mib2c.table_data.conf b/local/mib2c.table_data.conf new file mode 100644 index 0000000..2cc665c --- /dev/null +++ b/local/mib2c.table_data.conf @@ -0,0 +1,742 @@ +## -*- c -*- +# +@if "$cache" eq "" @ +@open -@ +This framework can work in one of two ways: + + 1) Hold a local cached copy of some external data + which is then used to service incoming requests. + + 2) Hold the data for the table solely within the agent + +Which is more appropriate for your needs? + @prompt $ans Select your choice : @ + @if $ans == 1 @ + @eval $cache = 1@ + @elsif $ans == 2@ + @eval $cache = 0@ + @else@ +Invalid answer - generating caching code + @eval $cache = 1@ + @end@ +@else@ # -S cache={something} + @if "$cache" ne "1" && "$cache" ne "0" @ +@open -@ +Invalid value for 'cache' ($cache) - generating caching code + @eval $cache = 1@ + @end@ +@end@ + +###################################################################### +## Do the .h file +###################################################################### +@open ${name}.h@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ +#ifndef $name.uc_H +#define $name.uc_H + +/* function declarations */ +void init_$name(void); +@foreach $i table@ +void initialize_table_$i(void); +Netsnmp_Node_Handler ${i}_handler; +@if "$cache" == 1 @ +NetsnmpCacheLoad ${i}_load; +NetsnmpCacheFree ${i}_free; +#define $i.uc_TIMEOUT 60 +@end@ +@end@ +@foreach $i table@ + +/* column number definitions for table $i */ + @foreach $c column@ + #define COLUMN_$c.uc $c.subid + @end@ +@end@ +#endif /* $name.uc_H */ +###################################################################### +## Do the .c file +###################################################################### +@open ${name}.c@ +/* + * Note: this file originally auto-generated by mib2c using + * $Id$ + */ + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "${name}.h" + +/** Initializes the $name module */ +void +init_$name(void) +{ + /* here we initialize all the tables we're planning on supporting */ + @foreach $i table@ + initialize_table_$i(); + @end@ +} + +@foreach $i table@ + ## Determine the first/last column names + @eval $first_column = "-"@ + @eval $last_column = "-"@ + @foreach $c column@ + @if $c.readable@ + @if "$first_column" eq "-"@ + @eval $first_column = $c@ + @end@ + @eval $last_column = $c@ + @end@ + @end@ + +/** Initialize the $i table by defining its contents and how it's structured */ +void +initialize_table_$i(void) +{ + const oid ${i}_oid[] = {$i.commaoid}; + const size_t ${i}_oid_len = OID_LENGTH(${i}_oid); + netsnmp_handler_registration *reg; + netsnmp_tdata *table_data; + netsnmp_table_registration_info *table_info; +@if "$cache" == 1 @ + netsnmp_cache *cache; +@end@ + + DEBUGMSGTL(("${name}:init", "initializing table $i\n")); + + reg = netsnmp_create_handler_registration( + "$i", ${i}_handler, + ${i}_oid, ${i}_oid_len, +@if $i.settable@ + HANDLER_CAN_RWRITE +@else@ + HANDLER_CAN_RONLY +@end@ + ); + + table_data = netsnmp_tdata_create_table( "$i", 0 ); + if (NULL == table_data) { + snmp_log(LOG_ERR,"error creating tdata table for $i\n"); + return; + } +@if "$cache" == 1 @ + cache = netsnmp_cache_create($i.uc_TIMEOUT, + ${i}_load, ${i}_free, + ${i}_oid, ${i}_oid_len); + if (NULL == cache) { + snmp_log(LOG_ERR,"error creating cache for $i\n"); + } + else + cache->magic = (void *)table_data; +@end@ + table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); + if (NULL == table_info) { + snmp_log(LOG_ERR,"error creating table info for $i\n"); + return; + } + netsnmp_table_helper_add_indexes(table_info, + @foreach $idx index@ + $idx.type, /* index: $idx */ + @end@ + 0); + + table_info->min_column = COLUMN_$first_column.uc; + table_info->max_column = COLUMN_$last_column.uc; + + netsnmp_tdata_register( reg, table_data, table_info ); +@if "$cache" == 1 @ + if (cache) + netsnmp_inject_handler_before( reg, netsnmp_cache_handler_GET(cache), + TABLE_TDATA_NAME); +@end@ + + /* Initialise the contents of the table here */ +} + + /* Typical data structure for a row entry */ +struct ${i}_entry { + /* Index values */ + @foreach $idx index@ + @if $idx.needlength@ + $idx.decl $idx[NNN]; + size_t ${idx}_len; + @else@ + $idx.decl $idx; + @end@ + @end@ + + /* Column values */ + @foreach $c column@ + @if $c.readable@ + @if $c.needlength@ + $c.decl $c[NNN]; + size_t ${c}_len; + @else@ + $c.decl $c; + @end@ + @if $c.settable@ + @if !$c.rowstatus@ + @if $c.needlength@ + $c.decl old_$c[NNN]; + size_t old_${c}_len; + @else@ + $c.decl old_$c; + @end@ + @end@ + @end@ + @end@ + @end@ + + int valid; +}; + +/* create a new row in the table */ +netsnmp_tdata_row * +${i}_createEntry(netsnmp_tdata *table_data + @foreach $idx index@ + @if $idx.needlength@ + , $idx.decl* $idx + , size_t ${idx}_len + @else@ + , $idx.decl $idx + @end@ + @end@ + ) { + struct ${i}_entry *entry; + netsnmp_tdata_row *row; + + entry = SNMP_MALLOC_TYPEDEF(struct ${i}_entry); + if (!entry) + return NULL; + + row = netsnmp_tdata_create_row(); + if (!row) { + SNMP_FREE(entry); + return NULL; + } + row->data = entry; + + DEBUGMSGT(("${i}:entry:create", "row 0x%x\n", (uintptr_t)row)); + @foreach $idx index@ + @if $idx.needlength@ + memcpy(entry->$idx, $idx, ${idx}_len); + entry->${idx}_len = ${idx}_len; + netsnmp_tdata_row_add_index( row, $idx.type, + entry->$idx, ${idx}_len); + @else@ + entry->$idx = $idx; + netsnmp_tdata_row_add_index( row, $idx.type, + &(entry->$idx), + sizeof(entry->$idx)); + @end@ + @end@ + if (table_data) + netsnmp_tdata_add_row( table_data, row ); + return row; +} + +/* remove a row from the table */ +void +${i}_removeEntry(netsnmp_tdata *table_data, + netsnmp_tdata_row *row) { + struct ${i}_entry *entry; + + if (!row) + return; /* Nothing to remove */ + + DEBUGMSGT(("${i}:entry:remove", "row 0x%x\n", (uintptr_t)row)); + + entry = (struct ${i}_entry *)row->data; + SNMP_FREE( entry ); /* XXX - release any other internal resources */ + + if (table_data) + netsnmp_tdata_remove_and_delete_row( table_data, row ); + else + netsnmp_tdata_delete_row( row ); +} + +@if "$cache" == 1 @ +/* Example cache handling - set up table_data list from a suitable file */ +int +${i}_load( netsnmp_cache *cache, void *vmagic ) { + netsnmp_tdata *table = (netsnmp_tdata *)vmagic; + netsnmp_tdata_row *row; + struct ${i}_entry *this; + FILE *fp; + char buf[STRMAX]; + @foreach $idx index@ + @if $idx.needlength@ + $idx.decl* $idx; + size_t ${idx}_len; + @else@ + $idx.decl $idx; + @end@ + @end@ + + /* The basic load routine template assumes that the data to + be reported is held in a file - with one row of the file + for each row of the table. + If your data is available via a different API, you + should amend this initial block (and the control of the + 'while' loop) accordingly. + 'XXX' marks places where the template is incomplete and + code will definitely need to be added. */ + + fp = fopen( "/data/for/${i}", "r" ); + if ( !fp ) { + return -1; + } + while ( fgets( buf, STRMAX, fp )) { + /* XXX - Unpick 'buf' to extract the individual field values + (or at least the index values for this row) ... */ + row = ${i}_createEntry(table + @foreach $idx index@ + @if $idx.needlength@ + , $idx + , ${idx}_len + @else@ + , $idx + @end@ + @end@ + ); + if (row == NULL) + continue; + this = (struct ${i}_entry *)row->entry; + /* XXX - ... and then populate the 'this' data structure with + column values (typically) extracted from 'buf' above */ + } + fclose(fp); + return 0; /* OK */ +} + +void +${i}_free( netsnmp_cache *cache, void *vmagic ) { + netsnmp_tdata *table = (netsnmp_tdata *)vmagic; + netsnmp_tdata_row *this; + + while ((this = netsnmp_tdata_get_first_row(table))) { + netsnmp_tdata_remove_and_delete_row(table, this); + } +} +@end@ + +/** handles requests for the $i table */ +int +${i}_handler( + netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) { + + netsnmp_request_info *request; + netsnmp_table_request_info *table_info; + netsnmp_tdata *table_data; + netsnmp_tdata_row *table_row; + struct ${i}_entry *table_entry; + int ret; + + DEBUGMSGTL(("${name}:handler", "Processing request (%d)\n", reqinfo->mode)); + + switch (reqinfo->mode) { + /* + * Read-support (also covers GetNext requests) + */ + case MODE_GET: + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + + table_entry = (struct ${i}_entry *) + netsnmp_tdata_extract_entry(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.readable@ + case COLUMN_$c.uc: + if ( !table_entry ) { + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHINSTANCE); + continue; + } + @if $c.needlength@ + snmp_set_var_typed_value( request->requestvb, $c.type, + table_entry->$c, + table_entry->${c}_len); + @else@ + snmp_set_var_typed_integer( request->requestvb, $c.type, + table_entry->$c); + @end@ + break; + @end@ + @end@ + default: + netsnmp_set_request_error(reqinfo, request, + SNMP_NOSUCHOBJECT); + break; + } + } + break; + +@if $i.settable@ + /* + * Write-support + */ + case MODE_SET_RESERVE1: + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + + table_entry = (struct ${i}_entry *) + netsnmp_tdata_extract_entry(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: + @if $c.rowstatus@ + ret = netsnmp_check_vb_rowstatus(request->requestvb, + (table_entry ? RS_ACTIVE : RS_NONEXISTENT )); + @else@ + @if $c.needlength@ + /* or possibly 'netsnmp_check_vb_type_and_size' */ + ret = netsnmp_check_vb_type_and_max_size( + request->requestvb, $c.type, sizeof(table_entry->$c)); + @else@ + /* or possibly 'netsnmp_check_vb_int_range' */ + ret = netsnmp_check_vb_int( request->requestvb ); + @end@ + @end@ + if ( ret != SNMP_ERR_NOERROR ) { + netsnmp_set_request_error( reqinfo, request, ret ); + return SNMP_ERR_NOERROR; + } + break; + @end@ + @end@ + default: + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_NOTWRITABLE ); + return SNMP_ERR_NOERROR; + } + } + break; + + case MODE_SET_RESERVE2: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + + table_row = netsnmp_tdata_extract_row( request); + table_data = netsnmp_tdata_extract_table(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + table_row = ${i}_createEntry(table_data + @foreach $idx index@ + @if $idx.needlength@ + , table_info->indexes->val.string + , table_info->indexes->val_len + @else@ + , *table_info->indexes->val.integer + @end@ + @end@ + ); + if (table_row) { + netsnmp_insert_tdata_row( request, table_row ); + } else { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_RESOURCEUNAVAILABLE ); + return SNMP_ERR_NOERROR; + } + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( !table_row ) { + table_row = ${i}_createEntry(table_data + @foreach $idx index@ + @if $idx.needlength@ + , table_info->indexes->val.string + , table_info->indexes->val_len + @else@ + , *table_info->indexes->val.integer + @end@ + @end@ + ); + if (table_row) { + netsnmp_insert_tdata_row( request, table_row ); + } else { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_RESOURCEUNAVAILABLE ); + return SNMP_ERR_NOERROR; + } + } + break; +@end@ + } + } +@end@ + break; + + case MODE_SET_FREE: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + + table_entry = (struct ${i}_entry *) + netsnmp_tdata_extract_entry(request); + table_row = netsnmp_tdata_extract_row( request); + table_data = netsnmp_tdata_extract_table(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + if (table_entry && !table_entry->valid) { + ${i}_removeEntry(table_data, table_row ); + } + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( table_entry && !table_entry->valid ) { + ${i}_removeEntry(table_data, table_row ); + } + break; +@end@ + } + } +@end@ + break; + + case MODE_SET_ACTION: + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + + table_entry = (struct ${i}_entry *) + netsnmp_tdata_extract_entry(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + @if !$c.rowstatus@ + case COLUMN_$c.uc: + @if $c.needlength@ + memcpy( table_entry->old_$c, + table_entry->$c, + sizeof(table_entry->$c)); + table_entry->old_${c}_len = + table_entry->${c}_len; + memset( table_entry->$c, 0, + sizeof(table_entry->$c)); + memcpy( table_entry->$c, + request->requestvb->val.string, + request->requestvb->val_len); + table_entry->${c}_len = + request->requestvb->val_len; + @else@ + table_entry->old_$c = table_entry->$c; + table_entry->$c = *request->requestvb->val.integer; + @end@ + break; + @end@ + @end@ + @end@ + } + } +@if $i.rowstatus@ + /* Check the internal consistency of an active row */ + for (request=requests; request; request=request->next) { + table_entry = (struct ${i}_entry *) + netsnmp_tdata_extract_entry(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_ACTIVE: + case RS_CREATEANDGO: + if (/* XXX */) { + netsnmp_set_request_error( reqinfo, request, + SNMP_ERR_INCONSISTENTVALUE ); + return SNMP_ERR_NOERROR; + } + } + @end@ + @end@ + } + } +@end@ + break; + + case MODE_SET_UNDO: + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + + table_entry = (struct ${i}_entry *) + netsnmp_tdata_extract_entry(request); + table_row = netsnmp_tdata_extract_row( request); + table_data = netsnmp_tdata_extract_table(request); + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { + @foreach $c column@ + @if $c.settable@ + case COLUMN_$c.uc: +@if $i.rowstatus@ + @if $c.rowstatus@ + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + case RS_CREATEANDWAIT: + if (table_entry && !table_entry->valid) { + ${i}_removeEntry(table_data, table_row ); + } + } + @else@ + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + @end@ +@else@ + @if $c.creatable@ + if ( table_entry && !table_entry->valid ) { + ${i}_removeEntry(table_data, table_row ); + } else { + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + } + @else@ + @if $c.needlength@ + memcpy( table_entry->$c, + table_entry->old_$c, + sizeof(table_entry->$c)); + memset( table_entry->old_$c, 0, + sizeof(table_entry->$c)); + table_entry->${c}_len = + table_entry->old_${c}_len; + @else@ + table_entry->$c = table_entry->old_$c; + table_entry->old_$c = 0; + @end@ + @end@ +@end@ + break; + @end@ + @end@ + } + } + break; + + case MODE_SET_COMMIT: +@if $i.creatable@ + for (request=requests; request; request=request->next) { + if (request->processed) + continue; + + table_entry = (struct ${i}_entry *) + netsnmp_tdata_extract_entry(request); +@if $i.rowstatus@ + table_row = netsnmp_tdata_extract_row( request); + table_data = netsnmp_tdata_extract_table(request); +@end@ + table_info = netsnmp_extract_table_info( request); + + switch (table_info->colnum) { +@if $i.rowstatus@ + @foreach $c column@ + @if $c.rowstatus@ + case COLUMN_$c.uc: + switch (*request->requestvb->val.integer) { + case RS_CREATEANDGO: + table_entry->valid = 1; + /* Fall-through */ + case RS_ACTIVE: + table_entry->$c = RS_ACTIVE; + break; + + case RS_CREATEANDWAIT: + table_entry->valid = 1; + /* Fall-through */ + case RS_NOTINSERVICE: + table_entry->$c = RS_NOTINSERVICE; + break; + + case RS_DESTROY: + ${i}_removeEntry(table_data, table_row ); + } + @end@ + @end@ +@else@ + @foreach $c column@ + @if $c.creatable@ + case COLUMN_$c.uc: + @end@ + @end@ + if ( table_entry && !table_entry->valid ) { + table_entry->valid = 1; + } +@end@ + } + } +@end@ + break; +@end@ + } + return SNMP_ERR_NOERROR; +} +@end@ diff --git a/local/minimalist/feature-check b/local/minimalist/feature-check new file mode 100755 index 0000000..aa4987e --- /dev/null +++ b/local/minimalist/feature-check @@ -0,0 +1,126 @@ +#!/bin/sh + +# +# feature-check: This script looks for feature statements in a file by +# executing a compiler under CPP and extracting lines of the following +# form: +# +# netsnmp_feature_provides(foo): used to indicate it provides feature "foo" +# netsnmp_feature_require(bar): used to indicate it requires feature "bar" +# netsnmp_feature_want(bar): used to indicate it wants feature "bar" +# (but can live without it) +# +# Using these lines it then generates a list of feature requirement +# defines in "include/net-snmp/net-snmp-features.h" which can then be +# loaded in later passes at compile time. +# +# Defines produced when netsnmp_featre_require(bar) is present: +# #define NETSNMP_FEATURE_REQUIRE_BAR 1 +# +# Defines produced when netsnmp_feature_want(bar) is present: +# #define NETSNMP_FEATURE_WANT_BAR 1 +# +# And when all the features have been collected, they are also inverted via +# the feature-post-check script: +# +# #define NETSNMP_FEATURE_REMOVE_BARX 1 /* never required */ +# #undef NETSNMP_FEATURE_REMOVE_BAR /* required */ + +# Usage: + +# feature-check NORMAL-COMPILE-LINE + +SED=sed +GREP=grep +CC=gcc +RM=rm + +if test "x$1" = "x--feature-global" ; then + # a global file should be included + shift + global=$1 + shift +fi + +sourcedir=$1 +shift + +source=$1 +shift + +destination=$1 +shift + +tmpf="$destination.1" + +compileline="$@" +$compileline '-DNETSNMP_FEATURE_CHECKING=1' \ + '-DNETSNMP_MINIMAL_CODE=1' \ + '-Dnetsnmp_feature_require(X)=X NSF_RR' \ + '-Dnetsnmp_feature_provide(X)=X NSF_PP' \ + '-Dnetsnmp_feature_child_of(X,Y)=X,Y NSF_CO' \ + '-Dnetsnmp_feature_want(X)=X NSF_WW' $source | \ + $GREP NSF_ | $GREP -v '^#define' > $tmpf + +$RM -f $destination; +touch $destination; + +# process requires +firstrequire=1 +for i in `grep NSF_RR $tmpf | sed 's/ NSF_RR//'` ; do + if test $firstrequire = 1 ; then + echo "" >> $destination + echo "/* required by $sourcedir/$source */" >> $destination + firstrequire=0 + fi + upper=`echo $i | tr a-z A-Z` + echo "#define NETSNMP_FEATURE_REQUIRE_$upper 1" >> $destination +done + +# process provides +firstfeature=1 +for i in `grep NSF_PP $tmpf | sed 's/ NSF_PP//'` ; do + if test $firstfeature = 1 ; then + echo "" >> $destination + echo "/* features provided by $sourcedir/$source */" >> $destination + firstfeature=0 + fi + upper=`echo $i | tr a-z A-Z` + echo "#define NETSNMP_FEATURE_PROVIDE_$upper 1" >> $destination +done + +# process children +firstfeature=1 +for i in `grep NSF_CO $tmpf | sed 's/ NSF_CO//'` ; do + parent=`echo $i | sed 's/.*,//'` + child=`echo $i | sed 's/,.*//'` + if test $firstfeature = 1 ; then + echo "" >> $destination + echo "/* features provided by $sourcedir/$source */" >> $destination + firstfeature=0 + fi + upperchild=`echo $child | tr a-z A-Z` + upperparent=`echo $parent | tr a-z A-Z` + echo "#define NETSNMP_FEATURE_${upperchild}_CHILD_OF_${upperparent} 1" >> $destination + echo "#define NETSNMP_FEATURE_PROVIDE_$upperchild 1" >> $destination +done + +# process wants +firstfeature=1 +for i in `grep NSF_WW $tmpf | sed 's/ NSF_WW//'` ; do + if test $firstfeature = 1 ; then + echo "" >> $destination + echo "/* features wanted by $sourcedir/$source */" >> $destination + firstfeature=0 + fi + upper=`echo $i | tr a-z A-Z` + echo "#define NETSNMP_FEATURE_WANT_$upper 1" >> $destination +done + +if test "x$global" != "x" ; then + cat $destination >> $global +fi + + +# clean up +#$RM -f $tmpf diff --git a/local/minimalist/feature-makedocs b/local/minimalist/feature-makedocs new file mode 100644 index 0000000..f960191 --- /dev/null +++ b/local/minimalist/feature-makedocs @@ -0,0 +1,221 @@ +#!/usr/bin/perl + +use strict; +use IO::File; +use Getopt::Std; +our %opts = (W => 2); + +getopts('ohwW:', \%opts) || usage(); + +usage() if ($opts{'h'}); + +usage() if ($#ARGV != 1); + +# should be the feature-details.h file +my $inputfile = shift @ARGV; +my $outputfile = shift @ARGV; + +my %features; +my %top; +my %files; +my $reqfile; + +gather_data($inputfile); +if ($opts{'w'}) { + print_wiki_mode($outputfile); +} else { + print_org_mode($outputfile); +} + +sub gather_data { + my ($inputfile) = @_; + + open(I, $inputfile); + while(<I>) { + if (/(required|provided|wanted) by (.*) \*/) { + $reqfile = $2; + } elsif (/define NETSNMP_FEATURE_PROVIDE_(.*) 1/) { + my $lc = lc($1); + die "no reqfile currently set; bug!" if (!defined($reqfile)); + + add($lc); + + push @{$files{$reqfile}{'provides'}}, $lc; + push @{$features{$lc}{'providedby'}}, $reqfile; + + } elsif (/define NETSNMP_FEATURE_REQUIRE_(.*) 1/) { + my $lc = lc($1); + die "no reqfile currently set; bug!" if (!defined($reqfile)); + + add($lc); + + push @{$files{$reqfile}{'requires'}}, $lc; + push @{$features{$lc}{'requiredby'}}, $reqfile; + + } elsif (/define NETSNMP_FEATURE_WANT_(.*) 1/) { + my $lc = lc($1); + die "no reqfile currently set; bug!" if (!defined($reqfile)); + + add($lc); + + push @{$files{$reqfile}{'wants'}}, $lc; + push @{$features{$lc}{'wantedby'}}, $reqfile; + } elsif (/define NETSNMP_FEATURE_(.*)_CHILD_OF_(.*) 1/) { + my $child = lc($1); + my $parent = lc($2); + + add_child($child, $parent); + } + } +} + +sub add { + my ($name) = @_; + if (!exists($features{$name})) { + # new feature entirely, mark it as a top node + $top{$name} = 1; + } +} + +sub add_child { + my ($child, $parent) = @_; + + add($parent); + + if (exists($top{$child})) { + # it's no longer a top node if it's a child of something else + delete $top{$child}; + } + $features{$parent}{'children'}{$child}++; + + $features{$child}{'providedby'}, $reqfile; +} + +###################################################################### +# org-mode output +# +sub print_org_mode { + my ($outputfile) = @_; + + my $fh = new IO::File; + + if (!$fh->open("> $outputfile")) { + die "error!\n"; + } + + foreach my $node (sort keys(%top)) { + print_org_node($fh, $node, "*"); + } +} + +sub print_org_node { + my ($fh, $node, $prefix) = @_; + + my $spaces = $prefix; + $spaces =~ s/./ /g; + + print $fh "$prefix $node\n"; + if (exists($features{$node}{'providedby'})) { + print $fh "$spaces + provided in file: " . + join(", ", org_link_files(@{$features{$node}{'providedby'}})) . "\n"; + } + if (exists($features{$node}{'requiredby'})) { + print $fh "$spaces + required in file: " . + join(", ", org_link_files(@{$features{$node}{'requiredby'}})) . "\n"; + } + if (exists($features{$node}{'wantedby'})) { + print $fh "$spaces + wanted in file: " . + join(", ", org_link_files(@{$features{$node}{'wantedby'}})) . "\n"; + } + if (exists($features{$node}{'children'})) { + foreach my $child (sort keys(%{$features{$node}{'children'}})) { + print_org_node($fh, $child, $prefix . "*"); + } + } +} + +sub org_link_files { + my @files = @_; + map { $_ = "[[file:$_][$_]]"; } @files; + return @files; +} + +###################################################################### +# wiki output +# +sub print_wiki_mode { + my ($outputfile) = @_; + + my $fh = new IO::File; + my $depth = 0; + + if (!$fh->open("> $outputfile")) { + die "error!\n"; + } + + print $fh "'''The contents of this page is auto-generated from local/minimalist/feature-makedocs; do not edit by hand (changes will be lost)'''\n\n"; + print $fh "Details of the feature marking system and how it is used can be found at [[Feature Marking and Selection]]\n\n"; + + foreach my $node (sort keys(%top)) { + print_wiki_node($fh, $node, $depth+1); + } +} + +sub print_wiki_node { + my ($fh, $node, $depth) = @_; + + my $dataheader; + my $depthincrease = 1; + + if ($depth > $opts{'W'}) { + print $fh "*" x ($depth - $opts{'W'} + 1) . " $node\n"; + $dataheader = "*" x ($depth - $opts{'W'} + 2); + $depthincrease++; + } else { + print $fh "=" x $depth . " $node " . "=" x $depth . "\n"; + $dataheader = "*"; + } + + if (exists($features{$node}{'providedby'})) { + print $fh "$dataheader provided in file: " . + join(", ", wiki_link_files(@{$features{$node}{'providedby'}})) ."\n"; + } + if (exists($features{$node}{'requiredby'})) { + print $fh "$dataheader required in file: " . + join(", ", wiki_link_files(@{$features{$node}{'requiredby'}})) ."\n"; + } + if (exists($features{$node}{'wantedby'})) { + print $fh "$dataheader wanted in file: " . + join(", ", wiki_link_files(@{$features{$node}{'wantedby'}})) . "\n"; + } + if (exists($features{$node}{'children'})) { + if ($depth >= $opts{'W'}) { + print $fh "$dataheader Children:\n"; + } + foreach my $child (sort keys(%{$features{$node}{'children'}})) { + print_wiki_node($fh, $child, $depth + $depthincrease); + } + } +} + +sub wiki_link_files { + my @files = @_; + map { $_ = "[http://net-snmp.svn.sourceforge.net/viewvc/net-snmp/trunk/net-snmp/$_?view=markup $_]"; } @files; + return @files; +} + +###################################################################### +# help output +# +sub usage { + print "Usage: $0 [FLAGS] INPUTFILE OUTPUTFILE\n\n"; + print "FLAGS:\n"; + print "\t-h\thelp\n"; + print "\t-o\tOutput style: Org-Mode [default]\n"; + print "\t-w\tOutput style: wiki\n"; + print "\t-W DEPTH\tWiki header to bullet list depth (default 2)\n"; + print "\nINPUT/OUTPUT\n"; + print "\tINPUTFILE:\tlocation of the include/net-snmp/feature-details.h file\n"; + print "\tOUTPUTFILE:\tthe file to write the results to\n"; + exit; +} diff --git a/local/minimalist/feature-remove b/local/minimalist/feature-remove new file mode 100755 index 0000000..aae340d --- /dev/null +++ b/local/minimalist/feature-remove @@ -0,0 +1,128 @@ +#!/bin/sh + +# +# feature-remove: removes features by defining special variables +# indicating particular code segments are unneeded. This is done by +# examining the list of provided features and defining a REMOVE +# equivalent if no other module required it. +# +# Arguments: +# feature-remove mainheader.h globalheader.h THEREST +# +# Where THEREST are a list of features to remove (default) or add. +# +# --add switches to "adding" and --remove switches back. +# +# E.G. +# feature-remove mainheader.h globalheader.h oid_stash --add container_null +# +CP=cp + +featureheader="$1" +featureheaderin="$1.in" +featureheaderglobal="$2" +shift +shift + +argumenttype="--remove" +removethese="" +addthese="" + +while test "x$1" != "x" ; do + uc=`echo $1 | tr a-z A-Z` + case $1 in + --*) + argumenttype="$1" + ;; + + *) + if test "x$argumenttype" = "x--remove" ; then + removethese="$removethese $uc" + else + addthese="$addthese $uc" + fi + ;; + esac + shift +done + +echo "" > $featureheader +echo "/* The following defines features that Net-SNMP HAS or has REMOVEd */" >> $featureheader + +haslist=" " + +for i in `grep NETSNMP_FEATURE_PROVIDE_ $featureheaderin | sed 's/.*FEATURE_PROVIDE_//;s/ .*//'` ; do + # + # If we were specifically asked to include it, then do so + # + if echo " $addthese " | grep " ${i} " > /dev/null ; then + # The user required it ; report we're using it + echo "#define NETSNMP_FEATURE_HAS_$i 1" >> $featureheader + haslist="${haslist}${i} " + + # + # check to see if something requried it + # + elif grep "NETSNMP_FEATURE_REQUIRE_${i} " $featureheaderglobal > /dev/null ; then + # if so, make sure we weren't asked to remove it + if echo " $removethese "| grep " $i " > /dev/null ; then + # Uh oh; they specifically wanted it removed but it was required + echo "Feature Error:" + echo " Feature '$i' was asked to be removed but something required it" + echo " See the $featureheaderglobal file for dependency details" + echo "" + rm -f $featureheader + exit 1 + fi + + # something required it ; report we're using it + echo "#define NETSNMP_FEATURE_HAS_$i 1" >> $featureheader + haslist="${haslist}${i} " + + # + # check to see if something "wanted" it + # + elif grep "NETSNMP_FEATURE_WANT_${i} " $featureheaderglobal > /dev/null ; then + # nothing required it, but something "wanted" it + + # check to make sure we weren't asked to exclude it + if echo " $removethese " | grep " $i " > /dev/null ; then + # we were specifically asked *not* to use this feature + echo "#define NETSNMP_FEATURE_REMOVE_$i 1" >> $featureheader + else + # something wanted it, so we'll include it + echo "#define NETSNMP_FEATURE_HAS_$i 1" >> $featureheader + haslist="${haslist}${i} " + fi + + + # + # check to see if something required a parent + # + elif egrep NETSNMP_FEATURE_${i}_CHILD_OF $featureheaderglobal > /dev/null ; then + parentnames=`egrep NETSNMP_FEATURE_${i}_CHILD_OF $featureheaderglobal | sed 's/.*CHILD_OF_//;s/ .*//;'` + + foundone=0 + for parentname in $parentnames ; do + # if the parent was desired, then we are too: + + if test $foundone = 0 ; then + if egrep "NETSNMP_FEATURE_HAS_${parentname} " $featureheader > /dev/null ; then + echo "#define NETSNMP_FEATURE_HAS_$i 1" >> $featureheader + haslist="${haslist}${i} " + foundone=1 + fi + fi + done + if test $foundone = 0 ; then + echo "#define NETSNMP_FEATURE_REMOVE_$i 1" >> $featureheader + fi + + # + # no one required or wanted it -- it is safe to remove it + # + else + echo "#define NETSNMP_FEATURE_REMOVE_$i 1" >> $featureheader + fi +done + diff --git a/local/minimalist/find-unused-code b/local/minimalist/find-unused-code new file mode 100755 index 0000000..89e3c8c --- /dev/null +++ b/local/minimalist/find-unused-code @@ -0,0 +1,117 @@ +#!/usr/bin/perl + +# searches for .o files in the current directory and examines each for +# defined functions (T) and undefined references (U) and lists +# anything that isn't required by something else or itself + +# useful calling sorted by file: +# perl minimal/find-unused-code | sort + +use File::Find; +use Data::Dumper; +use strict; +use Getopt::GUI::Long; + +my %opts; + +LocalGetOptions(\%opts, + ['g|grep', "run find/grep to look for string usages"], + ); + +my (@files, %defined, %lookingfor); + +find(\&dot_os, "."); + +foreach my $file (@files) { + collect_from_file($file); +} + + +if ($#ARGV > -1) { + print "Dumping found symbol usages:\n\n"; + foreach my $arg (@ARGV) { + print "$arg:\n"; + print " ", join("\n ", @{$defined{$arg}{'usages'}}),"\n\n"; + } + exit; +} + +foreach my $function (sort { $defined{$a}{'source'} cmp $defined{$b}{'source'} } keys(%defined)) { + if (exists($defined{$function}{'source'}) && + !exists($defined{$function}{'usages'})) { + if (check_for_once_only($defined{$function}{'source'}, $function)) { + printf("%-38s %-38s\n", $defined{$function}{'source'}, $function); + if ($opts{'g'}) { + open(P,"find . -regex '.*.\\(c\\|xs\\)' | xargs grep -n $function|"); + while(<P>) { + print " $_"; + } + close(P); + print "\n"; + } + } + } +} + +sub dot_os { + return if (!/\.o$/); + return if ($File::Find::name =~ /\/.libs\//); +# return if ($File::Find::name =~ /\/(perl|python)\//); + push @files, $File::Find::name; +} + +sub collect_from_file { + my $file = shift; +# print "searching $file\n"; + open(I,"nm $file|"); + while(<I>) { + $defined{$1}{'source'} = $file if (/ T (.*)/); + push @{$defined{$1}{'usages'}}, $file if (/ U (.*)/); + } + close(I); +} + +sub check_for_once_only { + my ($file, $function) = @_; + $file =~ s/\.o/.c/; + open(I, $file); + my $usages = 0; + while(<I>) { + if (/$function/) { + $usages++; + } + if ($usages > 1) { + close(I); + return 0; + } + } + return 1; +} + + +sub LocalGetOptions { + if (eval {require Getopt::GUI::Long;}) { + import Getopt::GUI::Long; + # optional configure call + Getopt::GUI::Long::Configure(qw(display_help no_ignore_case capture_output no_gui allow_zero)); + return GetOptions(@_); + } + require Getopt::Long; + import Getopt::Long; + # optional configure call + Getopt::Long::Configure(qw(display_help no_ignore_case capture_output)); + GetOptions(LocalOptionsMap(@_)); +} + +sub LocalOptionsMap { + my ($st, $cb, @opts) = ((ref($_[0]) eq 'HASH') + ? (1, 1, $_[0]) : (0, 2)); + for (my $i = $st; $i <= $#_; $i += $cb) { + if ($_[$i]) { + next if (ref($_[$i]) eq 'ARRAY' && $_[$i][0] =~ /^GUI:/); + push @opts, ((ref($_[$i]) eq 'ARRAY') ? $_[$i][0] : $_[$i]); + push @opts, $_[$i+1] if ($cb == 2); + } + } + return @opts; +} diff --git a/local/minimalist/ignore.regexp b/local/minimalist/ignore.regexp new file mode 100644 index 0000000..e09b99c --- /dev/null +++ b/local/minimalist/ignore.regexp @@ -0,0 +1,4 @@ +~$ +#$ +\/.# +\/.svn\/ diff --git a/local/minimalist/remove-unneeded-modules b/local/minimalist/remove-unneeded-modules new file mode 100755 index 0000000..ad45954 --- /dev/null +++ b/local/minimalist/remove-unneeded-modules @@ -0,0 +1,130 @@ +#!/bin/perl + +# steps: +# 1) run configure +# 2) perl local/minimalist/remove-unneeded-modules +# 3) make depend +# 4) make + +use File::Find; +use Data::Dumper; +use strict; + +my %exceptions = qw( + agent/mibgroup/mibdefs.h 1 + agent/mibgroup/struct.h 1 + agent/mibgroup/agentx/protocol.h 1 + agent/mibgroup/agentx/agentx_config.h 1 + agent/mibgroup/agentx/subagent.h 1 + agent/mibgroup/mib_module_includes.h 1 + agent/mibgroup/mib_module_inits.h 1 + agent/mibgroup/mib_module_dot_conf.h 1 + agent/mibgroup/mib_module_shutdown.h 1 + agent/mibgroup/agent_module_includes.h 1 + agent/mibgroup/agent_module_inits.h 1 + agent/mibgroup/agent_module_dot_conf.h 1 + agent/mibgroup/agent_module_shutdown.h 1 + agent/mibgroup/mibII/vacm_conf.c 1 + agent/mibgroup/snmpv3/usmConf.c 1 + agent/mibgroup/utilities/iquery.c 1 + agent/mibgroup/mibII/vacm_conf.h 1 + agent/mibgroup/snmpv3/usmConf.h 1 + agent/mibgroup/utilities/iquery.h 1 + agent/mibgroup/util_funcs/MIB_STATS_CACHE_TIMEOUT.h 1 + agent/mibgroup/host_res.h 1 + agent/mibgroup/hr_filesys.h 1 + agent/mibgroup/if-mib/ifTable/ifTable_defs.h 1 + agent/mibgroup/ifTable_defs.h 1 + agent/mibgroup/mibII/mibII_common.h 1 + agent/mibgroup/mnttypes.h 1 + agent/mibgroup/utilities/execute.h 1 +); + +my %opts; + +LocalGetOptions(\%opts, + ['n|dry-run', "Dry-run, just say what you would do"], + ['v|verbose', "Show what files are being left too"], + ); + +my $inputMakefile = $ARGV[0] || "agent/mibgroup/Makefile"; +my $inputDir = $ARGV[1] || "agent/mibgroup"; + +my $collecting = 0; +my (%files, @files); + +open(I, $inputMakefile); +while(<I>) { + if (/mib_module_list_c=/) { + $collecting = 1; + } elsif ($collecting) { + if (! /\\\s*$/) { + $collecting = 0; + } + chomp(); + s/\\//; + s/\s*//g; + s/\.c//; + push @files, "agent/mibgroup/$_"; + $files{"agent/mibgroup/$_.c"} = 1; + $files{"agent/mibgroup/$_.h"} = 1; + } +} + +find(\&remove_files, $inputDir); + +sub remove_files { + return if (!/\.[ch]$/); + return if (/\.h$/); # XXX: we need to delete headers eventually too + return if (/_constants.h/); + return if (exists($exceptions{$File::Find::name})); + + if (!exists($files{$File::Find::name})) { + Unlink($_, $File::Find::name); + } elsif ($opts{'v'}) { + print "Leaving $File::Find::name\n"; + } +} + +sub Unlink { + my ($file, $fullfile) = @_; + print "Removing $fullfile\n"; + if (!$opts{'n'}) { + unlink($file); + if (-f "$file") { + print "*** FAILED to remove $file\n"; + } + } +} + + +sub LocalGetOptions { + if (eval {require Getopt::GUI::Long;}) { + import Getopt::GUI::Long; + # optional configure call + my @gopts = qw(no_ignore_case no_gui allow_zero); + if ($Getopt::GUI::Long::VERSION > 0.2) { + push @gopts, qw(display_help capture_output); + } + Getopt::GUI::Long::Configure(@gopts); + return GetOptions(@_); + } + require Getopt::Long; + import Getopt::Long; + # optional configure call + Getopt::Long::Configure(qw(display_help no_ignore_case capture_output)); + GetOptions(LocalOptionsMap(@_)); +} + +sub LocalOptionsMap { + my ($st, $cb, @opts) = ((ref($_[0]) eq 'HASH') + ? (1, 1, $_[0]) : (0, 2)); + for (my $i = $st; $i <= $#_; $i += $cb) { + if ($_[$i]) { + next if (ref($_[$i]) eq 'ARRAY' && $_[$i][0] =~ /^GUI:/); + push @opts, ((ref($_[$i]) eq 'ARRAY') ? $_[$i][0] : $_[$i]); + push @opts, $_[$i+1] if ($cb == 2); + } + } + return @opts; +} diff --git a/local/minimalist/removeifdefcode.pl b/local/minimalist/removeifdefcode.pl new file mode 100755 index 0000000..5a64c05 --- /dev/null +++ b/local/minimalist/removeifdefcode.pl @@ -0,0 +1,333 @@ +#! /usr/bin/perl + +use strict; +use File::Copy; +use File::Basename; +use IO::File; +use Getopt::Std; + +my $ID_NOREAD = "NETSNMP_NO_READ_SUPPORT"; +my $ID_NOWRITE = "NETSNMP_NO_WRITE_SUPPORT"; +# my $ID_MIN = "NETSNMP_MINIMAL_CODE"; + +my($ST_OFF, $ST_IF, $ST_ELSE, $ST_IFN, $ST_ELSEN) = + ("off", "if", "else", "ifnot", "elsenot"); + +my %opts = (); +my %thash = (); +my $canwrite = 1; # current write state + +my($appname,$apppath) = fileparse($0); + +my $minimal_include_path = "include/net-snmp/net-snmp-features.h"; + + +if ( (!getopts("rwmi:v",\%opts)) || (1 != $#ARGV) ) { + print "$appname [options] from-directory to-direpctory\n"; + print "-r parse out code unneeded by $ID_NOREAD ifdef\n"; + print "-w parse out code unneeded by $ID_NOWRITE ifdef (DEFAULT)\n"; + print "-m parse out code unneeded according minimalist ifdefs\n"; + print " requires from-directory to be the top of the net-snmp tree\n"; + print " (This is multiple ifdefs auto selected depending\n"; + print " on the net-snmp source configuration)\n\n"; + print "-i 'ignore-file' file of files to ignore (not copy)\n"; + print "-v print verbose info to standard out\n"; + die "Error: two command line arguments required\n"; +} + +#default to everything +if ( (!exists $opts{r}) && (!exists $opts{w}) && (!exists $opts{m}) ) { + $thash{"$ID_NOWRITE"} = "$ST_OFF"; +} +else { + $thash{"$ID_NOREAD"} = "$ST_OFF" if ( exists $opts{r} ); + $thash{"$ID_NOWRITE"} = "$ST_OFF" if ( exists $opts{w} ); +} + +my $fromdir = $ARGV[0]; +my $todir = $ARGV[1]; + +if ( !(-e $fromdir) ) { + die "Error: $appname: from directory does not exist: '$fromdir'\n"; +} +if ( !(-d $fromdir) ) { + die "Error: $appname: from directory, '$fromdir', must be a directory\n"; +} + +if ( exists $opts{m} ) { + load_minamal_ifdefs(); +} + +# create search string from tags +# minimal must be done before this +my $search = join "|", (keys %thash); + + +# check for and load ignore file +my $igstring = ""; +if ( exists $opts{i} ) { + open my($IH), "< $opts{i}" or + die "Could not open ignore-file '$opts{i}': $!"; + my @iglist = <$IH>; + $IH->close(); + chomp @iglist; + $igstring = join "|", @iglist; + $igstring = "(" . $igstring . ")"; + print "ignore string: \'$igstring\'\n" if (exists $opts{v}); +} + + + +if ( -e $todir ) { + if ( ((-d $fromdir) && !(-d $todir)) || + (!(-d $fromdir) && (-d $todir)) ) { + die "Error: $appname: from-directory and to-directory must both either be a file or both be a directory\n"; + } +} +else { + if (-d $fromdir) { + print "Warning: $appname: '$todir' does not exist, creating\n"; + mkdir "$todir" or + die "Error: $appname: Unable to create to-directory '$todir': $!\n"; + } +} + +if (-d $fromdir) { + parsedirectory($fromdir, $todir); +} +else { + parsefile($fromdir, $todir); +} + + + +exit(0); + + +# PROCEDURES PROCEDURES PROCEDURES PROCEDURES PROCEDURES + + +sub parsedirectory { + my($fdir, $tdir) = @_; + + my @ldirs = (); + my $DH; + opendir my($DH), $fdir or die "Could not open directory '$fdir': $!"; + if ( !(-e $tdir) || !(-d $tdir) ) { + mkdir $tdir or die "Could not create directory '$tdir': $!"; + } + my @flist = readdir $DH; + closedir $DH; + # remove . and .. + @flist = grep (! /(^\.$|^\.\.$)/ , @flist); + + while (my $name = shift @flist) { + if (-d "$fdir/$name") { + push @ldirs, "$name"; + } + else { + parsefile("$fdir/$name", "$tdir/$name"); + } + } + + while (my $name = shift @ldirs) { + parsedirectory("$fdir/$name", "$tdir/$name") + } + +} # parsedirectory + + +# returns 1 if current state for tag is write, 0 otherwise +sub iswritestate { + my $tag = ""; + + foreach $tag (keys %thash) { + if ( ($thash{$tag} eq "$ST_ELSE") || + ($thash{$tag} eq "$ST_IFN") ) { + return(0); + } + } + + return(1); +} # iswritestate + + +# Check $line for ifdef state changes for all of the tags and change +# state. +# If there is a state change error return 0, otherwise return 1; + +sub checkifdef { + my($TF, $line, $fromfilename) = @_; + + if ( $line =~ /(#ifdef|#ifndef|#else|#endif).*($search)(\s|$|\*)/ ) { + my $copt = $1; + my $tag = $2; + + if ( $copt eq "#ifdef" ) { + if ($thash{"$tag"} eq "$ST_OFF") { + $thash{"$tag"} = "$ST_IF"; + print "state change $tag: $ST_OFF -> $ST_IF\n" if (exists $opts{v}); + $canwrite = iswritestate(); + } + else { + print "Error: $fromfilename: Found '#ifdef $tag' with state $thash{$tag}\n"; + return 0; + } + } + elsif ( $copt eq "#ifndef" ) { + if ($thash{"$tag"} eq "$ST_OFF") { + # before changing to a non-write state (ifn) print #IFNDEF + # line, if current state is a write state. + print $TF "$line" if ( $canwrite ); + $thash{"$tag"} = "$ST_IFN"; + print "state change $tag: $ST_OFF -> $ST_IFN\n" if (exists $opts{v}); + $canwrite = iswritestate(); + } + else { + print "Error: $fromfilename: Found '#ifndef $tag' with state $thash{$tag}\n"; + return 0; + } + } + elsif ( $copt eq "#else" ) { + if ($thash{"$tag"} eq "$ST_IF") { + # before changing to a non-write state (else) print #else + # line, if current state is a write state. + print $TF "$line" if ( $canwrite ); + $thash{"$tag"} = "$ST_ELSE"; + print "state change $tag: $ST_IF -> $ST_ELSE\n" if (exists $opts{v}); + $canwrite = iswritestate(); + } + elsif ($thash{"$tag"} eq "$ST_IFN") { + $thash{"$tag"} = "$ST_ELSEN"; + print "state change $tag: $ST_IFN -> $ST_ELSEN\n" if (exists $opts{v}); + $canwrite = iswritestate(); + } + else { + print "Error: $fromfilename: Found '#else (...) $tag' with state $thash{$tag}\n"; + return 0; + } + } + elsif ( $copt eq "#endif" ) { + if (($thash{"$tag"} eq "$ST_ELSE") || ($thash{"$tag"} eq "$ST_IF") || + ($thash{"$tag"} eq "$ST_ELSEN") || ($thash{"$tag"} eq "$ST_IFN")) + { + print "state change $tag: $thash{$tag} -> $ST_OFF\n" + if (exists $opts{v}); + $thash{"$tag"} = "$ST_OFF"; + $canwrite = iswritestate(); + } + else { + print "Error: Found '#endif (...) $tag' with state $thash{$tag}\n"; + return 0; + } + } + + } # foreach tag + + return 1; +} # checkifdef + + +sub parsefile { + my($fname, $tname) = @_; + my $FF; my $TF; + my @fromfile = (); + $canwrite = 1; + + # ignore file for file names + if ( (exists $opts{i}) && ("$fname" =~ /$igstring/) ) { + print "IGNORING $fname\n" if ( exists $opts{v} ); + return 1; + } + + print "Info: Opening '$fname'\n" if ( exists $opts{v} ); + if ( !(open($FF, "< $fname")) ) { + print "Warning: unable to open input file, skipping: '$fname': $!\n"; + return 0; + } + + my @fromfile = <$FF>; + $FF->close(); + + if ( !(open($TF, "> $tname")) ) { + print "Warning: unable to open output file, skipping: '$tname': $!\n"; + return 0; + } + my $mode = (stat("$fname"))[2]; + if ($mode) { my $resp = chmod $mode, "$tname"; } + + my $line = ""; + my @tout = (); + my $retval = 1; + + while ( $line = shift @fromfile ) { + # check for any ifdef state changes + if ( ! checkifdef($TF, $line, $fname) ) { + $FF->close(); + $TF->close(); + die "Error: tag error in file \'$fname\', exiting\n"; + } + + if ( $canwrite ) { + print $TF "$line"; + } + else { + print "Info: not copying: $fname: $line" if ( exists $opts{v} ); + } + + } + + if (! $canwrite) { + print "End of $fname reached and we're not reset into 'canwrite' state\n"; + } + $TF->close(); + + return $retval; +} # parsefile + + +# note, fromdir should have already been checked to exist and be a +# directory +sub load_minamal_ifdefs { + my @filelist = (); + my $MF; + + if ( !(open($MF, "< $fromdir/$minimal_include_path")) ) { + die "Unable to open main minimal feature file: '$fromdir/$minimal_include_path'\n"; + } + my $line; + # skip preceding lines + while ( ($line = <$MF>) && + ($line !~ /^#else.*NETSNMP_FEATURE_CHECKING/ ) ) { + } + # grab the fetaure .h files + while ( ($line = <$MF>) && + ($line !~ /^#endif.*NET_SNMP_FEATURE_CHECKING/) ) { + if ($line =~ /include.*<(.*.h)>/) { + push @filelist, $1; + } + } + + close($MF); + + while (my $fname = shift @filelist) { + if ( !( -e "$fromdir/include/$fname" ) ) { + print "Warn: feature file does not exist, skipping: '$fromdir/include/$fname'"; + next; + } + if ( !(open($MF, "< $fromdir/include/$fname")) ) { + die "Unable to open minimal feature file: '$fromdir/include/$fname'\n"; + } + while ( ($line = <$MF>) ) { + if ( $line =~ /^#define.*(NETSNMP_FEATURE_REMOVE[^ ]+) / ) { + $thash{"$1"} = $ST_OFF; + } + } + close($MF); + } + +} # load_minamal_ifdefs + + + + diff --git a/local/minimalist/sizetests b/local/minimalist/sizetests new file mode 100644 index 0000000..4ecd654 --- /dev/null +++ b/local/minimalist/sizetests @@ -0,0 +1,359 @@ +#!/usr/bin/perl + +use strict; +use Data::Dumper; +use Getopt::Std; +use IO::File; +our %opts = (d => 'size-tests'); + +# default arguments go here +my @sets = + ({ + "with-defaults" => '', + }); + +my %base_args = %{$sets[0]}; +my ($basename, $basenum); + +getopts('t:hlcD:nj:o:eb:T', \%opts) || usage(); + +$opts{'j'} = "-j$opts{j}" if (exists($opts{'j'})); + +usage() if ($opts{'h'}); + +my @argumentsets = + ( + { + title => 'security-types', + arguments => + { + usm => { + 'with-security-modules' => 'usm', + }, + dtlsandtls => { + 'with-security-modules' => 'tsm', + 'with-out-security-modules' => 'usm', + 'with-transports' => 'DTLSUDP TLSTCP', + 'with-mib-modules' => 'tlstm-mib tsm-mib' + }, + usmanddtlsandtls => { + 'with-security-modules' => 'tsm usm', + 'with-transports' => 'DTLSUDP TLSTCP', + 'with-mib-modules' => 'tlstm-mib tsm-mib' + } + } + }, + { + title => 'minimalist', + arguments => + { + minimal => { + 'enable-minimalist' => '', + }, + 'not-minimal' => { + } + } + }, + { + title => 'mib-loading', + arguments => + { + 'without-mib-loading' => { + 'disable-mib-loading' => '', + }, + 'with-mib-loading' => { + } + } + }, + { + title => 'debugging', + arguments => + { + 'without-debugging' => { + 'disable-debugging' => '', + }, + 'with-debugging' => { + } + } + }, + { + title => 'logging', + arguments => + { + 'without-logging' => { + 'with-out-features' => 'logging', + }, + 'with-logging' => { + } + } + }, + { + title => 'agent-mibs', + arguments => + { + 'full-agent' => { + }, + 'mini-agent' => { + 'enable-mini-agent' => '', + } + } + }, + { + title => 'protocol-support', + arguments => + { + 'everything' => { + }, + 'read-only' => { + 'enable-read-only' => '', + }, + 'notify-only' => { + 'enable-notify-only' => '', + } + } + }, + { + title => 'perl', + arguments => + { + 'without-perl-modules' => { + 'without-perl-modules' => '', + 'disable-embedded-perl' => '', + }, + 'with-perl-no-embedding' => { + 'with-perl-modules' => '', + 'disable-embedded-perl' => '', + }, + 'with-perl-and-embedding' => { + 'with-perl-modules' => '', + 'enable-embedded-perl' => '', + } + } + }, + ); + +# map these to a referencable lookup hash +my %argumentsets; +foreach my $set (@argumentsets) { + $argumentsets{$set->{'title'}} = $set; +} + +if ($opts{'l'}) { + print "Types available:\n"; + printf(" %-40.40s %s\n", "Test Title", "Number of subtests"); + printf(" %-40.40s %s\n", "-" x 39, "-" x length("Number of subtests")); + foreach my $type (@argumentsets) { + my @keys = keys(%{$type->{'arguments'}}); + printf(" %-40.40s %s\n", $type->{'title'}, $#keys+1); + } + exit; +} + +my %types; +if ($opts{'t'}) { + my @types = split(/,\s*/, $opts{'t'}); + foreach my $type (@types) { + $types{$type} = 1; + } +} + +if ($opts{'b'}) { + # use this set as the base default set of arguments + ($basename, $basenum) = ($opts{'b'} =~ /(.*):(\d+)/); + if (!exists($argumentsets{$basename})) { + printf STDERR "failed to find set for -b argument: %s\n", $basename; + exit(1); + } + @sets = add_in_flags($argumentsets{$basename}, \%base_args, ()); + @sets = @sets[$basenum]; + %base_args = %{$sets[0]}; +} + +foreach my $set (@argumentsets) { + if (!$opts{'t'} || exists($types{$set->{'title'}})) { + if ($basename && $set->{'title'} eq $basename) { + next; + } + @sets = add_in_flags($set, \%base_args, @sets); + } +} + +if ($opts{'c'}) { + # print the configure flags + foreach my $set (@sets) { + print "$set->{'title'}:\n"; + print " ", generate_configure_flags($set), "\n"; + } + exit; +} + +my $summaryfile; +if ($opts{'o'} && !$opts{'n'}) { + $summaryfile = new IO::File; + $summaryfile->open(">$opts{o}"); +} + +my $count = 0; +foreach my $set (@sets) { + $count++; + build_set($count, $set); +} + +sub add_in_flags { + my ($argumentset, $base_flags, @current_flags) = @_; + + my @new_flags; + + # do a linear search of the options + if (! $opts{'e'}) { + @new_flags = @current_flags; + foreach my $newargs (keys(%{$argumentset->{'arguments'}})) { + my %flags = %$base_flags; + + $flags{'title'} = "$flags{'title'}:$newargs"; + + foreach my $newflag (keys(%{$argumentset->{'arguments'}{$newargs}})) { + + $flags{$newflag} .= " $argumentset->{'arguments'}{$newargs}{$newflag}"; + } + push @new_flags, \%flags; + } + return @new_flags; + } + + # or an exponential search + foreach my $flags (@current_flags) { + foreach my $newargs (keys(%{$argumentset->{'arguments'}})) { + my %flags = %{$flags}; # copy the existing hash-set of flags + + if (exists($flags{'title'})) { + $flags{'title'} .= ", $newargs"; + } else { + $flags{'title'} .= "$newargs"; + } + foreach my $newflag (keys(%{$argumentset->{'arguments'}{$newargs}})) { + + $flags{$newflag} .= " $argumentset->{'arguments'}{$newargs}{$newflag}"; + } + + push @new_flags, \%flags; + } + } + + return @new_flags; +} + +sub generate_configure_flags { + my ($arguments) = @_; + my $line = ""; + foreach my $arg (keys(%$arguments)) { + next if ($arg eq 'title'); + if ($arguments->{$arg} =~ /^\s*$/) { + $line .= " --$arg"; + } else { + $line .= " --$arg=\"$arguments->{$arg}\""; + } + } + return $line; +} + +sub build_set { + my ($num, $arguments) = @_; + + $num = sprintf("%03d", $num); + + my $file; + + if (!$opts{'n'}) { + mkdir $opts{'d'} if (! -d $opts{'d'}); + die "failed to create the $opts{'d'} directory" if (! -d $opts{'d'}); + + $file = new IO::File; + $file->open(">$opts{'d'}/$num-0-cmd-summary"); + $file->print("Creating output for: $arguments->{'title'}\n"); + } + + print "==================== $arguments->{'title'}\n"; + + + System ($file, $num, "1-distclean", "make distclean"); + System ($file, $num, "2-configure", + "./configure " . generate_configure_flags($arguments)); + System ($file, $num, "3-features", "make features"); + System ($file, $num, "4-make", "make $opts{'j'}"); + System ($file, $num, "5-unused-code", "perl local/minimalist/find-unused-code -g"); + System ($file, $num, "6-testing", "make -C testing test") if ($opts{'T'}); + + if (!$opts{'n'}) { + analyze_size($arguments->{'title'}, "$opts{'d'}/$num-9-results"); + } +} + +sub analyze_size { + my ($title, $filename) = @_; + + my $outfile = new IO::File; + $outfile->open(">$filename"); + + print "Results for: $title\n"; + printf $outfile "Results for: $title\n"; + printf ("%-50.50s %10s\n", "-" x 49, "-" x 10); + printf $outfile ("%-50.50s %10s\n", "-" x 49, "-" x 10); + + my $totalsize = 0; + foreach my $buildfile (glob("snmplib/.libs/lib*.so.*.0.0"), + glob("agent/.libs/lib*.so.*.0.0"), + "agent/.libs/snmpd") { + + my @info = stat($buildfile); + printf $outfile ("%-50.50s %10s\n", $buildfile, $info[7]); + printf("%-50.50s %10s\n", $buildfile, $info[7]); + $totalsize += $info[7]; + } + printf $outfile ("%-50.50s %10s\n", "-" x 49, "-" x 10); + printf $outfile ("%-50.50s %10s bytes\n", "TOTAL", $totalsize); + + printf("%-50.50s %10s\n", "-" x 49, "-" x 10); + printf("%-50.50s %10s bytes\n", "TOTAL", $totalsize); + + if (defined($summaryfile)) { + my $restricted_title = $title; + $restricted_title =~ s/[^a-zA-Z]/_/g; + printf $summaryfile "%10s %s \n", $totalsize, $title; + } + + return $totalsize; +} + +sub usage { + print "Usage: $0 [FLAGS]\n\n"; + print "FLAGS:\n"; + print " -h\t\thelp\n"; + print " -t TYPES\tSelect types to analyze (default = all)\n"; + print " -l\t\tList available types\n"; + print " -c\t\tPrint the configuration flags that would be used\n"; + print " -D DIR\tSave data results to this directory\n"; + print " -o FILE\tSave a complete summary chart to FILE\n"; + print " -n\t\tDry run -- only show the commands to execute\n"; + print " -j NUM\tExecute make with parallel building (-jNUM)\n"; + print " -e\t\tUse exponential expansion (all combos) of the requested options\n"; + print " -b NAME:NUM\tUse NAME and the NUM'th set as the base arguments to start from\n"; + print " -T\t\tRun 'make test' at each step as well\n"; + exit; +} + +sub System { + my $file = shift; + my $num = shift; + my $title = shift; + my $pipe = " 2>&1 | tee $opts{'d'}/$num-$title.out\n"; + + print "### ", join(" ", @_), $pipe; + if (!$opts{'n'} && $file) { + print $file "### ", join(" ", @_), "\n"; + } + + if (!$opts{'n'}) { + system(join(" ", @_) . $pipe); + } +} diff --git a/local/net-snmp-cert b/local/net-snmp-cert new file mode 100755 index 0000000..a45dc57 --- /dev/null +++ b/local/net-snmp-cert @@ -0,0 +1,2690 @@ +#!/usr/bin/perl + +use strict; +use 5.8.0; + +use Getopt::Long qw(:config gnu_getopt no_ignore_case pass_through); +use Cwd qw(getcwd realpath); +use File::Basename; +use File::Copy; +use File::Path; +use Sys::Hostname; +use Carp; + +our @CC = ( +["AF","AFGHANISTAN"], +["AX","ÅLAND ISLANDS"], +["AL","ALBANIA"], +["DZ","ALGERIA"], +["AS","AMERICAN SAMOA"], +["AD","ANDORRA"], +["AO","ANGOLA"], +["AI","ANGUILLA"], +["AQ","ANTARCTICA"], +["AG","ANTIGUA AND BARBUDA"], +["AR","ARGENTINA"], +["AM","ARMENIA"], +["AW","ARUBA"], +["AU","AUSTRALIA"], +["AT","AUSTRIA"], +["AZ","AZERBAIJAN"], +["BS","BAHAMAS"], +["BH","BAHRAIN"], +["BD","BANGLADESH"], +["BB","BARBADOS"], +["BY","BELARUS"], +["BE","BELGIUM"], +["BZ","BELIZE"], +["BJ","BENIN"], +["BM","BERMUDA"], +["BT","BHUTAN"], +["BO","BOLIVIA, PLURINATIONAL STATE OF"], +["BA","BOSNIA AND HERZEGOVINA"], +["BW","BOTSWANA"], +["BV","BOUVET ISLAND"], +["BR","BRAZIL"], +["IO","BRITISH INDIAN OCEAN TERRITORY"], +["BN","BRUNEI DARUSSALAM"], +["BG","BULGARIA"], +["BF","BURKINA FASO"], +["BI","BURUNDI"], +["KH","CAMBODIA"], +["CM","CAMEROON"], +["CA","CANADA"], +["CV","CAPE VERDE"], +["KY","CAYMAN ISLANDS"], +["CF","CENTRAL AFRICAN REPUBLIC"], +["TD","CHAD"], +["CL","CHILE"], +["CN","CHINA"], +["CX","CHRISTMAS ISLAND"], +["CC","COCOS (KEELING) ISLANDS"], +["CO","COLOMBIA"], +["KM","COMOROS"], +["CG","CONGO"], +["CD","CONGO, THE DEMOCRATIC REPUBLIC OF THE"], +["CK","COOK ISLANDS"], +["CR","COSTA RICA"], +["CI","CÔTE D'IVOIRE"], +["HR","CROATIA"], +["CU","CUBA"], +["CY","CYPRUS"], +["CZ","CZECH REPUBLIC"], +["DK","DENMARK"], +["DJ","DJIBOUTI"], +["DM","DOMINICA"], +["DO","DOMINICAN REPUBLIC"], +["EC","ECUADOR"], +["EG","EGYPT"], +["SV","EL SALVADOR"], +["GQ","EQUATORIAL GUINEA"], +["ER","ERITREA"], +["EE","ESTONIA"], +["ET","ETHIOPIA"], +["FK","FALKLAND ISLANDS (MALVINAS)"], +["FO","FAROE ISLANDS"], +["FJ","FIJI"], +["FI","FINLAND"], +["FR","FRANCE"], +["GF","FRENCH GUIANA"], +["PF","FRENCH POLYNESIA"], +["TF","FRENCH SOUTHERN TERRITORIES"], +["GA","GABON"], +["GM","GAMBIA"], +["GE","GEORGIA"], +["DE","GERMANY"], +["GH","GHANA"], +["GI","GIBRALTAR"], +["GR","GREECE"], +["GL","GREENLAND"], +["GD","GRENADA"], +["GP","GUADELOUPE"], +["GU","GUAM"], +["GT","GUATEMALA"], +["GG","GUERNSEY"], +["GN","GUINEA"], +["GW","GUINEA-BISSAU"], +["GY","GUYANA"], +["HT","HAITI"], +["HM","HEARD ISLAND AND MCDONALD ISLANDS"], +["VA","HOLY SEE (VATICAN CITY STATE)"], +["HN","HONDURAS"], +["HK","HONG KONG"], +["HU","HUNGARY"], +["IS","ICELAND"], +["IN","INDIA"], +["ID","INDONESIA"], +["IR","IRAN, ISLAMIC REPUBLIC OF"], +["IQ","IRAQ"], +["IE","IRELAND"], +["IM","ISLE OF MAN"], +["IL","ISRAEL"], +["IT","ITALY"], +["JM","JAMAICA"], +["JP","JAPAN"], +["JE","JERSEY"], +["JO","JORDAN"], +["KZ","KAZAKHSTAN"], +["KE","KENYA"], +["KI","KIRIBATI"], +["KP","KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF"], +["KR","KOREA, REPUBLIC OF"], +["KW","KUWAIT"], +["KG","KYRGYZSTAN"], +["LA","LAO PEOPLE'S DEMOCRATIC REPUBLIC"], +["LV","LATVIA"], +["LB","LEBANON"], +["LS","LESOTHO"], +["LR","LIBERIA"], +["LY","LIBYAN ARAB JAMAHIRIYA"], +["LI","LIECHTENSTEIN"], +["LT","LITHUANIA"], +["LU","LUXEMBOURG"], +["MO","MACAO"], +["MK","MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF"], +["MG","MADAGASCAR"], +["MW","MALAWI"], +["MY","MALAYSIA"], +["MV","MALDIVES"], +["ML","MALI"], +["MT","MALTA"], +["MH","MARSHALL ISLANDS"], +["MQ","MARTINIQUE"], +["MR","MAURITANIA"], +["MU","MAURITIUS"], +["YT","MAYOTTE"], +["MX","MEXICO"], +["FM","MICRONESIA, FEDERATED STATES OF"], +["MD","MOLDOVA, REPUBLIC OF"], +["MC","MONACO"], +["MN","MONGOLIA"], +["ME","MONTENEGRO"], +["MS","MONTSERRAT"], +["MA","MOROCCO"], +["MZ","MOZAMBIQUE"], +["MM","MYANMAR"], +["NA","NAMIBIA"], +["NR","NAURU"], +["NP","NEPAL"], +["NL","NETHERLANDS"], +["AN","NETHERLANDS ANTILLES"], +["NC","NEW CALEDONIA"], +["NZ","NEW ZEALAND"], +["NI","NICARAGUA"], +["NE","NIGER"], +["NG","NIGERIA"], +["NU","NIUE"], +["NF","NORFOLK ISLAND"], +["MP","NORTHERN MARIANA ISLANDS"], +["NO","NORWAY"], +["OM","OMAN"], +["PK","PAKISTAN"], +["PW","PALAU"], +["PS","PALESTINIAN TERRITORY, OCCUPIED"], +["PA","PANAMA"], +["PG","PAPUA NEW GUINEA"], +["PY","PARAGUAY"], +["PE","PERU"], +["PH","PHILIPPINES"], +["PN","PITCAIRN"], +["PL","POLAND"], +["PT","PORTUGAL"], +["PR","PUERTO RICO"], +["QA","QATAR"], +["RE","RÉUNION"], +["RO","ROMANIA"], +["RU","RUSSIAN FEDERATION"], +["RW","RWANDA"], +["BL","SAINT BARTHÉLEMY"], +["SH","SAINT HELENA, ASCENSION AND TRISTAN DA CUNHA"], +["KN","SAINT KITTS AND NEVIS"], +["LC","SAINT LUCIA"], +["MF","SAINT MARTIN"], +["PM","SAINT PIERRE AND MIQUELON"], +["VC","SAINT VINCENT AND THE GRENADINES"], +["WS","SAMOA"], +["SM","SAN MARINO"], +["ST","SAO TOME AND PRINCIPE"], +["SA","SAUDI ARABIA"], +["SN","SENEGAL"], +["RS","SERBIA"], +["SC","SEYCHELLES"], +["SL","SIERRA LEONE"], +["SG","SINGAPORE"], +["SK","SLOVAKIA"], +["SI","SLOVENIA"], +["SB","SOLOMON ISLANDS"], +["SO","SOMALIA"], +["ZA","SOUTH AFRICA"], +["GS","SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS"], +["ES","SPAIN"], +["LK","SRI LANKA"], +["SD","SUDAN"], +["SR","SURINAME"], +["SJ","SVALBARD AND JAN MAYEN"], +["SZ","SWAZILAND"], +["SE","SWEDEN"], +["CH","SWITZERLAND"], +["SY","SYRIAN ARAB REPUBLIC"], +["TW","TAIWAN, PROVINCE OF CHINA"], +["TJ","TAJIKISTAN"], +["TZ","TANZANIA, UNITED REPUBLIC OF"], +["TH","THAILAND"], +["TL","TIMOR-LESTE"], +["TG","TOGO"], +["TK","TOKELAU"], +["TO","TONGA"], +["TT","TRINIDAD AND TOBAGO"], +["TN","TUNISIA"], +["TR","TURKEY"], +["TM","TURKMENISTAN"], +["TC","TURKS AND CAICOS ISLANDS"], +["TV","TUVALU"], +["UG","UGANDA"], +["UA","UKRAINE"], +["AE","UNITED ARAB EMIRATES"], +["GB","UNITED KINGDOM"], +["US","UNITED STATES"], +["UM","UNITED STATES MINOR OUTLYING ISLANDS"], +["UY","URUGUAY"], +["UZ","UZBEKISTAN"], +["VU","VANUATU"], +["VE","VENEZUELA, BOLIVARIAN REPUBLIC OF"], +["VN","VIET NAM"], +["VG","VIRGIN ISLANDS, BRITISH"], +["VI","VIRGIN ISLANDS, U.S."], +["WF","WALLIS AND FUTUNA"], +["EH","WESTERN SAHARA"], +["YE","YEMEN"], +["ZM","ZAMBIA"], +["ZW","ZIMBABWE"], +); + +package NetSNMP::Term; +# gratefully taken from Wayne Thompson's Term::Complete +# if newer CORE modules could be used this could be removed +our($complete, $exit, $done, $kill, $erase1, $erase2, $tty_raw_noecho, $tty_restore, $stty, $tty_safe_restore); +our($tty_saved_state) = ''; +CONFIG: { + $exit = "\003"; + $done = "\004"; + $kill = "\025"; + $erase1 = "\177"; + $erase2 = "\010"; + foreach my $s (qw(/bin/stty /usr/bin/stty)) { + if (-x $s) { + $tty_raw_noecho = "$s raw -echo"; + $tty_restore = "$s -raw echo"; + $tty_safe_restore = $tty_restore; + $stty = $s; + last; + } + } +} + +sub Complete { + my($prompt, $dflt, $help, $match, @cmp_lst); + my (%help, $cmp, $test, $l, @match); + my ($return, $r, $exitting) = ("", 0, 0); + my $tab; + + $prompt = shift; + $dflt = shift; + $help = shift; + $match = shift; + + if (ref $_[0] and ref($_[0][0])) { + @cmp_lst = @{$_[0]}; + } else { + @cmp_lst = @_; + } + @cmp_lst = map {if (ref($_)) { $help{$_->[0]}=$_->[1]; $_->[0]} else {$_;}} @cmp_lst; + + # Attempt to save the current stty state, to be restored later + if (defined $stty && defined $tty_saved_state && $tty_saved_state eq '') { + $tty_saved_state = qx($stty -g 2>/dev/null); + if ($?) { + # stty -g not supported + $tty_saved_state = undef; + } else { + $tty_saved_state =~ s/\s+$//g; + $tty_restore = qq($stty "$tty_saved_state" 2>/dev/null); + } + } + system $tty_raw_noecho if defined $tty_raw_noecho; + LOOP: { + local $_; + print($prompt, $return); + while (($_ = getc(STDIN)) ne "\r") { + CASE: { + # (TAB) attempt completion + $_ eq "\t" && do { + if ($tab) { + print(join("\r\n", '', map {exists $help{$_} ? sprintf("\t%-10.10s - %s", $_, $help{$_}) : + "\t$_"} grep(/^\Q$return/, @cmp_lst)), "\r\n"); + $tab--; + redo LOOP; + } + @match = grep(/^\Q$return/, @cmp_lst); + unless ($#match < 0) { + $l = length($test = shift(@match)); + foreach $cmp (@match) { + until (substr($cmp, 0, $l) eq substr($test, 0, $l)) { + $l--; + } + } + print("\a"); + print($test = substr($test, $r, $l - $r)); + $r = length($return .= $test); + } + $tab++; + last CASE; + }; + + # (^C) exit + $_ eq $exit && do { + print("\r\naborting application...\r\n"); + $exitting++; + undef $return; + last LOOP; + }; + + # (^D) done + $_ eq $done && do { + undef $return; + last LOOP; + }; + + # (?) show help if available + $_ eq '?' && do { + print("\r\n$help\r\n"); + if (exists $help{$return}) { + print("\t$return - $help{$return}\r\n"); + } else { + print(join("\r\n", map {exists $help{$_} ? "\t$_ - $help{$_}" : + "\t$_"} grep(/^\Q$return/, @cmp_lst)), "\r\n"); + } + redo LOOP; + }; + + # (^U) kill + $_ eq $kill && do { + if ($r) { + $r = 0; + $return = ""; + print("\r\n"); + redo LOOP; + } + last CASE; + }; + + # (DEL) || (BS) erase + ($_ eq $erase1 || $_ eq $erase2) && do { + if ($r) { + print("\b \b"); + chop($return); + $r--; + } + last CASE; + }; + + # printable char + ord >= 32 && do { + $return .= $_; + $r++; + print; + last CASE; + }; + } + } + + if (defined $return) { + if (length($return) == 0 or $return eq $dflt) { + $return = $dflt; + } elsif ($match == $NetSNMP::Cert::MATCH and scalar(grep {/^$return$/} @cmp_lst) != 1 or + $match == $NetSNMP::Cert::PREMATCH and scalar(grep {$return=~/$_/} @cmp_lst) != 1) { + $r = 0; + $return = ""; + print("\r\nChoose a valid option, or ^D to exit\r\n"); + redo LOOP; + } + } + } + DONE: + # system $tty_restore if defined $tty_restore; + if (defined $tty_saved_state && defined $tty_restore && defined $tty_safe_restore) { + system $tty_restore; + if ($?) { + # tty_restore caused error + system $tty_safe_restore; + } + } + + exit(1) if $exitting; + print("\n"); + return $return; +} + +package NetSNMP::Cert; + +our $VERSION = '0.2.9'; + +our $PROG = ::basename($0); +our $DEBUG = 0; +our $OK = 0; +our $ERR = -1; + +# user input param +our $MATCH = 1; +our $PREMATCH = 2; + +# Load LWP if possible to import cert from URL +eval('use LWP::UserAgent;'); +our $haveUserAgent = ($@ ? 0 : 1); + +# required executables +our $OPENSSL = $ENV{NET_SNMP_CRT_OPENSSL} || 'openssl'; +our $CFGTOOL = $ENV{NET_SNMP_CRT_CFGTOOL} || 'net-snmp-config'; + +# default app config file +our $CFGFILE = $ENV{NET_SNMP_CRT_CFGFILE} || 'net-snmp-cert.conf'; + +# default OpenSSL config files +our $SSLCFGIN = $ENV{NET_SNMP_CRT_SSLCFGIN} || 'openssl.in'; +our $SSLCFGOUT = $ENV{NET_SNMP_CRT_SSLCFGIN} || '.openssl.conf'; +our $SSLCFG = $ENV{NET_SNMP_CRT_SSLCFG}; + +# default cmd logs +our $OUTLOG = $ENV{NET_SNMP_CRT_OUTLOG} || '.cmd.out.log'; +our $ERRLOG = $ENV{NET_SNMP_CRT_ERRLOG} || '.cmd.err.log'; + +# default cert dirs +our $TLSDIR = $ENV{NET_SNMP_CRT_TLSDIR} || 'tls'; +our $CRTDIR = $ENV{NET_SNMP_CRT_CRTDIR} || 'certs'; +our $NEWCRTDIR = $ENV{NET_SNMP_CRT_NEWCRTDIR}|| 'newcerts'; +our $CADIR = $ENV{NET_SNMP_CRT_CADIR} || 'ca-certs'; +our $PRIVDIR = $ENV{NET_SNMP_CRT_PRIVDIR} || 'private'; + +our $SERIAL = $ENV{NET_SNMP_CRT_SERIAL} || '.serial'; +our $INDEX = $ENV{NET_SNMP_CRT_INDEX} || '.index'; + +our $DEFCADAYS = $ENV{NET_SNMP_CRT_DEFCADAYS} || 1825; +our $DEFCRTDAYS = $ENV{NET_SNMP_CRT_DEFCRTDAYS}|| 365; + +our @TLSDIRS = ($CRTDIR, $NEWCRTDIR, $CADIR, $PRIVDIR); + +our @CRTSUFFIXES = qw(.pem .csr .der .crt); + +our @X509FMTS = qw(text modulus serial subject_hash issuer_hash hash subject + purpose issuer startdate enddate dates fingerprint C); + +sub dprint { + my $str = shift; + my $opts = shift; + my $dlevel = shift || 1; + my $flag = (defined $opts ? int($opts->{'debug'} >= $dlevel) : 1); + my ($pkg, $file, $line, $sub) = caller(1); + my ($pkg0, $file0, $line0) = caller(0); + print("${sub}():$line0: $str") if $flag; +} + +sub dwarn { + my $str = shift; + my $opts = shift; + my $dlevel = shift || 1; + my $flag = (defined $opts ? $opts->{'debug'} >= $dlevel : 1); + my ($pkg, $file, $line, $sub) = caller(1); + my ($pkg0, $file0, $line0) = caller(0); + warn("${sub}():$line0: $str") if $flag; +} + +sub vprint { + my $str = shift; + my $opts = shift; + my $flag = (defined $opts ? $opts->{'verbose'} : 1); + my $debug = (defined $opts ? $opts->{'debug'} : 1); + my ($pkg, $file, $line, $sub) = caller(1); + my ($pkg0, $file0, $line0) = caller(0); + $str = ($debug ? "${sub}():$line0: $str" : "$str"); + print("$str") if $flag; +} + +sub vwarn { + my $str = shift; + my $opts = shift; + my $flag = (defined $opts ? $opts->{'verbose'} : 1); + my ($pkg, $file, $line, $sub) = caller(1); + my ($pkg0, $file0, $line0) = caller(0); + warn("${sub}():$line0: $str") if $flag; +} + +sub rsystem { + my $cmd = shift; + my $flag = shift; + + # if not running as root try to use sudo + if ($>) { + $cmd = "sudo $flag $cmd"; + } else { + if ($flag =~ /-b/) { + $cmd = "$cmd \&"; + } + } + die("cmd failed($!): $cmd\n") if system("$cmd"); +} + +sub usystem { + my $cmd = shift; + my $opts = shift; + my $ret; + + unlink $NetSNMP::Cert::OUTLOG if -e $NetSNMP::Cert::OUTLOG; + unlink $NetSNMP::Cert::ERRLOG if -e $NetSNMP::Cert::ERRLOG; + + $ret = system("$cmd"); + + if ($ret) { + if ($opts->{'verbose'}) { + system("cat $NetSNMP::Cert::OUTLOG") if -r $NetSNMP::Cert::OUTLOG; + system("cat $NetSNMP::Cert::ERRLOG") if -r $NetSNMP::Cert::ERRLOG; + } + die("cmd failed($!): $cmd\n"); + } +} + +sub home_dir { + my $cdir = ::getcwd(); + + chdir("~"); + my $dir = ::getcwd(); + chdir($cdir); + + return $dir; +} + +sub find_bin_dir { + # This code finds the path to the currently invoked program and + my $dir; + + $0 =~ m%^(([^/]*)(.*)/)([^/]+)$%; + if ($1) { + if ($2) { + # Invoked using a relative path. CD there and use pwd. + $dir = `cd $1 && pwd`; + chomp $dir; + } else { + # Invoked using an absolute path; that's it! + $dir = $3; + } + } else { + # No path. Look in PATH for the first instance. + foreach my $p (split /:/, $ENV{PATH}) { + $p ||= '.'; + -x "$p/$4" or next; + $dir = $p; + } + } + $dir or die "Cannot locate program '$0'!"; +} + +sub fq_rel_path { + my $path = shift; + my $cwd = ::getcwd(); + my $rdir = shift || $cwd; + + chdir($rdir) or die("can't change directory: $rdir"); + # get fully qualified path + if ($path !~ m|^/|) { + my $pwd = `pwd`; chomp $pwd; + $path = "$pwd/$path"; + $path = ::realpath($path) if -e $path; + } + chdir($cwd) or die("can't change directory: $cwd"); + + return $path; +} + +sub dir_empty + { + my $path = shift; + opendir(DIR, $path); + foreach (readdir(DIR)) { + next if /^\.\.?$/; + closedir(DIR); + return 0; + } + closedir(DIR); + return 1; + } + +sub make_dirs { + my $opts = shift; + my $dir = shift; + my $mode = shift; + my @dirs = @_; + + my $wd = ::getcwd(); + + NetSNMP::Cert::dprint("make dirs [$dir:@dirs] from $wd\n", $opts); + + ::mkpath($dir, $opts->{'debug'}, $mode) or die("error - can't make $dir") + if defined $dir and not -d $dir; + + foreach my $subdir (@dirs) { + my $d = "$subdir"; + $d = "$dir/$d" if defined $dir; + NetSNMP::Cert::dprint("making directory: $d\n", $opts) unless -d $d; + mkdir($d, $mode) or die("error - can't make $d") unless -d $d; + } +} + +sub is_url { + my $url = shift; + + return $url =~ /^(?#Protocol)(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?#Username:Password)(?:\w+:\w+@)?(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?$/; +} + +sub in_set { + my $elem = shift; + my $set = shift; + for my $e (eval($set)) { + return 1 if $e == $elem; + } + return 0; +} + +sub in_arr { + my $elem = shift; + my $arr = shift; + for my $e (@{$arr}) { + return 1 if $e eq $elem; + } + return 0; +} + +sub map_bool { + my $val = shift; + + return 1 if $val =~ /^true$/i; + return 0 if $val =~ /^false$/i; + return $val; +} + +sub version { + my $ret = (@_ ? shift : 1); + print "$NetSNMP::Cert::PROG: $NetSNMP::Cert::VERSION\n"; + exit($ret); +} + +sub GetOptsFromArray { + my $args = shift; + local *ARGV; + @ARGV = @$args; + my $ret = ::GetOptions(@_); + @$args = @ARGV; # GetOptions strips out the ones it handles and leaves others + return $ret; +} +sub pull_cmd { + my $args = shift; + my $cmd; + + foreach (@{$args}) { + if (/^(gence?rt|genca|gencsr|signcsr|showcas?|showce?rts?|import)$/) { + $cmd = $1; + } + } + + @{$args} = grep {!/^$cmd$/} @{$args}; + + return $cmd; +} + + +sub usage { + my $ret = (@_ ? shift : 1); + my $arg = shift; + + print "Command not implmeneted yet: $arg\n" if $ret == 2; + print "Unknown: $arg\n" if $ret == 3; + print "\n NAME:\n"; + print " $NetSNMP::Cert::PROG: [$NetSNMP::Cert::VERSION] - "; + print "Net-SNMP Certificate Management Tool\n"; + print "\n DESCRIPTION:\n"; + print " net-snmp-cert creates, signs, installs and displays X.509\n"; + print " certificates used in the operation of Net-SNMP/(D)TLS.\n"; + print "\n SYNOPSIS:\n"; + print " net-snmp-cert [--help|-?]\n"; + print " net-snmp-cert [--version|-V]\n"; + print " net-snmp-cert genca [<flags>] [<dn-opts>] [--with-ca <ca>]\n"; + print " net-snmp-cert gencert [<flags>] [<dn-opts>] [--with-ca <ca>]\n"; + print " net-snmp-cert gencsr [<flags>] [<dn-opts>] [--from-crt <crt>]\n"; + print " net-snmp-cert signcsr [<flags>] [--install] --csr <csr> --with-ca <ca>\n"; + print " net-snmp-cert showca [<flags>] [<format-opts>] [<file>|<search-tag>]\n"; + print " net-snmp-cert showcert [<flags>] [<format-opts>] [<file>|<search-tag>]\n"; + print " net-snmp-cert import [<flags>] <file|url> [<key>]\n"; + print "\n COMMANDS:\n"; + print " genca -- generate a signed CA certificate suitable for signing other\n"; + print " certificates. default: self-signed unless --with-ca <ca> supplied\n\n"; + print " gencert -- generate a signed certificate suitable for identification, or\n"; + print " validation. default: self-signed unless --with-ca <ca> supplied\n\n"; + print " gencsr -- generate a certificate signing request. will create a new\n"; + print " key and certificate unless --from-crt <crt> supplied\n\n"; + print " signcsr -- sign a certificate signing request specified by --csr <csr>\n"; + print " with the CA certificate specified by --with-ca <ca>\n"; + print " import -- import an identity or CA certificate, optionally import <key>\n"; + print " if an URL is passed, will attempt to import certificate from site\n"; + print " showca,\n"; + print " showcert -- show CA or identity certificate(s). may pass fully qualified\n"; + print " file or directory name, or a search-tag which prefix matches\n"; + print " installed CA or identity certificate file name(s)\n"; + print " see FORMAT OPTIONS to specify output format\n\n"; + print "\n FLAGS:\n"; + print " -?, --help -- show this text and exit\n"; + print " -V, --version -- show version string and exit\n"; + print " -D, --debug -- raise debug level (-D -D for higher level)\n"; + print " -F, --force -- force overwrite of existing output files\n"; + print " -I, --nointeractive -- non-interactive run (default interactive)\n"; + print " -Q, --noverbose -- non-verbose output (default verbose)\n"; + print " -C, --cfgdir <dir> -- configuration dir (see man(5) snmp_config)\n"; + print " -T, --tlsdir <dir> -- root for cert storage (default <cfgdir>/tls)\n"; + print " -f, --cfgfile <file> -- config (default <cfgdir>/net-snmp-cert.conf)\n"; + print " -i, --identity <id> -- identity to use from config\n"; + print " -t, --tag <tag> -- application tag (default 'snmp')\n"; + print " --<cfg-param>[=<val>] -- additional config params\n"; + print "\n CERTIFICATE OPTIONS (<cert-opts>):\n"; + print " -a, --with-ca <ca> -- CA certificate used to sign request\n"; + print " -A, --ca <ca> -- explicit output CA certificate\n"; + print " -r, --csr <csr> -- certificate signing request\n"; + print " -x, --from-crt <crt> -- input certificate for current operation\n"; + print " -X, --crt <crt> -- explicit output certificate\n"; + print " -y, --install -- install result in local repository\n"; + print "\n DISTINGUISHED NAME OPTIONS (<dn-opts>):\n"; + print " -e, --email <email> -- email name\n"; + print " -h, --host <host> -- DNS host name, or IP address\n"; + print " -d, --days <days> -- number of days certificate is valid\n"; + print " -n, --cn <cn> -- common name (CN)\n"; + print " -o, --org <org> -- organiztion name\n"; + print " -u, --unit <unit> -- organiztion unit name\n"; + print " -c, --country <country> -- country code (e.g., US)\n"; + print " -p, --province <province> -- province name (synomynous w/ state)\n"; + print " -p, --state <state> -- state name (synomynous w/ province)\n"; + print " -l, --locality <locality> -- locality name (e.g, town)\n"; + print " -s, --san <san> -- subjectAltName, repeat for each <san>\n"; + print " -- <san> value format (<FMT>:<VAL>):\n"; + print " -- dirName:/usr/share/snmp/tls\n"; + print " -- DNS:net-snmp.org\n"; + print " -- email:admin\@net-snmp.org\n"; + print " -- IP:192.168.1.1\n"; + print " -- RID:1.1.3.6\n"; + print " -- URI:http://net-snmp.org\n"; + print "\n FORMAT OPTIONS (<format-opts>): \n"; + print " --brief -- minimized output (values only where applicable)\n"; + print " --text -- full text description\n"; + print " --subject -- subject description\n"; + print " --subject_hash -- hash of subject for indexing\n"; + print " --issuer -- issuer description\n"; + print " --issuer_hash -- hash of issuer for indexing\n"; + print " --fingerprint -- SHA1 digest of DER\n"; + print " --serial -- serial number\n"; + print " --modulus -- modulus of the public key\n"; + print " --dates -- start and end dates\n"; + print " --purpose -- displays allowed uses\n"; + print " --C -- C code description\n"; + print "\n EXAMPLES: \n"; + print " net-snmp-cert genca --cn ca-net-snmp.org --days 1000\n"; + print " net-snmp-cert genca -f .snmp/net-snmp-cert.conf -i nocadm -I\n"; + print " net-snmp-cert gencert -t snmpd --cn host.net-snmp.org\n"; + print " net-snmp-cert gencsr -t snmpapp\n"; + print " net-snmp-cert signcsr --csr snmpapp --with-ca ca-net-snmp.org\n"; + print " net-snmp-cert showcerts --subject --issuer --dates snmpapp\n"; + print " net-snmp-cert showcas --fingerprint ca-net-snmp.org --brief\n"; + print " net-snmp-cert import ca-third-party.crt\n"; + print " net-snmp-cert import signed-request.crt signed-request.key\n\n"; + + exit $ret; +} + +sub set_default { + my $opts = shift; + my $config = shift; + my $field = shift; + my $val = shift; + + if (not defined $opts->{$field}) { + my $cval = $config->inherit($field); + $opts->{$field} = (defined $cval ? $cval : $val); + } +} + +sub cfg_path { + my $path; + + $path = `$NetSNMP::Cert::CFGTOOL --snmpconfpath`; + chomp $path; + return (wantarray ? split(':', $path) : $path); +} + +sub find_cfgfile { + my $dir = shift; + my $file = shift; + + if (defined $dir and -d $dir and + defined $file and $file !~ /^[\.\/]/) { + return fq_rel_path("$dir/$file") if -f "$dir/$file"; + } + + if (defined $file) { + if (-f $file) { + return fq_rel_path($file); + } else { + return $file; # file is not found, complain later + } + } + + my @path = cfg_path(); + unshift(@path, $dir) if defined $dir; + while (@path) { + my $p = pop(@path); + next if $p eq '/var/lib/snmp'; + if (-r "$p/$NetSNMP::Cert::CFGFILE") { + return fq_rel_path("$p/$NetSNMP::Cert::CFGFILE"); + } + } + + return $file; +} + +sub find_cfgdir { + my $dir = shift; + my $file = shift; + + if (defined $dir) { + $dir = NetSNMP::Cert::fq_rel_path($dir); + return $dir; + } + + if (defined $file and -f $file) { + $dir = ::dirname($file); + return NetSNMP::Cert::fq_rel_path($dir); + } else { + my @path = cfg_path(); + # search first for writeable tls dir + # for root search top down, for user bottom up + while (@path) { + $dir = ($> ? pop(@path): shift(@path)); + next if $dir eq '/var/lib/snmp'; + return $dir if -d "$dir/$NetSNMP::Cert::TLSDIR" and -w "$dir/$NetSNMP::Cert::TLSDIR"; + } + @path = cfg_path(); + # if no tls dir found, pick first writable config dir + # for root search top down, for user bottom up + while (@path) { + $dir = ($> ? pop(@path): shift(@path)); + next if $dir eq '/var/lib/snmp'; + return $dir if -d $dir and -w $dir; + } + my $home = $ENV{HOME} || die "Unable to determine home directory: set \$HOME\n"; + return ($> ? "$home/.snmp" : "/usr/share/snmp"); # XXX hack - no dirs existed or were writable + } + # this should never happen + return undef; +} + +sub find_certs { + my $opts = shift; + my $dir = shift || 'certs'; + my $targ = shift; + my $suffix_regex; + my $cwd = ::getcwd(); + + if ($dir eq 'csrs') { + $dir = $NetSNMP::Cert::NEWCRTDIR; + $suffix_regex = '\.csr|\.pem'; + } else { + $suffix_regex = '\.crt|\.pem'; + } + + NetSNMP::Cert::dprint("find_certs(1:$cwd):$dir:$targ:$suffix_regex\n", $opts); + + if ($targ =~ /\.?\//) { + # see if targ could be file - calc orig. rel. path + my $arg = NetSNMP::Cert::fq_rel_path($targ, $opts->{'rdir'}); + NetSNMP::Cert::dprint("find_certs(2):$dir:$targ:$arg\n", $opts); + # targ is a file name - use it + return (wantarray ? ($arg) : $arg) if -f $arg; + # else mark as dir if it is + $targ = "$arg" if -d $arg; + } + + $targ =~ s/\/*$/\// if -d $targ; + + NetSNMP::Cert::dprint("find_certs(3):$dir:$targ\n", $opts); + + # find certs in targ if a dir (in tlsdir, or relative) + $dir = $1 if $targ =~ s/^(\S+)\/([^\/\s]*)$/$2/ and -d $1; + + NetSNMP::Cert::dprint("find_certs(4):${dir}:$targ:$cwd\n", $opts); + + my @certs; + my $glob = "$dir/$targ"; + foreach (<$dir/*>) { + NetSNMP::Cert::dprint("checking($dir:$targ): $_\n", $opts); + next unless /^$dir\/$targ(.*$suffix_regex)?$/; + # return exact match if not wantarray() + return $_ if (not wantarray()) and /^$dir\/$targ($suffix_regex)?$/; + NetSNMP::Cert::dprint("pushing: $_\n", $opts); + push(@certs, $_); + } + + return (wantarray ? @certs : $certs[0]); +} + +sub check_output_file { + my $opts = shift; + my $file = shift; + my $interactive = shift; + my $force = shift; + my $continue = 1; + + if (-w $file) { + if ($interactive and not $force) { + print "Output file ($file) exists; will be overwritten\nContinue [y/N]: "; + $continue = <STDIN>; chomp $continue; + $continue = 0 if $continue =~ /^\s*$|^no?\s*$/i; + } else { + if ($force) { + NetSNMP::Cert::vprint("Output file ($file) exists; overwriting...\n", $opts); + } else { + NetSNMP::Cert::vprint("Output file ($file) exists; exiting...\n", $opts); + $continue = 0; + } + } + } elsif (-e $file) { + NetSNMP::Cert::vprint("Output file ($file) not writable; exiting...\n", $opts); + $continue = 0; + } + exit(1) unless $continue; +} + +sub is_server { + my $tag = shift; + return 1 if $tag eq 'snmpd' or $tag eq 'snmptrapd'; + return 0; +} + +sub is_ca_cert { + my $crt = shift; + my $output = `openssl x509 -in '$crt' -text -noout`; + return ($output =~ /^\s*CA:TRUE\s*$/m ? 1 : 0); +} + +sub x509_format { + my $opts = shift; + my $fmt; + + foreach my $f (@NetSNMP::Cert::X509FMTS) { + $fmt .= " -$f" if defined $opts->{$f}; + } + + return $fmt; +} + +sub make_openssl_conf { + my $file = shift; + return if -r $file; + + open(F, ">$file") or die("could not open $file"); + + print F <<'END'; +# +# Net-SNMP (D)TLS OpenSSL configuration file. +# + +rdir = . +dir = $ENV::DIR +RANDFILE = $rdir/.rand +MD = sha1 +KSIZE = 2048 +CN = net-snmp.org +EMAIL = admin@net-snmp.org +COUNTRY = US +STATE = CA +LOCALITY = Davis +ORG = Net-SNMP +ORG_UNIT = Development +SAN = email:copy +DAYS = 365 +CRLDAYS = 30 + +default_days = $ENV::DAYS # how long to certify for +default_crl_days= $ENV::CRLDAYS # how long before next CRL +default_md = $ENV::MD # which md to use. + +database = $dir/.index # database index file. +serial = $dir/.serial # The current serial number +certs = $rdir/certs # identity certs +new_certs_dir = $dir/newcerts # default place for new certs. +ca_certs_dir = $rdir/ca-certs # default place for new certs. +key_dir = $rdir/private + +crl_dir = $dir/crl # crl's +crlnumber = $dir/.crlnumber # the current crl number + # must be commented out to leave V1 CRL +crl = $crl_dir/crl.pem # The current CRL + +preserve = no # keep passed DN ordering +unique_subject = yes # Set to 'no' to allow creation of + # certificates with same subject. +# Extra OBJECT IDENTIFIER info: +oid_section = new_oids + +[ new_oids ] + +# Add new OIDs in here for use by 'ca' and 'req'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Use config file substitution like this: +# testoid2=${testoid1}.5.6 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] +# certificate = $ca_certs_dir/$ENV::TAG.crt # CA certificate so sign with +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options +policy = policy_match +copy_extensions = copy # copy v3 extensions (subjectAltName) +subjectAltName = copy + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = $ENV::KSIZE +default_md = $ENV::MD +distinguished_name = req_distinguished_name +string_mask = MASK:0x2002 +req_extensions = v3_req +x509_extensions = v3_user_create + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = $ENV::COUNTRY +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = $ENV::STATE + +localityName = Locality Name (eg, city) +localityName_default = $ENV::LOCALITY + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = $ENV::ORG + +organizationalUnitName = Organizational Unit Name (eg, section) +organizationalUnitName_default = $ENV::ORG_UNIT + +commonName = Common Name (eg, your name or your server\'s hostname) +commonName_max = 64 +commonName_default = $ENV::CN + +emailAddress = Email Address +emailAddress_max = 64 +emailAddress_default = $ENV::EMAIL + +[ v3_req ] + +# Extensions to add to a certificate request +basicConstraints = CA:FALSE + +# Import the email address and/or specified SANs. +%[subjectAltName = $ENV::SAN] + +[ v3_user_create ] + +# Extensions to add to a certificate request +basicConstraints = CA:FALSE +#keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# PKIX recommendation. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always + +# Import the email address and/or specified SANs. +%[subjectAltName = $ENV::SAN] + +[ v3_ca_create ] +# Extensions to add to a CA certificate +basicConstraints = CA:TRUE +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate (net-snmp)" + +# PKIX recommendation. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always + +# Import the email address and/or specified SANs. +%[subjectAltName = $ENV::SAN] + +[ v3_ca_sign ] +# Extensions to add when 'ca' signs a request. +basicConstraints = CA:FALSE +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate (net-snmp)" + +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always + +[ v3_ca_sign_ca ] +# Extensions to add when 'ca' signs a ca request. +basicConstraints = CA:TRUE + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate (net-snmp)" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always + +END + +} + + +package NetSNMP::Cert::Obj; + +sub new { + my $class = shift; + my $cfield = shift; + my $parent = shift; + my $ind = shift; + my $this = {}; + bless($this, $class); + + # initialize hash of keys which are dynamically created or internal + $this->{'AUTOKEYS'}{'AUTOKEYS'}++; + + # store a reference to ourselves so our children can find us + $this->autoSet('CFIELD', $cfield); + $this->autoSet($cfield , $this); + $this->autoSet('CHILDREN', []); + $this->autoSet('INDEX', $ind) if defined $ind; + + if (defined $parent) { + # cache 'APP' in all objs for easy reference + $this->autoSet('APP', $parent->inherit('APP')); + my $app = $this->{'APP'}; + + die("net-snmp-cert: error: no NetSNMP::Cert::App context provided") + if not defined $app or not ref $app eq 'NetSNMP::Cert::App'; + # save children for list traversal + push(@{$parent->{'CHILDREN'}}, $this) unless $parent eq $this; + } else { + # we are the top of the list + $parent = $this; + } + $this->autoSet('PARENT' , $parent); + + return $this; +} + +sub autoSet { + my $this = shift; + my $key = shift; + if (@_) { + my $val = shift; + $this->{'AUTOKEYS'}{$key}++; + $this->{$key} = $val; + } + return exists $this->{'AUTOKEYS'}{$key}; +} + +sub inherit { + my $this = shift; + my $field = shift; + my $id; + + # cmd opts override config settings + if (exists $this->{'APP'} and exists $this->{'APP'}{'OPTS'}) { + my $opts = $this->{'APP'}{'OPTS'}; + $id = $opts->{'identity'}; + return $opts->{$field} if defined $opts->{$field}; + } + + if (defined $id and exists $this->{'identity'} and + exists $this->{'identity'}{$id} and + exists $this->{'identity'}{$id}{$field}) { + return $this->{'identity'}{$id}{$field}; + } + + # return our field if we have it + return $this->{$field} if defined $this->{$field}; + + # terminate recursion at top and return undef if not found + return undef if not defined $this->{'PARENT'} or $this->{'PARENT'} eq $this; + + # recurse to parent + $this->{'PARENT'}->inherit($field); +} + +sub resolve { + my $this = shift; + my $opts = $this->inherit('OPTS'); + my $val = shift; + + NetSNMP::Cert::dprint("resolving: $val\n", $opts); + + $val =~ s/(\$(\w+))/$this->inherit($2) or die("unresolved reference in config: $1")/ge; + $val =~ s/(\&\{(.*?)\})/$2/gee; + + NetSNMP::Cert::dprint("resolved: $val\n", $opts); + + return $val +} + +sub delete { + my $this = shift; + my $opts = $this->inherit('OPTS'); + + NetSNMP::Cert::dprint("Obj::delete: ($this) [$this->{CFIELD}]\n", $opts); + + my $parent = $this->{'PARENT'}; + my @children = @{$this->{'CHILDREN'}}; + + foreach my $child (@children) { + $child->delete(); + } + + NetSNMP::Cert::dwarn("Obj: children not freed\n", $opts) if @{$this->{'CHILDREN'}}; + + # delete all our self-references + delete($this->{$this->{'CFIELD'}}) if exists $this->{'CFIELD'}; + delete($this->{'PARENT'}); + delete($this->{'CHILDREN'}); + + $parent->disown($this) if defined $parent and ref($parent) =~ /NetSNMP::Cert/; +} + +sub disown { + my $this = shift; + my $thechild = shift; + my $opts = $this->inherit('OPTS'); + my $ind = 0; + + NetSNMP::Cert::dprint("Obj::disown: ($this) [$this->{CFIELD}] disowning ($thechild) [$thechild->{CFIELD}]\n", $opts); + + foreach my $child (@{$this->{'CHILDREN'}}) { + last if $child eq $thechild; + $ind++; + } + if ($ind < @{$this->{'CHILDREN'}}) { + splice(@{$this->{'CHILDREN'}}, $ind, 1); + } else { + NetSNMP::Cert::dwarn("Child ($thechild) not found in object ($this)\n", $opts); + } +} + +sub disabled { + my $this = shift; + return (exists $this->{'disable'} ? $this->{'disable'} : 0); +} + +my %cfg = ( + 'name' => 1, + 'type' => 2, + 'id' => 6, + ); + +sub cfgsort { + my $self = shift; + my $a = shift; + my $b = shift; + return -1 if exists $cfg{$a} and not exists $cfg{$b}; + return 1 if exists $cfg{$b} and not exists $cfg{$a}; + return $cfg{$a} <=> $cfg{$b} if exists $cfg{$a} and exists $cfg{$b}; + return -1 if !ref($self->{$a}) and ref($self->{$b}); + return 1 if !ref($self->{$b}) and ref($self->{$a}); + return -1 if ref($self->{$a}) =~ /NetSNMP::Cert/ and ref($self->{$b}) !~ /NetSNMP::Cert/; + return 1 if ref($self->{$b}) =~ /NetSNMP::Cert/ and ref($self->{$a}) !~ /NetSNMP::Cert/; + return 0; +} + +sub dump { + my $self = shift; + my $indent = shift; + my $self_str = $self->{'CFIELD'}; + my @data_keys = grep {!$self->autoSet($_)} keys %$self; + my @lines; + + push(@lines, "\n" . ' ' x $indent . "$self_str = {\n") if defined $indent; + + { + my $indent = (defined $indent ? $indent + 3 : 0); + foreach my $key (sort {cfgsort($self,$NetSNMP::Cert::Obj::a, + $NetSNMP::Cert::Obj::b)} (sort @data_keys)) { + if (ref($self->{$key}) =~ /NetSNMP::Cert/) { + push(@lines, $self->{$key}->dump($indent)); + } elsif (ref($self->{$key}) =~ /ARRAY/) { + push(@lines, "\n") if ref(${$self->{$key}}[0]) !~ /NetSNMP::Cert/; + foreach my $elem (@{$self->{$key}}) { + if (ref($elem) =~ /NetSNMP::Cert/) { + push(@lines, $elem->dump($indent)); + } else { + push(@lines, ' ' x $indent . "$key = $elem\n"); + } + } + } else { + my $str = $key . (defined $self->{$key} ? " = $self->{$key}\n" : "\n"); + push(@lines, ' ' x $indent . $str); + } + } + } + + push(@lines, ' ' x $indent . "}; # end $self_str\n") if defined $indent; + return @lines; +} + +sub DESTROY { + my $this = shift; + + print("Obj::DESTROY $this [", ref $this, "]\n") if $NetSNMP::Cert::DEBUG >= 3; +} + +package NetSNMP::Cert::App; +use vars qw(@ISA); + +@ISA = qw(NetSNMP::Cert::Obj); + +sub new { + my $class = shift; + + # the app is god, it is its own parent + my $this = $class->SUPER::new('APP'); + + bless($this, $class); + + # verify required tools or die + $this->checkReqs(); + + # internal intitialization + $this->initOpts(); + + # make a new empty config and init (not parsed) + $this->{'config'} = new NetSNMP::Cert::Config($this); + + return $this; +} + +sub checkReqs { + my $app = shift; + + die("$NetSNMP::Cert::OPENSSL does not exist or is not executable") + if system("$NetSNMP::Cert::OPENSSL version > /dev/null 2>&1"); + + my $ossl_ver = `$NetSNMP::Cert::OPENSSL version`; chomp $ossl_ver; + $ossl_ver =~ s/^OpenSSL\s+([\d\.]+).*$/$1/; + my $ossl_min_ver = $NetSNMP::Cert::OPENSSL_MIN_VER; + + die("$NetSNMP::Cert::OPENSSL (v$ossl_ver): must be $ossl_min_ver or later") + if ($ossl_ver cmp $ossl_min_ver) < 0; + + die("$NetSNMP::Cert::CFGTOOL not found: please install") + if system("$NetSNMP::Cert::CFGTOOL > /dev/null 2>&1"); +} + +sub initOpts { + my $app = shift; + my $opts = {}; + $app->autoSet('OPTS', $opts); + + # Define directories we need. + $opts->{'bindir'} = NetSNMP::Cert::find_bin_dir(); + + $opts->{'out'} = "> $NetSNMP::Cert::OUTLOG"; + $opts->{'err'} = "2> $NetSNMP::Cert::ERRLOG"; + + # set up paths for app install and runtime env + $ENV{PATH} = "/sbin:/usr/sbin:$ENV{PATH}"; + $ENV{PATH} = "$opts->{'bindir'}:$ENV{PATH}"; + + # default all privs to -rw------- + umask(077); +} + +sub init { + my $app = shift; + my $opts = $app->{'OPTS'}; + my $config = $app->{'config'}; + my @args = @_; # pass external args (typically ARGV) + + # parse command line + $app->parseArgs(@args); + + # lazy config parsing postponed until here, will not reparse + $config->parse(); + + # set defaults here if not already set in cmdline or config + # guided interactive mode by default + NetSNMP::Cert::set_default($opts, $config, 'interactive', 1); + # verbose output + NetSNMP::Cert::set_default($opts, $config, 'verbose', 1); + + # find tlsdir/subdirs or make it + $opts->{'tlsdir'} = $app->createTlsDir(); +} + +sub parseArgs { + my $app = shift; + my $opts = $app->{'OPTS'}; + my @args = @_; + + NetSNMP::Cert::GetOptsFromArray(\@args, $opts, 'help|?', 'version|V', + 'interactive!', 'I', 'verbose!', 'Q', 'force|F', + 'cfgfile|f=s', 'cfgdir|C=s', 'tlsdir|tlsDir|T=s', + 'tag|t=s', 'identity|i=s', 'debug|D+',); + + NetSNMP::Cert::version(0) if $opts->{'version'}; + + NetSNMP::Cert::usage(0) if $opts->{'help'}; + + # pull out the cmd - getOpts should leave it untouched for us + $opts->{'cmd'} = NetSNMP::Cert::pull_cmd(\@args); + + # save extra args for command specific processing + $opts->{'cmdargs'} = [@args]; + + # Check debug option first + $NetSNMP::Cert::DEBUG = $opts->{'debug'}; + $opts->{'err'} = $opts->{'out'} = "" if $opts->{'debug'}; + $opts->{'interactive'} = not $opts->{'I'} if defined $opts->{'I'}; + $opts->{'verbose'} = not $opts->{'Q'} if defined $opts->{'Q'}; + + # search for cfgdir and cfgfile based on opts and confpath + $opts->{'cfgdir'} = NetSNMP::Cert::find_cfgdir($opts->{'cfgdir'}, + $opts->{'cfgfile'}); + $opts->{'cfgfile'} = NetSNMP::Cert::find_cfgfile($opts->{'cfgdir'}, + $opts->{'cfgfile'}); +} + +sub createTlsDir { + my $app = shift; + my $opts = $app->{'OPTS'}; + my $config = $app->{'config'}; + my $dir; + my $file; + my $cmd; + + $dir = $config->inherit('tlsDir'); + + if (not defined $dir) { + my $cfgdir = $opts->{'cfgdir'}; + die("undefined cfgdir: unable to creat tlsdir: exiting...\n") unless defined $cfgdir; + $dir = "$cfgdir/$NetSNMP::Cert::TLSDIR"; + } + + NetSNMP::Cert::dprint("tlsDir is: $dir\n", $opts); + $dir = NetSNMP::Cert::fq_rel_path($dir); + NetSNMP::Cert::dprint("tlsDir is: $dir\n", $opts); + + NetSNMP::Cert::dprint("tlsDir not found, creating\n", $opts) unless -d $dir; + NetSNMP::Cert::make_dirs($opts, $dir, 0700, @NetSNMP::Cert::TLSDIRS); + + my $ssl_cfg_in = NetSNMP::Cert::fq_rel_path($NetSNMP::Cert::SSLCFGIN,$dir); + + # Existing openssl.conf tmpl will be preserved + if (-f $ssl_cfg_in) { + NetSNMP::Cert::dwarn("OpenSSL template exists ($ssl_cfg_in): preserving...", $opts); + } + + if (not -f $ssl_cfg_in) { + NetSNMP::Cert::dprint("make_openssl_conf($ssl_cfg_in)", $opts); + NetSNMP::Cert::make_openssl_conf($ssl_cfg_in); + chmod(0600, $ssl_cfg_in); + } + + NetSNMP::Cert::dprint("createTlsDir: done\n", $opts); + return $dir; +} + +my @interactive_ops = (['gencert', "Generate a signed certificate"], + ['genca', "Generate a CA certificate"], + ['gencsr', "Generate a Certificate Signing Request"], + ['signcsr', "Sign a Certificate Signing Request"], + ); + +sub run { + my $app = shift; + my $opts = $app->{'OPTS'}; + my $cmd = $opts->{'cmd'}; + + # must have a command in non-Interactive mode + NetSNMP::Cert::usage(3) if !$opts->{'interactive'} and !$opts->{'cmd'}; + + # change dir tls dir - the cwd for all commands - save cwd first + $opts->{'rdir'} = ::getcwd(); + chdir($opts->{'tlsdir'}) or die("could'nt change directory: $opts->{tlsdir}"); + # display context + NetSNMP::Cert::dprint("PATH: $ENV{PATH}\n\n", $opts); + NetSNMP::Cert::dprint("config file: $opts->{cfgfile}\n", $opts); + NetSNMP::Cert::dprint("config dir: $opts->{cfgdir}\n", $opts); + NetSNMP::Cert::dprint("tls dir: $opts->{tlsdir}\n", $opts); + + my $cmdstr = join(' ', $cmd, @{$opts->{'cmdargs'}}); + NetSNMP::Cert::dprint("command: $cmdstr\n", $opts); + + NetSNMP::Cert::GetOptsFromArray(\@{$opts->{'cmdargs'}}, $opts, + 'with-ca|a=s', 'ca|A=s','csr|r=s', + 'from-crt|x=s', 'crt|X=s', 'days|d=s', + 'cn|n=s', 'email|e=s', 'host|h=s', + 'san|s=s@', 'org|o=s', 'unit|u=s', + 'country|c=s', 'state|province|p=s', + 'locality|l=s', 'brief|b', + @NetSNMP::Cert::X509FMTS); + + # process extra args; --<cfg-name>[=<val>] + $app->processExtraArgs(); + + # If in interactive mode - fill missing info by interviewing user + if (not defined $cmd or grep {/$cmd/} map {$_->[0]} @interactive_ops) { + $app->userInput() if $opts->{interactive}; + $cmd = $opts->{'cmd'}; # may have changed + } + + # resolve input args to filenames + $app->resolveCrtArgs(); + + # use env. or merge template for OpenSSL config + $NetSNMP::Cert::SSLCFG ||= $app->opensslCfg(); + + if ($cmd =~ /^genca$/) { + NetSNMP::Cert::usage(1) if defined $opts->{'from-crt'} or defined $opts->{'csr'}; + $app->genCa($opts->{'with-ca'}); + } elsif ($cmd =~ /^gence?rt$/) { + NetSNMP::Cert::usage(1) if defined $opts->{'from-crt'} or defined $opts->{'csr'}; + $app->genCert($opts->{'with-ca'}); + } elsif ($cmd =~ /^gencsr$/) { + NetSNMP::Cert::usage(1) if defined $opts->{'with-ca'} or defined $opts->{'crt'}; + $app->genCsr(); + } elsif ($cmd =~ /^signcsr$/) { + NetSNMP::Cert::usage(1) unless defined $opts->{'with-ca'} and defined $opts->{'csr'}; + $app->signCsr(); + } elsif ($cmd =~ /^show(ce?rts?)?$/) { + $app->show('certs'); + } elsif ($cmd =~ /^showcas?$/) { + $app->show('ca-certs'); + } elsif ($cmd =~ /^import$/) { + $app->import(); + } else { + NetSNMP::Cert::usage(); + } +} + +sub processExtraArgs { + my $app = shift; + my $opts = $app->{'OPTS'}; + my @args; + + NetSNMP::Cert::dprint("processing extra args...\n", $opts); + + while (@{$opts->{'cmdargs'}}) { + my $arg = shift(@{$opts->{'cmdargs'}}); + NetSNMP::Cert::dprint("found: arg --> $arg\n", $opts); + if ($arg =~ /^-+(\w+)(?:=(.*?))?\s*$/) { + NetSNMP::Cert::dprint("found: arg($1) val($2)\n", $opts); + if (exists $opts->{$1}) { + NetSNMP::Cert::vwarn("option ($1) already set: overwriting\n", $opts); + } + $opts->{$1} = (defined $2 ? $2 : 1); + } else { + push(@args, $arg); + } + } + @{$opts->{'cmdargs'}} = @args; +} + +sub resolveCrtArgs { + my $app = shift; + my $opts = $app->{'OPTS'}; + + # find ca, crt, csr args if present and return fully qualified path + if (defined $opts->{'with-ca'}) { + my $ca; + $ca = NetSNMP::Cert::find_certs($opts, 'ca-certs', $opts->{'with-ca'}); + die("unable to locate CA certificate ($opts->{'with-ca'})\n") unless -e $ca; + die("unable read CA certificate ($opts->{'with-ca'})\n") unless -r $ca; + $opts->{'with-ca'} = $ca; + } + + if (defined $opts->{'from-crt'}) { + my $crt; + $crt = NetSNMP::Cert::find_certs($opts, 'certs', $opts->{'from-crt'}); + die("unable to locate certificate ($opts->{'from-crt'})\n") unless -e $crt; + die("unable read certificate ($opts->{'from-crt'})\n") unless -r $crt; + $opts->{'from-crt'} = $crt; + } + + if (defined $opts->{'csr'}) { + my $csr; + $csr = NetSNMP::Cert::find_certs($opts, 'csrs', $opts->{'csr'}); + die("unable to locate CSR certificate ($opts->{csr})\n") unless -e $csr; + die("unable read CSR certificate ($opts->{csr})\n") unless -r $csr; + $opts->{'csr'} = $csr; + } +} + +sub opensslCfg { + my $app = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $san = $config->inherit('san') || $config->inherit('subjectAltName'); + my $ssl_cfg_in = NetSNMP::Cert::fq_rel_path($NetSNMP::Cert::SSLCFGIN); + my $ssl_cfg_out = NetSNMP::Cert::fq_rel_path($NetSNMP::Cert::SSLCFGOUT); + + if (not -f $ssl_cfg_in) { + NetSNMP::Cert::vwarn("OpenSSL template not found: $ssl_cfg_in\n", $opts); + die("no OpenSSL template"); + } + + open(IN, $ssl_cfg_in) or die("unable to open OpenSSL template: $ssl_cfg_in\n"); + open(OUT, ">$ssl_cfg_out") or die("unable to open OpenSSL config: $ssl_cfg_out\n"); + + print OUT "#######################################################\n"; + print OUT "##### Warning: Do Not Edit - Generated File #####\n"; + print OUT "#######################################################\n"; + while (<IN>) { + if ($san) { + s/\%\[([^\]]*?)\]/$1/; + } else { + s/\%\[([^\]]*?)\]//; + } + print OUT $_; + } + close(IN); + close(OUT); + + return $ssl_cfg_out; +} + +sub opensslEnv { + my $app = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + + my $cn = shift; + my $days = shift; + my $dir = shift || "."; + # XXX - need to handle config'd identity here + my $name = $config->inherit("name"); + my $host = $config->inherit("host"); + my $email = $config->inherit("email"); + my $country = $config->inherit("country"); + my $state = $config->inherit("state"); + my $locality = $config->inherit("locality"); + my $org = $config->inherit("org"); + my $org_unit = $config->inherit("unit") || $config->inherit("orgUnit"); + my $san; + my $san_arr_ref; + my $md = $config->inherit("msgDigest"); + my $ksize = $config->inherit("keySize"); + + my $env; + + $env .= " KSIZE=$ksize" if defined $ksize; + $env .= " DAYS=$days" if defined $days; + $env .= " MD=$md" if defined $md; + + $env .= " DIR='$dir'" if defined $dir; + + $env .= " EMAIL=$email" if defined $email; + $env .= " CN='$cn'" if defined $cn; + $env .= " ORG='$org'" if defined $org; + $env .= " ORG_UNIT='$org_unit'" if defined $org_unit; + $env .= " COUNTRY=$country" if defined $country; + $env .= " STATE=$state" if defined $state; + $env .= " LOCALITY=$locality" if defined $locality; + + $san_arr_ref = $config->inherit('subjectAltName'); + $san = join('\,\ ', @{$san_arr_ref}) if ref $san_arr_ref; + $san_arr_ref = $config->inherit('san'); + $san .= join('\,\ ', @{$san_arr_ref}) if ref $san_arr_ref; + $san =~ s/EMAIL:/email:/g; + $env .= " SAN=$san" if defined $san; + + NetSNMP::Cert::dprint("opensslEnv: $env\n", $opts); + + return $env; +} +our @san_prefix = (['dirName:', "e.g., dirName:/usr/share/snmp/tls"], + ['DNS:', "e.g., DNS:test.net-snmp.org)"], + ['email:', "e.g., email:admin\@net-snmp.org"], + ['IP:', "e.g., IP:192.168.1.1"], + ['RID:', "e.g., RID:1.1.3.6.20"], + ['URI:', "e.g., URI:http://www.net-snmp.org"]); + +sub userInputDN { + my $app = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $prompt; + my $ret; + my $host = $config->inherit("host") || ::hostname(); + my $email = $config->inherit('email') || getlogin() . "\@$host"; + + # get EMAIL + $prompt = "Enter Email"; + $ret = $email; + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret, + "\tEmail Address - (e.g., <name>@<domain>)"); + $email = $opts->{'email'} = $ret if defined $ret; + # get CN + $prompt = "Enter Common Name"; + $ret = ($opts->{'cmd'} eq 'genca' ? "ca-$host" : $email); + $ret = $config->inherit('cn') || $config->inherit('commonName') || $ret; + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret, + "\tCommon Name - (e.g., net-snmp.org)"); + $opts->{'cn'} = $ret if defined $ret; + # get ORG + $prompt = "Enter Organization"; + $ret = $config->inherit('org') || 'Net-SNMP'; + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret, + "\tOrganization - (e.g., Net-SNMP)"); + $opts->{'org'} = $ret if defined $ret; + # get ORG_UNIT + $prompt = "Enter Organizational Unit"; + $ret = $config->inherit('unit') || 'Development'; + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret, + "\tOrganizational Unit - (e.g., Development)"); + $opts->{'unit'} = $ret if defined $ret; + # get COUNTRY + $prompt = "Enter Country Code"; + $ret = $config->inherit('country') || 'US'; + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret, + "Country Code, 2 letters (<tab> for options)", + $NetSNMP::Cert::MATCH, \@CC); + $opts->{'country'} = $ret if defined $ret; + # get STATE(Province) + $prompt = "Enter State or Province"; + $ret = $config->inherit('state') || 'CA'; + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret, + "\tState or Province - (e.g., CA)"); + $opts->{'state'} = $ret if defined $ret; + # get LOCALITY + $prompt = "Enter Locality"; + $ret = $config->inherit('locality') || 'Davis'; + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret, "\tLocality - (e.g., Davis)"); + $opts->{'locality'} = $ret if defined $ret; + # get SAN (loop) + if (!$config->{'brief'}) { + print "Enter Subject Alt Names. Examples:\n"; + foreach my $pair (@san_prefix) { + printf("\t%-10.10s %s\n", $pair->[0], $pair->[1]); + } + } + do { + $ret = 'done'; + $prompt = "Enter Subject Alt Name (enter 'done' when finished) [$ret]: "; + $ret = NetSNMP::Term::Complete ($prompt, $ret, + "\tSubject Alt Name - (<type>:<val>)", + $NetSNMP::Cert::PREMATCH, \@san_prefix); + push(@{$opts->{'san'}}, $ret) if defined $ret and $ret ne 'done'; + } while (defined $ret and $ret ne 'done'); +} + +our @snmp_apps = (['snmp', 'Generic Certificate'], + ['snmpapp','Client Certificate'], + ['snmpd','Agent Certificate'], + ['snmptrapd','Trap-agent Certificate']); + +sub userInputTag { + my $app = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $ret; + my $prompt; + + print "Application Tag:\n\tused to name the certificate and dictate its filename\n"; + print "\tIt may also associate it with a particular application (eg \"snmpd\")\n"; + print "\tif 'none', a name will be generated from other parameters\n"; + print "\tenter <tab><tab> for typical options, or enter new name\n"; + $prompt = "Enter Application Tag"; + $ret = $config->inherit('tag') || 'none'; + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret, + "Application Tag assocaiated with certificate", + (not $NetSNMP::Cert::MATCH), \@snmp_apps); + $opts->{'tag'} = $ret if defined $ret and $ret ne 'none'; +} + +sub userInput { + my $app = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $prompt; + my $ret; + + print "Choose an operation:\n"; + foreach my $op (@interactive_ops) { + print "\t$op->[0]\t- $op->[1]\n"; + } + + $prompt = "Operation"; + $ret = $config->inherit('cmd') || $interactive_ops[0][0]; + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret, + "Certifciate Operation to perform", + $NetSNMP::Cert::MATCH, \@interactive_ops); + + $opts->{'cmd'} = $ret; + + if ($ret =~ /^gencert$/) { + # get tag + $app->userInputTag(); + # get DN + $app->userInputDN(); + # self-signed/CA-signed(ca-cert) + } elsif ($ret =~ /^genca$/) { + # get DN + $app->userInputDN(); + } elsif ($ret =~ /^gencsr$/) { + # get tag + $app->userInputTag(); + # get DN + $app->userInputDN(); + } elsif ($ret =~ /^signcsr$/) { + # get csr + $prompt = "Choose Certificate Signing Request"; + $ret = $config->inherit('csr'); + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret); + $opts->{'csr'} = $ret if defined $ret; + # get ca + $prompt = "Choose CA Certificate"; + $ret = $config->inherit('with-ca'); + $prompt .= (defined $ret ? " [$ret]: " : ": "); + $ret = NetSNMP::Term::Complete($prompt, $ret); + $opts->{'with-ca'} = $ret if defined $ret; + } else { + NetSNMP::Cert::vwarn("aborting operation: exiting...\n", $opts); + exit(1); + } +} + +sub createCaDir { + my $app = shift; + my $dir = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $file; + my $cmd; + + NetSNMP::Cert::make_dirs($opts, $dir, 0700,'newcerts','private'); + + $file = "$dir/$NetSNMP::Cert::SERIAL"; + if (not -f $file) { + $cmd = "echo '01' > '$file'"; + NetSNMP::Cert::dprint("$cmd\n", $opts); + NetSNMP::Cert::usystem($cmd, $opts); + chmod(0600, $file); + } + + $file = "$dir/$NetSNMP::Cert::INDEX"; + if (not -f $file) { + $cmd = "touch '$file'"; + NetSNMP::Cert::dprint("$cmd\n", $opts); + NetSNMP::Cert::usystem($cmd, $opts); + chmod(0600, $file); + } +} + +sub genCa { + my $app = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $host = $config->inherit('host') || ::hostname(); + my $days = $config->inherit('days') || $config->inherit('caDays') || + $NetSNMP::Cert::DEFCADAYS; + my $cn = $config->inherit('cn') || $config->inherit('commonName') || + "ca-$host"; + my $ca = $config->inherit('with-ca'); + my $tag = $config->inherit('tag') || $cn; + + # create CA dir + my $dir = ".ca/$tag"; + $app->createCaDir($dir); + + my $outCrt = "$NetSNMP::Cert::CADIR/$tag.crt"; + my $outKey = "$NetSNMP::Cert::PRIVDIR/$tag.key"; + + # set command env + my $env = $app->opensslEnv($cn, $days); + + NetSNMP::Cert::check_output_file($opts, $outCrt, + $config->inherit('interactive'), + $config->inherit('force')); + NetSNMP::Cert::check_output_file($opts, $outKey, + $config->inherit('interactive'), + $config->inherit('force')); + + my $cmd = "$env openssl req -extensions v3_ca_create -new -days $days -batch -config $NetSNMP::Cert::SSLCFG -keyout '$outKey'"; + $cmd .= " -nodes"; + + if (defined $ca) { + # we have to gen a csr and then sign it, must preserve CA:TRUE + my $outCsr = "$NetSNMP::Cert::NEWCRTDIR/$tag.csr"; + NetSNMP::Cert::check_output_file($opts, $outCsr, + $config->inherit('interactive'), + $config->inherit('force')); + $cmd .= " -out '$outCsr'"; + + NetSNMP::Cert::dprint("genCa (gencsr): $cmd\n", $opts); + NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts); + + my $ca_base = ::basename($ca, @NetSNMP::Cert::CRTSUFFIXES); + NetSNMP::Cert::dprint("ca_base: $ca_base\n", $opts); + + # set command env + $env = $app->opensslEnv($cn, $days, ".ca/$ca_base"); + + $cmd = "$env openssl ca -extensions v3_ca_sign_ca -days $days -cert '$ca' -keyfile '$NetSNMP::Cert::PRIVDIR/$ca_base.key' -in '$outCsr' -batch -config $NetSNMP::Cert::SSLCFG -out '$outCrt'"; + + NetSNMP::Cert::dprint("genCa (signcsr): $cmd\n", $opts); + NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts); + } else { + $cmd .= " -x509 -out '$outCrt'"; + + NetSNMP::Cert::dprint("genCa: $cmd\n", $opts); + NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts); + } + + NetSNMP::Cert::vprint("CA Generated:\n", $opts); + NetSNMP::Cert::vprint(" $outCrt\n", $opts); + NetSNMP::Cert::vprint(" $outKey\n", $opts); +} + +sub genCert { + my $app = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $host = $config->inherit("host") || ::hostname(); + my $email = $config->inherit("email") || getlogin() . "\@$host"; + my $days = $config->inherit('days') || $config->inherit('crtDays') || $NetSNMP::Cert::DEFCRTDAYS; + my $cn = $config->inherit('cn') || $config->inherit('commonName'); + my $ca = $config->inherit('with-ca'); + my $cmd; + + my $tag = $opts->{'tag'} || 'snmp'; + $cn ||= (NetSNMP::Cert::is_server($tag) ? $host : $email); + + my $env = $app->opensslEnv($cn, $days); + + my $outCrt = "$NetSNMP::Cert::CRTDIR/$tag.crt"; + my $outKey = "$NetSNMP::Cert::PRIVDIR/$tag.key"; + + NetSNMP::Cert::check_output_file($opts, $outCrt, + $config->inherit('interactive'), + $config->inherit('force')); + NetSNMP::Cert::check_output_file($opts, $outKey, + $config->inherit('interactive'), + $config->inherit('force')); + + $cmd = "$env openssl req -extensions v3_user_create -new -days $days -keyout '$outKey' -batch -config $NetSNMP::Cert::SSLCFG"; + $cmd .= " -nodes"; + + if (defined $ca) { + my $outCsr = "$NetSNMP::Cert::NEWCRTDIR/$tag.csr"; + NetSNMP::Cert::check_output_file($opts, $outCsr, + $config->inherit('interactive'), + $config->inherit('force')); + $cmd .= " -out '$outCsr'"; + + NetSNMP::Cert::dprint("genCert (gencsr): $cmd\n", $opts); + NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts); + + # XXX cleanup this temp CSR + my $ca_base = ::basename($ca, @NetSNMP::Cert::CRTSUFFIXES); + NetSNMP::Cert::dprint("ca_base: $ca_base\n", $opts); + + # set command env + $env = $app->opensslEnv($cn, $days, ".ca/$ca_base"); + + $cmd = "$env openssl ca -extensions v3_ca_sign -days $days -cert '$ca' -keyfile '$NetSNMP::Cert::PRIVDIR/$ca_base.key' -in '$outCsr' -batch -config $NetSNMP::Cert::SSLCFG -out '$outCrt'"; + + NetSNMP::Cert::dprint("gencert (signcsr): $cmd\n", $opts); + NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts); + } else { + $cmd .= " -x509 -out '$outCrt'"; + + NetSNMP::Cert::dprint("genCert: $cmd\n", $opts); + NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts); + } + + NetSNMP::Cert::vprint("Certificate Generated:\n", $opts); + NetSNMP::Cert::vprint(" $outCrt\n", $opts); + NetSNMP::Cert::vprint(" $outKey\n", $opts); +} + +sub genCsr { + my $app = shift; + my $isCa = shift; # XXX - not implemented yet + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $host = $config->inherit("host") || ::hostname(); + my $email = $config->inherit("email") || getlogin() . "\@$host"; + my $days = $config->inherit('days') || $config->inherit('crtDays') || $NetSNMP::Cert::DEFCRTDAYS; + my $cn = $config->inherit('cn') || $config->inherit('commonName'); + my $tag = $config->inherit('tag'); + my $inCrt = $config->inherit('from-crt') || $config->inherit('fromCert'); + my $outCsr; + my $csrKey; + + if (defined $inCrt) { + $inCrt = NetSNMP::Cert::find_certs($opts, 'certs', $inCrt); + my $crt = ::basename($inCrt, @NetSNMP::Cert::CRTSUFFIXES); + $csrKey = "$NetSNMP::Cert::PRIVDIR/$crt.key"; + $tag ||= $crt; + } else { + $tag ||= 'snmp'; + $csrKey ||= "$NetSNMP::Cert::PRIVDIR/$tag.key"; + } + + $outCsr = "$NetSNMP::Cert::NEWCRTDIR/$tag.csr"; + + $cn ||= (NetSNMP::Cert::is_server($tag) ? $host : $email); + + my $env = $app->opensslEnv($cn, $days); + + NetSNMP::Cert::check_output_file($opts, $outCsr, + $config->inherit('interactive'), + $config->inherit('force')); + NetSNMP::Cert::check_output_file($opts, $csrKey, + $config->inherit('interactive'), + $config->inherit('force')); + + my $cmd = (defined $inCrt ? + "$env openssl x509 -x509toreq -in $inCrt -out '$outCsr' -signkey '$csrKey' -nodes -days $days -batch -config $NetSNMP::Cert::SSLCFG" : + "$env openssl req -new -nodes -days $days -batch -keyout '$csrKey' -out '$outCsr' -config $NetSNMP::Cert::SSLCFG"); + + $cmd .= ($isCa ? " -extensions v3_ca_create" : " -extensions v3_user_create"); + + NetSNMP::Cert::dprint("genCsr: $cmd\n", $opts); + + NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts); + + NetSNMP::Cert::vprint("Certificate Signing Request Generated:\n", $opts); + NetSNMP::Cert::vprint(" $outCsr\n", $opts); + NetSNMP::Cert::vprint(" $csrKey\n", $opts); +} + +sub signCsr { + my $app = shift; + my $isCa = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $host = $config->inherit("host") || ::hostname(); + my $email = $config->inherit("email") || getlogin() . "\@$host"; + my $days = $config->inherit('days') || $config->inherit('crtDays') || $NetSNMP::Cert::DEFCRTDAYS; + my $cn = $config->inherit('cn') || $config->inherit('commonName'); + my $tag = $config->inherit('tag') || 'snmp'; + my $install = $config->inherit('install'); + + $cn = (NetSNMP::Cert::is_server($tag) ? $host : $email); + + my $ca = $opts->{'with-ca'}; + NetSNMP::Cert::dprint("ca: $ca\n", $opts); + my $ca_base = ::basename($ca, @NetSNMP::Cert::CRTSUFFIXES); + NetSNMP::Cert::dprint("ca_base: $ca_base\n", $opts); + my $ca_key = "$NetSNMP::Cert::PRIVDIR/$ca_base.key"; + + my $csr = $opts->{'csr'}; + NetSNMP::Cert::dprint("csr: $csr\n", $opts); + my $csr_base = ::basename($csr, @NetSNMP::Cert::CRTSUFFIXES); + NetSNMP::Cert::dprint("csr_base: $csr_base\n", $opts); + my $outdir = ($install ? $NetSNMP::Cert::CRTDIR : $NetSNMP::Cert::NEWCRTDIR); + my $outCrt = "$outdir/$csr_base.crt"; + + my $env = $app->opensslEnv($cn, $days, ".ca/$ca_base"); + + NetSNMP::Cert::check_output_file($opts, $outCrt, + $config->inherit('interactive'), + $config->inherit('force')); + + # XXX - handle keyfile search?? + my $cmd = "$env openssl ca -batch -days $days -extensions v3_ca_sign -cert '$ca' -keyfile '$ca_key' -in '$csr' -out '$outCrt' -config $NetSNMP::Cert::SSLCFG"; + + # $cmd .= ($isCa ? " -extensions v3_ca_sign_ca" : " -extensions v3_ca_sign"); + + NetSNMP::Cert::dprint("signCsr: $cmd\n", $opts); + + NetSNMP::Cert::usystem("$cmd $opts->{out} $opts->{err}", $opts); + + NetSNMP::Cert::vprint("Signed Certificate Signing Request:\n", $opts); + NetSNMP::Cert::vprint(" $csr\n", $opts); + NetSNMP::Cert::vprint("with CA:\n", $opts); + NetSNMP::Cert::vprint(" $ca\n", $opts); + NetSNMP::Cert::vprint(" $ca_key\n", $opts); + NetSNMP::Cert::vprint("Generated Certificate:\n", $opts); + NetSNMP::Cert::vprint(" $outCrt\n", $opts); +} + +sub show { + my $app = shift; + my $type = shift || 'certs'; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $stag = shift(@{$opts->{'cmdargs'}}); + my $fmt = NetSNMP::Cert::x509_format($opts) || '-subject'; + my $brief = $config->inherit('brief'); + my $output; + my $cmd; + + my $cwd = ::getcwd(); + NetSNMP::Cert::dprint("show ($cwd):$type:$stag:$fmt\n", $opts); + NetSNMP::Cert::vprint("$opts->{'tlsdir'}:\n", $opts) unless $brief; + + foreach my $c (NetSNMP::Cert::find_certs($opts, $type, $stag)) { + print "\n$c:\n" unless $brief; + $cmd = "openssl x509 -in '$c' -noout $fmt"; + NetSNMP::Cert::dprint("show: $cmd\n", $opts); + + $output = `$cmd`; chomp $output; + NetSNMP::Cert::vwarn("show-$type failed ($?): $output\n", $opts) if $?; + + $output =~ s/^[^\n=]+=// if $brief; + + print "$output\n"; + print "\n" unless $brief; + } +} + +sub import_file { + my ($file, $suffix, $targ, $rdir, $tag) = @_; + + if (NetSNMP::Cert::is_url($file)) { + if ($NetSNMP::Cert::haveUserAgent) { + + require File::Temp; + import File::Temp qw(tempfile); + + my ($fh, $newfilename) = tempfile(SUFFIX => $suffix); + return if (!$fh || !$newfilename); + my $ua = LWP::UserAgent->new; + my $response = $ua->get($file); + if ($response->is_success) { + print $fh $response->decoded_content(); + } else { + NetSNMP::Cert::vwarn("failed to download a certificate from $file"); + return; + } + $fh->close; + $file = $newfilename; + } else { + NetSNMP::Cert::vwarn("LWP::UserAgent not installed: unable to import certificate"); + return; + } + } + + $file = NetSNMP::Cert::fq_rel_path($file, $rdir); + die("file unreadable: $file\n") unless -r $file; + + + if (! $targ) { + $targ = (NetSNMP::Cert::is_ca_cert($file) ? $NetSNMP::Cert::CADIR : $NetSNMP::Cert::CRTDIR); + } + + $targ .= "/" . $tag . $suffix if ($tag); + ::copy($file, $targ); +} + + +sub import { + my $app = shift; + my $config = $app->{'config'}; + my $opts = $app->{'OPTS'}; + my $carg = shift(@{$opts->{'cmdargs'}}); + my $karg = shift(@{$opts->{'cmdargs'}}); + my $targ; + + if (not defined $carg) { + NetSNMP::Cert::vwarn("import: no certificate supplied\n", $opts); + NetSNMP::Cert::usage(1); + } + + import_file($carg, '.crt', '',, + $opts->{'rdir'}, $opts->{'tag'}); + + return unless defined $karg; + + import_file($karg, '.key', 'private',, + $opts->{'rdir'}, $opts->{'tag'}); +} + + +package NetSNMP::Cert::Config; +use vars qw(@ISA); +@ISA = qw(NetSNMP::Cert::Obj); + +sub new { + my $class = shift; + my $parent = shift; + my $this = $class->SUPER::new('config', $parent); + + bless($this, $class); +} + +sub parse { + my $config = shift; + my $app = $config->{'APP'}; + my $opts = $app->{'OPTS'}; + my $cfgfile = shift; + $cfgfile ||= $opts->{'cfgfile'}; + + return '0 but true' if $config->{'PARSED'}; + return '0 but true' unless defined $cfgfile; + + open( CONFIG, "<$cfgfile" ) + or die "error - could not open configuration file: $cfgfile"; + + while (<CONFIG>) { + next if /^\s*#/ or /^\s*$/; + + if (/^\s*(\w+)(?:\(([\)\(\d\,\.]+)\))?\s*=\s*{/) { + my $obj = $1; + + NetSNMP::Cert::dprint("object: $obj ($2) = {\n", $opts); + die "error - identity: indices not supported: $2" if defined $2; + + # Found an object. + if ( $obj eq 'identity' ) { + my $identity = NetSNMP::Cert::Identity::parse(*CONFIG, $config); + my $id = $identity->{'id'}; + die "error - identity: 'id' not defined" unless defined $id; + die "error - identity: duplicate '$id'" if exists $config->{$obj}{$id}; + $config->{$obj}{$id} = $identity; + } else { + die "error - unrecognized object ($1) at scope (config)"; + } + } elsif (/^\s*(\w+)\s*=?\s*(.*?)\s*$/) { + my $key = $1; + my $val = $2; + + $val = $config->resolve($val) if $val =~ /\$\w+|\&\{.*?\}/; + # Found a symbol. + NetSNMP::Cert::dprint(" $key = $val\n", $opts); + if ($key eq 'subjectAltName' or $key eq 'san') { + push(@{$config->{$key}}, $val); + } elsif ( defined $config->{$key} ) { + die "error - duplicate symbol $key"; + } else { + $config->{$key} = (defined $val ? NetSNMP::Cert::map_bool($val) : 1 ); + } + } elsif (/^\s*env\s*(\w+=\S+)\s*$/) { + # Found an environment variable. + NetSNMP::Cert::dprint("$&\n", $opts); + push(@{$config->{'env'}}, $1); + } else { + die("error in config file [$cfgfile:line $.]"); + } + } + + NetSNMP::Cert::dprint("end parse config\n", $opts); + close(CONFIG); + + # augment with any config directives supplied in opts + foreach my $cfg (@{$opts->{'config'}}) { + NetSNMP::Cert::dprint("augmenting config: $cfg\n", $opts); + + $config->autoSet($1, (defined($2) ? $2 : 1 )) + if $cfg =~ /^\s*(\w+)\s*=?\s*(.*?)\s*$/; + } + $config->autoSet('PARSED', 1); + return $config->{'PARSED'}; +} + +package NetSNMP::Cert::Identity; +use vars qw(@ISA); +@ISA = qw(NetSNMP::Cert::Obj); + +sub new { + my $class = shift; + my $this = shift || $class->SUPER::new('identity', @_); + my $ind = $this->{'INDEX'} || 1; + + $this->autoSet('name', "$this->{type}.$ind") unless exists $this->{'name'}; + + $this->autoSet('LOG','') unless exists $this->{'LOG'}; + $this->autoSet('TTY_LOG','') unless exists $this->{TTY_LOG}; + + bless($this, $class); +} + + +sub parse { + my $FILE = shift; + my $parent = shift; + my $opts = $parent->inherit('OPTS'); + my $identity = new NetSNMP::Cert::Obj('identity', $parent); + + NetSNMP::Cert::dprint("parse identity\n", $opts); + + while (<$FILE>) { + next if /^\s*#/ or /^\s*$/; + + if (/^\s*(\w+)\s*=\s*{/) { + # Found an object. + die "error - can't have nested $1"; + } elsif (/^\s*(\w+)\s*=?\s*(.*?)\s*$/) { + my $key = $1; + my $val = $2; + + # Found a symbol. + NetSNMP::Cert::dprint(" $key = $val\n", $opts); + + $val = $identity->resolve($val) if $val =~ /\$\w+|\&\{.*?\}/; + + if ( $key eq 'subjectAltName' or $key eq 'san') { + push(@{$identity->{$key}}, $val); + } elsif ( defined $identity->{$key} ) { + die "error - duplicate symbol $key"; + } else { + $identity->{$key} = (defined $val ? NetSNMP::Cert::map_bool($val) : 1 ); + } + } elsif (/\s*\}\s*\;/) { + # End of definition. + NetSNMP::Cert::dprint("end parse identity\n", $opts); + return new NetSNMP::Cert::Identity($identity); + } else { + die("error in config file [$opts->{cfgfile}:line $.]"); + } + } + die "error - unexpected end of conf file"; +} + +package main; + +my $app = new NetSNMP::Cert::App(); +$app->init(@ARGV); +$app->run(); + +__END__ +=pod + +=head1 NAME + +net-snmp-cert - Net-SNMP Certificate Management Tool + +=head1 SYNOPSIS + +=over + +=item $ net-snmp-cert genca -I --cn ca-Net-SNMP + +=item $ net-snmp-cert gencert -I -t snmpapp --with-ca ca-Net-SNMP + +=item $ net-snmp-cert gencert -I -t snmpd --cn net-snmp.org + +=item $ net-snmp-cert showcas + +=item $ net-snmp-cert showcerts + +=back + +=head1 DESCRIPTION + +net-snmp-cert creates, signs, installs and displays X.509 +certificates used in the operation of Net-SNMP/(D)TLS. + +=head1 SYNTAX + +=over + +=item net-snmp-cert [--help|-?] + +=item net-snmp-cert [--version|-V] + +=item net-snmp-cert genca [<flags>] [<dn-opts>] [--with-ca <ca>] + +=item net-snmp-cert gencert [<flags>] [<dn-opts>] [--with-ca <ca>] + +=item net-snmp-cert gencsr [<flags>] [<dn-opts>] [--from-crt <crt>] + +=item net-snmp-cert signcsr [<flags>] [--install] --csr <csr> --with-ca <ca> + +=item net-snmp-cert showca [<flags>] [<format-opts>] [<file>|<search-tag>] + +=item net-snmp-cert showcert [<flags>] [<format-opts>] [<file>|<search-tag>] + +=item net-snmp-cert import [<flags>] <file|url> [<key>] + +=back + +=head1 COMMANDS + +=over + +=item genca + +generate a signed CA certificate suitable for signing other +certificates. default: self-signed unless --with-ca <ca> supplied + +=item gencert + +generate a signed certificate suitable for identification, or +validation. default: self-signed unless --with-ca <ca> supplied + +=item gencsr + +generate a certificate signing request. will create a new +key and certificate unless --from-crt <crt> supplied + +=item signcsr + +sign a certificate signing request specified by --csr <csr> +with the CA certificate specified by --with-ca <ca> + +=item import + +import an identity or CA certificate, optionally import <key> +if an URL is passed, will attempt to import certificate from site + +=item showca, showcert + +show CA or identity certificate(s). may pass fully qualified +file or directory name, or a search-tag which prefix matches +installed CA or identity certificate file name(s) +see FORMAT OPTIONS to specify output format + + +=back + +=head1 FLAGS + +=over + +=item -?, --help -- show this text and exit + +=item -V, --version -- show version string and exit + +=item -D, --debug -- raise debug level (-D -D for higher level) + +=item -F, --force -- force overwrite of existing output files + +=item -I, --nointeractive -- non-interactive run (default interactive) + +=item -Q, --noverbose -- non-verbose output (default verbose) + +=item -C, --cfgdir <dir> -- configuration dir (see man(5) snmp_config) + +=item -T, --tlsdir <dir> -- root for cert storage (default <cfgdir>/tls) + +=item -f, --cfgfile <file> -- config (default <cfgdir>/net-snmp-cert.conf) + +=item -i, --identity <id> -- identity to use from config + +=item -t, --tag <tag> -- application tag (default 'snmp') + +=item --<cfg-param>[=<val>] -- additional config params + +=back + +=head1 CERTIFICATE OPTIONS (<cert-opts>) + +=over + +=item -a, --with-ca <ca> -- CA certificate used to sign request + +=item -A, --ca <ca> -- explicit output CA certificate + +=item -r, --csr <csr> -- certificate signing request + +=item -x, --from-crt <crt> -- input certificate for current operation + +=item -X, --crt <crt> -- explicit output certificate + +=item -y, --install -- install result in local repository + +=back + +=head1 DISTINGUISHED NAME OPTIONS (<dn-opts>) + +=over + +=item -e, --email <email> -- email name + +=item -h, --host <host> -- DNS host name, or IP address + +=item -d, --days <days> -- number of days certificate is valid + +=item -n, --cn <cn> -- common name (CN) + +=item -o, --org <org> -- organiztion name + +=item -u, --unit <unit> -- organiztion unit name + +=item -c, --country <country> -- country code (e.g., US) + +=item -p, --province <province> -- province name (synomynous w/ state) + +=item -p, --state <state> -- state name (synomynous w/ province) + +=item -l, --locality <locality> -- locality name (e.g, town) + +=item -s, --san <san> -- subjectAltName, repeat for each <san> + +=over 2 + +=item <san> value format (<FMT>:<VAL>): + +=over 3 + +=item dirName:/usr/share/snmp/tls + +=item DNS:net-snmp.org + +=item email:admin@net-snmp.org + +=item IP:192.168.1.1 + +=item RID:1.1.3.6 + +=item URI:http://net-snmp.org + +=back + +=back + +=back + +=head1 FORMAT OPTIONS (<format-opts>) + +=over + +=item --brief -- minimized output (values only where applicable) + +=item --text -- full text description + +=item --subject -- subject description + +=item --subject_hash -- hash of subject for indexing + +=item --issuer -- issuer description + +=item --issuer_hash -- hash of issuer for indexing + +=item --fingerprint -- SHA1 digest of DER + +=item --serial -- serial number + +=item --modulus -- modulus of the public key + +=item --dates -- start and end dates + +=item --purpose -- displays allowed uses + +=item --C -- C code description + +=back + +=head1 OPERATIONAL NOTES + + +=head2 Interactive Mode + +The application operates in interactive mode by default. In this mode +basic operations of offered and required input is queried through the +command line. + +Typical <tab> completion is provided when possible and field specific +help may be obtained by entering '?'. + +To turn off interactive mode, supply '--nointeractive' or '-I' on the +initial command line. Equivalantly, 'interactive = false' maybe placed +in the configuration file (see below). + +=head2 Configuration + +A configuration file may be supplied on the command line or found in a +default location (<snmpconfpath>/net-snmp-cert.conf). This file may +contain configuration parameters equivalent to those supplied on the +command line and effectively provides default values for these +values. If a command line parameter is supplied it will override the +value in the config file. If neither is present then an application +value will be used. + +=head2 Certificate Naming + +Filenames of created certificates, as stored in the configuration +directory, are chosen in the following manner. If and application tag +is supplied, it will be used as the basename for the certificate and +key generated. Otherwise, for CA certificates, the basename will be +derived from the Common Name. For non-CA certificates the application +tag defaults to 'snmp' which will then be used as the basename of the +certificate and key. + +=head1 EXAMPLES + +=over + +=item net-snmp-cert genca --cn ca-net-snmp.org --days 1000 + +=item net-snmp-cert genca -f .snmp/net-snmp-cert.conf -i nocadm + +=item net-snmp-cert gencert -t snmpd --cn host.net-snmp.org + +=item net-snmp-cert gencsr -t snmpapp + +=item net-snmp-cert signcsr --csr snmpapp --with-ca ca-net-snmp.org + +=item net-snmp-cert showcerts --subject --issuer --dates snmpapp + +=item net-snmp-cert showcas --fingerprint ca-net-snmp.org --brief + +=item net-snmp-cert import ca-third-party.crt + +=item net-snmp-cert import signed-request.crt signed-request.key + +=back + +=head1 COPYRIGHT + +Copyright (c) 2010 Cobham Analytic Solutions - All rights reserved. +Copyright (c) 2010 G. S. Marzot - All rights reserved. + +=head1 AUTHOR + +G. S. Marzot (marz@users.sourceforge.net) + +=cut + diff --git a/local/net-snmp-cert.conf b/local/net-snmp-cert.conf new file mode 100644 index 0000000..7def1cc --- /dev/null +++ b/local/net-snmp-cert.conf @@ -0,0 +1,72 @@ +# +# Net-SNMP Certificate Generation and Management Tool Configuration +# + +# default mode to non-interactive +# interactive = false + +# location of 'tls' directory relative to configuration dir +# tlsDir = ./tls + +# encryptCA = false - XXX not-implemented +# encryptCrt = false - XXX not-implemented + +# default valid lifetime duration for CA certificates +# caDays = 1825 + +# default valid lifetime duration for certificates +# crtDays = 365 + +# default key types generated +# keyType = rsa + +# default key size generated +# keySize = 2048 + +# default type of message digest used +# msgDigest = sha1 + +# to set individual defaults, a specific identity may be indicated +# on the net-snmp-cert command line: '--identity <id>' or '-i <id>' +# values defined at the global/file level will be used unless +# overriden by values supplied in the specified identity. + +identity = { + id = nocadm + host = net-snmp.org + cn = Client-identity + email = admin@net-snmp.org + org = Net-SNMP Developers + orgUnit = SNMP-DTLS + country = US + state = MA + locality = Boston + + # 10 years + caDays = 3654 + # 2 years + crtDays = 730 + + subjectAltName = email:client@net-snmp.org + subjectAltName = URI:http://net-snmp.org +}; + +identity = { + id = CA-identity + host = net-snmp.org + cn = CA-identity + email = ca-admin@net-snmp.org + org = Net-SNMP Developers + orgUnit = SNMP-DTLS + country = US + state = MA + locality = Boston + + # 10 years + caDays = 1000 + # 2 years + crtDays = 500 + + subjectAltName = DNS:test.net-snmp.org +}; + diff --git a/local/pass_persisttest b/local/pass_persisttest new file mode 100755 index 0000000..f4045cc --- /dev/null +++ b/local/pass_persisttest @@ -0,0 +1,98 @@ +#!/usr/bin/perl + +# Persistent perl script to respond to pass-through smnp requests + +# put the following in your snmpd.conf file to call this script: +# +# Unix systems and Cygwin: +# pass_persist .1.3.6.1.4.1.8072.2.255 /path/to/pass_persisttest +# Windows systems except Cygwin: +# pass_persist .1.3.6.1.4.1.8072.2.255 perl /path/to/pass_persisttest + +# Forces a buffer flush after every print +$|=1; + +# Save my PID, to help kill this instance. +$PIDFILE=$ENV{'PASS_PERSIST_PIDFILE'} || "/tmp/pass_persist.pid"; +open(PIDFILE, ">$PIDFILE"); +print PIDFILE "$$\n"; +close(PIDFILE); + +use strict; + +my $counter = 0; +my $place = ".1.3.6.1.4.1.8072.2.255"; + +while (<>){ + if (m!^PING!){ + print "PONG\n"; + next; + } + + my $cmd = $_; + my $req = <>; + my $ret; + chomp($cmd); + chomp($req); + + if ( $cmd eq "getnext" ) { + if (($req eq "$place") || + ($req eq "$place.0") || + ($req =~ m/$place\.0\..*/) || + ($req eq "$place.1")) { $ret = "$place.1.0";} # netSnmpPassString.0 + elsif (($req =~ m/$place\.1\..*/) || + ($req eq "$place.2") || + ($req eq "$place.2.0") || + ($req =~ m/$place\.2\.0\..*/) || + ($req eq "$place.2.1") || + ($req eq "$place.2.1.0") || + ($req =~ m/$place\.2\.1\.0\..*/) || + ($req eq "$place.2.1.1") || + ($req =~ m/$place\.2\.1\.1\..*/) || + ($req eq "$place.2.1.2") || + ($req eq "$place.2.1.2.0")) { $ret = "$place.2.1.2.1";} # netSnmpPassInteger.1 + elsif (($req =~ m/$place\.2\.1\.2\..*/) || + ($req eq "$place.2.1.3") || + ($req eq "$place.2.1.3.0")) { $ret = "$place.2.1.3.1";} # netSnmpPassOID.1 + elsif (($req =~ m/$place\.2\..*/) || + ($req eq "$place.3")) { $ret = "$place.3.0";} # netSnmpPassTimeTicks.0 + elsif (($req =~ m/$place\.3\..*/) || + ($req eq "$place.4")) { $ret = "$place.4.0";} # netSnmpPassIpAddress.0 + elsif (($req =~ m/$place\.4\..*/) || + ($req eq "$place.5")) { $ret = "$place.5.0";} # netSnmpPassCounter.0 + elsif (($req =~ m/$place\.5\..*/) || + ($req eq "$place.6")) { $ret = "$place.6.0";} # netSnmpPassGauge.0 + else { + print "NONE\n"; + next; + } + } else { + if ($req eq $place) { + print "NONE\n"; + next; + } else { + $ret = $req; + } + } + + print "$ret\n"; + + if ($ret eq "$place.1.0") { + print "string\nLife, the Universe, and Everything\n"; + } elsif ($ret eq "$place.2.1.2.1") { + print "integer\n42\n"; + } elsif ($ret eq "$place.2.1.3.1") { + print "objectid\n$place.99\n"; + } elsif ($ret eq "$place.3.0") { + print "timeticks\n363136200\n"; + } elsif ($ret eq "$place.4.0") { + print "ipaddress\n127.0.0.1\n"; + } elsif ($ret eq "$place.5.0") { + $counter++; + print "counter\n$counter\n"; + } elsif ($ret eq "$place.6.0") { + print "gauge\n42\n"; + } else { + print "string\nack... $ret $req\n"; + } +} diff --git a/local/passtest b/local/passtest new file mode 100755 index 0000000..40209c9 --- /dev/null +++ b/local/passtest @@ -0,0 +1,82 @@ +#!/bin/sh -f + +PLACE=".1.3.6.1.4.1.8072.2.255" # NET-SNMP-PASS-MIB::netSnmpPassExamples +REQ="$2" # Requested OID + +# +# Process SET requests by simply logging the assigned value +# Note that such "assignments" are not persistent, +# nor is the syntax or requested value validated +# +if [ "$1" = "-s" ]; then + echo $* >> /tmp/passtest.log + exit 0 +fi + +# +# GETNEXT requests - determine next valid instance +# +if [ "$1" = "-n" ]; then + case "$REQ" in + $PLACE| \ + $PLACE.0| \ + $PLACE.0.*| \ + $PLACE.1) RET=$PLACE.1.0 ;; # netSnmpPassString.0 + + $PLACE.1.*| \ + $PLACE.2| \ + $PLACE.2.0| \ + $PLACE.2.0.*| \ + $PLACE.2.1| \ + $PLACE.2.1.0| \ + $PLACE.2.1.0.*| \ + $PLACE.2.1.1| \ + $PLACE.2.1.1.*| \ + $PLACE.2.1.2| \ + $PLACE.2.1.2.0) RET=$PLACE.2.1.2.1 ;; # netSnmpPassInteger.1 + + $PLACE.2.1.2.*| \ + $PLACE.2.1.3| \ + $PLACE.2.1.3.0) RET=$PLACE.2.1.3.1 ;; # netSnmpPassOID.1 + + $PLACE.2.*| \ + $PLACE.3) RET=$PLACE.3.0 ;; # netSnmpPassTimeTicks.0 + $PLACE.3.*| \ + $PLACE.4) RET=$PLACE.4.0 ;; # netSnmpPassIpAddress.0 + $PLACE.4.*| \ + $PLACE.5) RET=$PLACE.5.0 ;; # netSnmpPassCounter.0 + $PLACE.5.*| \ + $PLACE.6) RET=$PLACE.6.0 ;; # netSnmpPassGauge.0 + + *) exit 0 ;; + esac +else +# +# GET requests - check for valid instance +# + case "$REQ" in + $PLACE.1.0| \ + $PLACE.2.1.2.1| \ + $PLACE.2.1.3.1| \ + $PLACE.3.0| \ + $PLACE.4.0| \ + $PLACE.5.0| \ + $PLACE.6.0) RET=$REQ ;; + *) exit 0 ;; + esac +fi + +# +# "Process" GET* requests - return hard-coded value +# +echo "$RET" +case "$RET" in + $PLACE.1.0) echo "string"; echo "Life, the Universe, and Everything"; exit 0 ;; + $PLACE.2.1.2.1) echo "integer"; echo "42"; exit 0 ;; + $PLACE.2.1.3.1) echo "objectid"; echo "$PLACE.99"; exit 0 ;; + $PLACE.3.0) echo "timeticks"; echo "363136200"; exit 0 ;; + $PLACE.4.0) echo "ipaddress"; echo "127.0.0.1"; exit 0 ;; + $PLACE.5.0) echo "counter"; echo "42"; exit 0 ;; + $PLACE.6.0) echo "gauge"; echo "42"; exit 0 ;; + *) echo "string"; echo "ack... $RET $REQ"; exit 0 ;; # Should not happen +esac diff --git a/local/passtest.pl b/local/passtest.pl new file mode 100755 index 0000000..c87fe18 --- /dev/null +++ b/local/passtest.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl +$place = ".1.3.6.1.4.1.8072.2.255"; # NET-SNMP-PASS-MIB::netSnmpPassExamples +$req = $ARGV[1]; # Requested OID + +# +# Process SET requests by simply logging the assigned value +# Note that such "assignments" are not persistent, +# nor is the syntax or requested value validated +# +if ($ARGV[0] eq "-s") { + open LOG,">>/tmp/passtest.log"; + print LOG "@ARGV\n"; + close LOG; + exit 0; +} + +# +# GETNEXT requests - determine next valid instance +# +if ($ARGV[0] eq "-n") { + if (($req eq "$place") || + ($req eq "$place.0") || + ($req =~ m/$place\.0\..*/) || + ($req eq "$place.1")) { $ret = "$place.1.0";} # netSnmpPassString.0 + elsif (($req =~ m/$place\.1\..*/) || + ($req eq "$place.2") || + ($req eq "$place.2.0") || + ($req =~ m/$place\.2\.0\..*/) || + ($req eq "$place.2.1") || + ($req eq "$place.2.1.0") || + ($req =~ m/$place\.2\.1\.0\..*/) || + ($req eq "$place.2.1.1") || + ($req =~ m/$place\.2\.1\.1\..*/) || + ($req eq "$place.2.1.2") || + ($req eq "$place.2.1.2.0")) { $ret = "$place.2.1.2.1";} # netSnmpPassInteger.1 + elsif (($req =~ m/$place\.2\.1\.2\..*/) || + ($req eq "$place.2.1.3") || + ($req eq "$place.2.1.3.0")) { $ret = "$place.2.1.3.1";} # netSnmpPassOID.1 + elsif (($req =~ m/$place\.2\..*/) || + ($req eq "$place.3")) { $ret = "$place.3.0";} # netSnmpPassTimeTicks.0 + elsif (($req =~ m/$place\.3\..*/) || + ($req eq "$place.4")) { $ret = "$place.4.0";} # netSnmpPassIpAddress.0 + elsif (($req =~ m/$place\.4\..*/) || + ($req eq "$place.5")) { $ret = "$place.5.0";} # netSnmpPassCounter.0 + elsif (($req =~ m/$place\.5\..*/) || + ($req eq "$place.6")) { $ret = "$place.6.0";} # netSnmpPassGauge.0 + else {exit 0;} +} +else { +# +# GET requests - check for valid instance +# + if ( ($req eq "$place.1.0") || + ($req eq "$place.2.1.2.1") || + ($req eq "$place.2.1.3.1") || + ($req eq "$place.3.0") || + ($req eq "$place.3.0") || + ($req eq "$place.3.0") || + ($req eq "$place.3.0")) { $ret = $req; } + else { exit 0;} +} + +# +# "Process" GET* requests - return hard-coded value +# +print "$ret\n"; + if ($ret eq "$place.1.0") { print "string\nLife, the Universe, and Everything\n"; exit 0;} +elsif ($ret eq "$place.2.1.2.1") { print "integer\n42\n"; exit 0;} +elsif ($ret eq "$place.2.1.3.1") { print "objectid\n$place.99\n"; exit 0;} +elsif ($ret eq "$place.3.0") { print "timeticks\n363136200\n"; exit 0;} +elsif ($ret eq "$place.4.0") { print "ipaddress\n127.0.0.1\n"; exit 0;} +elsif ($ret eq "$place.5.0") { print "counter\n42\n"; exit 0;} +elsif ($ret eq "$place.6.0") { print "gauge\n42\n"; exit 0;} +else { print "string\nack... $ret $req\n"; exit 0;} # Should not happen diff --git a/local/snmp-bridge-mib b/local/snmp-bridge-mib new file mode 100644 index 0000000..880ea6b --- /dev/null +++ b/local/snmp-bridge-mib @@ -0,0 +1,1170 @@ +#!/usr/bin/perl +# +# Copyright (c) IBM Corp. 2009, 2010, All Rights Reserved +# +# Author(s): Vasileios Pappas <vpappas@us.ibm.com> +# Jens Osterkamp <jens@linux.vnet.ibm.com> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +use NetSNMP::OID (':all'); +use NetSNMP::ASN (':all'); +use NetSNMP::agent (':all'); + +sub STP_PROP_VALUE() { 0x01} +sub STP_PROP_FILE() { 0x02} +sub STP_PROP_DEC() { 0x04} +sub STP_PROP_HEX() { 0x08} + +sub FDB_STATUS_OTHER { 1 } +sub FDB_STATUS_INVALID { 2 } +sub FDB_STATUS_LEARNED { 3 } +sub FDB_STATUS_SELF { 4 } +sub FDB_STATUS_MGMT { 5 } + +my %oid_value; +my %oid_type; +my %oid_next; +my (%indexes, %interfaces, %macs, %ages, %locals, %vlans, %tagged); +my $oid; + +$subagent=0; +my $netdir="/sys/class/net/"; +my $targetbridge; + +$cache_timout=60; +$last_populated=0; + +my $regoid = ".1.3.6.1.2.1.17"; +# are we running embedded ? If not, register as subagent +if (!$agent) { + $agent = new NetSNMP::agent('Name' => 'dot1qbridge', + 'AgentX' => 1); + $subagent = 1; + if ( $#ARGV != 0) { + usage($0); + exit(); + } + $targetbridge = $ARGV[0]; +} else { + if (!defined($bridge)) { + usage($0); + exit(); + } + $targetbridge = $bridge; +} + +$agent->register("dot1qbridge", ".1.3.6.1.2.1.17", \&request_handler) or die "registration of handler failed !\n"; + +if ($subagent) { + # register handler for graceful shutdown + $SIG{'INT'} = \&shutdown; + $SIG{'QUIT'} = \&shutdown; + $running = 1; + + while($running) { + $agent->agent_check_and_process(1); + } + + $agent->shutdown(); +} + +sub usage { + my $name = shift; + if ($subagent) { + print $name."\n\n"; + print "usage: \t$name <bridge> start snmp bridge module". + " for bridge <bridge>\n\n"; + print "arguments: bridge name of the Linux bridge". + " which you want to provide info via snmp for.\n\n"; + } else { + print 'usage in snmpd.conf: perl $bridge="br0"; perl do <path to $name>\n'; + } + print "number of arguments given $#ARGV\n\n"; +} + +sub request_handler { + my ($handler, $registration_info, $request_info, $requests)=@_; + my $request; + + populate_mib($targetbridge); + + for($request = $requests; $request; $request = $request->next()) { + if($request_info->getMode() == MODE_GET) { + mode_get($request); + } elsif($request_info->getMode() == MODE_GETNEXT) { + mode_get_next($request); + } else { + print STDERR "mode not implemented.\n"; + } + } +} + +sub mode_get{ + my ($request)=@_; + my $oid=$request->getOID(); + + $SNMP::use_numeric = 1; + my $noid=SNMP::translateObj($oid); + + reply($request, $noid) if (exists $oid_value{$noid}); +} + +sub find_next{ + my ($noid) = @_; + my $nextoid = $oid_next{$noid}; + if(!defined($nextoid) || !defined($oid_value{$nextoid})) { + # find the lowest OID whis is higher than $noid + $prev = ".1.3.6.1.2.1.18"; + $prev_oid = new NetSNMP::OID($prev); + $noid_oid = new NetSNMP::OID($noid); + #print "looking for next of $noid\n"; + for my $candidate (keys %oid_value) { + #print "evaluating $candidate\n"; + $candidate_oid = new NetSNMP::OID($candidate); + if ($noid_oid < $candidate_oid && $prev_oid > $candidate_oid) { + #print "found candidate $candidate\n"; + $prev = $candidate; + $prev_oid = $candidate_oid; + } + } + if ($prev eq ".1.3.6.1.2.1.18") { + return; # no OID higher than $noid found + } + $nextoid = $prev; + } + return $nextoid; +} + +sub mode_get_next{ + my ($request)=@_; + my $oid=$request->getOID(); + $SNMP::use_numeric = 1; + my $noid=SNMP::translateObj($oid); + my $nextoid = find_next($noid); + #print "found $nextoid\n"; + if(defined($nextoid)) { + my $type = $oid_type{$nextoid}; + my $value = $oid_value{$nextoid}; + if (defined($type) and defined($value)) { + reply($request, $nextoid); + } + } +} + +sub reply{ + my ($request, $oid)=@_; + + my $type=$oid_type{$oid}; + my $value=$oid_value{$oid}; + + $request->setOID(new NetSNMP::OID($oid)); + $request->setValue($type, $value); +} + +# Populated MIB OID +# +## dot1dBasePort: 1.3.6.1.2.1.17.1.4.1.1.<dot1dBasePort> +# INTEGER32 +# +# dot1dBasePortIfIndex: 1.3.6.1.2.1.17.1.4.1.2.<dot1dBasePort> +# INTEGER32 +# +# dot1dStp*: 1.3.6.1.2.1.17.2.<1-14> +# INTEGER, OCTET STRING +# +# dot1dTpFdbAddress: 1.3.6.1.2.1.17.4.3.1.1.<dot1dTpFdbAddress> +# OCTET STRING (SIZE (6)) +# +# dot1dTpFdbPort: 1.3.6.1.2.1.17.4.3.1.2.<dot1dTpFdbAddress> +# INTEGER32 +# +# dot1dTpFdbStatus: 1.3.6.1.2.1.17.4.3.1.3.<dot1dTpFdbAddress> +# INTEGER +# 1 : other +# 2 : invalid +# 3 : learned +# 4 : self +# 5 : mgmt +# + +sub populate_mib { + my $now; + my $bridge = shift; + my $seconds_passed; + $ports=0; + $oid=""; + + $now=time(); + $seconds_passed=$now-$last_populated; + return if($seconds_passed <= $cache_timout); + $last_populated=$now; + + %oid_value=(); + %oid_type=(); + %oid_next=(); + %indexes=(); + %interfaces=(); + %macs=(); + %ages=(); + %locals=(); + %vlans=(); + %tagged=(); + + # first populated oid + $oid_next{".1.3.6.1.2.1.17"}=".1.3.6.1.2.1.17.1.1"; + + createbaseinfo($bridge); + + readindexes($bridge); + readforwards($bridge); + readvlans($bridge); + + createports($bridge); + + stpproperties($bridge); + + stpportproperties($bridge); + + dot1dTpproperties(); + + my $prevoid = $oid; + my $curroid = createmacs($bridge); + $oid_next{$prevoid} = $curroid; + + portproperties($bridge); + + + dot1qbase($bridge); + + dot1qfdb($bridge); + + dot1qcurrentvlans($bridge); + + return 0; +} + +sub findbridges() +{ + my @bridges; + + opendir(DIR, $netdir) or die "unable to open $netdir !\n"; + + while(my $br=readdir(DIR)){ + next if $br eq "."; + next if $br eq ".."; + next unless -d $netdir.$br."/bridge"; + push @bridges, $br; + } + + close(DIR); + + return @bridges; +} + +sub createbaseinfo() +{ + my $bridge = shift; + + $oid=".1.3.6.1.2.1.17.1.1"; + $oid_value{$oid}=mac2hex(readfile($netdir.$bridge."/address", 0)); + $oid_type{$oid}=ASN_OCTET_STR; + $oid_next{$oid}=".1.3.6.1.2.1.17.1.2"; + + opendir(DIR, $netdir.$bridge."/brif/") or die "Could not open ".$netdir.$bridge."brif !\n"; + + foreach $entry (readdir(DIR)) { + next if $entry eq "."; + next if $entry eq ".."; + # only count non-vlan interfaces + next if $entry =~ /\.[0-9]*/; + $ports++; + } + + closedir(DIR); + + $oid=".1.3.6.1.2.1.17.1.2"; + $oid_value{$oid}=$ports; + $oid_type{$oid}=ASN_INTEGER; + $oid_next{$oid}=".1.3.6.1.2.1.17.1.3"; + + $oid=".1.3.6.1.2.1.17.1.3"; + $oid_value{$oid}="2"; # transparent only + $oid_type{$oid}=ASN_INTEGER; + $oid_next{$oid}=".1.3.6.1.2.1.17.1.4.1.1.1"; + +} + +sub createmacs() +{ + my $bridge = shift; + my $start_oid = $oid = ".1.3.6.1.2.1.17.4.3.1"; + + foreach my $mac (sort {$a cmp $b} keys %macs) { + my $mac_oid=mac2oid($mac); + unless(defined($first_mac_oid)){ + $first_mac_oid=$mac_oid; + $oid_next{$oid.".1"}=$oid.".1".$mac_oid; + $oid_next{$oid.".2"}=$oid.".2".$mac_oid; + $oid_next{$oid.".3"}=$oid.".3".$mac_oid; + } + my $port=$macs{$mac}{$bridge}; + my $baseport=$baseports{$bridge}{$port}; + my $status=$locals{$mac}{$bridge}; + my $age=$ages{$mac}{$bridge}; + + $oid_value{$oid.".1".$mac_oid}=mac2hex($mac); + $oid_type{$oid.".1".$mac_oid}=ASN_OCTET_STR; + if(defined($prv_mac_oid)){ + $oid_next{$oid.".1".$prv_mac_oid}= + $oid.".1".$mac_oid; + } + + $oid_value{$oid.".2".$mac_oid}=$baseport; + $oid_type{$oid.".2".$mac_oid}=ASN_INTEGER; + if(defined($prv_mac_oid)){ + $oid_next{$oid.".2".$prv_mac_oid}= + $oid.".2".$mac_oid; + } + + $oid_value{$oid.".3".$mac_oid}=$status; + $oid_type{$oid.".3".$mac_oid}=ASN_INTEGER; + if(defined($prv_mac_oid)){ + $oid_next{$oid.".3".$prv_mac_oid}= + $oid.".3".$mac_oid; + } + + $prv_mac_oid=$mac_oid; + } + + if ($prv_mac_oid and $first_mac_oid) { + $oid_next{$oid.".1".$prv_mac_oid}=$oid.".2".$first_mac_oid; + $oid_next{$oid.".2".$prv_mac_oid}=$oid.".3".$first_mac_oid; + $oid_next{$oid.".3".$prv_mac_oid}=".1.3.6.1.2.1.17.4.4.1.1.1"; + } + + undef $prv_mac_oid; + undef $first_mac_oid; + + return $start_oid.".1".$first_mac_oid; +} + +# TODO: is this sequence complete ? +sub createports() +{ + my $bridge = shift; + my ($baseport, $prev_baseport, $first_baseport); + $baseport=1; + my $oid= '.1.3.6.1.2.1.17.1.4.1'; + + foreach my $port (keys %{$interfaces{$bridge}}) { + unless(defined($first_baseport)){ + $first_baseport=$baseport; + $oid_next{$oid.".1"}=$oid.".1.".$baseport; + $oid_next{$oid.".2"}=$oid.".2.".$baseport; + } + my $index=$indexes{$bridge}{$port}; + $baseports{$bridge}{$port}=$baseport; + + $oid_value{$oid.".1.".$baseport}=$baseport; + $oid_type{$oid.".1.".$baseport}=ASN_INTEGER; + if(defined($prv_baseport)){ + $oid_next{$oid.".1.".$prv_baseport}= + $oid.".1.".$baseport; + } + + $oid_value{$oid.".2.".$baseport}=$index; + $oid_type{$oid.".2.".$baseport}=ASN_INTEGER; + if(defined($prv_baseport)){ + $oid_next{$oid.".2.".$prv_baseport}= + $oid.".2.".$baseport; + } + + $prv_baseport=$baseport; + $baseport++; + } + + if ( $prv_baseport and $first_baseport ) { + $oid_next{$oid.".1.".$prv_baseport}=$oid.".2.".$first_baseport; + $oid_next{$oid.".2.".$prv_baseport}=".1.3.6.1.2.1.17.2.1"; + } + + undef $prv_baseport; + undef $first_baseport; + +} + +sub stpproperties() +{ + my $bridge = shift; + my $dir = $netdir.$bridge."/bridge/"; + + @stpprops = ( { oid => ".1.3.6.1.2.1.17.2.1", + flags => STP_PROP_VALUE, + value => "3", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.2" }, + { oid => ".1.3.6.1.2.1.17.2.2", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."priority", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.3" }, + { oid => ".1.3.6.1.2.1.17.2.3", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."topology_change_timer", + type => ASN_TIMETICKS, + nextoid => ".1.3.6.1.2.1.17.2.4" }, + { oid => ".1.3.6.1.2.1.17.2.4", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."topology_change", + type => ASN_COUNTER, + nextoid => ".1.3.6.1.2.1.17.2.5" }, + { oid => ".1.3.6.1.2.1.17.2.5", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."root_id", + type => ASN_OCTET_STR, + nextoid => ".1.3.6.1.2.1.17.2.6" }, + { oid => ".1.3.6.1.2.1.17.2.6", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."root_path_cost", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.7" }, + { oid => ".1.3.6.1.2.1.17.2.7", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."root_port", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.8" }, + { oid => ".1.3.6.1.2.1.17.2.8", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."max_age", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.9" }, + { oid => ".1.3.6.1.2.1.17.2.9", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."hello_time", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.11" }, + # TODO ...17.2.10 + { oid => ".1.3.6.1.2.1.17.2.11", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."forward_delay", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.12" }, + { oid => ".1.3.6.1.2.1.17.2.12", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."max_age", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.13" }, + { oid => ".1.3.6.1.2.1.17.2.13", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."hello_time", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.14" }, + { oid => ".1.3.6.1.2.1.17.2.14", + flags => STP_PROP_FILE | STP_PROP_DEC, + value => $dir."forward_delay", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.2.15.1.1.1" }, + ); + + for ($i=0; $i <= $#stpprops; $i++) { + my %props = %{$stpprops[$i]}; + $oid = $props{'oid'}; + if ( $props{'flags'} & STP_PROP_VALUE,) { + $oid_value{$oid} = $props{'value'}; + } + if ( $props{'flags'} & STP_PROP_FILE,) { + $oid_value{$oid} = + readfile($props{'value'}, + $props{'flags'}); + } + $oid_type{$oid} = $props{'type'}; + $oid_next{$oid} = $props{'nextoid'}; + } +} + +sub stpportproperties +{ + my $bridge = shift; + my $brifdir = $netdir.$bridge."/brif/"; + my ($baseport, $first_baseport, $prev_baseport); + + $oid='.1.3.6.1.2.1.17.2.15.1'; + + foreach my $port (keys %{$interfaces{$bridge}}) { + $baseport = $baseports{$bridge}{$port}; + + unless(defined($first_baseport)){ + $first_baseport=$baseport; + $oid_next{$oid.".1"}=$oid.".1.".$baseport; + $oid_next{$oid.".2"}=$oid.".2.".$baseport; + } + + my $interface = $interfaces{$bridge}{$port}; + my $ifdir = $brifdir.$interface; + + # dot1dStpPort + $oid_value{$oid.".1.".$baseport}=$baseport; + $oid_type{$oid.".1.".$baseport}=ASN_INTEGER; + if(defined($prev_baseport)){ + $oid_next{$oid.".1.".$prev_baseport}= + $oid.".1.".$baseport; + } + + # dot1dStpPortPriority + $oid_value{$oid.".2.".$baseport}=readfile($ifdir."/priority", 0); + $oid_type{$oid.".2.".$baseport}=ASN_INTEGER; + if(defined($prev_baseport)){ + $oid_next{$oid.".2.".$prev_baseport}= + $oid.".2.".$baseport; + } + + # dot1dStpPortState + my @translation = (1, 3, 4, 5, 2); + my $state = readfile($ifdir."/state", 0); + $oid_value{$oid.".3.".$baseport}=$translation[$state]; + $oid_type{$oid.".3.".$baseport}=ASN_INTEGER; + if(defined($prev_baseport)){ + $oid_next{$oid.".3.".$prev_baseport}= + $oid.".3.".$baseport; + } + + # dot1dStpPortEnable + @translation = (2, 1, 1, 1, 1); + $oid_value{$oid.".4.".$baseport}=$translation[$state]; + $oid_type{$oid.".4.".$baseport}=ASN_INTEGER; + if(defined($prev_baseport)){ + $oid_next{$oid.".4.".$prev_baseport}= + $oid.".4.".$baseport; + } + + # dot1dStpPortPathCost + $oid_value{$oid.".5.".$baseport}=readfile($ifdir."/path_cost", 0); + $oid_type{$oid.".5.".$baseport}=ASN_INTEGER; + if(defined($prev_baseport)){ + $oid_next{$oid.".5.".$prev_baseport}= + $oid.".5.".$baseport; + } + + # dot1dStpPortDesignatedRoot + $oid_value{$oid.".6.".$baseport}=readfile($ifdir."/designated_root", 0); + $oid_type{$oid.".6.".$baseport}=ASN_OCTET_STR; + if(defined($prev_baseport)){ + $oid_next{$oid.".6.".$prev_baseport}= + $oid.".6.".$baseport; + } + + # dot1dStpPortDesignatedCost + $oid_value{$oid.".7.".$baseport}=readfile($ifdir."/designated_cost", 0); + $oid_type{$oid.".7.".$baseport}=ASN_INTEGER; + if(defined($prev_baseport)){ + $oid_next{$oid.".7.".$prev_baseport}= + $oid.".7.".$baseport; + } + + # dot1dStpPortDesignatedBridge + $oid_value{$oid.".8.".$baseport}=readfile($ifdir."/designated_bridge", 0); + $oid_type{$oid.".8.".$baseport}=ASN_OCTET_STR; + if(defined($prev_baseport)){ + $oid_next{$oid.".8.".$prev_baseport}= + $oid.".8.".$baseport; + } + + # dot1dStpPortDesignatedPort + $oid_value{$oid.".9.".$baseport}=readfile($ifdir."/designated_port", 0); + $oid_type{$oid.".9.".$baseport}=ASN_OCTET_STR; + if(defined($prev_baseport)){ + $oid_next{$oid.".9.".$prev_baseport}= + $oid.".9.".$baseport; + } + + # dot1dStpPortForwardTransitions (no value in bridge module) + + # dot1dStpPortPathCost32 + $oid_value{$oid.".11.".$baseport}=readfile($ifdir."/path_cost", 0); + $oid_type{$oid.".11.".$baseport}=ASN_INTEGER; + if(defined($prev_baseport)){ + $oid_next{$oid.".11.".$prev_baseport}= + $oid.".11.".$baseport; + } + + + $prev_baseport=$baseport; + } + + if ( $prev_baseport and $first_baseport ) { + $oid_next{$oid.".1.".$prev_baseport}=$oid.".2.".$first_baseport; + $oid_next{$oid.".2.".$prev_baseport}=$oid.".3.".$first_baseport; + $oid_next{$oid.".3.".$prev_baseport}=$oid.".4.".$first_baseport; + $oid_next{$oid.".4.".$prev_baseport}=$oid.".5.".$first_baseport; + $oid_next{$oid.".5.".$prev_baseport}=$oid.".6.".$first_baseport; + $oid_next{$oid.".6.".$prev_baseport}=$oid.".7.".$first_baseport; + $oid_next{$oid.".7.".$prev_baseport}=$oid.".8.".$first_baseport; + $oid_next{$oid.".8.".$prev_baseport}=$oid.".9.".$first_baseport; + $oid_next{$oid.".9.".$prev_baseport}=$oid.".11.".$first_baseport; + $oid_next{$oid.".11.".$prev_baseport}=".1.3.6.1.2.1.17.4.1"; + } + + $oid = $oid.".11.".$prev_baseport; + + undef $prev_baseport; + undef $first_baseport; + +} + +sub dot1dTpproperties() +{ + @stpprops = ( { oid => ".1.3.6.1.2.1.17.4.1", + flags => STP_PROP_VALUE, + value => "0", + type => ASN_COUNTER, + nextoid => ".1.3.6.1.2.1.17.4.2" }, + { oid => ".1.3.6.1.2.1.17.4.2", + flags => STP_PROP_VALUE, + value => "300", + type => ASN_INTEGER, + nextoid => ".1.3.6.1.2.1.17.4.3" }, + ); + + for ($i=0; $i <= $#stpprops; $i++) { + my %props = %{$stpprops[$i]}; + $oid = $props{'oid'}; + if ( $props{'flags'} & STP_PROP_VALUE,) { + $oid_value{$oid} = $props{'value'}; + } + if ( $props{'flags'} & STP_PROP_FILE,) { + $oid_value{$oid} = + readfile($props{'value'}, + $props{'flags'}); + } + $oid_type{$oid} = $props{'type'}; + $oid_next{$oid} = $props{'nextoid'}; + } +} + +sub portproperties +{ + my $bridge = shift; + my ($baseport, $first_baseport, $prev_baseport); + + $oid='.1.3.6.1.2.1.17.4.4.1'; + + foreach my $port (keys %{$interfaces{$bridge}}) { + $baseport = $baseports{$bridge}{$port}; + + unless(defined($first_baseport)){ + $first_baseport=$baseport; + $oid_next{$oid.".1"}=$oid.".1.".$baseport; + $oid_next{$oid.".2"}=$oid.".2.".$baseport; + } + + my $interface = $interfaces{$bridge}{$port}; + my $ifdir = $netdir.$interface; + + # dot1dTpPort + $oid_value{$oid.".1.".$baseport}=$baseport; + $oid_type{$oid.".1.".$baseport}=ASN_INTEGER; + if(defined($prev_baseport)){ + $oid_next{$oid.".1.".$prev_baseport}= + $oid.".1.".$baseport; + } + + # dot1dTpPortMaxInfo + $oid_value{$oid.".2.".$baseport}=readfile($ifdir."/mtu", 0); + $oid_type{$oid.".2.".$baseport}=ASN_INTEGER; + if(defined($prev_baseport)){ + $oid_next{$oid.".2.".$prev_baseport}= + $oid.".2.".$baseport; + } + + # dot1dTpPortInFrames + $oid_value{$oid.".3.".$baseport}=readfile($ifdir."/statistics/rx_packets", 0); + $oid_type{$oid.".3.".$baseport}=ASN_COUNTER; + if(defined($prev_baseport)){ + $oid_next{$oid.".3.".$prev_baseport}= + $oid.".3.".$baseport; + } + + # dot1dTpPortOutFrames + $oid_value{$oid.".4.".$baseport}=readfile($ifdir."/statistics/tx_packets", 0); + $oid_type{$oid.".4.".$baseport}=ASN_COUNTER; + if(defined($prev_baseport)){ + $oid_next{$oid.".4.".$prev_baseport}= + $oid.".4.".$baseport; + } + + # dot1dTpPortInDiscards + $oid_value{$oid.".5.".$baseport}=readfile($ifdir."/statistics/rx_dropped", 0); + $oid_type{$oid.".5.".$baseport}=ASN_COUNTER; + if(defined($prev_baseport)){ + $oid_next{$oid.".5.".$prev_baseport}= + $oid.".5.".$baseport; + } + + $prev_baseport=$baseport; + } + + if ( $prev_baseport and $first_baseport ) { + $oid_next{$oid.".1.".$prev_baseport}=$oid.".2.".$first_baseport; + $oid_next{$oid.".2.".$prev_baseport}=$oid.".3.".$first_baseport; + $oid_next{$oid.".3.".$prev_baseport}=$oid.".4.".$first_baseport; + $oid_next{$oid.".4.".$prev_baseport}=$oid.".5.".$first_baseport; + $oid_next{$oid.".5.".$prev_baseport}=".1.3.6.1.2.1.17.5.1"; + } + + $oid = $oid.".5.".$prev_baseport; + + undef $prev_baseport; + undef $first_baseport; +} + +sub dot1qbase() +{ + my $bridge = shift; + + my $oid=".1.3.6.1.2.1.17.7.1.1"; + $oid_next{".1.3.6.1.2.1.17.7"}=$oid.".1"; + $oid_next{".1.3.6.1.2.1.17.7.1"}=$oid.".1"; + + $oid_value{$oid.".1"}=1; + $oid_type{$oid.".1"}=ASN_INTEGER; + $oid_next{$oid.".1"}=$oid.".2"; + + $oid_value{$oid.".2"}=4094; + $oid_type{$oid.".2"}=ASN_UNSIGNED; + $oid_next{$oid.".2"}=$oid.".3"; + + $oid_value{$oid.".3"}=4094; + $oid_type{$oid.".3"}=ASN_UNSIGNED; + $oid_next{$oid.".3"}=$oid.".4"; + + $oid_value{$oid.".4"}=0; # filled in by currentvlans + $oid_type{$oid.".4"}=ASN_UNSIGNED; + $oid_next{$oid.".4"}=$oid.".5"; + + $oid_value{$oid.".5"}=2; + $oid_type{$oid.".5"}=ASN_INTEGER; + $oid_next{$oid.".5"}=0; # filled in by dot1qfdb + +} + +sub dot1qfdb() +{ + my $bridge = shift; + + my $oid=".1.3.6.1.2.1.17.7.1.2"; + foreach my $vlan (sort {$a<=>$b} keys %vlans){ + unless(defined($first_vlan)){ + $first_vlan=$vlan; + $oid_next{".1.3.6.1.2.1.17.7.1.1.5"}=$oid.".1.1.".$vlan; + $oid_next{$oid}=$oid.".1.1.".$vlan; + $oid_next{$oid.".1"}=$oid.".1.1.".$vlan; + $oid_next{$oid.".1.1"}=$oid.".1.1.".$vlan; + $oid_next{$oid.".1.2"}=$oid.".1.2.".$vlan; + } + + $oid_value{$oid.".1.1.".$vlan}=$vlan; + $oid_type{$oid.".1.1.".$vlan}=ASN_UNSIGNED; + if(defined($prv_vlan)){ + $oid_next{$oid.".1.1.".$prv_vlan}=$oid.".1.1.".$vlan; + } + + $oid_value{$oid.".1.2.".$vlan}=0; # to be filled later + $oid_type{$oid.".1.2.".$vlan}=ASN_COUNTER; + if(defined($prv_vlan)){ + $oid_next{$oid.".1.2.".$prv_vlan}=$oid.".1.2.".$vlan; + } + + $prv_vlan=$vlan; + } + if($prv_vlan and $first_vlan){ + $oid_next{$oid.".1.1.".$prv_vlan}=$oid.".1.2.".$first_vlan; + $oid_next{$oid.".1.2.".$prv_vlan}=0; # to be filled later + } + + my %macvlan=(); + my %vlanmac=(); + + foreach my $vlan (sort {$a<=>$b} keys %vlans){ + my $count=0; + foreach my $mac (sort {$a cmp $b} keys %macs) { + my $vbridge=$bridge."_vlan".$vlan; + next unless(defined($macs{$mac}{$vbridge})); + $count++; + my $mac_oid=mac2oid($mac); + $macvlan{$vlan.$mac_oid}=$mac; + $vlanmac{$vlan.$mac_oid}=$vlan; + } + $oid_value{$oid.".1.2.".$vlan}=$count; + } + + foreach my $vmac_oid (sort {$a cmp $b} keys %vlanmac){ + my $mac=$macvlan{$vmac_oid}; + my $vlan=$vlanmac{$vmac_oid}; + + #print "VMAC: $vmac_oid ($vlan:$mac)\n"; + unless(defined($first_vmac_oid)){ + $first_vmac_oid=$vmac_oid; + $oid_next{$oid.".1.2.".$prv_vlan}=$oid.".2.1.".$vmac_oid; + $oid_next{$oid.".2"}=$oid.".2.1.".$vmac_oid; + $oid_next{$oid.".2.1"}=$oid.".2.1.".$vmac_oid; + $oid_next{$oid.".2.2"}=$oid.".2.2.".$vmac_oid; + $oid_next{$oid.".2.3"}=$oid.".2.3.".$vmac_oid; + } + + my $port=$macs{$mac}{$bridge}; + my $baseport=$baseports{$bridge}{$port}; + my $status=$locals{$mac}{$bridge}; + my $age=$ages{$mac}{$bridge}; + + print "VLAN $vlan: $mac -> $port:$baseport ($status, $age)\n"; + + $oid_value{$oid.".2.1.".$vmac_oid}=mac2hex($mac); + $oid_type{$oid.".2.1.".$vmac_oid}=ASN_OCTET_STR; + if(defined($prv_vmac_oid)){ + $oid_next{$oid.".2.1.".$prv_vmac_oid}=$oid.".2.1.".$vmac_oid; + } + + $oid_value{$oid.".2.2.".$vmac_oid}=$baseport; + $oid_type{$oid.".2.2.".$vmac_oid}=ASN_INTEGER; + if(defined($prv_vmac_oid)){ + $oid_next{$oid.".2.2.".$prv_vmac_oid}=$oid.".2.2.".$vmac_oid; + } + + $oid_value{$oid.".2.3.".$vmac_oid}=$status; + $oid_type{$oid.".2.3.".$vmac_oid}=ASN_INTEGER; + if(defined($prv_vmac_oid)){ + $oid_next{$oid.".2.3.".$prv_vmac_oid}=$oid.".2.3.".$vmac_oid; + } + $prv_vmac_oid=$vmac_oid; + } + + if ($prv_vmac_oid and $first_vmac_oid) { + $oid_next{$oid.".2.1.".$prv_vmac_oid}=$oid.".2.2.".$first_vmac_oid; + $oid_next{$oid.".2.2.".$prv_vmac_oid}=$oid.".2.3.".$first_vmac_oid; + $oid_next{$oid.".2.3.".$prv_vmac_oid}=".1.3.6.1.2.1.17.7.1.4.1"; + } + + undef $first_vmac_oid; + undef $prv_vmac_oid; + + undef $first_vlan; + undef $prv_vlan; +} + +sub dot1qcurrentvlans() +{ + my $bridge = shift; + + + my $oid=".1.3.6.1.2.1.17.7.1.4.1"; + $oid_next{".1.3.6.1.2.1.17.7.1.4"}=$oid; + + $oid_value{$oid}=0; # can't keep track of this info + $oid_type{$oid}=ASN_COUNTER; + + my $count=0; + $oid=".1.3.6.1.2.1.17.7.1.4.2"; + foreach my $vlan (sort {$a<=>$b} keys %vlans){ + $count++; + my @allports=(); + my @untaggedports=(); + foreach my $port (keys %{$vlans{$vlan}}){ + $baseport=$baseports{$bridge}{$port}; + push @allports, $baseport; + push @untaggedports, $baseport + unless($tagged{$vlan}{$port}); + } + #print "ADDING: vlan $vlan (@allports, @untaggedports)\n"; + unless(defined($first_vlan)){ + $first_vlan=$vlan; + $oid_next{".1.3.6.1.2.1.17.7.1.4.1"}=$oid.".1.0.".$vlan; + $oid_next{$oid}=$oid.".1.0.".$vlan; + $oid_next{$oid.".1"}=$oid.".1.0.".$vlan; + $oid_next{$oid.".2"}=$oid.".2.0.".$vlan; + $oid_next{$oid.".3"}=$oid.".3.0.".$vlan; + $oid_next{$oid.".4"}=$oid.".4.0.".$vlan; + $oid_next{$oid.".5"}=$oid.".5.0.".$vlan; + $oid_next{$oid.".6"}=$oid.".6.0.".$vlan; + $oid_next{$oid.".7"}=$oid.".7.0.".$vlan; + } + + $oid_value{$oid.".1.0.".$vlan}=0; # can't keep track of this + $oid_type{$oid.".1.0.".$vlan}=ASN_TIMETICKS; + if(defined($prv_vlan)){ + $oid_next{$oid.".1.0.".$prv_vlan}=$oid.".1.0.".$vlan; + } + + $oid_value{$oid.".2.0.".$vlan}=$vlan; + $oid_type{$oid.".2.0.".$vlan}=ASN_UNSIGNED; + if(defined($prv_vlan)){ + $oid_next{$oid.".2.0.".$prv_vlan}=$oid.".2.0.".$vlan; + } + + $oid_value{$oid.".3.0.".$vlan}=$vlan; + $oid_type{$oid.".3.0.".$vlan}=ASN_UNSIGNED; + if(defined($prv_vlan)){ + $oid_next{$oid.".3.0.".$prv_vlan}=$oid.".3.0.".$vlan; + } + + $oid_value{$oid.".4.0.".$vlan}=getportlist(@allports); + $oid_type{$oid.".4.0.".$vlan}=ASN_OCTET_STR; + if(defined($prv_vlan)){ + $oid_next{$oid.".4.0.".$prv_vlan}=$oid.".4.0.".$vlan; + } + + $oid_value{$oid.".5.0.".$vlan}=getportlist(@untaggedports); + $oid_type{$oid.".5.0.".$vlan}=ASN_OCTET_STR; + if(defined($prv_vlan)){ + $oid_next{$oid.".5.0.".$prv_vlan}=$oid.".5.0.".$vlan; + } + + $oid_value{$oid.".6.0.".$vlan}=1; + $oid_type{$oid.".6.0.".$vlan}=ASN_INTEGER; + if(defined($prv_vlan)){ + $oid_next{$oid.".6.0.".$prv_vlan}=$oid.".6.0.".$vlan; + } + + $oid_value{$oid.".7.0.".$vlan}=0; + $oid_type{$oid.".7.0.".$vlan}=ASN_TIMETICKS; + if(defined($prv_vlan)){ + $oid_next{$oid.".7.0.".$prv_vlan}=$oid.".7.0.".$vlan; + } + $prv_vlan=$vlan; + } + + $oid_value{".1.3.6.1.2.1.17.7.1.1.4"}=$count; + if($prv_vlan and $first_vlan){ + $oid_next{$oid.".1.0.".$prv_vlan}=$oid.".2.0.".$first_vlan; + $oid_next{$oid.".2.0.".$prv_vlan}=$oid.".3.0.".$first_vlan; + $oid_next{$oid.".3.0.".$prv_vlan}=$oid.".4.0.".$first_vlan; + $oid_next{$oid.".4.0.".$prv_vlan}=$oid.".5.0.".$first_vlan; + $oid_next{$oid.".5.0.".$prv_vlan}=$oid.".6.0.".$first_vlan; + $oid_next{$oid.".6.0.".$prv_vlan}=$oid.".7.0.".$first_vlan; + $oid_next{$oid.".7.0.".$prv_vlan}=".1.3.6.1.2.1.17.7.1.4.3"; + } + + undef $prv_vlan; + undef $first_vlan; + +} + +sub readforwards() +{ + my $bridge = shift; + my $fdb=$netdir.$bridge."/brforward"; + + open(FWD, $fdb) or return -1; + while(sysread(FWD, $data, 20)){ + my $mac; + my ($b1,$b2,$b3,$b4,$b5,$b6,$port,$local,$age,$hi)= + unpack("C6 C C L C x3", $data); + $mac=sprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", + $b1,$b2,$b3,$b4,$b5,$b6); + $age=$age/100; + $macs{$mac}{$bridge}=$port; + $ages{$mac}{$bridge}=$age; + $locals{$mac}{$bridge}=FDB_STATUS_LEARNED; + next if ($local); + $macs{$mac}{$bridge}=$bridge; + $locals{$mac}{$bridge}=FDB_STATUS_SELF; + } + close(FWD); +} + +sub readindexes() +{ + my $bridge = shift; + + my $brifdir=$netdir.$bridge."/brif/"; + + opendir(DIR, $brifdir) or return -1; + my @interfaces = readdir(DIR); + next if ($#interfaces lt 2); + foreach my $if (@interfaces) { + next if $if eq "."; + next if $if eq ".."; + + my $port=hex(readfile($brifdir.$if."/port_no", 0)); + my $index=readfile($netdir.$if."/ifindex", 0); + + $indexes{$bridge}{$port}=$index; + $interfaces{$bridge}{$port}=$if; + } + + $indexes{$bridge}{$bridge}=readfile($netdir.$bridge."/ifindex", 0); + + close(DIR); +} + +sub readvlans() +{ + my $bridge = shift; + my $brifdir = $netdir.$bridge."/brif/"; + my %ifs; + + opendir(DIR, $brifdir) or return -1; + my @interfaces = readdir(DIR); + return if ($#interfaces < 2); + foreach my $if (@interfaces) { + next if $if eq "."; + next if $if eq ".."; + $ifs{$if}=1; + } + close(DIR); + + opendir(DIR, $netdir) or return -1; + @interfaces = readdir(DIR); + return 0 if ($#interfaces < 2); + foreach my $if (@interfaces) { + next if $if eq "."; + next if $if eq ".."; + next unless($if=~/^(.*)\.(\d+)$/); + my $pif=$1; + my $vlan=$2; + next unless(defined($ifs{$pif})); + tracevlan($vlan, $if, $bridge); + } + close(DIR); +} + +sub tracevlan{ + my ($vlan, $interface, $bridge)=@_; + + my $brifdir=$netdir.$interface."/brport/bridge/brif/"; + + opendir(DIR, $brifdir) or return -1; + my @interfaces = readdir(DIR); + return if ($#interfaces < 2); + foreach my $if (@interfaces) { + next if $if eq "."; + next if $if eq ".."; + my $port; + my $index; + if($if=~/^(.*)\.(\d+)$/){ + my $pif=$1; + $brifdir=$netdir.$pif."/brport/bridge/brif/"; + $port=hex(readfile($brifdir.$pif."/port_no", 0)); + $index=readfile($netdir.$pif."/ifindex", 0); + #$indexes{$bridge}{$port}=$index; + #$interfaces{$bridge}{$port}=$if; + $tagged{$vlan}{$port}=1; + }else{ + my $brid=readfile($netdir.$if."/brport/bridge/ifindex", 0); + $brifdir=$netdir.$if."/brport/bridge/brif/"; + $port=hex(readfile($brifdir.$if."/port_no", 0)); + $port=$brid*1000+$port; #create a unique port number + $index=readfile($netdir.$if."/ifindex", 0); + $indexes{$bridge}{$port}=$index; + $interfaces{$bridge}{$port}=$if; + $tagged{$vlan}{$port}=0; + } + $vlans{$vlan}{$port}=1; + #print "VLAN: $vlan -> $if ($port <-> $index)\n"; + } + close(DIR); + + my $brid=readfile($netdir.$interface."/brport/bridge/ifindex", 0); + my $fdb=$netdir.$interface."/brport/bridge/brforward"; + + my $vbridge=$bridge."_vlan".$vlan; + open(FWD, $fdb) or return -1; + while(sysread(FWD, $data, 20)){ + my $mac; + my ($b1,$b2,$b3,$b4,$b5,$b6,$port,$local,$age,$hi)= + unpack("C6 C C L C x3", $data); + $mac=sprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", + $b1,$b2,$b3,$b4,$b5,$b6); + $port=$brid*1000+$port unless(defined($indexes{$bridge}{$port})); #create a unique port number + $age=$age/100; + #$macs{$mac}{$bridge}=$port; + $macs{$mac}{$vbridge}=$port; + #$ages{$mac}{$bridge}=$age; + $ages{$mac}{$vbridge}=$age; + #$locals{$mac}{$bridge}=FDB_STATUS_LEARNED; + $locals{$mac}{$vbridge}=FDB_STATUS_LEARNED; + next if ($local); + #$macs{$mac}{$bridge}=$bridge; + $macs{$mac}{$vbridge}=$bridge; + #$locals{$mac}{$bridge}=FDB_STATUS_SELF; + $locals{$mac}{$vbridge}=FDB_STATUS_SELF; + } + close(FWD); +} + +sub readfile() +{ + my $file = shift; + my $flags = shift; + + open(FILE, $file) or print "Could not open file $file !\n"; + my $value=<FILE>; + chomp $value; + close(FILE); + + if ($flags & STP_PROP_HEX) { + return hex($value); + } + + return $value; +} + +sub getportlist{ + my @ports=@_; + my $portlist=""; + + @ports=sort {$a <=> $b} @ports; + my $last=1; + foreach my $port (@ports){ + for(my $i=$last; $i<$port; $i++){ + $portlist=$portlist."0"; + } + $portlist=$portlist."1"; + $last=$port+1; + } + return pack('B*',$portlist); +} + +sub mac2oid{ + my ($mac)=@_; + my @octets=split(/:/,$mac); + + $mac=""; + foreach my $octet (@octets){ + $mac=$mac.".".hex($octet); + } + return $mac; +} + +sub mac2hex{ + my ($mac)=@_; + my @decimals; + my @octets=split(/:/,$mac); + + foreach my $octet (@octets){ + push @decimals, hex($octet); + } + return pack("CCCCCC", @decimals); +} + +sub shutdown { + $running = 0; +} diff --git a/local/snmp-ucd.sh b/local/snmp-ucd.sh new file mode 100755 index 0000000..7d16b74 --- /dev/null +++ b/local/snmp-ucd.sh @@ -0,0 +1,187 @@ +#!/bin/sh +# +# snmpd-ucd.sh +# +# Start UCD SNMP daemon and trap catcher. Backup the log file *first* +# since currently the daemon truncates and overwrites any pre-existing file. +# +# killproc() and pidofproc() lifted from Linux's /etc/init.d/functions. +# +# NOTE: Solaris users must uncomment the proper PSARGS definition below. XXX +# + +USAGE="Usage: `basename $0` start|stop|restart" + + + +#------------------------------------ -o- +# Globals. +# +DAEMONLOG=/var/log/snmpd.log + TRAPLOG=/var/log/snmptrapd.log + LOGDIR=/var/log/SNMPDLOGS + +D=".`date '+%h%d_%H%M' | sed 's/\([a-z]\)0/\1/' | tr 'A-Z' 'a-z'`" + +PSARGS=auwwx +#PSARGS=-ef # Solaris. + +DEBUGFLAG= # -D # Toggles use of debugging + + + + +#------------------------------------ -o- +# Function definitions. +# +killproc() { # <program> [signal] + base= + killlevel="-9" + notset=1 + pid= + + + # + # Parse. + # + [ $# = 0 ] && { + echo "`basename $0`: Wrong arguments to killproc()." 1>&2 + return 1 + } + base="`basename $1`" + [ -n "$2" ] && { + killlevel=$2 + notset=0 + } + + + # + # Kill process. + # + pid=`pidofproc $base 2>/dev/null` + [ -z "$pid" ] && { + pid=`ps $PSARGS | egrep $base | egrep -v egrep | egrep -v $0 | awk '{ print $2 }'`; + } + [ -z "$pid" ] && { + echo "`basename $0`: killproc: Could not find process ID." + } + + [ -n "$pid" ] && { + echo -n "$base " + + # + # Kill with -TERM then -KILL by default. Use given + # instead if one was passed in. + # + [ "$notset" = 1 ] && { + kill -TERM $pid + sleep 1 + + [ -n "`ps $PSARGS | + awk '{print $2}' | grep $pid`" ] && { + sleep 3 + kill -KILL $pid + } + + true + } || { + kill $killlevel $pid + } + } + + rm -f /var/run/$base.pid + +} # end killproc() + + +pidofproc() { # <program> + pid= + + [ $# = 0 ] && { + echo "`basename $0`: Wrong argument to pidofproc()." 1>&2 + return 1 + } + + # + # Try looking for a /var/run file. + # + [ -f /var/run/$1.pid ] && { + pid=`head -1 /var/run/$1.pid` + + [ -n "$pid" ] && { + echo $pid + return 0 + } + } + + # + # Try pidof. (Linux offering.) + # + pid=`pidof $1` + [ -n "$pid" ] && { + echo $pid + return 0 + } + + # + # Try ps. + # + ps $PSARGS | awk ' BEGIN { prog=ARGV[1]; ARGC=1 } + { if ((prog == $11) || + (("(" prog ")") == $11) || + ((prog ":") == $11)) + { + print $2 + } + }' $1 +} # end pidofproc() + + + +#------------------------------------ -o- +# Action. +# +case "$1" in + start) + echo "Starting SNMP. " + + cp $DAEMONLOG ${DAEMONLOG}$D + cp $TRAPLOG ${TRAPLOG}$D + cat /dev/null >$TRAPLOG + + [ ! -e $LOGDIR ] && mkdir $LOGDIR + mv ${DAEMONLOG}$D ${TRAPLOG}$D $LOGDIR + gzip -r $LOGDIR 2>/dev/null & + + snmpd -a -d -V $DEBUGFLAG + snmptrapd -Lf "$TRAPLOG" + + echo + ;; + + stop) + echo -n "Shutting down SNMP: " + + killproc snmpd + killproc snmptrapd + + echo + ;; + + restart) + $0 stop + $0 start + ;; + + *) + echo $USAGE 1>&2 + exit 1 +esac + + + +#------------------------------------ -o- +# +exit 0 + + diff --git a/local/snmpcheck.def b/local/snmpcheck.def new file mode 100755 index 0000000..ad392ee --- /dev/null +++ b/local/snmpcheck.def @@ -0,0 +1,1224 @@ +#!/usr/local/bin/perl -w + +use strict 'refs'; +require Net::Ping; +require Term::ReadKey; + +#defaults +$mibident=".ERRORNAME"; +$miberrflag=".ERRORFLAG"; +$miberrmsg=".ERRORMSG"; +$mibfix=".ERRORFIX"; +$mibheadall=".EXTENSIBLEDOTMIB"; +$mibclearcache="$mibheadall.VERSIONMIBNUM.VERCLEARCACHE"; +$mibrestartagent="$mibheadall.VERSIONMIBNUM.VERRESTARTAGENT"; +$mibupdateconfig="$mibheadall.VERSIONMIBNUM.VERUPDATECONFIG"; +%miblist=( '.PROCMIBNUM.1' => 'processes', + '.SHELLMIBNUM.1' => 'scripts', + '.MEMMIBNUM' => 'swap space', + '.DISKMIBNUM.1' => 'disks', + '.LOADAVEMIBNUM.1' => 'load-average', + '.ERRORMIBNUM' => 'snmp-agent-errors'); +@fixitlist=('.PROCMIBNUM.1','.SHELLMIBNUM.1'); +%mibchecklist = ('.PROCMIBNUM.1' => 1, + '.SHELLMIBNUM.1' => 1, + '.MEMMIBNUM' => 1, + '.DISKMIBNUM.1' => 1, + '.LOADAVEMIBNUM.1' => 1, + '.ERRORMIBNUM' => 1); +$errlog="/net/tyfon/1/OV/log/ece-log"; +$default_get_args = "-v 1 %s private"; +$default_set_args = "-v 1 %s private"; +$andlog=0; +$snmppath="BINDIR"; +$eraseline=" \r"; +$fixit=0; # this should be 0 not -1, but is necessary till getc(STDIN) works +$rescanWhen = 300; +$display = $ENV{'DISPLAY'}; +$hidden = 0; +$pinghost = 0; +$loglevel = 1; +$logwindowatstart = 0; +$numloglevels = 5; +$dontstart = 0; +$raiseonnew = 1; + +# +# Mib Package: Each mib has a mib number attached and can check/fix itself; +# + +package Mib; + +# @ISA = qw( Host ); + +sub new { + my $tmp = shift; + my $self = {}; + $self->{'HostId'} = shift; + $self->{'Host'} = $self->{'HostId'}->{'Name'}; + $_ = shift; + $self->{'Mib'} = $_; + print "test: $_\n"; + ($self->{'MibSuffix'}) = /(\.[0-9]+)$/; + if (!defined($mibchecklist{$self->{'MibSuffix'}})) { + ($self->{'MibSuffix'}) = /(\.[0-9]+\.1)$/; + } + print "suff: $self->{'MibSuffix'}\n"; + $self->{'MibDesc'} = shift; + $self->{'Frame'} = shift; + bless $self; +} + +sub getmibnum { + my $self = shift; + return ($self->{'Mib'}); +} + +sub snmp_walk { + my $self = shift; + $self->{'Frame'}->toplevel->Busy() if ($::display); + my $mib = shift; + my $cmd = "$::snmppath/snmpwalk " . sprintf($::default_get_args, $self->{'Host'}) . " $mib|"; + ::addToLog("running: $cmd",5); + open(OUT,"$cmd"); + my $outcount = 0; + my @result = []; + while (<OUT>) { + $result[$outcount] = $_; + chop; + ::addToLog("snmpwalk: $_",5); + if ($::display) { + $self->{'Frame'}->toplevel->update; + } + $outcount++; + } + close(OUT); + for($i=0; $i <= $#result; $i++) { + $result[$i] =~ s/ Hex:.*$//g; + $result[$i] =~ s/\"//g; + } + $self->{'Frame'}->toplevel->Unbusy() if ($::display); + if ($result[0] =~ /No Response/) { + $self->{'HostId'}->hostdown; + splice(@result,0); + } + return @result; +} + +sub check { + my $self = shift; + my $tmp = []; + if ($::mibchecklist{$self->{'MibSuffix'}} != 1) { + return @{$tmp}; + } + if (! $::display) { + printf "%sChecking %s: %s\r", $::eraseline,$self->{'Host'}, + $self->{'MibDesc'}; + } + my @walkout = $self->snmp_walk("$self->{'Mib'}$::miberrflag"); + while ($#walkout > -1) { + $_ = shift @walkout; + ($result) = /= ([0-9]+)/; + if (defined($result) && $result > 0) + { + ($mibloc) = /\.([0-9]+) /; + push(@{$tmp},FixProblem::new("",$self->{'HostId'}, $self->{'Mib'}, + $self->{'MibDesc'}, + $mibloc,$self->{'Frame'})); + if (! $::display) { + printf("%s%-8.8s %-12.12s %2d -- %-37.37s",$::eraseline, + $self->{'Host'},$tmp->[0]->{'ErrName'}, + $result,$tmp->[0]->{'ErrMsg'}); + if ($tmp->[0]->canfix() && $::fixit == 0) { + printf(" / Fix? "); + $ans = Term::ReadKey::ReadKey(0); + if ("$ans" eq "y" || "$ans" eq "Y") { + printf("\b\b\b\b\b\b\b\b"); + $tmp->[0]->fix($mibloc); # fix now if curses + } else { + print $ans; + printf("\nChecking %s: %s\r",$self->{'Host'}, + $self->{'MibDesc'}); + } + } elsif ($::fixit > 0) { + $tmp->[0]->fix($mibloc); # fix now if curses + } + shift @{$tmp}; + } + } + } + return(@{$tmp}); +} + +# +# Problem Package: A problem comes into existence when found. It may +# or may not know how to fix itself (Problem/FixProblem). +# + +package Problem; + +@ISA = qw( Mib ); + +sub snmp_get { + my $self = shift; + $self->{'Frame'}->toplevel->Busy() if ($::display); + my $mib = shift; + my $args = sprintf($::default_get_args, $self->{'Host'}); + $_ = `$::snmppath/snmpget $args $mib`; + my ($result) = /= (.*)$/; + if (!defined($result) || $result =~ /No Response/) { + $self->{'HostId'}->hostdown; + $result = ""; + } + $result =~ s/\"//g; + $result =~ s/ Hex:.*$//g; + ::addToLog("snmpget: $_",5); + $self->{'Frame'}->toplevel->Unbusy() if ($::display); + return $result; +} + +sub snmp_set { + my $self = shift; + $self->{'Frame'}->toplevel->Busy() if ($::display); + my $mib = shift; + my $args = sprint($::default_set_args, $self->{'Host'}); + $_ = `$::snmppath/snmpset $args $mib`; + my ($result) = /= (.*)$/; + $result = "" if (!defined($result)); + $result =~ s/\"//g; + ::addToLog("snmpset: $_",5); + $self->{'Frame'}->toplevel->Unbusy() if ($::display); + return $result; +} + +sub new{ + my $tmp = shift; + my $hostId = shift; + my $mib = shift; + my $mibname = shift; + my $self = new Mib ($hostId,$mib,$mibname); + $self->{'MibLocation'} = shift; + $tmp = shift; + if ($::display) { + $self->{'Frame'} = $tmp->Frame(); + } + bless $self; + $self->{'ErrName'} = + $self->snmp_get("$self->{'Mib'}$::mibident.$self->{'MibLocation'}"); + $self->{'ErrMsg'} = + $self->snmp_get("$self->{'Mib'}$::miberrmsg.$self->{'MibLocation'}"); + if (exists $self->{'HostId'}->{'Down'}) { + return $self; + } + if ($::display) { + $self->{'Frame'}->pack(); + $self->{'Desc'} = + $self->{'Frame'}->Button(-text => sprintf("%-12.12s %-42.42s", + $self->{'ErrName'}, + $self->{'ErrMsg'}), + -font => "6x13", + -highlightcolor => "#ffffff", + -borderwidth => 0, + -relief => "flat", + -bd => 0, -padx => 0, -pady => 0, + -activeforeground => 'red', + -activebackground => '#C9C9C9', + -background => '#E0C9C9', + -command => [\&selectme,$self]); + $self->{'Desc'}->pack(-fill => "x",-expand => 1,-side=>"left"); # + if ($::raiseonnew) { + $tmp->toplevel->deiconify(); + $tmp->toplevel->raise(); + } + ::addToLog("problem found: $self->{'Host'}\t$self->{'ErrName'}\t$self->{'ErrMsg'}",2); + } + bless $self; + return $self; +} + +sub haveseen { + my $self = shift; + $self->{'Desc'}->configure(-background => '#C9C9C9'); +} + +sub selectme { + my $self = shift; + if ($main::hidden) { + main::makeappear(); + return; + } + if (exists $self->{'Selected'}) { + main::deselectitem($self); + delete $self->{'Selected'}; + } else { + main::selectitem($self); + $self->{'Desc'}->configure(-foreground => "red"); + $self->{'Selected'} = 1; + } + $self->haveseen(); +} + +sub deselectme { + my $self = shift; + $self->{'Desc'}->configure(-foreground => "black"); + delete $self->{'Selected'}; +} + +sub check { + my $self = shift; + if ($::display) { + main::setstatus("Checking $self->{'Host'} -- $self->{'ErrName'}"); + } + else { + printf("Checking \b\b\b\b\b\b\b\b\b"); + } + $result = $self->snmp_get("$self->{'Mib'}$::miberrflag.$self->{'MibLocation'}"); + if (exists $self->{'HostId'}->{'Down'}) { + return 0; + } + if ($result == 0) { + $self->deleteme(); + } + main::setstatus("idle"); + return $result; +} + +sub fix { +# Don't fix and/or unable to + my $self = shift; + main::setmsg("Don't know how to fix $self->{'ErrName'}"); +} + +sub rsh { + my $self = shift; + if ($::display) { + system "xterm -e rsh $self->{'HostId'}->{'Name'} -l root &"; + } +} + +sub deleteme { + my $self = shift; + my $host = $self->{'HostId'}; + $host->deleteProb($self); +} + +sub deleteself { + my $self = shift; + if ($::display) { + if ($self->{'Selected'}) { + main::deselectitem($self); + } + $self->{'Desc'}->destroy(); + $self->{'Frame'}->destroy(); + } +} + +sub canfix { + return 0; +} + +package FixProblem; + +@ISA = qw( Problem ); + +sub new { + my $tmp = shift; + my $hostId = shift; + my $mib = shift; + my $mibdesc = shift; + my $mibloc = shift; + my $frame = shift; + my $self = new Problem ($hostId,$mib,$mibdesc,$mibloc,$frame); + $_ = $mib; + ($mymib) = /(\.[0-9]+)$/; + if (grep(/$mymib/,@::fixitlist) && ($::fixit >= 0)) { + bless $self; # Make it a FixProblem if fixable + } + return $self; # else just return a Problem +} + +sub canfix { + return 1; +} + +sub fix { + my $self = shift; + my $mibloc = shift; + if ($::display) { + main::setstatus(sprintf("Fixing %s: %s", + $self->{'Host'}, $self->{'ErrName'})); + } + else { + printf(" / Fixing...\b\b\b\b\b\b\b\b\b"); + } + $self->snmp_set("$self->{'Mib'}$::mibfix.$self->{'MibLocation'} integer 1"); + $self->snmp_set("$::mibclearcache integer 1"); + if (exists $self->{'HostId'}->{'Down'}) { + return; + } + if ($::display) { + main::setstatus("Sleeping"); + } + else { + printf("Sleeping \b\b\b\b\b\b\b\b\b"); + } + sleep(2); + if ($::display) { + main::setstatus("Checking"); + } + else { + printf("Checking\b\b\b\b\b\b\b\b"); + } + if ($self->check() != 0) { + if (! $::display) { + printf("*failed* \n"); + } else { + main::setmsg("Failed to fix $self->{'ErrName'} on $self->{'Host'}"); + } + } + else { + if ($::display) { +# $self->{'HostId'}->deleteProb($self); + main::setmsg("Fixed $self->{'ErrName'} on $self->{'Host'}"); + } + else { + printf("Fixed \n"); + } + } + main::setstatus("Idle"); +} + +# +# Host Package: Each object is a host which can check itself and display +# the results +# +package Host; + +sub mibsort { + $_ = $a; + ($av) = /\.([0-9]+)/; + $_ = $b; + ($bv) = /\.([0-9]+)/; + return $av <=> $bv; +} + +sub new { + my $self = {}; + my $tmp = shift; + $self->{'Name'} = shift; + $self->{'Host'} = $self->{'Name'}; + $self->{'Mibs'} = []; + $self->{'Problems'} = []; + bless $self; + if ($::display) { + $self->{'MainFrame'} = $::HostFrame->Frame(); + if (!$::hidden) { + $self->{'MainFrame'}->configure(-relief =>"sunken",-borderwidth=>2); + } + $self->{'ProbFrame'} = $self->{'MainFrame'}->Frame(); + $self->{'hostlabel'} = + $self->{'MainFrame'}->Button(-text => sprintf("%-9.9s", + $self->{'Name'}), + -bd => 0, -padx => 0, -pady => 0, + -command =>[\&selectme,$self], + -activeforeground => 'red', + -activebackground => '#C9C9C9', + -width => 9, + -anchor => "w", + -relief => "flat"); + $self->{'hostlabel'}->pack(-side=>"left",-ipadx=>1, + -padx=> 1,-pady =>1); + $self->{'ProbFrame'}->pack(-side=>"left",-ipadx=>1, + -padx=> 1,-pady =>1); + $self->{'MainFrame'}->pack( #-padx => 2,-pady =>2, + -fill => "x", -expand => 1); + } + foreach $mibx ( sort mibsort keys(%::miblist) ) { + push(@{$self->{'Mibs'}}, + new Mib ($self,"$::mibheadall$mibx",$::miblist{$mibx}, + $self->{'ProbFrame'})); + } + return $self; +} + +sub rsh { + my $self = shift; + if ($::display) { + system "xterm -e rsh $self->{'Name'} -l root &"; + } +} + +sub selectme { + my $self = shift; + if ($main::hidden) { + main::makeappear(); + return; + } + if (exists $self->{'Selected'}) { + main::deselectitem($self); + delete $self->{'Selected'}; + } else { + main::selectitem($self); + $self->{'hostlabel'}->configure(-foreground => "red"); + $self->{'Selected'} = 1; + } +} + +sub deselectme { + my $self = shift; + $self->{'hostlabel'}->configure(-foreground => "black"); + delete $self->{'Selected'}; +} + +sub fix { + my $self = shift; + if (! exists $self->{'Down'}) { + foreach $i (@{$self->{'Problems'}}) { + if ($i->canfix() && ref($i) ne Host) { + $i->fix(); + } + } + } +} + +sub seenall { + my $self = shift; + foreach $i (@{$self->{'Problems'}}) { + if (ref($i) ne Host) { + $i->haveseen(); + } + } +} + +sub canfix { + return 1; +} + +sub hostdown { + my $self = shift; + $self->deleteProbs(); + push(@{$self->{'Problems'}},$self); + $self->{'Down'} = 1; + if ($::display) { + if (!exists $self->{'hostlabel'}) { + $self->{'hostlabel'} = + $self->{'MainFrame'}->Button(-text => sprintf("%-9.9s", + $self->{'Name'}), + -bd => 0, -padx => 0, -pady => 0, + -command =>[\&selectme,$self], + -activeforeground => 'red', + -activebackground => '#C9C9C9', + -width => 9, + -anchor => "w", + -relief => "flat"); + } + ::addToLog("$self->{'Name'} is down",2); + $self->{'hostlabel'}->configure(-text => + sprintf("%-9.9s down",$self->{'Name'}), + -width => 14); + } +} + +sub check { + my $self = shift; + $self->{'noDelete'} = 1; + $self->deleteProbs(); + delete $self->{'noDelete'}; + if ($::display) { + $self->{'hostlabel'}->configure(-text => $self->{'Name'},-width=>9); + } + delete $self->{'Down'}; + main::setstatus("pinging $self->{'Name'}"); + if (!($::pinghost) || Net::Ping::pingecho($self->{'Name'},2)) { + foreach $i (@{$self->{'Mibs'}}) { + if (ref($i) ne Mib) { + print "$i is a ref($i) not a Mib\n"; + } else { + main::setstatus("Checking $self->{'Name'}: " . $i->{'MibDesc'}); + push(@{$self->{'Problems'}},$i->check()); + } + if (exists $self->{'Down'}) { + last; + } + } + } else { + $self->hostdown(); + } + main::setstatus("Idle"); + if ($#{$self->{'Problems'}} == -1) { + $self->deleteme(); + } +} + +sub deleteme { + my $self = shift; + if ($self->{'Selected'}) { + main::deselectitem($self); + } + $self->deleteProbs(); + if ($::display) { + $self->{'hostlabel'}->destroy(); + $self->{'ProbFrame'}->destroy(); + my $top = $self->{'MainFrame'}->toplevel; + $self->{'MainFrame'}->destroy(); + $top->update; + } + main::deletehost($self->{'Name'}); +} + +sub deleteProbs { + my $self = shift; + foreach $i (@{$self->{'Problems'}}) { + if (ref($i) eq Host) { + delete $self->{'Problems'}; + return; + } + if (ref($i) ne Problem && ref($i) ne FixProblem) { + print "i: $i is a ", ref($i), "\n"; + next; + } + $self->deleteProb($i); + } +} + +sub deleteProb { + my $self = shift; + my $child = shift; + for ($k = 0; $k <= $#{$self->{'Problems'}}; $k++) { + if (ref($self->{'Problems'}->[$k]) eq Problem || + ref($self->{'Problems'}->[$k]) eq FixProblem ) { + if ($self->{'Problems'}->[$k]->{'Mib'} eq $child->{'Mib'} && + $self->{'Problems'}->[$k]->{'MibLocation'} eq + $child->{'MibLocation'}) { + splice(@{$self->{'Problems'}},$k,1); + $child->deleteself(); + if ($#{$self->{'Problems'}} == -1 && + !exists $self->{'noDelete'}) { + $self->deleteme(); + } + last; + } + } else { + print " not: ",$self->{'Problems'}->[$k],"/", + ref($self->{'Problems'}->[$k]),"\n"; + } + } +} + +package main; + +# +# Read arguments +# + +if ($#ARGV != -1) { + while ($#ARGV >= 0 && $ARGV[0] =~ /^-/) { + $_ = shift; + $andlog = 1 if (/^-a/); + $dontstart = 1 if (/^-d/); + $fixit = -1 if (/^-n/); + $fixit = 1 if (/^-y/); + $display = 0 if (/^-x/); + $pinghost = 1 if (/^-p/); + $hidden = 1 if (/^-H/); + $loglevel = shift if (/^-V/); + $logwindowatstart = 1 if (/^-L/); + &display_help() if (/^-h/); + &setmibchecklist(@fixitlist) if (/^-f/); + } +} + +# +# If necessary check the ece-log file for problems +# + +if (($andlog || $#ARGV == -1) && !$dontstart) { + open(LOG,$errlog); + while (<LOG>) { + @fields = split; + @tmp = grep(/$fields[0]/,@ARGV); + if ($#tmp == -1) { # && $fields[1] ne "down") { + $ARGV[$#ARGV + 1] = $fields[0]; + } + } + close(LOG); +} + +# +# Check all the found hosts +# + +if ($display) { + use Tk; +# $tk_strictMotif = 1; + $top = MainWindow->new(); + $top->bind('all',"<Control-q>",[\&quit]); + $top->bind('all',"<Control-h>",[\&makehidden]); + $top->bind('all',"<Control-s>",[\&seenall]); + $top->bind('all',"<Control-f>",[\&fixall]); + $top->option('add','*highlightThickness','0'); #wish this worked +# $top->option('add','*highlightbackground','#C9C9C9'); + $top->option('add','*background','#C9C9C9'); + $top->option('add','*font','6x13'); + $HostFrame = $top->Frame(); + $MenuFrame = $top->Frame(-relief => "raised",-borderwidth => 2); + $MenuFrame->pack(-fill => "x",-expand => 1); + $statusBar = $top->Frame(-relief => "raised",-borderwidth => 2); + $status = $statusBar->Label(-text => "initializing",-anchor =>"e"); + $statusl = $statusBar->Label(-text => "Status: ", -anchor => "w"); + $msgBar = $top->Frame(-relief => "raised",-borderwidth => 2); + $msg = $msgBar->Label(-text => "",-anchor =>"e"); + $msgl = $msgBar->Label(-text => "Note: ", -anchor => "w"); + + $botFrame = $top->Frame(); + $butFrame = $top->Frame(); + $entryhost = ""; + $NewHost = $botFrame->Entry(-textvariable => \$entryhost,-width=>20, + -relief => "sunken"); + $NewHost->bind("<Return>",sub {newHost("$entryhost"); + $NewHost->delete(0,length($entryhost));}); + $BotLabel = $botFrame->Label(-text => "Check New Host: ", + -anchor => "w"); + $CmdsMenuBut = $MenuFrame->Menubutton(-text => "Cmds"); + $CmdsMenu = $CmdsMenuBut->Menu(-tearoff => 1); + $CmdsMenuBut->configure(-menu => $CmdsMenu); + $CmdsMenuBut->pack(-side => "left"); + $CmdsMenuBut->command(-label => "Check Hosts", -command => [\&rescanhosts]); + $CmdsMenuBut->command(-label => "Check Log", -command => [\&scanlog]); + $CmdsMenuBut->command(-label => "Fix All", -command => [\&fixall], + -accelerator => "Ctrl-f"); + $CmdsMenuBut->command(-label => "Seen All", -command => [\&seenall], + -accelerator => "Ctrl-s"); + $CmdsMenuBut->separator(); + $CmdsMenuBut->command(-label => "Hide", -command => [\&makehidden], + -accelerator => "Ctrl-h"); + $CmdsMenuBut->command(-label => "Quit", -command => [\&quit], + -accelerator => "Ctrl-q"); + $PrefsMenuBut = $MenuFrame->Menubutton(-text => "Prefs"); + $PrefsMenu = $PrefsMenuBut->Menu(-tearoff => 1); + $PrefsMenuBut->configure(-menu => $PrefsMenu); + $PrefsMenuBut->pack(-side => "left"); + $PrefsMenuBut->cascade(-label => "Rescan"); + $RescanPrefsBut = $PrefsMenu->Menu(); + $PrefsMenuBut->entryconfigure("Rescan",-menu => $RescanPrefsBut); + $AutoRescan = 1; + if ($AutoRescan) { + $afterId = Tk::after($rescanWhen*1000,[\&autorescan]); + } + $RescanPrefsBut->checkbutton(-label =>"Auto Rescan", + -variable =>\$AutoRescan, + -command => sub {if ($AutoRescan) { + $afterId = + Tk::after($rescanWhen*1000,[\&autorescan]) + } else { + Tk::after("cancel",$afterId); + }}); + $AutoCheckLog = 1; + $RescanPrefsBut->checkbutton(-label =>"Checks Log", + -variable =>\$AutoCheckLog); + $AutoCheckHosts = 0; + $RescanPrefsBut->checkbutton(-label =>"Checks Hosts", + -variable =>\$AutoCheckHosts); + $RescanWhenHidden = 1; + $RescanPrefsBut->checkbutton(-label =>"Only When Hidden", + -variable =>\$RescanWhenHidden); + + $RescanPrefsBut->checkbutton(-label =>"Pop forward with new", + -variable =>\$raiseonnew); + + $PrefsMenuBut->cascade(-label => "Log Verbosity"); + $LogVerbBut = $PrefsMenu->Menu(); + $PrefsMenuBut->entryconfigure("Log Verbosity", + -menu => $LogVerbBut); + for ($i=1; $i <= $numloglevels; $i++) { + $LogVerbBut->radiobutton(-label => "$i", -variable => \$loglevel, + -value => $i); + } + + $PrefsMenuBut->cascade(-label => "Check For"); + $CheckForBut = $PrefsMenu->Menu(); + $PrefsMenuBut->entryconfigure("Check For", + -menu => $CheckForBut); + $CheckForBut->command(-label => "Fixable Problems", + -command => [\&setmibchecklist,@fixitlist]); + $CheckForBut->command(-label => "Everything", + -command => [\&setmibchecklist,keys(%miblist)]); + $CheckForBut->separator(); + foreach $i ( sort mibsort keys(%::mibchecklist) ) { + $CheckForBut->checkbutton(-label => $miblist{$i}, + -variable => \$mibchecklist{$i}); + } + + $PrefsMenuBut->checkbutton(-label => "Ping Host First", + -variable => \$pinghost); + + # Agent control + + $agentMenuBut = $MenuFrame->Menubutton(-text => "Agent-Control"); + $agentMenu = $agentMenuBut->Menu(-tearoff => 1); + $agentMenuBut->configure(-menu => $agentMenu); + $agentMenuBut->pack(-side => "left"); + $agentMenuBut->command(-label => "Re-read Configuration", + -command => [sub {if ($selected) { $top->Busy(); + my $args = sprint($::default_get_args, $selected->{'Host'}); +$_ = `$::snmppath/snmpset $args $mibupdateconfig i 1`; $top->Unbusy();}}]); + $agentMenuBut->command(-label => "Clear Exec Cache", + -command => [sub {if ($selected) { $top->Busy(); + my $args = sprint($::default_get_args, $selected->{'Host'}); +$_ = `$::snmppath/snmpset $args $mibclearcache i 1`; $top->Unbusy();}}]); + $agentMenuBut->separator(); + $agentMenuBut->command(-label => "Re-start Agent", + -command => [sub {if ($selected) { $top->Busy(); + my $args = sprint($::default_get_args, $selected->{'Host'}); +$_ = `$::snmppath/snmpset $args $mibrestartagent i 1`; $top->Unbusy();} }]); + + # set up remote commands + + $remoteMenuBut = $MenuFrame->Menubutton(-text => "Remote-Info"); + $remoteMenu = $remoteMenuBut->Menu(-tearoff => 1); + $remoteMenuBut->configure(-menu => $remoteMenu); + $remoteMenuBut->pack(-side => "left"); + $remoteMenuBut->command(-label => "Load-Av", -command => [\&remote_load]); + $remoteMenuBut->separator(); + $remoteMenuBut->command(-label => "top", -command => [\&remote_cmd,"top"]); + $remoteMenuBut->command(-label => "mailq", -command => [\&remote_cmd,"mailq"]); + $remoteMenuBut->command(-label => "ps", -command => [\&remote_cmd,"ps"]); + $remoteMenuBut->command(-label => "conf", -command => [\&remote_cmd,"conf"]); + + # set up log file menu + $logFileMenuBut = $MenuFrame->Menubutton(-text => "Log"); + $logFileMenu = $logFileMenuBut->Menu(-tearoff => 1); + $logFileMenuBut->configure(-menu => $logFileMenu); + $logFileMenuBut->pack(-side => "left"); + $logFileMenuBut->command(-label => "show log", -command => [\&displayLog]); + $logFileMenuBut->command(-label => "clear log", -command => [\&clearLog]); + $logFileMenuBut->separator(); + $logFileMenuBut->command(-label => "show Tyfon's log", -command => [\&displayTyfon]); + + + # set up status bar + + $statusl->pack(-fill => "x", -expand => 1, -side =>"left"); + $status->pack(-fill => "x", -expand => 1, -side =>"left"); + $msgl->pack(-fill => "x", -expand => 1, -side => "left"); + $msg->pack(-fill => "x", -expand => 1, -side => "left"); + $statusBar->pack(-fill => "x", -expand => 1); + $msgBar->pack(-fill => "x", -expand => 1); + $HostFrame->pack(-fill => "x",-expand => 1); + $butFrame->pack(-fill => "x",-expand => 1); + $botFrame->pack(-fill => "x",-expand => 1); + $FixBut = $butFrame->Button(-text => "Fix",-command=>[sub{print "hi\n"}], + -state => "disabled"); + $FixBut->pack(-side => "left",-padx => 4,-pady => 2,-ipadx => 2, + -ipady => 2); + $RshBut = $butFrame->Button(-text => "Rsh",-command=>[sub{print "hi\n"}], + -state => "disabled"); + $RshBut->pack(-side => "left",-padx => 4,-pady => 2,-ipadx => 2, + -ipady => 2); + $DelBut = $butFrame->Button(-text => "Del", + -state => "disabled"); + $DelBut->pack(-side => "left",-padx => 4,-pady => 2,-ipadx => 2, + -ipady => 2); + $ChkBut = $butFrame->Button(-text => "Chk", + -state => "disabled"); + $ChkBut->pack(-side => "left",-padx => 4,-pady => 2,-ipadx => 2, + -ipady => 2); + $BotLabel->pack(-fill => "x",-expand => 1,-side=>"left"); + $NewHost->pack(-side=>"left"); + &makehidden() if ($hidden); + $top->update(); + + # generate log window, but tell it not to create display + $logwindow = MainWindow->new; + $logwindow->option('add','*highlightThickness','0'); #wish this worked +# $logwindow->option('add','*highlightbackground','#C9C9C9'); + $logwindow->option('add','*background','#C9C9C9'); + $logwindow->option('add','*font','6x13'); + + $logbuttons = $logwindow->Frame; + $logbuttons->pack(-side => 'bottom', -expand => 1, -fill => 'x'); + $logclose = $logbuttons->Button(-text => 'Close', + -command => ['withdraw',$logwindow]); + $logclose->pack(-side => 'left', -expand => 1); + + $logtext = $logwindow->Text(-height => 40, -setgrid => 1); + $logtext->pack(-side => 'left', -fill => 'both', -expand => 1); + $logscroll = $logwindow->Scrollbar(-command => ['yview',$logtext]); + $logscroll->pack(-side => 'right', -fill => 'y'); + $logtext->configure(-yscrollcommand => ['set', $logscroll]); + $logwindow->title("snmpcheck Action Log file"); + $logwindow->iconname("snmpcheck-log"); + $logtext->delete('1.0','end'); + $logclear = $logbuttons->Button(-text => 'Clear Log', + -command => [\&deleteLog]); + $logclear->pack(-side => 'right', -expand => 1); + if (! $logwindowatstart) { + $logwindow->withdraw; + } + + $status->configure(-text => "Idle"); + $selected = 0; + # fill table with hosts + if (!$dontstart) { + loadAllHosts(@ARGV); + } + MainLoop; +} +else { + select(STDOUT); + $| = 1; + if ($::fixit == 0) { + Term::ReadKey::ReadMode(3); + } + loadAllHosts(@ARGV); + printf("$eraseline"); +} + +sub loadAllHosts { + my @hostlist = @_; + foreach $host ( @hostlist ) { + newHost($host); + } +} + +sub newHost { + my $name = shift; + if (!exists $chost{"$name"}) { + $chost{"$name"} = new Host ($name); + if ($::display) { $top->update(); } + $chost{"$name"}->check; + } else { + setmsg("$name all ready exists"); + } +} + +sub deletehost { + my $name = shift; + delete $chost{"$name"}; +} + +sub setstatus { + my $arg = shift; + if ($display) { + $status->configure(-text => $arg); + $top->update(); + addToLog($arg,4); + } +} + +sub setmsg { + my $arg = shift; + if ($display) { + $msg->configure(-text => $arg); + $top->update(); + addToLog($arg); + } +} + +sub addToLog { + if ($display) { + my $logmsg = shift; + my $logaddlevel = shift; + if (! defined($logaddlevel)) { + $logaddlevel = 1; + } + if ($logaddlevel <= $loglevel) { + $logtext->insert('end'," " x ($logaddlevel-1) . "$logmsg\n"); + } + } +} + +sub displayTyfon { + remote_cmd_generic("cat /net/tyfon/1/OV/log/ece-log","Tyfon -- ece-log"); +} + +sub displayLog { + $logwindow->deiconify; + $logwindow->raise; +} + +sub deleteLog { + $logtext->delete('1.0','end'); +} + +sub deselectitem { + $obj = shift; + $obj->deselectme(); + $FixBut->configure(-state => "disabled"); + $RshBut->configure(-state => "disabled"); + $DelBut->configure(-state => "disabled"); + $ChkBut->configure(-state => "disabled"); + $selected = 0; +} + +sub selectitem { + if ($selected) { + $selected->deselectme(); + } + $selected = shift; + if (ref($selected) ne Host || !(exists $selected->{'Down'})) { + $RshBut->configure(-state => "normal", -command => ['rsh',$selected]); + } else { + $RshBut->configure(-state => "disabled"); + } + $DelBut->configure(-state => "normal", -command => ['deleteme',$selected]); + $ChkBut->configure(-state => "normal", -command => ['check',$selected]); + if ($selected->canfix() && !(exists $selected->{'Down'})) { + $FixBut->configure(-state => "normal", + -command => ['fix',$selected]); + } else { + $FixBut->configure(-state => "disabled"); + } + if ($hidden == 1) { + makeappear(); + } +} + +sub makehidden { + $MenuFrame->pack("forget"); + $statusBar->pack("forget"); + $msgBar->pack("forget"); + $butFrame->pack("forget"); + $botFrame->pack("forget"); + flatten(); + $hidden=1; +} + +sub makeappear { + $HostFrame->pack("forget"); + $MenuFrame->pack(-expand => 1, -fill => "x"); + $statusBar->pack(-expand => 1, -fill => "x"); + $msgBar->pack(-expand => 1, -fill => "x"); + $HostFrame->pack(-expand => 1, -fill => "x"); + $butFrame->pack(-expand => 1, -fill => "x"); + $botFrame->pack(-expand => 1, -fill => "x"); + reliefen(); + $hidden=0; +} + +sub quit { + $top->destroy(); + exit(); +} + +sub scanlog { + my (@fields, @tmp); + open(LOG,$::errlog); + while (<LOG>) { + @fields = split; + @tmp = grep(/$fields[0]/,@ARGV); + if ($#tmp == -1 && !exists $::chost->{$fields[0]}) { + newHost($fields[0]); + } + } + close(LOG); +} + +sub rescanhosts { + foreach $i (keys(%chost)) { + $chost{$i}->check(); + } +} + +sub autorescan { + $afterId = Tk::after($rescanWhen*1000,[\&autorescan]); + if ($RescanWhenHidden && !$hidden) {return;} + if ($AutoCheckHosts) { + rescanhosts(); + } + if ($AutoCheckLog) { + scanlog(); + } +} + +sub flatten { + foreach $i (keys(%chost)) { + $chost{$i}->{'MainFrame'}->configure(-relief => "flat",-borderwidth=>0); + } +} + +sub reliefen { + foreach $i (keys(%chost)) { + $chost{$i}->{'MainFrame'}->configure(-relief =>"sunken",-borderwidth=>2); + } +} + +sub fixall { + foreach $i (keys(%chost)) { + $chost{$i}->fix(); + } +} + +sub seenall { + foreach $i (keys(%chost)) { + $chost{$i}->seenall(); + } +} + +sub remote_cmd { + my $type = shift; + if ($selected) { + remote_cmd_generic("$::snmppath/rsnmp -p $type $selected->{'Host'}", + "$selected->{'Host'} -- $type",1); + } else { + setmsg("Error: Nothing selected"); + } +} + +sub remote_load { + if ($selected) { + remote_cmd_generic("$::snmppath/snmpwalk " . sprintf($::default_get_args,$selected->{'Host'}) . " .EXTENSIBLEDOTMIB.LOADAVEMIBNUM.LOADAVE", + "$selected->{'Host'} -- LoadAve"); + } else { + setmsg("Error: Nothing selected"); + } +} + +sub remote_cmd_generic { + my $cmd = shift; + my $title = shift; + my $insert = shift; + addToLog("running: $cmd ... "); + my $newwin = MainWindow->new; + $newwin->Busy(); + + $newwin->option('add','*highlightThickness','0'); #wish this worked +# $newwin->option('add','*highlightbackground','#C9C9C9'); + $newwin->option('add','*background','#C9C9C9'); + $newwin->option('add','*font','6x13'); + + my $buttons = $newwin->Frame; + $buttons->pack(-side => 'bottom', -expand => 1, -fill => 'x'); + my $entries = $newwin->Frame; + $entries->pack(-side => 'bottom', -expand => 1, -fill => 'x'); + + my $text = $newwin->Text(-height => 40, -setgrid => 1); + $text->pack(-side => 'left', -fill => 'both', -expand => 1); + my $scroll = $newwin->Scrollbar(-command => ['yview',$text]); + $scroll->pack(-side => 'left', -fill => 'y'); + $text->configure(-yscrollcommand => ['set', $scroll]); + + my $close = $buttons->Button(-text => 'Close', + -command => ['destroy',$newwin]); + $close->pack(-side => 'left', -expand => 1); + my $rerun = $buttons->Button(-text => 'Re-Run', + -command=>[\&fill_text,'',$text, + \$cmd,$insert]); + $rerun->pack(-side => 'left', -expand => 1); + + my $cmdlabel = $entries->Label(-text => "Command: "); + my $cmdtexte = $entries->Entry(-textvariable => \$cmd, + -relief => "sunken"); + $cmdtexte->bind('<Return>' => [\&fill_text,$text, \$cmd,$insert]); + $cmdlabel->pack(-side => 'left'); + $cmdtexte->pack(-side => 'left'); + + my $searchtext = ''; + my $searchlabel = $entries->Label(-text => "Search for: "); + my $searchtexte = $entries->Entry(-textvariable => \$searchtext, + -relief => "sunken"); + + $searchtexte->pack(-side => 'right'); + $searchlabel->pack(-side => 'right'); + $searchtexte->bind('<Return>' => [sub { $text->tag('remove','search','0.0','end'); + my($current, $length) = ('1.0', 0); + while (1) { + $current = $text->search(-count => \$length, $searchtext, $current, 'end'); + last if not $current; + $text->tag('add', 'search', $current, "$current + $length char"); + $current = $text->index("$current + $length char"); + $text->tag('configure','search', + -background => + 'lightBlue');}}]); + + if (defined($title)) { + $newwin->title($title); + $newwin->iconname($title); + } + fill_text('',$text,\$cmd,$insert); +} + +sub fill_text { + my $dump = shift; + my $textw = shift; + my $cmd = shift; + my $insert = shift; + $textw->delete('1.0','end'); + if (defined($insert) && $insert) { + $textw->insert('end',"running: $$cmd\n\n"); + } + $textw->toplevel->update(); + $textw->toplevel->Busy(); + open(OUT,"$$cmd|"); + while (<OUT>) { + $textw->insert('end',$_); + $textw->toplevel->update(); + $textw->toplevel->Busy(); + } + close(OUT); + if (defined ($insert) && $insert) { + $textw->insert('end',"\ndone.\n"); + } + $textw->toplevel->Unbusy(); + $textw->Unbusy(); + addToLog("done: $$cmd"); +} + +sub display_help { + print " +Usage: snmpcheck [-x] [-n|y] [-h] [-H] [-V NUM] [-L] [-f] [[-a] HOSTS] + + -h\tDisplay this message. + -a\tcheck error log file AND hosts specified on command line. + -p\tDon't try and ping-echo the host first + -f\tOnly check for things I can fix + HOSTS\tcheck these hosts for problems. + +X Options: + -x\tforces ascii base if \$DISPLAY set (instead of tk). + -H\tstart in hidden mode. (hides user interface) + -V NUM\tsets the initial verbosity level of the command log (def: 1) + -L\tShow the log window at startup + -d\tDon't start by checking anything. Just bring up the interface. + +Ascii Options: + -n\tDon't ever try and fix the problems found. Just list. + -y\tAlways fix problems found. + +"; + exit(0); + +} + +sub option_get { + my $resource = shift; + return $top->option('get',$resource); +} + +sub option_set { + my $resource = shift; + my $value = shift; + $top->option('add',"*$resource",$value); +} + +sub option_save { + +} + +sub mibsort { + $_ = $a; + ($av) = /\.([0-9]+)/; + $_ = $b; + ($bv) = /\.([0-9]+)/; + return $av <=> $bv; +} + +sub setmibchecklist { + my $i; + foreach $i (keys(%mibchecklist)) { + $mibchecklist{$i} = 0; + } + foreach $i (@_) { + $mibchecklist{$i} = 1; + } +} diff --git a/local/snmpconf b/local/snmpconf new file mode 100755 index 0000000..37a5136 --- /dev/null +++ b/local/snmpconf @@ -0,0 +1,933 @@ +#!/usr/bin/perl -w + +# +# A simple configuration file builder based on questions listed in +# its own configuration file. It would certainly be easy to use this +# for other (non-snmp) programs as well. +# + +use Getopt::Std; +use Term::ReadLine; +use IO::File; +use Data::Dumper; +use File::Copy; +if ($^O eq 'MSWin32') { + eval 'require Win32::Registry;'; + if ($@) { + print "\nWarning: Perl module Win32::Registry is not installed. This module is\n"; + print " required to read the SNMPSHAREPATH and SNMPCONFPATH values from \n"; + print " the registry. To use snmpconf without the module you need to\n"; + print " define SNMPSHAREPATH and SNMPCONFPATH as environment variables\n"; + print " or use the -c and -I command line options.\n"; + } +} + +# globals +%tokenitems=qw(line 1 info 1 comment 1); +%arrayitems=qw(question 1 validanswer 1); + +# default folder for snmpconf-data +if (defined(&my_getenv("SNMPSHAREPATH"))) { + $opts{'c'} = &my_getenv("SNMPSHAREPATH") . "/snmpconf-data"; +} +else { + $opts{'c'} = "/usr/local/share/snmp/snmpconf-data"; +} + +# default config file path +if (defined(&my_getenv("SNMPCONFPATH"))) { + $confpath = &my_getenv("SNMPCONFPATH"); +} +else { + $confpath = "/usr/local/share/snmp"; +} + +# home environment variable +if (defined(&my_getenv("HOME"))) { + $home = &my_getenv("HOME") . "/.snmp"; +} +else { + $home = "(HOME dir - n/a)"; +} + +# read the argument string +getopts("qadhfc:piI:r:R:g:G", \%opts); + +# display help +if ($opts{'h'}) { + print "$0 [options] [FILETOCREATE...]\n"; + print "options:\n"; + print " -f overwrite existing files without prompting\n"; + print " -i install created files into $confpath.\n"; + print " -p install created files into $home.\n"; + print " -I DIR install created files into DIR.\n"; + print " -a Don't ask any questions, just read in current\n"; + print " current .conf files and comment them\n"; + print " -r all|none Read in all or none of the .conf files found.\n"; + print " -R file,... Read in a particular list of .conf files.\n"; + print " -g GROUP Ask a series of GROUPed questions.\n"; + print " -G List known GROUPs.\n"; + print " -c conf_dir use alternate configuration directory.\n"; + print " -q run more quietly with less advice.\n"; + print " -d turn on debugging output.\n"; + print " -D turn on debugging dumper output.\n"; + exit; +} + +# setup terminal interface. +$ENV{'PERL_RL'}='o=0' if (!exists($ENV{'PERL_RL'})); +$term = new Term::ReadLine 'snmpconf'; + +# read in configuration file set +read_config_files($opts{'c'}, \%filetypes); +debug(my_Dumper(\%filetypes)); + +if ($opts{'G'}) { + Print("\nKnown GROUPs of tokens:\n\n"); + foreach my $group (keys(%groups)) { + print " $group\n"; + } + Print("\n"); + exit; +} + +# +# Expand the search path in case it contains multiple directories +# separated by : (Unix) or ; (Win32) +# +my $ENV_SEPARATOR = ':'; +if ($^O eq 'MSWin32') { + $ENV_SEPARATOR = ';'; +} +my @searchpath = split(/$ENV_SEPARATOR/, $confpath); +push @searchpath, "/usr/local/etc/snmp"; +push @searchpath, "."; +push @searchpath, "$home"; + +# Remove trailing /'s or \'s +for (my $i=0; $i <= $#searchpath; $i++) { + $searchpath[$i] =~ /(.*?)([\/\\])*$/; + $searchpath[$i] = $1; +} + +# Determine persistent directory. Order of preference: +# +# file in SNMP_PERSISTENT_FILE environment variable +# directory defined by persistentDir snmp.conf variable +# directory in SNMP_PERSISTENT_DIR environment variable +# default PERSISTENT_DIRECTORY directory +my $persistentDir = ""; +my $persistentFile = ""; + +# SNMP_PERSISTENT_FILE environment variable +if (defined(&my_getenv("SNMP_PERSISTENT_FILE"))) { + $persistentFile = &my_getenv("SNMP_PERSISTENT_FILE"); + debug ("persistent file: SNMP_PERSISTENT_FILE environment variable set\n"); +} + +# snmp.conf persistentDir +if (!($persistentDir) && !($persistentFile)) { + foreach my $i (@searchpath) { + debug ("Searching file $i/snmp.conf for persistentDir\n"); + my $temp = get_persistentDir("$i/snmp.conf"); + if ($temp) { + debug("persistent directory: set to $temp in $i/snmp.conf\n"); + $persistentDir = $temp; + last; + } + } +} + +# SNMP_PERSISTENT_DIR environment variable +if (!($persistentDir) && !($persistentFile)) { + if (&my_getenv("SNMP_PERSISTENT_DIR")) { + $persistentDir = &my_getenv("SNMP_PERSISTENT_DIR"); + debug ("persistent directory: SNMP_PERSISTENT_DIR environment variable set\n"); + } +} + +# PERSISTENT_DIRECTORY default variable +if (!($persistentDir) && !($persistentFile)) { + $persistentDir = "/var/net-snmp"; + debug ("persistent directory: Using default value\n"); +} + +# Rebuild search path without persistent folder +# Note: persistent file handled in Find existing +# files to possibly read in section +if ($persistentDir) { + # Remove trailing /'s or \'s + $persistentDir =~ /(.*?)([\/\\])*$/; + $persistentDir = $1; + debug ("persistent directory: $persistentDir\n"); + + my @searchpath_old = @searchpath; + @searchpath = (); + foreach my $path_temp (@searchpath_old) { + if ($path_temp eq $persistentDir) { + debug("skipping persistent directory $path_temp\n"); + next; + } + push @searchpath, $path_temp; + } +} + +# Reset $confpath to the first path +$confpath = $searchpath[0]; + +# +# Find existing files to possibly read in. +# +push @searchpath, $opts{I} if ($opts{I}); +foreach my $i (@searchpath) { + debug("searching $i\n"); + foreach my $ft (keys(%filetypes)) { + if ("$i/$ft" eq $persistentFile) { + debug("skipping persistent file $i/$ft\n"); + next; + } + debug("searching for $i/$ft\n"); + $knownfiles{"$i/$ft"} = $ft if (-f "$i/$ft"); + my $localft = $ft; + $localft =~ s/.conf/.local.conf/; + $knownfiles{"$i/$localft"} = $ft if (-f "$i/$localft"); + } +} + +# +# Ask the user if they want them to be read in and read them +# +if (keys(%knownfiles)) { + my @files; + if (defined($opts{'r'})) { + if ($opts{'r'} eq "all" || $opts{'r'} eq "a") { + @files = keys(%knownfiles); + } elsif ($opts{'r'} ne "none" && $opts{'r'} ne "n") { + print "unknown argument to -r: $opts{'r'}\n"; + exit(1); + } + } elsif(defined($opts{'R'})) { + @files = split(/\s*,\s*/,$opts{'R'}); + foreach my $i (@files) { + my $x = $i; + $x =~ s/.*\/([^\/]+)$/$1/; + $knownfiles{$i} = $x; + } + Print("reading: ", join(",",@files),"\n"); + } else { + @files = display_menu(-head => "The following installed configuration files were found:\n", + -tail => "Would you like me to read them in? Their content will be merged with the\noutput files created by this session.\n\nValid answer examples: \"all\", \"none\",\"3\",\"1,2,5\"\n", + -multiple => 1, + -question => 'Read in which', + -defaultvalue => 'all', + sort keys(%knownfiles)); + } + foreach my $i (@files) { + debug("reading $i\n"); + read_config($i, $knownfiles{$i}); + } +} + +if ($opts{'g'}) { + my @groups = split(/,:\s/,$opts{'g'}); + foreach my $group (@groups) { + do_group($group); + } +} elsif ($#ARGV >= 0) { + # + # loop through requested files. + # + foreach my $i (@ARGV) { + if (!defined($filetypes{$i})) { + warn "invalid file: $i\n"; + } else { + if ($opts{'a'}) { + $didfile{$i} = 1; + } else { + build_file($term, $i, $filetypes{$i}); + } + } + } +} else { + # + # ask user to select file type to operate on. + # + while(1) { + my $line = display_menu(-head => "I can create the following types of configuration files for you.\nSelect the file type you wish to create:\n(you can create more than one as you run this program)\n", + -question => 'Select File', + -otheranswers => ['quit'], + -mapanswers => { 'q' => 'quit' }, + keys(%filetypes)); + last if ($line eq "quit"); + debug("file selected: $line\n"); + build_file($term, $line, $filetypes{$line}); + } +} + +# +# Write out the results to the output files. +# +output_files(\%filetypes, $term); + + +# +# Display the files that have been created for the user. +# +Print("\n\nThe following files were created:\n\n"); +@didfiles = keys(%didfile); +foreach my $i (@didfiles) { + if ($didfile{$i} ne "1") { + if ($opts{'i'} || $opts{'I'}) { + $opts{'I'} = "$confpath" if (!$opts{'I'}); + + if (! (-d "$opts{'I'}") && ! (mkdir ("$opts{'I'}", 0755))) { + print "\nCould not create $opts{'I'} directory: $!\n"; + print ("File $didfile{$i} left in current directory\n"); + } + else { + move ("$opts{'I'}/$i", "$opts{'I'}/$i.bak") if (-f "$opts{'I'}/$i"); + if (move ("$didfile{$i}", "$opts{'I'}")) { + print(" $didfile{$i} installed in $opts{'I'}\n"); + } + else { + print "\nCould not move file $didfile{$i} to $opts{'I'}/$i: $!\n"; + print ("File $didfile{$i} left in current directory\n"); + } + } + } elsif ($opts{'p'}) { + if (! (-d "$home") && ! (mkdir ("$home", 0755))) { + print "\nCould not create $home directory: $!\n"; + print ("File $didfile{$i} left in current directory\n"); + } + else { + move ("$home/$i", "$home/$i.bak") if (-f "$home/$i"); + if (move ("$didfile{$i}", "$home")) { + print(" $didfile{$i} installed in $home\n"); + } + else { + print "\nCould not move file $didfile{$i} to $home: $!\n"; + print ("File $didfile{$i} left in current directory\n"); + } + } + } else { + Print(" $didfile{$i} ", + ($i ne $didfile{$i})?"[ from $i specifications]":" ","\n"); + if ($opts{'d'}) { + open(I,$didfile{$i}); + debug(" " . join(" ",<I>) . "\n"); + close(I); + } + } + } +} + +if (!$opts{'p'} && !$opts{'i'} && !$opts{'I'}) { + Print("\nThese files should be moved to $confpath if you +want them used by everyone on the system. In the future, if you add +the -i option to the command line I'll copy them there automatically for you. + +Or, if you want them for your personal use only, copy them to +$home . In the future, if you add the -p option to the +command line I'll copy them there automatically for you. + +"); +} + +########################################################################### +# Functions +########################################################################### + +sub Print { + print @_ if (!$opts{'q'}); +} +# +# handle a group of questions +# +sub get_yn_maybe { + my $question = shift; + my $ans = "y"; + if ($question ne "") { + $ans = get_answer($term, $question, + valid_answers(qw(yes y no n)), 'y'); + } + return ($ans =~ /^y/)?1:0; +} + +sub do_group { + my $group = shift; + die "no such group $group\n" if (!$groups{$group}); + foreach my $token (@{$groups{$group}}) { + if ($token->[0] eq "message") { + Print ("$token->[1] $token->[2]\n"); + } elsif ($token->[0] eq "subgroup") { + do_group($token->[1]) if (get_yn_maybe($token->[2])); + } elsif (defined($tokenmap{$token->[1]})) { + if (get_yn_maybe($token->[2])) { + do { + do_line($token->[1], $tokenmap{$token->[1]}); + } until ($token->[0] ne "multiple" || + get_answer($term, "Do another $token->[1] line?", + valid_answers(qw(yes y no n)), 'y') + =~ /n/); + } + } elsif (defined($filetypes{$token->[1]})) { + $didfile{$token->[1]} = 1; + } else { + die "invalid member $token->[1] of group $group\n"; + } + } +} + +# +# build a particular type of file by operating on sections +# +sub build_file { + my ($term, $filename, $fileconf) = @_; + $didfile{$filename} = 1; + my (@lines); + while(1) { + my $line = display_menu(-head => "The configuration information which can be put into $filename is divided\ninto sections. Select a configuration section for $filename\nthat you wish to create:\n", + -otheranswers => ['finished'], + -mapanswers => { 'f' => 'finished' }, + -question => "Select section", + -numeric => 1, + map { $_->{'title'}[0] } @$fileconf); + + return @lines if ($line eq "finished"); + do_section($fileconf->[$line-1]); + } +} + +# +# configure a particular section by operating on token types +# +sub do_section { + my $confsect = shift; + my @lines; + while(1) { + Print ("\nSection: $confsect->{'title'}[0]\n"); + Print ("Description:\n"); + Print (" ", join("\n ",@{$confsect->{'description'}}),"\n"); + my $line = + display_menu(-head => "Select from:\n", + -otheranswers => ['finished','list'], + -mapanswers => { 'f' => 'finished', + 'l' => 'list' }, + -question => 'Select section', + -descriptions => [map { $confsect->{$_}{info}[0] } + @{$confsect->{'thetokens'}}], + @{$confsect->{'thetokens'}}); + return @lines if ($line eq "finished"); + if ($line eq "list") { + print "Lines defined for section \"$confsect->{title}[0]\" so far:\n"; + foreach my $i (@{$confsect->{'thetokens'}}) { + if ($#{$confsect->{$i}{'results'}} >= 0) { + print " ",join("\n ",@{$confsect->{$i}{'results'}}),"\n"; + } + } + next; + } + do_line($line, $confsect->{$line}); + } + return; +} + +# +# Ask all the questions related to a particular line type +# +sub do_line { + my $token = shift; + my $confline = shift; + my (@answers, $counter, $i); +# debug(my_Dumper($confline)); + Print ("\nConfiguring: $token\n"); + Print ("Description:\n ",join("\n ",@{$confline->{'info'}}),"\n\n"); + for($i=0; $i <= $#{$confline->{'question'}}; $i++) { + if (defined($confline->{'question'}[$i]) && + $confline->{'question'}[$i] ne "") { + my $q = $confline->{'question'}[$i]; + $q =~ s/\$(\d+)/$answers[$1]/g; + debug("after: $term, $q, ",$confline->{'validanswer'}[$i],"\n"); + $answers[$i] = get_answer($term, $q, + $confline->{'validanswer'}[$i]); + $answers[$i] =~ s/\"/\\\"/g; + $answers[$i] = '"' . $answers[$i] . '"' if ($answers[$i] =~ /\s/); + } + } + if ($#{$confline->{'line'}} == -1) { + my ($i,$line); + for($i=0; $i <= $#{$confline->{'question'}}; $i++) { + next if (!defined($confline->{'question'}[$i]) || + $confline->{'question'}[$i] eq ""); + $line .= " \$" . $i; + } + push @{$confline->{'line'}}, $line; + } + + foreach my $line (@{$confline->{'line'}}) { + my $finished = $line; + debug("preline: $finished\n"); + debug("answers: ",my_Dumper(\@answers)); + $finished =~ s/\$(\d+)/$answers[$1]/g; + if ($line =~ s/^eval\s+//) { + debug("eval: $finished\n"); + $finished = eval $finished; + debug("eval results: $finished\n"); + } + $finished = $token . " " . $finished; + Print ("\nFinished Output: $finished\n"); + push @{$confline->{'results'}},$finished; + } +} + +# +# read all sets of config files in the various subdirectories. +# +sub read_config_files { + my $readdir = shift; + my $filetypes = shift; + opendir(DH, $readdir) || die "no such directory $readdir, did you run make install?\n"; + my $dir; + my $configfilename="snmpconf-config"; + + while(defined($dir = readdir(DH))) { + next if ($dir =~ /^\./); + next if ($dir =~ /CVS/); + debug("dir entry: $dir\n"); + if (-d "$readdir/$dir" && -f "$readdir/$dir/$configfilename") { + + my $conffile; + + # read the top level configuration inforamation about the direcotry. + open(I, "$readdir/$dir/$configfilename"); + while(<I>) { + $conffile = $1 if (/forconffile: (.*)/); + } + close(I); + + # no README informatino. + if ($conffile eq "") { + print STDERR "Warning: No 'forconffile' information in $readdir/$dir/$configfilename\n"; + next; + } + + # read all the daat in the directory + $filetypes->{$conffile} = read_config_items("$readdir/$dir", $conffile); + } else { + # no README informatino. + print STDERR "Warning: No $configfilename file found in $readdir/$dir\n"; + } + } + closedir DH; +} + +# +# read each configuration file in a directory +# +sub read_config_items { + my $itemdir = shift; + my $type = shift; + opendir(ITEMS, $itemdir); + my $file; + my @results; + while(defined($file = readdir(ITEMS))) { + next if ($file =~ /~$/); + next if ($file =~ /^snmpconf-config$/); + if (-f "$itemdir/$file") { + my $res = read_config_item("$itemdir/$file", $type); + if (scalar(keys(%$res)) > 0) { + push @results, $res; + } + } + } + closedir(ITEMS); + return \@results; +} + +# +# mark a list of tokens as a special "group" +# +sub read_config_group { + my ($fh, $group, $type) = @_; + my $line; + debug("handling group $group\n"); + push (@{$groups{$group}},['filetype', $type]); + while($line = <$fh>) { + chomp($line); + next if ($line =~ /^\s*$/); + next if ($line =~ /^\#/); + return $line if ($line !~ /^(single|multiple|message|filetype|subgroup)/); + my ($type, $token, $rest) = ($line =~ /^(\w+)\s+([^\s]+)\s*(.*)/); + debug ("reading group $group : $type -> $token -> $rest\n"); + push (@{$groups{$group}}, [$type, $token, $rest]); + } + return; +} + + +# +# Parse one file +# +sub read_config_item { + my $itemfile = shift; + my $itemcount; + my $type = shift; + my $fh = new IO::File($itemfile); + return if (!defined($fh)); + my (%results, $curtoken); + debug("tokenitems: ", my_Dumper(\%tokenitems)); + topwhile: + while($line = <$fh>) { + next if ($line =~ /^\s*\#/); + my ($token, $rest) = ($line =~ /^(\w+)\s+(.*)/); + next if (!defined($token) || !defined($rest)); + while ($token eq 'group') { + # handle special group list + my $next = read_config_group($fh, $rest,$type); + if ($next) { + ($token, $rest) = ($next =~ /^(\w+)\s+(.*)/); + } else { + next topwhile; + } + } + debug("token: $token => $rest\n"); + if ($token eq 'steal') { + foreach my $stealfrom (keys(%{$results{$rest}})) { + if (!defined($results{$curtoken}{$stealfrom})) { + @{$results{$curtoken}{$stealfrom}} = + @{$results{$rest}{$stealfrom}}; + } + } + } elsif (defined($tokenitems{$token})) { + if (!defined($curtoken)) { + die "error in configuration file $itemfile, no token set\n"; + } + $rest =~ s/^\#//; + push @{$results{$curtoken}{$token}},$rest; + } elsif (defined($arrayitems{$token})) { + if (!defined($curtoken)) { + die "error in configuration file $itemfile, no token set\n"; + } + my ($num, $newrest) = ($rest =~ /^(\d+)\s+(.*)/); + if (!defined($num) || !defined($newrest)) { + warn "invalid config line: $line\n"; + } else { + $results{$curtoken}{$token}[$num] = $newrest; + } + } elsif ($token =~ /^token\s*$/) { + $rest = lc($rest); + $curtoken = $rest; + if (! exists $results{$curtoken}{'defined'}) { + push @{$results{'thetokens'}}, $curtoken; + $results{$curtoken}{'defined'} = 1; + } + $tokenmap{$curtoken} = $results{$curtoken}; + debug("current token set to $token\n"); + } else { + push @{$results{$token}},$rest; + } + } + return \%results; +} + +sub debug { + print @_ if ($opts{'d'}); +} + +sub output_files { + my $filetypes = shift; + my $term = shift; + foreach my $ft (keys(%$filetypes)) { + next if (!$didfile{$ft}); + my $outputf = $ft; + if (-f $outputf && !$opts{'f'}) { + print "\nError: An $outputf file already exists in this directory.\n\n"; + my $ans = get_answer($term,"'overwrite', 'skip', 'rename' or 'append'? ",valid_answers(qw(o overwrite r rename s skip a append))); + next if ($ans =~ /^(s|skip)$/i); + if ($ans =~ /^(a|append)/) { + $outputf = ">$outputf"; + } elsif ($ans =~ /^(r|rename)$/i) { + # default to rename for error conditions + $outputf = $term->readline("Save to what new file name instead (or 'skip')? "); + } + } + $didfile{$ft} = $outputf; + open(O,">$outputf") || warn "couldn't write to $outputf\n"; + print O "#" x 75,"\n"; + print O "#\n# $ft\n"; + print O "#\n# - created by the snmpconf configuration program\n#\n"; + foreach my $sect (@{$filetypes->{$ft}}) { + my $secthelp = 0; + foreach my $token (@{$sect->{'thetokens'}}) { + if ($#{$sect->{$token}{'results'}} >= 0) { + if ($secthelp++ == 0) { + print O "#" x 75,"\n# SECTION: ", + join("\n# ", @{$sect->{title}}), "\n#\n"; + print O "# ", join("\n# ",@{$sect->{description}}), + "\n"; + } + print O "\n# $token: ", + join("\n# ",@{$sect->{$token}{info}}), "\n\n"; + foreach my $result (@{$sect->{$token}{'results'}}) { + print O "$result\n"; + } + } + } + print O "\n\n\n"; + } + if ($#{$unknown{$ft}} > -1) { + print O "#\n# Unknown directives read in from other files by snmpconf\n#\n"; + foreach my $unknown (@{$unknown{$ft}}) { + print O $unknown,"\n"; + } + } + close(O); + } +} + +sub get_answer { + my ($term, $question, $regexp, $defaultval) = @_; + $question .= " (default = $defaultval)" if (defined($defaultval) && $defaultval ne ""); + $question .= ": "; + my $ans = $term->readline($question); + return $defaultval if ($ans eq "" && defined($defaultval) && + $defaultval ne ""); + while (!(!defined($regexp) || + $regexp eq "" || + $ans =~ /$regexp/)) { + print "invalid answer! It must match this regular expression: $regexp\n"; + $ans = $term->readline($question); + } + return $defaultval if ($ans eq "" && defined($defaultval) && + $defaultval ne ""); + return $ans; +} + +sub valid_answers { + my @list; + foreach $i (@_) { + push @list, $i if ($i); + } + return "^(" . join("|",@list) . ")\$"; +} + +sub read_config { + my $file = shift; + my $filetype = shift; + return if (!defined($filetypes{$filetype})); + if (! -f $file) { + warn "$file does not exist\n"; + return; + } + open(I,$file); + while(<I>) { + next if (/^\s*\#/); + next if (/^\s*$/); + chomp; + my ($token, $rest) = /^\s*(\w+)\s+(.*)/; + $token = lc($token); + next if (defined($alllines{$_})); # drop duplicate lines + if (defined($tokenmap{$token})) { + push @{$tokenmap{$token}{'results'}},$_; + } else { + push @{$unknown{$filetype}},$_; + } + $alllines{$_}++; + } + close(I); +} + +sub display_menu { + my %config; + + while ($#_ > -1 && $_[0] =~ /^-/) { + my $key = shift; + $config{$key} = shift; + } + + my $count=1; + print "\n" if (!defined($config{'-dense'})); + if ($config{'-head'}) { + print $config{'-head'}; + print "\n" if (!defined($config{'-dense'})); + } + my @answers = @_; + my @list; + if (defined($config{'-descriptions'}) && + ref($config{'-descriptions'}) eq "ARRAY") { + @list = @{$config{'-descriptions'}} + } else { + @list = @_; + } + foreach my $i (@list) { + printf " %2d: $i\n", $count++ if ($i); + } + print "\n" if (!defined($config{'-dense'})); + if (defined($config{'-otheranswers'})) { + if (ref($config{'-otheranswers'}) eq 'ARRAY') { + print "Other options: ", join(", ", + @{$config{'-otheranswers'}}), "\n"; + push @answers, @{$config{'-otheranswers'}}; + push @answers, keys(%{$config{'-mapanswers'}}); + } else { + my $maxlen = 0; + push @answers,keys(%{$config{'-otheranswers'}}); + foreach my $i (keys(%{$config{'-otheranswers'}})) { + $maxlen = length($i) if (length($i) > $maxlen); + } + foreach my $i (keys(%{$config{'-otheranswers'}})) { + printf(" %-" . $maxlen . "s: %s\n", $i, + $config{'-otheranswers'}{$i}); + } + } + print "\n" if (!defined($config{'-dense'})); + } + if ($config{'-tail'}) { + print $config{'-tail'}; + print "\n" if (!defined($config{'-dense'})); + } + + if (defined($config{'-question'})) { + while(1) { + my $numexpr; + if ($config{'-multiple'}) { + $numexpr = '[\d\s,]+|all|a|none|n'; + } else { + $numexpr = '\d+'; + } + push @answers,"" if ($config{'-defaultvalue'}); + $ans = get_answer($term, $config{'-question'}, + valid_answers($numexpr,@answers), + $config{'-defaultvalue'}); + if ($config{'-mapanswers'}{$ans}) { + $ans = $config{'-mapanswers'}{$ans}; + } + + if ($ans =~ /^$numexpr$/) { + if ($config{'-multiple'}) { + my @list = split(/\s*,\s*/,$ans); + my @ret; + $count = 0; + foreach my $i (@_) { + $count++; + if ($ans eq "all" || $ans eq "a" + || grep(/^$count$/,@list)) { + push @ret, $i; + } + } + return @ret; + } else { + if ($ans <= 0 || $ans > $#_+1) { + warn "invalid selection: $ans [must be 1-" . + ($#_+1) . "]\n"; + } else { + return $ans if ($config{'-numeric'}); + $count = 0; + foreach my $i (@_) { + $count++; + if ($ans eq $count) { + return $i; + } + } + } + } + } else { + return $ans; + } + } + } +} + +sub my_Dumper { + if ($opts{'D'}) { + return Dumper(@_); + } else { + return "\n"; + } +} + +sub get_persistentDir { + my $file = shift; + my $result = 0; + if (! -f $file) { + return 0; + } + open(I,$file); + while(<I>) { + next if (/^\s*\#/); + next if (/^\s*$/); + chomp; + my ($token, $rest) = /^\s*(\w+)\s+(.*)/; + if (lc($token) eq "persistentdir") { + $result = $rest; + } + next; + } + close(I); + return $result; +} + +# Usage: &win32_reg_read("key", "value") +# Example: &win32_reg_read("SOFTWARE\\Net-SNMP","SNMPSHAREPATH"); +# Returns: Value if found in HKCU or HCLM. Otherwise an empty string. +sub win32_reg_read { + my $sub_key = shift; + my $value = shift; + + require Win32::Registry; + + my ($hkey, %key_values, $temp, $no_warn); + + # Try HKCU first + $no_warn = $HKEY_CURRENT_USER; + if ($HKEY_CURRENT_USER->Open($sub_key, $hkey)) + { + $hkey->GetValues(\%key_values); + foreach $temp (sort keys %key_values) { + if ($temp eq $value) { + return $key_values{$temp}[2]; + } + } + $hkey->Close(); + } + + # Try HKLM second + $no_warn = $HKEY_LOCAL_MACHINE; + if ($HKEY_LOCAL_MACHINE->Open($sub_key, $hkey)) + { + $hkey->GetValues(\%key_values); + foreach $temp (sort keys %key_values) { + if ($temp eq $value) { + return $key_values{$temp}[2]; + } + } + $hkey->Close(); + } + return ""; +} + +# Usage: &my_getenv("key") +# Example: &my_getenv("SNMPSHAREPATH"); +# Returns: Unix: Environment variable value (undef if not defined) +# Win32: HKCU\Software\Net-SNMP\(key) or +# Win32: HKLM\Software\Net-SNMP\(key) or +# Win32: Environment variable value (undef if not defined) +sub my_getenv { + my $key = shift; + + # Unix + if ($^O ne 'MSWin32') { + return $ENV{$key}; + } + # Windows + else { + my $temp = &win32_reg_read("SOFTWARE\\Net-SNMP","$key"); + if ($temp ne "") { + return $temp; + } + else { + return $ENV{$key}; + } + } +} + diff --git a/local/snmpconf.dir/snmp-data/authopts b/local/snmpconf.dir/snmp-data/authopts new file mode 100644 index 0000000..12cd8e5 --- /dev/null +++ b/local/snmpconf.dir/snmp-data/authopts @@ -0,0 +1,77 @@ +title Default Authentication Options +description This section defines the default authentication +description information. Setting these up properly in your +description ~/.snmp/snmp.conf file will greatly reduce the amount of +description command line arguments you need to type (especially for snmpv3). + +token defaultPort +info The default port number to use +info This token specifies the default port number you want packets to +info be sent to and received from. +info override: with -p on the command line. +info arguments: portnum +question 1 Enter the default port number to use + +token defVersion +info The default snmp version number to use. +info override: with -v on the command line. +info arguments: 1|2c|3 +question 1 Enter the default snmp version number to use (1|2c|3) +validanswer 1 ^(1|2c|3)$ + +token defCommunity +info The default snmpv1 and snmpv2c community name to use when needed. +info If this is specified, you don't need to include the community +info name as an argument to the snmp applications. +info override: with -c on the command line. +info arguments: communityname +question 1 Enter the default community name to use + +token defSecurityName +info The default snmpv3 security name to use when using snmpv3 +info override: with -u on the command line. +info arguments: securityname +question 1 Enter the default security name to use + +token defContext +info The default snmpv3 context name to use +info override: with -n on the command line. +info arguments: contextname +question 1 Enter the default context name to use + +token defSecurityLevel +info The default snmpv3 security level to use +info override: with -l on the command line. +info arguments: noAuthNoPriv|authNoPriv|authPriv +question 1 Enter the default privacy pass phrase to use +validanswer 1 ^(noAuthNoPriv|authNoPriv|authPriv|nanp|anp|ap)$ + +token defAuthType +info The default snmpv3 authentication type name to use +info override: with -a on the command line. +info arguments: authtype +question 1 Enter the default authentication type to use (MD5|SHA) +validanswer 1 ^(MD5|SHA)$ + +token defAuthPassphrase +info The default snmpv3 authentication pass phrase to use +info Note: It must be at least 8 characters long. +info override: with -A on the command line. +info arguments: passphrase +question 1 Enter the default authentication pass phrase to use + +token defPrivType +info The default snmpv3 privacy (encryption) type name to use +info override: with -x on the command line. +info arguments: privtype +question 1 Enter the default privacy type to use (DES|AES) +validanswer 1 ^(DES|AES)$ + +token defPrivPassphrase +info The default snmpv3 privacy pass phrase to use +info Note: It must be at least 8 characters long. +info override: with -X on the command line. +info arguments: passphrase +question 1 Enter the default privacy pass phrase to use + + diff --git a/local/snmpconf.dir/snmp-data/debugging b/local/snmpconf.dir/snmp-data/debugging new file mode 100644 index 0000000..90a271f --- /dev/null +++ b/local/snmpconf.dir/snmp-data/debugging @@ -0,0 +1,39 @@ +title Debugging output options +description This section allows debugging output of various kinds to +description be turned on or off. + +token doDebugging +info Turns debugging output on or off (0|1) +info arguments: (0|1) +question 1 Turn debugging on (0|1) +validanswer 1 ^(0|1)$ + +token debugTokens +info Debugging tokens specify which lines of debugging +info output you'd actually like to see. Each section of code is most +info likely instrumented with a particular "tag". So, to see that tag you +info would specify it here. Specifying a tag will match against all +info tags that begin with that prefix, so the tag "test" will match +info "test_function" and "test_something" and... +info There are a few special tokens as well: +info - ALL: turns on all the tokens (which generates lots of output) +info - trace: prints 'trace' lines showing source code files and +info - line numbers as they're traversed. +info - dump: Nicely breaks down packets as they're parsed or sent out. +info command line equivelent: -Dtoken[,token...] +info arguments: token[,token...] +question 1 Enter the tokens (comma seperated) you wish to see output for + +token dumpPacket +info Print packets as they are received or sent +info arguments: (1|yes|true|0|no|false) +info command line equivelent: -d +validanswer 1 ^(1|yes|true|0|no|false)$ +question 1 Print packets as they are received or sent + + +token noTokenWarnings +info Silence warnings about unknown tokens in configuration files +question 1 Silence warnings about unknown tokens in configuration files +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ diff --git a/local/snmpconf.dir/snmp-data/mibs b/local/snmpconf.dir/snmp-data/mibs new file mode 100644 index 0000000..c3cfd74 --- /dev/null +++ b/local/snmpconf.dir/snmp-data/mibs @@ -0,0 +1,56 @@ +title Textual mib parsing +description This section controls the textual mib parser. Textual +description mibs are parsed in order to convert OIDs, enumerated +description lists, and ... to and from textual representations +description and numerical representations. + +token mibdirs +info Specifies directories to be searched for mibs. +info Adding a '+' sign to the front of the argument appends the new +info directory to the list of directories already being searched. +info arguments: [+]directory[:directory...] +question 1 Enter the list of directories to search through for mibs + +token mibs +info Specifies a list of mibs to be searched for and loaded. +info Adding a '+' sign to the front of the argument appends the new +info mib name to the list of mibs already being searched for. +info arguments: [+]mibname[:mibname...] +question 1 Enter the list of mibs to read + +token mibfile +info Loads a particular mib file from a particualar path +info arguments: /path/to/mibfile +question 1 Enter the mib file name to read + +token showMibErrors +info Should errors in mibs be displayed when the mibs are loaded +question 1 Should errors in mibs be displayed when the mibs are loaded +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ + +token mibWarningLevel +info Should warnings about mibs be displayed when the mibs are loaded +question 1 Should warnings about mibs be displayed when the mibs are loaded +info arguments: 1|2 +validanswer 1 ^(1|2)$ + +token strictCommentTerm +info Be strict about about mib comment termination. +info Strictly follow comment rules about parsing mibs. +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ +question 1 Be strict about about mib comment termination + +token mibAllowUnderline +info Should underlines be allowed in mib symbols (illegal) +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ +question 1 Should underlines be allowed in mib symbols + +token mibReplaceWithLatest +info Force replacement of older mibs with known updated ones +question 1 Force replacement of older mibs with known updated ones +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ + diff --git a/local/snmpconf.dir/snmp-data/output b/local/snmpconf.dir/snmp-data/output new file mode 100644 index 0000000..ae3c561 --- /dev/null +++ b/local/snmpconf.dir/snmp-data/output @@ -0,0 +1,79 @@ +title Output style options +description This section allows you to control how the output of the +description various commands will be formated + +token logTimestamp +info Should timestamps be shown on the output +info arguments: (1|yes|true|0|no|false) +question 1 Should timestamps be shown on the output +validanswer 1 ^(1|yes|true|0|no|false)$ + +token printNumericEnums +info Print enums numericly or textually +info command line equivelent: -Oe +question 1 Print enums numericly +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ + +token printNumericOids +info Print OIDs numericly or textually +info command line equivelent: -On +question 1 Print enums numericly +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ + +token dontBreakdownOids +info When OIDs contain a index to a table, they are broken +info into the displayable pieces and shown to you. +info For example the oid vacmSecurityModel.0.3.119.101.115 +info is nicely broken down by +info default and the string hidden in the oid is shown +info to you as vacmSecurityModel.0."wes". This token and the -Ob +info option diables this feature and displays it as +info vacmSecurityModel.0.3.119.101.115 again. +info command line equivelent: -Ob +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ +question 1 Disable the breaking-down of OIDs? + +token escapeQuotes +info Should the quotation marks in broken down oids be escaped +info If you want to cut and paste oids that have been broken down +info into indexes and strings, this will put a backslash in front of them +info so your shell will pass them rather than interpret them. +info arguments: (1|yes|true|0|no|false) +question 1 Should the quotation marks in broken down oids be escaped +validanswer 1 ^(1|yes|true|0|no|false)$ + +token quickPrinting +info Make the output simple for quick parsing +info This option removes the equal sign and value identifies leaving +info just the oid and the value on the output for easier parsing in scripts +info command line equivelent: -Oq +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ +question 1 Make the output simple for quick parsing + +token numericTimeticks +info Print timeticks as a number and not a time-string +info command line equivelent: +info arguments: (1|yes|true|0|no|false) +question 1 Print timeticks as a number and not a time-string +validanswer 1 ^(1|yes|true|0|no|false)$ + +token suffixPrinting +info Shorten OIDs printed to the screen +info possible values: +info - 0: UCD-style. OIDs are displayed like: +info - system.sysUpTime.0 +info - 1: deletes all by the last symbolic part of the OID: +info - system.sysUpTime.0 becomes sysUpTime.0 +info - 2: is a variant of this, adding the name of the MIB +info - that defined this object: +info - system.sysUpTime.0 becomes SNMPv2-MIB::sysUpTime.0 +info - (This is the default with net-snmp v5) +info command line equivelent: 0 = -Ou, 1 = -Os, 2 = -OS +info arguments: (1|2) +question 1 Shorten OIDs (0|1|2) +validanswer 1 ^(0|1|2)$ + diff --git a/local/snmpconf.dir/snmp-data/snmpconf-config b/local/snmpconf.dir/snmp-data/snmpconf-config new file mode 100644 index 0000000..9be7d10 --- /dev/null +++ b/local/snmpconf.dir/snmp-data/snmpconf-config @@ -0,0 +1 @@ +forconffile: snmp.conf diff --git a/local/snmpconf.dir/snmpd-data/acl b/local/snmpconf.dir/snmpd-data/acl new file mode 100644 index 0000000..14f44a8 --- /dev/null +++ b/local/snmpconf.dir/snmpd-data/acl @@ -0,0 +1,36 @@ +title Access Control Setup +description This section defines who is allowed to talk to your running +description snmp agent. + +token rwuser +info a SNMPv3 read-write user +info arguments: user [noauth|auth|priv] [restriction_oid] +question 1 The SNMPv3 user that should have read-write access +question 2 The minimum security level required for that user [noauth|auth|priv, default = auth] +validanswer 2 (noauth|auth|priv|) +question 3 The OID that this community should be restricted to [if appropriate] + +token rouser +info a SNMPv3 read-only user +info arguments: user [noauth|auth|priv] [restriction_oid] +steal rwuser +question 1 Enter the SNMPv3 user that should have read-only access to the system + +token rocommunity +info a SNMPv1/SNMPv2c read-only access community name +info arguments: community [default|hostname|network/bits] [oid] +question 1 The community name to add read-only access for +question 2 The hostname or network address to accept this community name from [RETURN for all] +question 3 The OID that this community should be restricted to [RETURN for no-restriction] + +token rwcommunity +info a SNMPv1/SNMPv2c read-write access community name +info arguments: community [default|hostname|network/bits] [oid] +steal rocommunity +question 1 Enter the community name to add read-write access for + +group access_control +multiple rwuser Do you want to allow SNMPv3 read-write user based access +multiple rouser Do you want to allow SNMPv3 read-only user based access +multiple rwcommunity Do you want to allow SNMPv1/v2c read-write community access +multiple rocommunity Do you want to allow SNMPv1/v2c read-only community access diff --git a/local/snmpconf.dir/snmpd-data/basic_setup b/local/snmpconf.dir/snmpd-data/basic_setup new file mode 100644 index 0000000..28721e1 --- /dev/null +++ b/local/snmpconf.dir/snmpd-data/basic_setup @@ -0,0 +1,17 @@ +group basic_setup +message ************************************************ +message *** Beginning basic system information setup *** +message ************************************************ +subgroup system_setup Do you want to configure the information returned in the system MIB group (contact info, etc)? +message ************************************** +message *** BEGINNING ACCESS CONTROL SETUP *** +message ************************************** +subgroup access_control Do you want to configure the agent's access control? +message **************************************** +message *** Beginning trap destination setup *** +message **************************************** +subgroup trapsinks Do you want to configure where and if the agent will send traps? +message **************************************** +message *** Beginning monitoring setup *** +message **************************************** +subgroup monitoring_services Do you want to configure the agent's ability to monitor various aspects of your system? diff --git a/local/snmpconf.dir/snmpd-data/extending b/local/snmpconf.dir/snmpd-data/extending new file mode 100644 index 0000000..039b103 --- /dev/null +++ b/local/snmpconf.dir/snmpd-data/extending @@ -0,0 +1,68 @@ +title Extending the Agent +description You can extend the snmp agent to have it return information +description that you yourself define. + +token exec +info run a simple command using exec() +info arguments: [oid] name /path/to/executable arguments +question 1 The OID where the results table should be display [default=extTable] +question 2 The "name" to associate with this command when displaying the results. +question 3 The path to the program to be run. +question 4 The arguments to pass to $3 + +token pass +info Run a command that intepretes the request for an entire tree. +info The pass program defined here will get called for all +info requests below a certain point in the mib tree. It is then +info responsible for returning the right data beyond that point. +info # +info arguments: miboid program +info # +info example: pass .1.3.6.1.4.1.2021.255 /path/to/local/passtest +info # +info See the snmpd.conf manual page for further information. +info # +info Consider using "pass_persist" for a performance increase. +question 1 The OID where the script should take control of +question 2 The path to the program that should be called + +token pass_persist +info Run a persistant process that intepretes the request for an entire tree. +info The pass program defined here will get called for all +info requests below a certain point in the mib tree. It is then +info responsible for returning the right data beyond that point. +info The pass_persist scripts must be able to stay running and accept input +info from stdin. +info # +info arguments: miboid program +info # +info example: pass_persist .1.3.6.1.4.1.2021.255 /path/to/local/pass_persisttest +info # +info See the snmpd.conf manual page for further information. +steal pass + +token proxy +info Proxy requests to an external agent running somewhere else +info This passes all requests for a certain point of the mib tree to +info an external agent using snmp requests and then returning the +info results to the caller that spoke to our agent. +info arguments: [snmpcmd args] host oid [remoteoid] +question 1 Enter the "snmpcmd" arguments that specify how to talk to the remote host +question 2 The host you want to pass the requests to +qusetion 3 The oid that we should pass beyond +question 4 The oid of the remote site that we should talk to if different from $3 + +token sh +info run a simple command using system() +info arguments: [oid] name command arguments +info similar to exec, but implemented using system() instead of exec() +info # +info For security reasons, exec should be preferred. +steal exec + +token dlmod +info dynamically extend the agent using a shared-object +info arguments: module-name module-path +question 1 Enter the name of the module +question 2 Enter the path to the $1 module + diff --git a/local/snmpconf.dir/snmpd-data/monitor b/local/snmpconf.dir/snmpd-data/monitor new file mode 100644 index 0000000..9458793 --- /dev/null +++ b/local/snmpconf.dir/snmpd-data/monitor @@ -0,0 +1,72 @@ +title Monitor Various Aspects of the Running Host +description The following check up on various aspects of a host. + +token proc +info Check for processes that should be running. +info # proc NAME [MAX=0] [MIN=0] +info # +info # NAME: the name of the process to check for. It must match +info # exactly (ie, http will not find httpd processes). +info # MAX: the maximum number allowed to be running. Defaults to 0. +info # MIN: the minimum number to be running. Defaults to 0. +info # +info The results are reported in the prTable section of the UCD-SNMP-MIB tree +info Special Case: When the min and max numbers are both 0, it assumes +info you want a max of infinity and a min of 1. +question 1 Name of the process you want to check on +question 2 Maximum number of processes named '$1' that should be running [default = 0] +question 3 Minimum number of processes named '$1' that should be running [default = 0] + +token disk +info Check for disk space usage of a partition. +info The agent can check the amount of available disk space, and make +info sure it is above a set limit. +info +info # disk PATH [MIN=100000] +info # +info # PATH: mount path to the disk in question. +info # MIN: Disks with space below this value will have the Mib's errorFlag set. +info # Can be a raw integer value (units of kB) or a percentage followed by the % +info # symbol. Default value = 100000. +info # +info The results are reported in the dskTable section of the UCD-SNMP-MIB tree +question 1 Enter the mount point for the disk partion to be checked on +question 2 Enter the minimum amount of space that should be available on $1 + +token load +info Check for unreasonable load average values. +info Watch the load average levels on the machine. +info +info # load [1MAX=12.0] [5MAX=12.0] [15MAX=12.0] +info # +info # 1MAX: If the 1 minute load average is above this limit at query +info # time, the errorFlag will be set. +info # 5MAX: Similar, but for 5 min average. +info # 15MAX: Similar, but for 15 min average. +info # +info The results are reported in the laTable section of the UCD-SNMP-MIB tree +question 1 Enter the maximum allowable value for the 1 minute load average +question 2 Enter the maximum allowable value for the 5 minute load average +question 3 Enter the maximum allowable value for the 15 minute load average +validanswer 1 ^[\d\.]+$ +validanswer 2 ^([\d\.]+|)$ +validanswer 3 ^([\d\.]+|)$ + +token file +info Check on the size of a file. +info Display a files size statistics. +info If it grows to be too large, report an error about it. +info +info # file /path/to/file [maxsize_in_kilobytes] +info # +info # if maxsize is not specified, assume only size reporting is needed. +info # +info The results are reported in the fileTable section of the UCD-SNMP-MIB tree +question 1 Enter the path to the file you wish to monitor +question 2 Enter the maximum size (in kilobytes) allowable for $1 + +group monitoring_services +multiple proc Do you want to configure the agents ability to monitor processes? +multiple disk Do you want to configure the agents ability to monitor disk space? +multiple load Do you want to configure the agents ability to monitor load average? +multiple file Do you want to configure the agents ability to monitor file sizes? diff --git a/local/snmpconf.dir/snmpd-data/operation b/local/snmpconf.dir/snmpd-data/operation new file mode 100644 index 0000000..8a286e5 --- /dev/null +++ b/local/snmpconf.dir/snmpd-data/operation @@ -0,0 +1,32 @@ +title Agent Operating Mode +description This section defines how the agent will operate when it +description is running. + +token master +info Should the agent operate as a master agent or not. +info Currently, the only supported master agent type for this token +info is "agentx". +info # +info arguments: (on|yes|agentx|all|off|no) +question 1 Should the agent run as a AgentX master agent? +validanswer 1 ^(on|yes|agentx|all|off|no)$ + +token agentuser +info The system user that the agent runs as. +info arguments: name|#uid +question 1 Enter the name of the user that you want the agent to run as + +token agentgroup +info The system group that the agent runs as. +info arguments: group|#GID +question 1 Enter the name of the group that you want the agent to run as + +token agentaddress +info The IP address and port number that the agent will listen on. +info By default the agent listens to any and all traffic from any +info interface on the default SNMP port (161). This allows you to +info specify which address, interface, transport type and port(s) that you +info want the agent to listen on. Multiple definitions of this token +info are concatenated together (using ':'s). +info arguments: [transport:]port[@interface/address],... +question 1 Enter the port numbers, etc that you want the agent to listen to diff --git a/local/snmpconf.dir/snmpd-data/snmpconf-config b/local/snmpconf.dir/snmpd-data/snmpconf-config new file mode 100644 index 0000000..49d06bb --- /dev/null +++ b/local/snmpconf.dir/snmpd-data/snmpconf-config @@ -0,0 +1 @@ +forconffile: snmpd.conf diff --git a/local/snmpconf.dir/snmpd-data/system b/local/snmpconf.dir/snmpd-data/system new file mode 100644 index 0000000..4cd09b9 --- /dev/null +++ b/local/snmpconf.dir/snmpd-data/system @@ -0,0 +1,43 @@ +title System Information Setup +description This section defines some of the information reported in +description the "system" mib group in the mibII tree. + +token syslocation +info The [typically physical] location of the system. +info Note that setting this value here means that when trying to +info perform an snmp SET operation to the sysLocation.0 variable will make +info the agent return the "notWritable" error code. IE, including +info this token in the snmpd.conf file will disable write access to +info the variable. +info arguments: location_string +question 1 The location of the system + +token syscontact +info The contact information for the administrator +info Note that setting this value here means that when trying to +info perform an snmp SET operation to the sysContact.0 variable will make +info the agent return the "notWritable" error code. IE, including +info this token in the snmpd.conf file will disable write access to +info the variable. +info arguments: contact_string +question 1 The contact information + +token sysservices +info The proper value for the sysServices object. +info arguments: sysservices_number +question 1 does this host offer physical services (eg, like a repeater) [answer 0 or 1] +question 2 does this host offer datalink/subnetwork services (eg, like a bridge) +question 3 does this host offer internet services (eg, supports IP) +question 4 does this host offer end-to-end services (eg, supports TCP) +question 7 does this host offer application services (eg, supports SMTP) +validanswer 1 ^(0|1)$ +validanswer 2 ^(0|1)$ +validanswer 3 ^(0|1)$ +validanswer 4 ^(0|1)$ +validanswer 7 ^(0|1)$ +line eval $1*1 + $2*2 + $3*4 + $4*8 + $7*64 + +group system_setup +single syslocation +single syscontact +single sysservices Do you want to properly set the value of the sysServices.0 OID (if you don't know, just say no)? diff --git a/local/snmpconf.dir/snmpd-data/trapsinks b/local/snmpconf.dir/snmpd-data/trapsinks new file mode 100644 index 0000000..cc29b66 --- /dev/null +++ b/local/snmpconf.dir/snmpd-data/trapsinks @@ -0,0 +1,46 @@ +title Trap Destinations +description Here we define who the agent will send traps to. + +token trapsink +info A SNMPv1 trap receiver +info arguments: host [community] [portnum] +question 1 A host name that should receive the trap +question 2 The community to be used in the trap sent [optional] +question 3 The port number the trap should be sent to [optional] +validanswer 3 ^(\d+|)$ + +token trap2sink +info A SNMPv2c trap receiver +info arguments: host [community] [portnum] +steal trapsink + +token informsink +info A SNMPv2c inform (acknowledged trap) receiver +info arguments: host [community] [portnum] +steal trapsink + +token trapsess +info A generic trap receiver defined using snmpcmd style arguments. +info Read the snmpcmd manual page for further information. +info arguments: [snmpcmdargs] host +question 1 Specify the command line snmpcmd style options for this host +question 2 Specify the host name + +token trapcommunity +info Default trap sink community to use +info arguments: community-string +question 1 The default community name to use when sending traps + + +token authtrapenable +info Should we send traps when authentication failures occur +info arguments: 1 | 2 (1 = yes, 2 = no) +question 1 Should traps be sent when authentication failures occur? (1=yes, 2=no) +validanswer 1 ^(1|2)$ + +group trapsinks +single authtrapenable Do you want the agent to send snmp traps on snmp authentication failures? +single trapcommunity +multiple informsink Do you want the agent to send snmpv2c informs to a trap receiver +multiple trap2sink Do you want the agent to send snmpv2c traps to a trap receiver +multiple trapsink Do you want the agent to send snmpv1 traps to a trap receiver diff --git a/local/snmpconf.dir/snmptrapd-data/authentication b/local/snmpconf.dir/snmptrapd-data/authentication new file mode 100644 index 0000000..6591b2d --- /dev/null +++ b/local/snmpconf.dir/snmptrapd-data/authentication @@ -0,0 +1,8 @@ +title Authentication options +description Authentication options + +token ignoreAuthFailure +info Ignore authentication failure traps +info arguments: (1|yes|true|0|no|false) +question 1 Ignore authentication failure traps +validanswer 1 ^(1|yes|true|0|no|false) diff --git a/local/snmpconf.dir/snmptrapd-data/formatting b/local/snmpconf.dir/snmptrapd-data/formatting new file mode 100644 index 0000000..5b824b2 --- /dev/null +++ b/local/snmpconf.dir/snmptrapd-data/formatting @@ -0,0 +1,15 @@ +title Output formatting for traps received. +description Output from snmptrapd is formatted according to the +description rules defined by the formatting configuration directives. + +token format1 +info How SNMPv1 traps are formatted. +info See the snmptrapd.conf manual page for format string details. +info arguments: formatstring +question 1 The format specification string for SNMPv1 traps + +token format2 +info How SNMPv2 and SNMPv3 traps are formatted. +info See the snmptrapd.conf manual page for format string details. +info arguments: formatstring +question 1 The format specification string for SNMPv2 and SNMPv3 traps. diff --git a/local/snmpconf.dir/snmptrapd-data/logging b/local/snmpconf.dir/snmptrapd-data/logging new file mode 100644 index 0000000..81abe37 --- /dev/null +++ b/local/snmpconf.dir/snmptrapd-data/logging @@ -0,0 +1,26 @@ +title Logging options +description Logging options + +token doNotLogTraps +info Prevent traps from being logged +info Useful when you only want to use traphandles +info arguments: (1|yes|true|0|no|false) +question 1 Should traps be logged +validanswer 1 ^(1|yes|true|0|no|false) + +token logOption +info Set options controlling where to log to +info See -L options in the snmptrapd.conf man page +question 1 Logging options + +token outputOption +info Toggle options controlling output display +info See -O options in the snmptrapd.conf man page +question 1 Logging options + +token printEventNumbers +info Print event numbers (rising/falling alarm, etc.) +info arguments: (1|yes|true|0|no|false) +question 1 Print event numbers +validanswer 1 ^(1|yes|true|0|no|false) + diff --git a/local/snmpconf.dir/snmptrapd-data/runtime b/local/snmpconf.dir/snmptrapd-data/runtime new file mode 100644 index 0000000..5a342c2 --- /dev/null +++ b/local/snmpconf.dir/snmptrapd-data/runtime @@ -0,0 +1,13 @@ +title Runtime options +description Runtime options + +token doNotFork +info Do not fork from the shell +question 1 Do not fork from the shell +info arguments: (1|yes|true|0|no|false) +validanswer 1 ^(1|yes|true|0|no|false)$ + +token pidFile +info Store Process ID in file +info arguments: PID file +question 1 PID file diff --git a/local/snmpconf.dir/snmptrapd-data/snmpconf-config b/local/snmpconf.dir/snmptrapd-data/snmpconf-config new file mode 100644 index 0000000..6f1e4f7 --- /dev/null +++ b/local/snmpconf.dir/snmptrapd-data/snmpconf-config @@ -0,0 +1 @@ +forconffile: snmptrapd.conf diff --git a/local/snmpconf.dir/snmptrapd-data/traphandle b/local/snmpconf.dir/snmptrapd-data/traphandle new file mode 100644 index 0000000..384364e --- /dev/null +++ b/local/snmpconf.dir/snmptrapd-data/traphandle @@ -0,0 +1,17 @@ +title Trap Handlers +description Here we define what programs are run when a trap is +description received by the trap receiver. + +token traphandle +info When traps are received, a program can be run. +info When traps are received, the list of configured trap +info handles is consulted and any configured program is run. +info If no handler is found, any handler with "default" as the +info traphandle type is run instead. The information contained +info in trap is passed to the program via standard input (see +info the snmptrapd.conf manual page for details). +info # +info arguments: oid|"default" program args +question 1 The oid of the trap you want to handle. +question 2 The program you want to run If the program is a script, specify the script program first (ie /bin/sh /path/to/script). +question 3 Arguments that you want passed to the program diff --git a/local/snmpdump.pl b/local/snmpdump.pl new file mode 100755 index 0000000..0220beb --- /dev/null +++ b/local/snmpdump.pl @@ -0,0 +1,107 @@ +#!/usr/bin/perl +# +# Reformat 'snmpcmd -d' style raw dump output +# into something a little easier to understand. +# + + +sub parse_dump { + # + # Basic formatting technique: + # Display the contents of each nested SEQUENCE + # indented from the enclosing level. + # Individual data fields are all on one line + # + my @data = @_; + my $indent = shift( @data ); + my $datalen = shift( @data ); + + while ( $datalen > 0 ) { + my ($tag, $tlen, $tmp); + my ($tag1, $tag2 ); + $tag = shift( @data ); + $tmp = shift( @data ); + $tlen = hex($tmp); + # + # Handle 2-octet lengths + if ( $tlen >= 128 ) { + $tlen -= 128; + $tmp = shift( @data ); + $tlen += hex($tmp); + } + $datalen -= ($tlen + 2 ); + + $tag1 = substr($tag, 0, 1); + $tag2 = substr($tag, 1, 1); + + # + # Sequence-based tags - display and indent + # + if ( $tag1 eq 3 ) { + print " "x$indent, "$tag $tmp\n"; + parse_dump( $indent+3, $tlen, @data ); + } + elsif ( $tag1 eq "A" ) { + print " "x$indent, "$tag $tmp\n"; + parse_dump( $indent+3, $tlen, @data ); + } + + # + # Leaf-data tags - just display + # + else { + $val = ""; + while ( $tlen > 0 ) { + $val .= " "; + $val .= shift( @data ); + $tlen--; + } + if ( $tag1 eq "0" ) { # leaf data + print " "x$indent, "$tag $tmp$val\n"; + } + elsif ( $tag1 eq "8" ) { # exceptions + print " "x$indent, "$tag $tmp$val\n"; + } + else { # unknown + print " "x$indent, "$tag $tmp$val\n"; + } + } + } +} + +$inpacket=0; +$rawdump=""; + +while (<>) { + if ( $inpacket ) { + # + # Strip off the extraneous junk, and join + # the raw dump output into a single line + # + if ( /^[0-9]*: / ) { + chomp; + s/^[0-9]*: //; + s/ .*$//; + s/ / /g; + $rawdump = "$rawdump $_"; + } else { + # + # Once this line is complete, display the + # dump in a vaguely sensible layout + # + @rawdata = split( " ", $rawdump ); + parse_dump( 3, $#rawdata, @rawdata ); + $inpacket=0; + $rawdump=""; + } + } else { + # + # Pass everything else through untouched + # + print; + if ( /^Sending / || /^Received / ) { + $inpacket=1; + $rawdump=""; + } + } +} diff --git a/local/tkmib b/local/tkmib new file mode 100755 index 0000000..560c64f --- /dev/null +++ b/local/tkmib @@ -0,0 +1,994 @@ +#!/usr/bin/perl +#!/usr/bin/perl -w + +require 5; + +# attempt to determine if they have the proper modules installed. + +# SNMP +my $havesnmp = eval {require SNMP;}; + +# the Tk packages + +my $havetk = eval {require Tk; + require Tk::Table; + require Tk::HList; + require Tk::FileSelect; + require Tk::Dialog;}; +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/ . The perl module is contained in +the perl/SNMP directory. See the INSTALL file there for +instructions. +"; +} + +if (!$havetk) { + print " +ERROR: You don't have the Tk module installed. You should be able to +install this by running (as root): + + perl -MCPAN -e 'install Tk' +"; +} + +if (!$havetk || !$havesnmp) { + print "\n"; + exit; +} + +if ($havetk) { + # Tk doesn't seem to like require so we force use here. + eval {import Tk; + import Tk::Table; + import Tk::HList; + import Tk::FileSelect; + import Tk::Dialog; + import SNMP;}; +} + +use Getopt::Std; +use Data::Dumper; + +$host = 'localhost'; +$OID = '.1.3.6.1'; +$opts{'f'} = $ENV{'HOME'} . "/.snmp/tkmibrc"; + +getopts("hp:v:a:A:x:X:n:u:l:r:t:o:c:Cf:", \%opts); + +# default session options +print "setting opts\n"; +%session_opts = ( + 'Community' => "public", + 'RemotePort' => 161, + 'Timeout' => 5000000, + 'Retries' => 5, + 'Version' => 1, + 'AuthProto' => 'MD5', + 'PrivProto' => 'DES', + 'AuthPass' => '', + 'PrivPass' => '', + 'Context' => '', + 'SecName' => 'initial', + 'SecLevel' => 'authNoPriv', + ); + +sub usage { + print " +tkmib [-C] [-o OID] [SNMPCMD arguments] [host] + -f CONFIG_FILE load CONFIG_FILE after starting up. (default: ~/.snmp/tkmibrc) + (use -f /dev/null to not read one). + + See the snmpcmd manual page for related SNMPCMD arguments. (Not all + options are currently supported.) +"; + exit(); +} + +usage() if ($opts{'h'}); + +# initialize defaults, may be overridden by config file below +@displayInfo=qw(type access status units hint moduleID enums indexes); +@saveoptions = ('displayoidas', 'writecolor', 'graphtime', 'graphdelta'); +$displayoidas='full'; +$writecolor = "blue"; +$graphtime=5; +$graphdelta=1; +foreach $i (@displayInfo) { + $displayInfoStates{$i} = 1; +} + + +# source config file +do $opts{'f'} if ($opts{'f'} && -f $opts{'f'}); + +$session_opts{'UseLongNames'} => 1; +$session_opts{'RemotePort'} = $opts{'p'} if ($opts{'p'}); +$session_opts{'Community'} = $opts{'c'} if ($opts{'c'}); +$session_opts{'Version'} = $opts{'v'} if ($opts{'v'}); +$session_opts{'AuthProto'} = $opts{'a'} if ($opts{'a'}); +$session_opts{'AuthPass'} = $opts{'A'} if ($opts{'A'}); +$session_opts{'PrivProto'} = $opts{'x'} if ($opts{'x'}); +$session_opts{'PrivPass'} = $opts{'X'} if ($opts{'X'}); +$session_opts{'Context'} = $opts{'n'} if ($opts{'n'}); +$session_opts{'SecName'} = $opts{'u'} if ($opts{'u'}); +$session_opts{'SecLevel'} = $opts{'l'} if ($opts{'l'}); +$session_opts{'Retries'} = $opts{'r'} if ($opts{'r'}); +$session_opts{'Timeout'} = $opts{'t'} if ($opts{'t'}); + +$host = shift if ($#ARGV > -1); +$session_opts{'Community'} = shift if ($#ARGV > -1); + +@graphcolors=qw(blue red green yellow purple); + +# initialize SNMP module +$SNMP::save_descriptions=1; +$SNMP::use_long_names=1; +$SNMP::use_enums=1; +$SNMP::verbose = 1; +my $tmpbd = 1; + +$top = MainWindow->new(); +$top->title("tkmib"); + +#Menus +$MenuFrame = $top->Frame(-relief => "raised",-borderwidth => 2); +$MenuFrame->pack(-fill => "x",-expand => 1); +$FileMenuBut = $MenuFrame->Menubutton(-pady => $tmpbd, -padx => $tmpbd, -text => "File", + -menuitems => + [ +# [Button => "Save Output", -command => [\&saveOutput]], + [Button => "Quit", -command => [\&exit]] + ]); +$FileMenuBut->pack(-side => 'left'); + +$MibMenuBut = $MenuFrame->Menubutton(-pady => $tmpbd, -padx => $tmpbd, -text => "Mib", + -menuitems => + [[Button => "Find a mib node", + -command => sub { my $var; + entryBox("Find a Mib Node", + "Enter a mib node name to search for:", + \$var, \&findANode );}], + [Button => "Load a New Mib File", -command => [\&loadNewMibFile]], + [Button => "Load a New Mib Module", + -command => sub { my $var; + entryBox("Load a Module", + "Enter a SNMP MIB module name to load:", + \$var, \&loadIt);}] + ]); +$MibMenuBut->pack(-side => 'left'); + +$OptMenuBut = $MenuFrame->Menubutton(-pady => $tmpbd, -padx => $tmpbd, -text => "Options", + -menuitems => + [[Cascade => "~Display", -menuitems => + [ + [Cascade => "~MIB Information"], + [Cascade => "~OID Display", -menuitems => + [ + [Radiobutton => 'full', -variable => \$displayoidas], + [Radiobutton => 'numeric', -variable => \$displayoidas], + [Radiobutton => 'short', -variable => \$displayoidas], + [Radiobutton => 'module', -variable => \$displayoidas] + ] + ], + [Button => "Writable Color", + -command => [\&entryBox,"Writable Color", + "Color for writable objects:", + \$writecolor]] + ]], + [Cascade => "Use SNMP Version", -menuitems => + [ + [Radiobutton => '1', -variable => \$session_opts{'Version'}], + [Radiobutton => '2c', -variable => \$session_opts{'Version'}], + [Radiobutton => '3', -variable => \$session_opts{'Version'}] + ] + ], # ends version number specification + [Cascade => "SNMPv1/2c options", -menuitems => + [ + [Button => "Community Name", + -command => [\&entryBox,"Community Name", "Community name to use:", + \$session_opts{'Community'}]] + ] + ], + [Cascade => "SNMP3 options", -menuitems => + [ + [Button => "Security Name", + -command => [\&entryBox,"Security Name", "Security Name to use:", + \$session_opts{'SecName'}]], + [Cascade => "Security Level", -menuitems => + [ + [Radiobutton => 'noAuthNoPriv', + -variable => \$session_opts{'SecLevel'}], + [Radiobutton => 'authNoPriv', + -variable => \$session_opts{'SecLevel'}], + [Radiobutton => 'authPriv', + -variable => \$session_opts{'SecLevel'}] + ] + ], + [Button => "Authentication Passphrase", + -command => [\&entryBox,"Authentication Passphrase", + "Authentication Passphrase to use:", + \$session_opts{'AuthPass'}]], + [Cascade => "Authentication Type", -menuitems => + [ + [Radiobutton => 'MD5', + -variable => \$session_opts{'AuthProto'}], + [Radiobutton => 'SHA', + -variable => \$session_opts{'AuthProto'}], + ] + ], + [Button => "Privacy Passphrase", + -command => [\&entryBox,"Privacy Passphrase", + "Privacy Passphrase to use:", + \$session_opts{'PrivPass'}]], + [Cascade => "Privacy Type", -menuitems => + [ + [Radiobutton => 'DES', + -variable => \$session_opts{'PrivProto'}], + ] + ], + ] + ], + [Button => "Time between graph polls", + -command => sub { entryBox("graph polls", "Time between graph polls:", + \$graphtime);}], + [Button => "Port number", + -command => sub { entryBox("Port Number", "SNMP Port number to use:", + \$session_opts{'RemotePort'});}], + [Button => "TimeOut", + -command => sub { entryBox("Time Out", "Timeout for SNMP requests:", + \$session_opts{'Timeout'});}], + [Button => "Retries", + -command => sub { entryBox("Retries", + "Number of Times to Retransmit Requests:", + \$session_opts{'Retries'});}], + [Button => "Save Options", + -command => \&save_options] + ])->pack(-side => 'left'); + +$tmp = $OptMenuBut->cget(-menu); +$OptMenuWidgets = $tmp->entrycget("Display", -menu); +$OptMenuWidgets = $OptMenuWidgets->entrycget("MIB Information", -menu); + +$hlist=$top->Scrolled(qw(HList -itemtype imagetext -browsecmd main::showInfo + -command main::showChildren -width 80 -height 15)); +$hlist->pack(-side => 'top', -expand => 1, -fill => 'both'); +my $sFrame = $top->Frame(-relief => 'raised', -borderwidth => $tmpbd); +$sFrame->pack(-side => 'top', -fill => 'x'); +$sFrame->Label(-pady => $tmpbd, -padx => $tmpbd, -text => 'OID: ', -relief => 'raised', -borderwidth => $tmpbd) + ->pack(-side => 'left'); +$mibOID = $sFrame->Entry(-textvariable => \$OID, -relief => 'flat', -width => 40); +$mibOID->pack(-side => 'left'); +$mibTextOID = $sFrame->Label(-pady => $tmpbd, -padx => $tmpbd, -text => ''); +$mibTextOID->pack(-side => 'right'); + +$dispFrame=$top->Frame(-relief => 'raised', -borderwidth => $tmpbd); +$dispFrame->pack(-side => 'top', -fill =>'x'); +for($i=0;$i<= $#displayInfo;$i++) { + createRow($i) if ($displayInfoStates{$displayInfo[$i]}); + optionalWidget($i,$OptMenuWidgets, \$displayInfoStates{$displayInfo[$i]}); +} + +$descrFrame=$top->Frame(-relief => 'raised', -borderwidth => $tmpbd); +$descrFrame->pack(-side => 'top', -fill =>'x'); +$descrFrame->Label(-pady => $tmpbd, -padx => $tmpbd, -text => 'Description:', -anchor => 'w')->pack(-side => 'top', + -fill => 'x'); +$descr = $descrFrame->Scrolled(qw(Text -width 80 -height 4)); +$descr->pack(-side => 'top', -fill => 'x'); + +$bFrame = $top->Frame(-relief => 'raised', -borderwidth => $tmpbd); +$bFrame->pack(-side => 'top', -fill => 'x'); +$hostEntry = $bFrame->Entry(-textvariable => \$host, -width => 12); +$hostEntry->pack(-side => 'left'); +$bFrame->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'graph', -command => \&snmpgraph)->pack(-side => 'right'); +$tablebutton = $bFrame->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'table', -command => \&snmptable); +$tablebutton->pack(-side => 'right'); +$bFrame->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'walk', -command => \&snmpwalk)->pack(-side => 'right'); +$bFrame->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'getnext', -command => \&snmpgetnext)->pack(-side => 'right'); +$bFrame->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'get', -command => \&snmpget)->pack(-side => 'right'); +$bFrame->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'set', -command => [\&snmpsetbegin, 'OID'])->pack(-side => 'right'); +$stopBut = $bFrame->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'stop', -command => sub { stop(1) }, + -state => 'disabled'); +$stopBut->pack(-side => 'right'); +$oFrame = $top->Frame(-borderwidth => $tmpbd, -relief => 'raised'); +$oFrame->pack(-side => 'top', -fill => 'both'); +$output = $oFrame->Scrolled(qw(Text -width 80 -height 14)); +$output->pack(-side => 'top', -fill => 'both', -expand => 1); + +$tmpFrame = $top->Frame(-relief => 'raised', -borderwidth => $tmpbd); +$tmpFrame->pack(-side => 'top', -fill => 'x'); +$tmpFrame->Label(-pady => $tmpbd, -padx => $tmpbd, -text => "Status: ", -anchor => 'w') +# -relief => 'raised', -borderwidth => $tmpbd) + ->pack(-side => 'left'); +$status = $tmpFrame->Label(-pady => $tmpbd, -padx => $tmpbd, -anchor => 'w'); +$status->pack(-side => 'left', -fill => 'x'); + +# initialize the browser +foreach $i (qw(.1 .1.3 .1.3.6 .1.3.6.1)) { + addMibOID($i); +} +showChildren("$OID"); +if (defined($opts{'o'})) { + findANode($opts{'o'}); +} + +MainLoop(); + +sub insertresult { + my $oid = shift; + my $val = shift; + $oid = $OID if ($oid eq "OID"); + $output->insert('end', $oid, "oid:$oid"); + $output->tagBind("oid:$oid", '<1>', [sub{shift; + my $oid = shift; + findANode($oid); + my $tag = SNMP::translateObj($oid); + showInfo($tag);},$oid]); + $output->insert('end', " = "); + my $mib = $SNMP::MIB{format_oid("$oid",'numeric')}; + $output->insert('end', $val, "value:$oid"); + if ($mib->{'access'} =~ /(Write|Create)/) { + $output->tagConfigure("value:$oid", -foreground => $writecolor); + $output->tagBind("value:$oid", '<1>', [sub{shift; + my $oid = shift; + my $value = shift; + snmpsetmaybebegin($oid, $value); + findANode($oid); + my $tag = SNMP::translateObj($oid); + showInfo($tag);},format_oid($oid,'full'), $val]); + } + $output->insert('end', "\n"); +} + +sub insertvar { + my $var = shift; + my $name = get_oid($var); + + insertresult($name,SNMP::Varbind::val($var)); +} + +sub snmpsetup { + my $oid = $OID; + my $tag = SNMP::translateObj($oid); + my $sess = new SNMP::Session(DestHost => $host, %session_opts); + my $var = new SNMP::Varbind([$oid]); + if (!defined($var)) { + print "ack: $@ $SNMP::ErrorStr $!\n"; + } + stop(0); + initText(); + $oid = "." . $oid if ($oid !~ /^\./); + return ($oid, $sess, $var); +} + +sub initText { + if (ref($output) eq "Tk::Frame" && defined($$output{'_#text'})) { + $output->delete('0.0','end'); + } else { + $output->destroy(); + $output = $oFrame->Scrolled(qw(Text -width 80 -height 14)); + $output->pack(-side => 'top', -fill => 'both', -expand => 1); + } +} + +sub initTable { + $output->destroy(); + $oFrame->packPropagate(0); + $output = $oFrame->Table(-columns => shift, -width => 80, -height => 14, + -fixedrows => 2, -fixedcolumns => 1); + $output->pack(-side => 'top', -fill => 'both', -expand => 1); +} + +sub initCanvas { + $output->destroy(); + $oFrame->packPropagate(0); + $output = $oFrame->Scrolled(qw(Canvas -width 80c -height 14c)); + $output->pack(-side => 'top', -fill => 'both', -expand => 1); +} + +sub snmpget { + (my $oid, my $sess, my $var) = snmpsetup(); + $status->configure(-text => "getting: $host $community $oid"); + $top->update(); + my $val = $sess->get($var); + if ($sess->{ErrorStr}) { + $status->configure(-text => $sess->{ErrorStr}); + } else { + insertvar($var); + $status->configure(-text => ""); + } +} + +sub snmpsetbegin { + my $startoid = shift; + my $startval = shift; + my $setwin = MainWindow->new(); + $setwin->title("SNMP set"); + my $varswin = $setwin->Frame(-relief => "raised",-borderwidth => $tmpbd); + my $vars = new SNMP::VarList; + $varswin->pack(-side => 'top'); + my $buttons = $setwin->Frame(-relief => "raised")->pack(-side => 'top', -fill => "x",-expand => 1); + $buttons->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'Add a varbind', -command => [\&snmpsetbegin_addvar, $vars, $varswin, 'OID'])->pack(-side => 'left', -fill => "x",-expand => 1); + $buttons->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'perform set', -command => [\&snmpsetbegin_ok, $vars, $setwin, $varswin])->pack(-side => 'left'); + $buttons->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'Cancel', -command => [sub { my $widget = shift; $varswin = shift; if ($setmain == $varswin) { $setmain = undef; } $widget->destroy();}, $setwin, $varswin])->pack(-side => 'right'); + if ($startoid ne "") { + snmpsetbegin_addvar($vars, $varswin, $startoid, $startval); + } + if (!$setmain) { + $setmain = $varswin; + $setvars = $vars; + } +} + +sub make_enum_button { + my $win = shift; + my $var = shift; + my @objs; + foreach my $i (@_) { + push @objs,[Radiobutton => $i, -variable => $var]; + } + return $win->Menubutton(-pady => $tmpbd, -padx => $tmpbd, -textvariable => $var, + -relief => raised, + -menuitems => \@objs); +} + +sub snmpsetmaybebegin { + my ($oid, $val) = @_; + if ($setmain) { + snmpsetbegin_addvar($setvars, $setmain, $oid, $val); + } else { + snmpsetbegin($oid, $val); + } +} + + +sub snmpsetbegin_addvar { + my ($vars, $place, $oid, $val) = @_; + $oid = $OID if ($oid eq "OID"); + my $mib = $SNMP::MIB{format_oid("$oid",'numeric')}; + my $var = new SNMP::Varbind([$oid, '', $val, $mib->{'type'} || 'INTEGER']); + push @$vars,$var; + my $frame = $place->Frame(); + $frame->Entry(-textvariable => \$var->[0], -width => 20)->pack(-side => 'left'); + make_enum_button($frame, \$var->[3], qw(OBJECTID OCTETSTR INTEGER NETADDR IPADDR COUNTER COUNTER64 GAUGE UINTEGER TICKS OPAQUE NULL))->pack(-side => 'left'); + if (ref($mib->{'enums'}) eq HASH && scalar(keys(%{$mib->{'enums'}})) > 0) { + make_enum_button($frame, \$var->[2], keys(%{$mib->{'enums'}}))->pack(-side => 'left'); + } else { + $frame->Entry(-textvariable => \$var->[2])->pack(-side => 'left'); + } + $frame->pack(-expand => 1, -fill => 'x'); +} + +sub snmpsetbegin_ok { + my ($vars, $win, $frame) = @_; + snmpset($vars); + $setmain = undef if ($setmain == $frame); + $win->destroy(); +} + +sub snmpset { + my $vars = shift; + (my $oid, my $sess, my $var) = snmpsetup(); + $status->configure(-text => "setting: $host -> " . Dumper($vars) . "\n"); + $top->update(); + my $val = $sess->set($vars); + if ($sess->{ErrorStr}) { + $output->insert('end', "Set failed.\nReason: $sess->{ErrorStr}"); + $status->configure(-text => $sess->{ErrorStr}); + } else { + foreach my $i (@$vars) { + insertvar($i); + } + $status->configure(-text => ""); + } +} + +sub snmpgetnext { + (my $oid, my $sess, my $var) = snmpsetup(); + $status->configure(-text => "get next: $host $community $oid"); + $top->update(); + my $val = $sess->getnext($var); + if ($sess->{ErrorStr}) { + $status->configure(-text => $sess->{ErrorStr}); + } else { + insertvar($var); + $status->configure(-text => ""); + } +} + +sub snmpwalk { + (my $oid, my $sess, my $var) = snmpsetup(); + $status->configure(-text => "walking: $host $community $oid"); + $top->update(); + while (!$sess->{ErrorStr} && !$stopit) { + my $val = $sess->getnext($var); + last if (!defined($var->tag) || + $sess->{ErrorStr} || + $val eq "ENDOFMIBVIEW" || + !is_in_subtree($oid, $var->tag . "." . $var->iid)); + insertvar($var); + $top->update(); + } + if ($sess->{ErrorStr}) { + $status->configure(-text => $sess->{ErrorStr}); + $output->insert('end',"$sess->{ErrorStr} ($sess->{ErrorNum})\n"); + } else { + $status->configure(-text => ""); + } + stop(1); +} + +sub snmptable { + (my $oid, my $sess, my $var) = snmpsetup(); + $status->configure(-text => "collecting data: $host $community $oid"); + $top->update(); + my (%tb, @tags, @index, %tboids); + while (!$sess->{ErrorStr} && !$stopit) { + my $val = $sess->getnext($var); + last if (!defined($var->tag) || + $sess->{ErrorStr} || + $val eq "ENDOFMIBVIEW" || + !is_in_subtree($oid, $var->tag . "." . $var->iid)); + $newoid = SNMP::Varbind::tag($var).".".SNMP::Varbind::iid($var); + insertvar($var); + $top->update(); + $newoid =~ /([^\.]+)\.([0-9\.]+)$/; + if (!grep(/$1/,@tags)) { + push @tags,$1; + } + if (!grep(/$2/,@index)) { + push @index,$2; + } + $tb{$2}{$1} = $var->val; +# $tboids{$2}{$1} = $var->tag; + $tboids{$2}{$1} = $newoid; + } + initTable($#tags+1); + for(my $k=0;$k <= $#tags;$k++) { + $output->put(1,$k+2,$tags[$k]); + } + $output->put(1,1,"Index"); + for(my $i=0;$i <= $#index;$i++) { + $output->put($i+2,1,$index[$i]); + } + for(my $i=0;$i <= $#index; $i++) { + for(my $k=0;$k <= $#tags;$k++) { + my $mib = $SNMP::MIB{format_oid("$tboids{$index[$i]}{$tags[$k]}",'numeric')}; + if ($mib->{'access'} =~ /(Write|Create)/) { + $output->put($i+2,$k+2,$output->Button(-fg => $writecolor, -pady => $tmpbd, -padx => $tmpbd, -text => $tb{$index[$i]}{$tags[$k]}, -command => [\&snmpsetmaybebegin, $tboids{$index[$i]}{$tags[$k]}, $tb{$index[$i]}{$tags[$k]}], -padx => 0, -pady => 0)); + } else { + $output->put($i+2,$k+2,$tb{$index[$i]}{$tags[$k]}); + } + } + } + $status->configure(-text => ""); + stop(1); +} + +sub snmpgraph { + ($graphoid, $graphsess, my $graphvar) = snmpsetup(); + $top->update(); + %graphtb = (); + @graphvars = (); + initCanvas(); + $gcount=0; + $max=-1; + $min=2**32-1; + updateGraph(); + $output->repeat($graphtime*1000, \&updateGraph); +} + +sub updateGraph() { + $status->configure(-text => "collecting data: $host $community $graphoid"); + my $oid = $graphoid; + my $tag = SNMP::translateObj($graphoid,0); + my $var = new SNMP::Varbind([$oid]); + $graphsess->{ErrorStr} = ""; + while (!$graphsess->{ErrorStr} && !$stopit) { + my $val = $graphsess->getnext($var); + if ($#graphvars == -1 && SNMP::translateObj($var->tag) !~ /^$oid/) { + # if an exact oid, do a get instead. + $var = new SNMP::Varbind([$oid]); + $val = $graphsess->get($var); + } + if ($graphsess->{ErrorStr} || + !defined($var->tag) || + SNMP::translateObj($var->tag) !~ /^$oid/) { + last; + } + my $newoid = SNMP::translateObj(SNMP::Varbind::tag($var).".".SNMP::Varbind::iid($var)); + $top->update(); + $newoid =~ /$oid\.([0-9\.]+)$/; + if (defined($1)) { + if (!grep(/$1/,@graphvars)) { + push @graphvars,$1; + } + if ($graphdelta) { + if ($gcount > 0) { + $graphtb{$1}[$gcount-1] = $var->val - $prev{$1}; + } + $prev{$1} = $var->val; + } else { + $graphtb{$1}[$gcount] = $var->val; + } + $max = $graphtb{$1}[$#{$graphtb{$1}}] + if ($#{$graphtb{$1}} >= 0 && + $graphtb{$1}[$#{$graphtb{$1}}] > $max); + $min = $graphtb{$1}[$#{$graphtb{$1}}] + if ($#{$graphtb{$1}} >= 0 && + $graphtb{$1}[$#{$graphtb{$1}}] < $min); + } + } + if ($gcount > 1) { + $output->delete('all'); + my $canvas = $$output{'SubWidget'}{'canvas'}; + my $h=$canvas->cget(-height); + foreach $i (@graphvars) { + my @a = (); + for(my $j=0; $j <= $#{$graphtb{$i}}; $j++) { + $a[$j*2] = $j; + $a[$j*2+1] = $h-(($h-3)*($graphtb{$i}[$j]-$min))/($max-$min)-3; + } + $output->createLine(@a, -fill => $graphcolors[$i%$#graphcolors]); + } + $output->create('text',5, $h-3, -text => "$max"); + $output->create('text',5, 3, -text => "$min"); + } + $gcount++; + $status->configure(-text => "sleeping for $graphtime seconds"); +} + +sub addMibOID { + my $i = shift; + $i = ".$i" if ($i !~ /^\./); + my $name = SNMP::translateObj($i,1); + if (defined($name)) { + $name =~ s/.*\.([^.]+)$/$1/; + } else { + return; + } + $i =~ s/^\.//; + $hlist->add($i, -text => $name); +} + +sub showInfo { + my $full = shift; + $full = ".$full" if ($full !~ /^\./); + my $oid = $full; + my $tag = $oid; + + if ($tag =~ /^[.0-9]+$/) { + # strip off index in case there is one + $tag = SNMP::translateObj("$oid"); + $tag = ".iso.org.dod.internet.private.$tag" if $tag =~ /^enterprises/; + } else { + $full = SNMP::translateObj("$oid"); + } + + $tag =~ s/\.[.0-9]+$//; + $oid = SNMP::translateObj($tag); + + if (!defined($last) || "$last" ne $oid) { + updateInfo($oid); + } + $OID = $full; + $mibOID->configure(-textvariable => \$OID); + $mibOID->update(); + $last = $oid; +} + +sub showAllChildren { + my $id = shift; + $id =~ s/^\.//; + my @pieces = split(/\./,$id); + my ($i, $lastvalid); + for($i = 0; $i <= $#pieces; $i++) { + my $a = join(".", @pieces[0..$i]); + if ($hlist->infoExists($a) && !($hlist->infoChildren($a))) { + showChildren(join(".", $a)); + } + if ($hlist->infoExists($a)) { + $lastvalid = $a; + } else { + last; + } + } + $hlist->see($lastvalid); + $hlist->selectionClear($hlist->selectionGet); + $hlist->selectionSet($lastvalid); +} + +sub showChildren { + $OID = shift; + $OID =~ s/^\.//; + my $oid = $OID; + $mibOID->configure(-textvariable => \$OID); + if ($hlist->infoChildren($oid)) { + my @a = $hlist->infoChildren($oid); + my $i; + foreach $i (@a) { + $hlist->deleteEntry($i); + } + } else { + $oid = ".$oid"; + my $mib = $SNMP::MIB{format_oid($oid,'full')}; + if (defined($mib)) { + my $children = $$mib{'children'}; + if (ref($children) eq "ARRAY") { + foreach $i (sort {$$a{'subID'} <=> $$b{'subID'}} @{$children}) { + addMibOID($$i{'objectID'}); + } + } else { + $status->configure(-text => SNMP::translateObj($oid,1) . + " has no children"); + return; + } + } + } + $status->configure(-text => ""); +} + +sub updateInfo { + $OID = shift; + my $oid = $OID; + my $mib = $SNMP::MIB{format_oid("$oid",'numeric')}; + if (!defined($mib->{'description'}) || $mib->{'description'} eq "") { + $oid =~ s/[.0-9]+$//; + $mib = $SNMP::MIB{format_oid("$oid",'numeric')}; + } + if (defined($mib)) { + if ($mib->{'label'} =~ /Table$/) { + $tablebutton->configure(-state => 'normal'); + } else { + $tablebutton->configure(-state => 'disabled'); + } + $mibOID->configure(-text => $mib->{'objectID'}); + $mibTextOID->configure(-text => + SNMP::translateObj($mib->{'objectID'},1)); + $descr->delete('0.0','end'); + if (defined($mib->{'description'}) && + $mib->{'description'} ne "") { + my $desc = $mib->{'description'}; + $desc =~ s/\n[ \t]+/\n/g; + $desc =~ s/^\n//; + $descr->insert('end',$desc); + } + for($i=0; $i<= $#displayInfo;$i++) { + $dpyInfo[$i] = $mib->{$displayInfo[$i]}; + if (ref($dpyInfo[$i]) eq HASH) { + my %hash = %{$dpyInfo[$i]}; + $dpyInfo[$i] = ""; + foreach $j (sort { $hash{$a} <=> $hash{$b} } keys(%hash)) { + $dpyInfo[$i] .= "$j = $hash{$j},"; + } + } elsif (ref($dpyInfo[$i]) eq ARRAY) { + $dpyInfo[$i] = join(", ", @{$dpyInfo[$i]}); + } + } + } +} + +sub optionalWidget { + my $num = shift; + my $menu = shift; + my $var = shift; + $menu->checkbutton(-label => $displayInfo[$num], + -variable => $var, + -command => [\&toggleWidgetShown, $num, $var]); +} + +sub createRow { + my $i = shift; + if (!$displayLabels[$i]) { + $displayLabels[$i] = $dispFrame->Label(-pady => $tmpbd, -padx => $tmpbd, + -text => $displayInfo[$i], + -anchor => 'w', + -borderwidth => $tmpbd); + } + if (!$displayEntries[$i]) { + $displayEntries[$i] = $dispFrame->Entry(-textvariable => \$dpyInfo[$i], + -width => 40, -relief => 'flat', + -borderwidth => $tmpbd); + } + $displayLabels[$i]->grid(-ipady => $tmpbd, -ipadx => $tmpbd, + -column => ($i%2)*2, -row => int($i/2), + -sticky => 'w'); + $dpyInfo[$i] = "" if (!defined($dpyInfo[$i])); + $displayEntries[$i]->grid(-ipady => $tmpbd, -ipadx => $tmpbd, -column => ($i%2)*2 + 1, -row => int($i/2), -sticky => 'w'); +} + +sub toggleWidgetShown { + my ($num, $var) = @_; + if ($$var) { + createRow($num); + } else { + $displayLabels[$num]->gridForget(); + $displayEntries[$num]->gridForget() + } +# my @widgets = $dispFrame->gridSlaves(-row => $num); +} + +sub loadNewMibFile { + my $sel = $top->FileSelect(); + my $file = $sel->Show(); + if (defined($file)) { + SNMP::addMibFiles($file); + showChildren("1.3.6.1"); + showChildren("1.3.6.1"); + } +} + +sub loadNewMibModule { + my $tmptop = MainWindow->new(); + my $var = ""; + $tmptop->Label(-pady => $tmpbd, -padx => $tmpbd, -text => "Enter a SNMP MIB module name") + ->pack(-side => 'top'); + my $e = $tmptop->Entry(-textvariable => \$var); + $e->pack(-side => 'top'); + $e->bind('<Return>',[\&loadIt,\$var,$tmptop]); + my $f = $tmptop->Frame(); + $f->pack(-side => 'top'); + $f->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'Ok', -command => [\&loadIt,"",\$var,$tmptop]) + ->pack(-side => 'left'); + $f->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'Cancel', -command => [sub { my $wid = shift; + $wid->destroy(); }, + $tmptop]) + ->pack(-side => 'left'); +} + +sub loadIt { + my $var = shift; + if ($var ne "") { + my $ret = SNMP::loadModules($var); + if ($ret) { + showChildren("1.3.6.1"); + showChildren("1.3.6.1"); + return 1; + } else { + $status->configure(-text => "Failed reading module $var"); + return 0; + } + } + return 0; +} + +sub stop { + $stopit = shift; + if ($stopit) { + $stopBut->configure(-state => 'disabled'); + } else { + $stopBut->configure(-state => 'normal'); + } +} + +sub entryBox { + my $title = shift; + my $text = shift; + my $var = shift; + my $callback = shift; + my $top = MainWindow->new(); + my $newvar = $$var if defined($var); + $top->title($title); + my $f = $top->Frame(); + $f->pack(-side => 'top'); + $f->Label(-pady => $tmpbd, -padx => $tmpbd, + -text => $text)->pack(-side => 'left'); + my $e = $f->Entry(-textvariable => \$newvar); + $e->pack(-side => 'left'); + $f = $top->Frame(); + $f->pack(-side => 'bottom'); + my $b = $f->Button(-pady => $tmpbd, -padx => $tmpbd, -text => 'Ok', + -command => [sub { my $w = shift; + my $v1 = shift; + my $v2 = shift; + my $call = shift; + my $ret = 1; + $$v1 = $$v2 if defined($v1); + $ret = $call->($$v2) + if defined($call); + $w->destroy() if ($ret);}, $top, $var, + \$newvar, $callback]); + $b->pack(-side => 'left'); + $e->bind('<Return>',[$b,'invoke']); + $b = $f->Button(-pady => $tmpbd, -padx => $tmpbd, + -text => 'Cancel', -command => [sub { my $w = shift; + $w->destroy();}, $top + ]); + $b->pack(-side => 'right'); + $e->bind('<Escape>',[$b,'invoke']); + +} + +sub findANode { + my $val = shift; + my $tag = SNMP::translateObj($val); + if ($tag) { + showAllChildren($tag); + return 1; + } else { + $top->Dialog(-text => "$val not found")->Show(); + return 0; + } +} + +sub test_version { + my ($gt, $major, $minor, $sub) = @_; + $SNMP::VERSION =~ /(\d)\.(\d).(\d)/; + if ($gt) { + if ($1 > $major || ($1 == $major && $2 > $minor) || + ($1 == $major && $2 == $minor && $3 >= $sub)) { + return 1; + } + } else { + if ($1 < $major || ($1 == $major && $2 < $minor) || ($1 == $major && $2 == $minor && $3 < $sub)) { + return 1; + } + } + return 0; +} + +sub save_options { + my $umask = umask(); + umask 0077; # make sure its not readable by the world by default. + if (!open(O,">$opts{'f'}")) { + warn "can't save to $opts{'f'}\n"; + umask $umask; + return; + } + umask $umask; + print O Data::Dumper->Dump([\%session_opts], [qw(*session_opts)]); + print O Data::Dumper->Dump([\%displayInfoStates], [qw(*displayInfoStates)]); + foreach my $var (@saveoptions) { + print O Data::Dumper->Dump([$$var], [$var]); + } + close(O); + $status->configure(-text => "saved options to $opts{'f'}"); +} + +# returns 1 if $oid2 is below $oid1 in the hierarchy +sub is_in_subtree { + my ($oid1, $oid2) = @_; + # get pure numeric + $oid1 = SNMP::translateObj($oid1) if ($oid1 !~ /^[\d\.]*$/); + $oid2 = SNMP::translateObj($oid2) if ($oid2 !~ /^[\d\.]*$/); + + # has more on it or is exactly the same + return 1 if ($oid2 =~ /^$oid1\./ || $oid2 =~ /^$oid1$/); + return 0; +} + +sub format_oid { + my ($oid, $type) = @_; + $oid =~ s/\.$//; + $type = $displayoidas if ($type eq ""); + + if ($type eq 'numeric') { + return SNMP::translateObj($oid) if ($oid !~ /^[\d\.]*$/); + return $oid; + } elsif ($type eq 'full') { + return SNMP::translateObj($oid, 1) if ($oid =~ /^[\d\.]*$/); + return SNMP::translateObj(SNMP::translateObj($oid), 1) if ($oid !~ /^\./); + return $oid; + } elsif ($type eq 'short' || $type eq 'module') { + $oid = SNMP::translateObj($oid) if ($oid =~ /^[\d\.]*$/); + $oid =~ s/.*\.([a-zA-Z]\w+)\.(.*)/$1.$2/; + if ($type eq 'module') { + $oid = $SNMP::MIB{format_oid($oid,'numeric')}->{'moduleID'} . "::" . $oid; + } + return $oid; + } elsif ($type eq 'module') { + $oid = SNMP::translateObj($oid) if ($oid =~ /^[\d\.]*$/); + $oid =~ s/.*\.([a-zA-Z]\w+)\.(.*)/$1.$2/; + return $oid; + } else { + warn 'unknown oid translation type: $type'; + return $oid; + } +} + +sub get_oid { + my ($var, $type) = @_; + return format_oid($var->tag . "." . $var->iid, $type); +} diff --git a/local/traptoemail b/local/traptoemail new file mode 100755 index 0000000..e738ba7 --- /dev/null +++ b/local/traptoemail @@ -0,0 +1,74 @@ +#!/usr/bin/perl + +# This is a snmptrapd handler script to convert snmp traps into email +# messages. + +# Usage: +# Put a line like the following in your snmptrapd.conf file: +# traphandle TRAPOID|default /usr/local/bin/traptoemail [-f FROM] [-s SMTPSERVER]b ADDRESSES +# FROM defaults to "root" +# SMTPSERVER defaults to "localhost" + +use Net::SMTP; +use Getopt::Std; +use POSIX qw(strftime); + +$opts{'s'} = "localhost"; +$opts{'f'} = 'root@' . `hostname`; +chomp($opts{'f'}); +getopts("hs:f:", \%opts); + +if ($opts{'h'}) { + print " +traptoemail [-s smtpserver] [-f fromaddress] toaddress [...] + + traptoemail shouldn't be called interatively by a user. It is + designed to be called as an snmptrapd extension via a \"traphandle\" + directive in the snmptrapd.conf file. See the snmptrapd.conf file for + details. + + Options: + -s smtpserver Sets the smtpserver for where to send the mail through. + -f fromaddress Sets the email address to be used on the From: line. + toaddress Where you want the email sent to. + +"; + exit; +} + +die "no recepients to send mail to" if ($#ARGV < 0); + +# process the trap: +$hostname = <STDIN>; +chomp($hostname); +$ipaddress = <STDIN>; +chomp($ipaddress); + +$maxlen = 0; +while(<STDIN>) { + ($oid, $value) = /([^\s]+)\s+(.*)/; + push @oids, $oid; + push @values, $value; + $maxlen = (length($oid) > $maxlen) ? length($oid) : $maxlen; +} +$maxlen = 60 if ($maxlen > 60); +$formatstr = "%" . $maxlen . "s %s\n"; + +die "illegal trap" if ($#oids < 1); + +# send the message +$message = Net::SMTP->new($opts{'s'}) || die "can't talk to server $opts{'s'}\n"; +$message->mail($opts{'f'}); +$message->to(@ARGV) || die "failed to send to the recepients ",join(",",@ARGV),": $!"; +$message->data(); +$message->datasend("To: " . join(", ",@ARGV) . "\n"); +$message->datasend("From: $opts{f}\n"); +$message->datasend("Date: ".strftime("%a, %e %b %Y %X %z", localtime())."\n"); +$message->datasend("Subject: trap received from $hostname: $values[1]\n"); +$message->datasend("\n"); +$message->datasend("Host: $hostname ($ipaddress)\n"); +for($i = 0; $i <= $#oids; $i++) { + $message->datasend(sprintf($formatstr, $oids[$i], $values[$i])); +} +$message->dataend(); +$message->quit; |