MRTG_lib.pm
上传用户:shbosideng
上传日期:2013-05-04
资源大小:1555k
文件大小:72k
源码类别:

SNMP编程

开发平台:

C/C++

  1. # -*- mode: Perl -*-
  2. package MRTG_lib;
  3. ###################################################################
  4. # MRTG 2.13.2  Support library MRTG_lib.pm
  5. ###################################################################
  6. # Created by Tobias Oetiker <oetiker@ee.ethz.ch>
  7. #            and Dave Rand <dlr@bungi.com>
  8. #
  9. # For individual Contributers check the CHANGES file
  10. #
  11. ###################################################################
  12. #
  13. # Distributed under the GNU General Public License
  14. #
  15. ###################################################################
  16. require 5.005;
  17. use strict;
  18. use vars qw($OS $SL $PS @EXPORT @ISA $VERSION %timestrpospattern);
  19. if (eval { require Net_SNMP_util} ) {
  20. import Net_SNMP_util;
  21. }
  22. else {
  23. require SNMP_util; import SNMP_util;
  24. }
  25. my %mrtgrules;
  26. BEGIN {
  27.     # Automatic OS detection ... do NOT touch
  28.     if ( $^O =~ /^(?:(ms)?(dos|win(32|nt)?))/i ) {
  29.         $OS = 'NT';
  30.         $SL = '\';
  31.         $PS = ';';
  32.     } elsif ( $^O =~ /^NetWare$/i ) {
  33. $OS = 'NW';
  34. $SL = '/';
  35. $PS = ';';
  36.     } elsif ( $^O =~ /^VMS$/i ) {
  37.         $OS = 'VMS';
  38.         $SL = '.';
  39.         $PS = ':';
  40.     } elsif ( $^O =~ /^os2$/i ) {
  41. $OS = 'OS2';
  42. $SL = '/';
  43. $PS = ';';
  44.     }  else {
  45.         $OS = 'UNIX';
  46.         $SL = '/';
  47.         $PS = ':';
  48.     }
  49. }
  50. require Exporter;
  51. @ISA = qw(Exporter);
  52. @EXPORT = qw(readcfg cfgcheck setup_loghandlers 
  53.      datestr expistr ensureSL timestamp
  54.              create_pid demonize_me debug log2rrd storeincache readfromcache clearfromcache cleanhostkey
  55.      populateconfcache readconfcache writeconfcache
  56.      v4onlyifnecessary);
  57. $VERSION = 2.100015;
  58. %timestrpospattern =
  59.       (
  60.        'NO' => 0,
  61.        'LU' => 1,
  62.        'RU' => 2,
  63.        'LL' => 3,
  64.        'RL' => 4
  65.       );
  66. %mrtgrules =
  67.       (                         # General CFG
  68.        'workdir' => 
  69.        [sub{$_[0] && (-d $_[0])}, sub{"Working directory $_[0] does not exist"}],
  70.        'htmldir' =>
  71.        [sub{$_[0] && (-d $_[0])}, sub{"Html directory $_[0] does not exist"}],
  72.        'imagedir' =>
  73.        [sub{$_[0] && (-d $_[0])}, sub{"Image directory $_[0] does not exist"}],
  74.        'logdir' =>
  75.        [sub{$_[0] && (-d $_[0] )}, sub{"Log directory $_[0] does not exist"}],
  76.        'forks' =>
  77.        [sub{$_[0] && (int($_[0]) > 0 and $MRTG_lib::OS eq 'UNIX')},
  78.         sub{"Less than 1 fork or not running on Unix/Linux"}],
  79.        'refresh' => 
  80.        [sub{int($_[0]) >= 300}, sub{"$_[0] should be 300 seconds or more"}],
  81.        'enablesnmpv3' =>
  82.        [sub{((lc($_[0])) eq 'yes' or (lc($_[0])) eq 'no')}, sub{"$_[0] must be yes or no"}],
  83.        'enableipv6' =>
  84.        [sub{((lc($_[0])) eq 'yes' or (lc($_[0])) eq 'no')}, sub{"$_[0] must be yes or no"}],
  85.        'interval' => 
  86.        [sub{int($_[0]) >= 1 and int($_[0]) <= 60}, sub{"$_[0] should be at least 1 Minute and no more than 60 Minutes"}], 
  87.        'writeexpires' =>  
  88.        [sub{1}, sub{"Internal Error"}],
  89.        'nomib2' => 
  90.        [sub{1}, sub{"Internal Error"}],
  91.        'singlerequest' => 
  92.        [sub{1}, sub{"Internal Error"}],
  93.        'icondir' =>
  94.        [sub{$_[0]}, sub{"Directory argument missing"}],
  95.        'language' =>
  96.        [sub{1}, sub{"Mrtg not localized for $_[0] - defaulting to english"}],
  97.        'loadmibs' =>
  98.        [sub{$_[0]}, sub{"No MIB Files specified"}],
  99.        'userrdtool' =>
  100.        [sub{0}, sub{"UseRRDtool is not valid any more. Use LogFormat, PathAdd and LibAdd instead"}],
  101.        'userrdtool[]' =>
  102.        [sub{0}, sub{"UseRRDtool[] is not valid any more. Check the new xyz*bla[] syntax for passing parameters to tool xyz who reads the mrtg.cfg"}],
  103.        
  104.        'logformat' =>
  105.        [sub{$_[0] =~ /^(rateup|rrdtool)$/}, sub{"Invalid Logformat '$_[0]'"}],
  106.        'pathadd' =>
  107.        [sub{-d $_[0]}, sub{"$_[0] is not the name of a directory"}],
  108.        'libadd' =>
  109.        [sub{-d $_[0]}, sub{"$_[0] is not the name of a directory"}],
  110.        
  111.        'runasdaemon' =>
  112.        [sub{1}, sub{"Internal Error"}],
  113.        'nodetach' =>
  114.        [sub{1}, sub{"Internal Error"}],
  115.        'maxage' =>
  116.        [sub{(($_[0] =~ /^[0-9]+$/) and ($_[0] > 0)) },
  117.         sub{"$_[0] must be a Number bigger than 0"}],
  118.        'nospacechar' =>
  119.        [sub{length($_[0]) == 1}, sub{"$_[0] must be one character long"}],
  120.        'snmpoptions' =>
  121.        [sub{ eval( '{'.$_[0].'}' ); return not $@},
  122.         sub{"Must have the format "OptA => Number, OptB => 'String', ... ""}],
  123.        'conversioncode' =>
  124.        [sub{-r $_[0]}, sub{"Cannot read conversion code file $_[0]"}],
  125.        # Per Router CFG
  126.        'target[]' => 
  127.        [sub{1}, sub{"Internal Error"}], #will test this later
  128.        'snmpoptions[]' =>
  129.        [sub{ eval( '{'.$_[0].'}' ); return not $@},
  130.         sub{"Must have the format "OptA => Number, OptB => 'String', ... ""}],
  131.        'routeruptime[]' => 
  132.        [sub{1}, sub{"Internal Error"}], #will test this later
  133.        'routername[]' => 
  134.        [sub{1}, sub{"Internal Error"}], #will test this later
  135.        'maxbytes[]' => 
  136.        [sub{(($_[0] =~ /^[0-9]+$/) && ($_[0] > 0)) },
  137.         sub{"$_[0] must be a Number bigger than 0"}],
  138.        'maxbytes1[]' =>
  139.        [sub{(($_[0] =~ /^[0-9]+$/) && ($_[0] > 0))},
  140.         sub{"$_[0] must be numerical and larger than 0"}],
  141.        'maxbytes2[]' =>
  142.        [sub{(($_[0] =~ /^[0-9]+$/) && ($_[0] > 0))},
  143.         sub{"$_[0] must a number bigger than 0"}],
  144.        'ipv4only[]' =>
  145.        [sub{((lc($_[0])) eq 'yes' or (lc($_[0])) eq 'no')}, sub{"$_[0] must be yes or no"}],
  146.        'absmax[]' => 
  147.        [sub{($_[0] =~ /^[0-9]+$/)}, sub{"$_[0] must be a Number"}],
  148.        'title[]' => 
  149.        [sub{1}, sub{"Internal Error"}], #what ever the user chooses.
  150.        'directory[]' => 
  151.        [sub{1}, sub{"Internal Error"}], #what ever the user chooses.
  152.        'pagetop[]' => 
  153.        [sub{1}, sub{"Internal Error"}], #what ever the user chooses.
  154.        'bodytag[]' => 
  155.        [sub{1}, sub{"Internal Error"}], #what ever the user chooses.
  156.        'pagefoot[]' => 
  157.        [sub{1}, sub{"Internal Error"}], #what ever the user chooses.
  158.        'addhead[]' => 
  159.        [sub{1}, sub{"Internal Error"}], #what ever the user chooses.
  160.        'rrdrowcount[]' => 
  161.        [sub{1}, sub{"Internal Error"}], #what ever the user chooses.
  162.        'extension[]' =>
  163.        [sub{1}, sub{"Internal Error"}], #what ever the user chooses.
  164.        'unscaled[]' => 
  165.        [sub{$_[0] =~ /[ndwmy]+/i}, sub{"Must be a string of [n]one, [d]ay, [w]eek, [m]onth, [y]ear"}],
  166.        'weekformat[]' => 
  167.        [sub{$_[0] =~ /[UVW]/}, sub{"Must be either W, V, or U"}],
  168.        'withpeak[]' =>
  169.        [sub{$_[0] =~ /[ndwmy]+/i}, sub{"Must be a string of [n]one, [d]ay, [w]eek, [m]onth, [y]ear"}],
  170.        'suppress[]' =>
  171.        [sub{$_[0] =~ /[ndwmy]+/i}, sub{"Must be a string of [n]one, [d]ay, [w]eek, [m]onth, [y]ear"}],
  172.        'xsize[]' =>
  173.        [sub{((int($_[0]) >= 30) && (int($_[0]) <= 600))}, sub{"$_[0] must be between 30 and 600 pixels"}],
  174.        'ysize[]' =>
  175.        [sub{(int($_[0]) >= 30)}, sub{"Must be >= 30 pixels"}],
  176.        'ytics[]' =>
  177.        [sub{(int($_[0]) >= 1) }, sub{"Must be >= 1"}],
  178.        'yticsfactor[]' =>
  179.        [sub{$_[0] =~ /[-+0-9.efg]+/}, sub{"Should be a numerical value"}],
  180.        'factor[]' =>
  181.        [sub{$_[0] =~ /[-+0-9.efg]+/}, sub{"Should be a numerical value"}],
  182.        'step[]'  =>
  183.        [sub{(int($_[0]) >= 0)}, sub{"$_[0] must be > 0"}],
  184.        'timezone[]' =>
  185.        [sub{1}, sub{"Internal Error"}],
  186.        'options[]' =>
  187.        [sub{1}, sub{"Internal Error"}],
  188.        'colours[]' =>
  189.        [sub{1}, sub{"Internal Error"}],
  190.        'background[]' =>
  191.        [sub{1}, sub{"Internal Error"}],
  192.        'kilo[]' => 
  193.        [sub{($_[0] =~ /^[0-9]+$/)}, sub{"$_[0] must be a Integer Number"}],
  194.        #define whatever k should be (1000, 1024, ???)
  195.        'kmg[]' =>
  196.        [sub{1}, sub{"Internal Error"}],
  197.        'pngtitle[]' =>
  198.        [sub{1}, sub{"Internal Error"}],
  199.        'ylegend[]' =>
  200.        [sub{1}, sub{"Internal Error"}],
  201.        'shortlegend[]' =>
  202.        [sub{1}, sub{"Internal Error"}],
  203.        'legend1[]' =>
  204.        [sub{1}, sub{"Internal Error"}],
  205.        'legend2[]' =>
  206.        [sub{1}, sub{"Internal Error"}],
  207.        'legend3[]' =>
  208.        [sub{1}, sub{"Internal Error"}],
  209.        'legend4[]' =>
  210.        [sub{1}, sub{"Internal Error"}],
  211.        'legend5[]' =>
  212.        [sub{1}, sub{"Internal Error"}],
  213.        'legendi[]' =>
  214.        [sub{1}, sub{"Internal Error"}],
  215.        'legendo[]' =>
  216.        [sub{1}, sub{"Internal Error"}],
  217.        'setenv[]' => 
  218.        [sub{$_[0] =~ /^(?:[-w]+="[^"]*"(?:s+|$))+$/},
  219.         sub{"$_[0] must be XY="dddd" AASD="kjlkj" ... "}],
  220.        'xzoom[]' =>
  221.        [sub{($_[0] =~ /^[0-9]+(?:.[0-9]+)?$/)},
  222.         sub{"$_[0] must be a Number xxx.xxx"}],
  223.        'yzoom[]' =>
  224.        [sub{($_[0] =~ /^[0-9]+(?:.[0-9]+)?$/)},
  225.         sub{"$_[0] must be a Number xxx.xxx"}],
  226.        'xscale[]' =>
  227.        [sub{($_[0] =~ /^[0-9]+(?:.[0-9]+)?$/)},
  228.         sub{"$_[0] must be a Number xxx.xxx"}],
  229.        'yscale[]' =>
  230.        [sub{($_[0] =~ /^[0-9]+(?:.[0-9]+)?$/)},
  231.         sub{"$_[0] must be a Number xxx.xxx"}],
  232.        'threshdir' =>
  233.        [sub{$_[0] && (-d $_[0])}, sub{"Threshold directory $_[0] does not exist"}],
  234.        'threshmini[]' =>
  235.        [sub{1}, sub{"Internal Threshold Config Error"}],
  236.        'threshmino[]' =>
  237.        [sub{1}, sub{"Internal Threshold Config Error"}],
  238.        'threshmaxi[]' =>
  239.        [sub{1}, sub{"Internal Threshold Config Error"}],
  240.        'threshmaxo[]' =>
  241.        [sub{1}, sub{"Internal Threshold Config Error"}],
  242.        'threshdesc[]' =>
  243.        [sub{1}, sub{"Internal Threshold Config Error"}],
  244.        'threshprogi[]' =>
  245.        [sub{$_[0] && (-e $_[0])}, sub{"Threshold program $_[0] cannot be executed"}],
  246.        'threshprogo[]' =>
  247.        [sub{$_[0] && (-e $_[0])}, sub{"Threshold program $_[0] cannot be executed"}],
  248.        'threshprogoki[]' =>
  249.        [sub{$_[0] && (-e $_[0])}, sub{"Threshold program $_[0] cannot be executed"}],
  250.        'threshprogoko[]' =>
  251.        [sub{$_[0] && (-e $_[0])}, sub{"Threshold program $_[0] cannot be executed"}],
  252.        'timestrpos[]' => 
  253.        [sub{$_[0] =~ /^(no|[lr][ul])$/i}, sub{"Must be a string of NO, LU, RU, LL, RL"}],
  254.        'timestrfmt[]' => 
  255.        [sub{1}, sub{"Internal Error"}] #what ever the user chooses.
  256. );
  257. # config file reading
  258. sub readcfg ($$$$;$$) {
  259.     my $cfgfile = shift;
  260.     my $routers = shift;
  261.     my $cfg = shift;
  262.     my $rcfg = shift;
  263.     my $extprefix = shift || '';
  264.     my $extrules = shift;
  265.     my ($first,$second,$key,$userules);
  266.     my (%seen);
  267.     my (%pre,%post,%deflt,%defaulted);
  268.     unless ($cfgfile) {
  269.         die "ERROR: readfg: no configfile specifiedn";
  270.     }
  271.     unless (ref($routers) eq 'ARRAY' and ref($cfg) eq 'HASH'
  272.             and ref($rcfg) eq 'HASH') {
  273.         die "ERROR: readcfg called with wrong argumentsn";
  274.     }
  275.     if ($extprefix and ref($extrules) ne 'HASH') {
  276.         die "ERROR: readcfg called with wrong args for mrtg extensionn";
  277.     }
  278.     my $hand;
  279.     my $file;
  280.     my @filestack;
  281.     local *CFG;
  282.     if ($cfgfile eq '-'){$cfgfile = '<&STDIN'};
  283.     open (CFG, $cfgfile) || die "ERROR: unable to open config file: $cfgfilen";
  284.     $hand = *CFG;
  285.     my @handstack;
  286.     my $nextfile = $cfgfile;
  287.     my %routerhash;
  288.     while (1) {        
  289.         if (eof $hand || not defined ($_ = <$hand>) ) {
  290.                 close $hand;
  291.                 if (scalar @handstack){
  292.                         $hand = pop @handstack;
  293.                         $nextfile = pop @filestack;
  294.                         next;
  295.                 } else {
  296.                         last;
  297.                 }
  298.         }
  299.         $file=$nextfile;
  300.         chomp;
  301.         my $line = $.;
  302.         if (/^include:s*(.*?S)s*$/i){
  303.                 push @filestack, $file;
  304.                 push @handstack, $hand;
  305.                 $nextfile = $1;
  306.                 local *FH;
  307.                 open (FH, $nextfile)
  308.                  || open (FH, ($cfgfile =~ m#(.+)${MRTG_lib::SL}[^${MRTG_lib::SL}]+$#)[0] . ${MRTG_lib::SL} . $nextfile)
  309.                  || do { die "ERROR: unable to open include file: $nextfilen"};
  310.                 $hand = *FH;
  311.                 next;
  312.         }
  313.         debug('cfg',"$file[$.]: $_");
  314.                 
  315.         s/t/ /g;               #replace tab by space
  316.         s/r$//;                # kill dos newlines ...
  317.         s/ +$//g;               #remove space at the end of the line
  318.         next if /^ *#/;       #ignore comment lines
  319.         next if /^ *$/;        #ignore empty lines
  320.         # oops spelling error
  321.         s/^supress/suppress/gi;
  322.                 
  323.         # the line we got starts with white space so it is to be appended to what ever
  324.         # was on the previous line.
  325.         if (defined $first && /^s+(.*S)s*$/) {
  326.             if (defined $second) {
  327.                $second eq '^' && do { $pre{$first} .= "n".$1; next};
  328.                $second eq '$' && do { $post{$first} .= "n".$1; next};
  329.                $second eq '_' && do { $deflt{$first} .= "n".$1; next};
  330.                $$rcfg{$first}{$second} .= " ".$1;
  331.             } else {
  332.                $$cfg{$first} .= "n".$1;
  333.             }
  334.             next;
  335.         }
  336.     
  337.         if (defined $first && defined $second && defined $post{$first} && ($second !~ /^[$^_]$/)) {
  338.             if (defined $defaulted{$first}{$second}) {
  339.                 $$rcfg{$first}{$second} = $post{$first};
  340.                 delete $defaulted{$first}{$second};
  341.             } else {
  342.                 $$rcfg{$first}{$second} .= ( defined $$cfg{nospacechar} and $post{$first} =~ /(.*)Q$$cfg{nospacechar}E$/) ? $1 : " ".$post{$first} ;
  343.             }
  344.         }
  345.         if (defined $first and $first =~ m/^([^*]+)*(.+)$/) {
  346.             $userules = ($1 eq $extprefix ? $extrules : '');
  347.         } else {
  348.             $userules = %mrtgrules;
  349.         }
  350.         if ($first && defined $deflt{$first} && ($second eq '_')) {
  351.             quickcheck($first,$second,$deflt{$first},$file,$line,$userules)
  352.         } elsif ($first && $second && ($second !~ /^[$^_]$/)) {
  353.             quickcheck($first,$second,$$rcfg{$first}{$second},$file,$line,$userules)
  354.         } elsif ($first && not $second) {
  355.             quickcheck($first,0,$$cfg{$first},$file, $line,$userules)
  356.         }
  357.         if (/^([A-Za-z0-9*]+)[(S+)]s*:s*(.*S?)s*$/) {
  358.             $first = lc($1);
  359.             $second = lc($2);
  360.             # For us spelling-handicapped Americans. ;)
  361.             # James Overbeck, grendel@gmo.jp, 2003/01/19
  362.             if ($first eq 'colors') { $first = 'colours' };
  363.             if ($second eq '^') {
  364.                 if ($3 ne '') {
  365.                     $pre{$first}=$3;
  366.                 } else {
  367.                     delete $pre{$first};
  368.                 }
  369.                 next;
  370.             }
  371.             if ($second eq '$') {
  372.                 if ($3 ne '') {
  373.                     $post{$first}=$3;
  374.                 } else {
  375.                     delete $post{$first};
  376.                 }
  377.                 next;
  378.             }
  379.             if ($second eq '_') {
  380.                 if ($3 ne '') {
  381.                     $deflt{$first}=$3;
  382.                 } else {
  383.                     delete $deflt{$first};
  384.                 }
  385.                 next;
  386.             }
  387.             if (not defined $routerhash{$second}) {
  388.                     push (@{$routers}, $second);
  389.                     $routerhash{$second} = 1;
  390.             }
  391.       
  392.             # make sure that default tags spring into existance upon first 
  393.             # call of a router
  394.             foreach $key (keys %deflt) {
  395.                 if (! defined $$rcfg{$key}{$second}) {
  396.                     $$rcfg{$key}{$second} = $deflt{$key};
  397.                     $defaulted{$key}{$second} = 1;
  398.                 }
  399.             }
  400.             # make sure that prefix-only tags spring into existance upon first 
  401.             # call of a router
  402.             foreach $key (keys %pre) {
  403.                 if (! defined $$rcfg{$key}{$second}) {
  404.                     delete $defaulted{$key}{$second} if $defaulted{$key}{$second};
  405.                     $$rcfg{$key}{$second} = ( defined $$cfg{nospacechar} && $pre{$key} =~ m/(.*)Q$$cfg{nospacechar}E$/ ) ? $1 : $pre{$key}." ";
  406.                 }
  407.             }
  408.             if ($seen{$first}{$second}) {
  409.                 die ("ERROR: Line $line ($_) in CFG file ($file)n".
  410.                      "contains a duplicate definition for $first[$second].n".
  411.                      "First definition is on line $seen{$first}{$second}n")
  412.             } else {
  413.                 $seen{$first}{$second} = $line;
  414.             }
  415.             if ($defaulted{$first}{$second}) {
  416.                 $$rcfg{$first}{$second} = '';
  417.                 delete $defaulted{$first}{$second};
  418.             }
  419.             $$rcfg{$first}{$second} .= $3;
  420.             next;
  421.         }
  422.         if (/^(S+):s*(.*S)s*$/) {
  423.             $first = lc($1);    
  424.             $$cfg{$first} = $2;
  425.             $second = '';
  426.             next;
  427.         }
  428.         die "ERROR: Line $line ($_) in CFG file ($file)  does not make sensen";
  429.     }
  430.     # append $ stuff to the very last tag in cfg file if necessary 
  431.     if (defined $first && defined $second && defined $post{$first} && ($second !~ /^[$^_]$/)) {
  432.         if ($defaulted{$first}{$second}) {
  433.             $$rcfg{$first}{$second} = $post{$first};
  434.             delete $defaulted{$first}{$second};
  435.         } else {
  436.             $$rcfg{$first}{$second} .= 
  437.       ( defined $$cfg{'nospacechar'} && $post{$first} =~ /(.*)Q$$cfg{nospacechar}E$/ ) ? $1 : " ".$post{$first} ;      
  438.         }
  439.     }
  440.   
  441.     #check the last input line
  442.     if ($first =~ m/^([^*]+)*(.+)$/) {
  443.         $userules = ($1 eq $extprefix ? $extrules : '');
  444.     } else {
  445.         $userules = %mrtgrules;
  446.     }
  447.     if ($first && defined $deflt{$first} && ($second eq '_')) {
  448.         quickcheck($first,$second,$deflt{$first},$file,$.,$userules)
  449.     } elsif ($first && $second && ($second !~ /^[$^_]$/)) {
  450.         quickcheck($first,$second,$$rcfg{$first}{$second},$file,$.,$userules)
  451.     } elsif ($first && not $second) {
  452.         quickcheck($first,0,$$cfg{$first},$file,$.,$userules)
  453.     }
  454.     close (CFG);
  455. }
  456. # quick checks
  457. sub quickcheck ($$$$$$) {
  458.     my ($first,$second,$arg,$file,$line,$rules) = @_;
  459.     return unless ref($rules) eq 'HASH';
  460.     my $braces = $second ? '[]':'';
  461.     if (exists $rules->{$first.$braces}) {
  462.         if (&{$rules->{$first.$braces}[0]}($arg)) {
  463.             return 1;
  464.         } else {
  465.             if ($second) {
  466.                 die "ERROR: CFG Error in "$first[$second]", line $line: ".
  467.                   &{$rules->{$first.$braces}[1]}($arg)."nn"; 
  468.             } else {
  469.                 die "ERROR: CFG Error in "$first", line $line: ".
  470.                   &{$rules->{$first.$braces}[1]}($arg)."nn"; 
  471.             } 
  472.         }
  473.     }
  474.     die "ERROR: CFG Error Unknown Option "$first" on line $line or above.n".
  475.       "           Check doc/reference.txt for Helpnn";
  476. }
  477. # complex config checks
  478. sub mkdirhier ($){
  479.     my @dirs = split /Q${MRTG_lib::SL}E+/, shift;
  480.     my $path = "";
  481.     while (@dirs){
  482. $path .= shift @dirs;
  483. $path .= ${MRTG_lib::SL};
  484. if (! -d $path){
  485.                 warn ("WARNING: $path did not exist I will create it nown");
  486. mkdir $path, 0777  or die ("ERROR: mkdir $path: $!n");
  487. }
  488.     }
  489. }
  490. sub cfgcheck ($$$$) {
  491.     my ($routers, $cfg, $rcfg, $target) = @_;
  492.     my ($rou, $confname, $one_option);
  493.     # Target index hash. Keys are "int:community@router" target definition
  494.     # strings and values are indices of the @$target array. Used to avoid
  495.     # duplicate entries in @$target.
  496.     my $targIndex = { };
  497.     my $error="no";
  498.     my(@known_options) = qw(growright bits noinfo absolute gauge nopercent avgpeak derive
  499.     integer perhour perminute transparent dorelpercent 
  500.     unknaszero withzeroes noborder noarrow noi noo
  501.     nobanner nolegend logscale secondmean pngdate printrouter);
  502.     snmpmapOID('hrSystemUptime' => '1.3.6.1.2.1.25.1.1');
  503.     if (defined $$cfg{workdir}) {
  504.         die ("ERROR: WorkDir must not contain spaces when running on Windows. (Yeat another reason to get Linux)n")
  505.                 if ($OS eq 'NT' or $OS eq 'OS2') and $$cfg{workdir} =~ /s/;
  506.         ensureSL($$cfg{workdir});
  507.         $$cfg{logdir}=$$cfg{htmldir}=$$cfg{imagedir}=$$cfg{workdir};
  508.         mkdirhier "$$cfg{workdir}";
  509.         
  510.     } elsif ( not (defined $$cfg{logdir} or defined $$cfg{htmldir} or defined $$cfg{imagedir})) {
  511.           die ("ERROR: "WorkDir" not specified in mrtg config filen");
  512.   $error = "yes";
  513.     } else {
  514.         if (! defined $$cfg{logdir}) {
  515.             warn ("WARNING: "LogDir" not specifiedn");
  516.             $error = "yes";
  517.         } else {
  518.           ensureSL($$cfg{logdir});
  519.           mkdirhier $$cfg{logdir};
  520.         }
  521.         if (! defined $$cfg{htmldir}) {
  522.             warn ("WARNING: "HtmlDir" not specifiedn");
  523.             $error = "yes";
  524.         } else {
  525.           ensureSL($$cfg{htmldir});
  526.           mkdirhier $$cfg{htmldir};
  527.         }
  528.         if (! defined $$cfg{imagedir}) {
  529.             warn ("WARNING: "ImageDir" not specifiedn");
  530.             $error = "yes";
  531.         } else {
  532.           ensureSL($$cfg{imagedir});
  533.           mkdirhier $$cfg{imagedir};
  534.         }
  535.     }
  536.     # build relativ path from htmldir to image dir.
  537.     my @htmldir = split /Q${MRTG_lib::SL}E+/, $$cfg{htmldir};
  538.     my @imagedir =  split /Q${MRTG_lib::SL}E+/, $$cfg{imagedir};
  539.     while (scalar @htmldir > 0 and $htmldir[0] eq $imagedir[0]) {
  540.      shift @htmldir; shift @imagedir;
  541.     }
  542.     # this is for the webpages so we use / path separator always
  543.     $$cfg{imagehtml} = "";
  544.     foreach my $dir ( @htmldir ) {
  545.         $$cfg{imagehtml} .= "../" if $dir;
  546.     }
  547.     map {$$cfg{imagehtml} .= "$_/" } @imagedir;
  548.     # relative path is built
  549.     debug('dir', "imagehtml = $$cfg{imagehtml}");
  550.     $SNMP_util::CacheFile = "$$cfg{'logdir'}oid-mib-cache.txt";
  551.     if (defined $$cfg{loadmibs}) {
  552.         my($mibFile);
  553.         foreach $mibFile (split /[,s]+/, $$cfg{loadmibs}) {
  554.             snmpQueue_MIB_File($mibFile);
  555.         }
  556.     }
  557.     if(defined $$cfg{pathadd}){
  558.         ensureSL($$cfg{pathadd});        
  559.         $ENV{PATH} = "$$cfg{pathadd}${MRTG_lib::PS}$ENV{PATH}";
  560.     }
  561.     if(defined $$cfg{libadd}){
  562.         ensureSL($$cfg{libadd});
  563.         eval "use lib qw($$cfg{libadd})";
  564. my @match;
  565. foreach my $dir (@INC){
  566. push @match, $dir if -f "$dir/RRDs.pm";
  567. }
  568. warn "WARN: found several copies of RRDs.pm in your path: ".
  569.         (join ", ", @match)." I will be using $match[0]. This could ".
  570. "be a problem if this is an old copy and you think I would be using a newer one!n"
  571. if $#match > 0;
  572.     }
  573.     $$cfg{logformat} = 'rateup' unless defined $$cfg{logformat};
  574.     if($$cfg{logformat} eq 'rrdtool') {
  575.         my ($name);
  576.         if ($MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2'){
  577.             $name = "rrdtool.exe";
  578.         } elsif ($MRTG_lib::OS eq 'NW'){
  579.             $name = "rrdtool.nlm";
  580.         } else {
  581.             $name = "rrdtool";
  582.         }
  583.         foreach my $path (split /Q${MRTG_lib::PS}E/, $ENV{PATH}) {
  584.             ensureSL($path);
  585.             -f "$path$name" && do { 
  586.                 $$cfg{'rrdtool'} = "$path$name";
  587.                 last;}
  588.         };
  589.         die "ERROR: could not find $name. Use PathAdd: in mrtg.cfg to help mrtg find rrdtooln" 
  590.                 unless defined $$cfg{rrdtool};
  591.         debug ('rrd',"found rrdtool in $$cfg{rrdtool}");
  592.         my $found;
  593.         foreach my $path (@INC) {
  594.             ensureSL($path);
  595.             -f "${path}RRDs.pm" && do { 
  596.                 $found=1;
  597.                 last;}
  598.         };
  599.         die "ERROR: could not find RRDs.pm. Use LibAdd: in mrtg.cfg to help mrtg find RRDs.pmn" 
  600.                 unless defined $found;
  601.     }
  602.     if (defined $$cfg{snmpoptions}) {
  603.            $cfg->{snmpoptions} = eval('{'.$cfg->{snmpoptions}.'}');
  604.     }
  605.     # default interval is 5 minutes
  606.     $$cfg{interval} = 5 unless defined $$cfg{interval};
  607.     unless ($$cfg{logformat} eq 'rrdtool') {
  608.         # interval has to be 5 minutes at least without userrdtool
  609.         if ($$cfg{interval} < 5) {
  610.             die "ERROR: CFG Error in "Interval": should be at least 5 Minutes (unless you use rrdtool)";
  611.         }
  612.     }
  613.     # Check for a Conversion Code file and evaluate its contents, which
  614.     # should consist of one or more subroutine definitions. The code goes
  615.     # into the MRTGConversion name space.
  616.     if( exists $cfg->{ conversioncode } ) {
  617.         open CONV, $cfg->{ conversioncode }
  618.             or die "ERROR: Can't open file $cfg->{ conversioncode }n";
  619.         my $code = "package MRTGConversion;n". join( '', <CONV> ) . "1;n";
  620.         close CONV;
  621.         die "ERROR: File $cfg->{ conversioncode } conversion code evaluation failedn$@n"
  622.             unless eval $code;
  623.     }
  624.     foreach $rou (@$routers) {
  625.         # and now for the testing
  626. if (! defined $rcfg->{snmpoptions}{$rou}) {
  627. $rcfg->{snmpoptions}{$rou} = {%{$cfg->{snmpoptions}}}
  628.   if defined $cfg->{snmpoptions};
  629.      } else {
  630.              $rcfg->{snmpoptions}{$rou} = eval('{'.$rcfg->{snmpoptions}{$rou}.'}');
  631.         }
  632.         $rcfg->{snmpoptions}{$rou}{avoid_negative_request_ids} = 1;
  633.         # $rcfg->{snmpoptions}{$rou}{domain} = 'udp';
  634.         
  635.         if (! defined $$rcfg{"title"}{$rou}) {
  636.             warn ("WARNING: "Title[$rou]" not specifiedn");
  637.             $error = "yes";
  638.         }
  639.         if (defined $$rcfg{'directory'}{$rou} and $$rcfg{'directory'}{$rou} ne "") {
  640.             # They specified a directory for this router.  Append the
  641.             # pathname seperator to it (so that it can either be present or
  642.             # absent, and the rules for including it are the same).
  643.     ensureSL($$rcfg{'directory'}{$rou});
  644.             for my $x (qw(imagedir logdir htmldir)) {
  645.                 mkdirhier $$cfg{$x}.$$rcfg{directory}{$rou};
  646.             }                   
  647.             $$rcfg{'directory_web'}{$rou} = $$rcfg{'directory'}{$rou};
  648.     $$rcfg{'directory_web'}{$rou} =~ s/Q${MRTG_lib::SL}E+///g;
  649.             debug('dir', "directory for $rou '$$rcfg{'directory_web'}{$rou}'");
  650.         } else {
  651.                 $$rcfg{'directory'}{$rou}="";
  652.                 $$rcfg{'directory_web'}{$rou}="";
  653.         }
  654.       if (defined $$rcfg{"pagetop"}{$rou}) {
  655.             $$rcfg{"pagetop"}{$rou} =~ s/\n/n/g;
  656.         }
  657.         if (defined $$rcfg{"pagefoot"}{$rou}) {
  658.             # allow for linebreaks
  659.             $$rcfg{"pagefoot"}{$rou} =~ s/\n/n/g;
  660.         }
  661.  
  662.         $$rcfg{"maxbytes1"}{$rou} = $$rcfg{"maxbytes"}{$rou} unless defined $$rcfg{"maxbytes1"}{$rou};
  663.         $$rcfg{"maxbytes2"}{$rou} = $$rcfg{"maxbytes"}{$rou} unless defined $$rcfg{"maxbytes2"}{$rou};
  664.         if (    not defined $$rcfg{"maxbytes"}{$rou} 
  665.             and not defined $$rcfg{"maxbytes1"}{$rou} 
  666.             and not defined $$rcfg{"maxbytes2"}{$rou}) {
  667.             warn ("WARNING: "MaxBytes[$rou]" not specifiedn");
  668.             $error = "yes";
  669.         } else {
  670.         if (not defined $$rcfg{"maxbytes1"}{$rou}) {
  671.             warn ("WARNING: "MaxBytes1[$rou]" not specifiedn");
  672.             $error = "yes";
  673.         }
  674.         if (not defined $$rcfg{"maxbytes2"}{$rou}) {
  675.             warn ("WARNING: "MaxBytes2[$rou]" not specifiedn");
  676.             $error = "yes";
  677.         }
  678.         }
  679.         # set default extension
  680.         if (! defined $$rcfg{"extension"}{$rou}) {
  681.             $$rcfg{"extension"}{$rou}="html";
  682.         }
  683.         # set default size 
  684.         if (! defined $$rcfg{"xsize"}{$rou}) {
  685.             $$rcfg{"xsize"}{$rou}=400;
  686.         } 
  687.         if (! defined $$rcfg{"ysize"}{$rou}) {
  688.             $$rcfg{"ysize"}{$rou}=100;
  689.         }
  690.         if (! defined $$rcfg{"ytics"}{$rou}) {
  691.             $$rcfg{"ytics"}{$rou}=4;
  692.         }
  693.         if (! defined $$rcfg{"yticsfactor"}{$rou}) {
  694.             $$rcfg{"yticsfactor"}{$rou}=1;
  695.         }
  696.         if (! defined $$rcfg{"factor"}{$rou}) {
  697.             $$rcfg{"factor"}{$rou}=1;
  698.         }
  699.     
  700.         if (defined $$rcfg{"options"}{$rou}) {      
  701.             my $opttemp = lc($$rcfg{"options"}{$rou});          
  702.             delete $$rcfg{"options"}{$rou};
  703.             foreach $one_option (split /[,s]+/, $opttemp) {
  704.                 if (grep {$one_option eq $_} @known_options) {
  705.                     $$rcfg{'options'}{$one_option}{$rou} = 1;
  706.                 } else {
  707.                     warn ("WARNING: Option[$rou]: "$one_option" is unknownn");
  708.                     $error="yes";
  709.                 }
  710.             }
  711.     if ($rcfg->{'options'}{derive}{$rou} and not $cfg->{logformat} eq 'rrdtool'){
  712.     warn ("WARNING: Option[$rou]: "derive" works only with rrdtool logformatn");
  713.     $error="yes";
  714.     }
  715.         }
  716.         #
  717.         # Check out routeruptime definition
  718.         #
  719.         if (defined $$rcfg{"routeruptime"}{$rou}) {
  720.             ($$rcfg{"community"}{$rou},$$rcfg{"router"}{$rou}) =
  721.               split(/@/,$$rcfg{"routeruptime"}{$rou});
  722.         }
  723.         #
  724.         # Check out target definition
  725.         #
  726.         if (defined $$rcfg{"target"}{$rou}) {
  727.             $$rcfg{targorig}{$rou} = $$rcfg{target}{$rou};
  728.     debug ('tarp',"Starting $rou -> $$rcfg{target}{$rou}");
  729.             # Decide whether to turn on IPv6 support for this target.
  730.             # IPv6 support is turned on only if the EnableIPv6 global
  731.             # setting is yes and the IPv4Only per-target setting is no.
  732.             # If IPv6 is disabled, we set IPv4Only to true for all
  733.             # targets, thus disabling all IPv6-related code.
  734.             my $ipv4only = 1;
  735.             if ($$cfg{enableipv6} and $$cfg{enableipv6} eq 'yes') {
  736.                 # IPv4Only is off by default
  737.                 $ipv4only = 0
  738.                   unless (defined $$rcfg{ipv4only}{$rou}) && (lc($$rcfg{ipv4only}{$rou}) eq 'yes');
  739.             }
  740.     ( $$rcfg{target}{$rou}, $$rcfg{uniqueTarget}{$rou} ) =
  741. targparser( $$rcfg{target}{$rou}, $target, $targIndex, $ipv4only, $rcfg->{snmpoptions}{$rou} );
  742.         } else {
  743.             warn ("WARNING: I can't find a "target[$rou]" definitionn");
  744.             $error = "yes";
  745.         }
  746.         # colors format: name#hexcol,
  747.         if (defined $$rcfg{"colours"}{$rou}) {
  748.             if ($$rcfg{'options'}{'dorelpercent'}{$rou}) {
  749.                 if ($$rcfg{"colours"}{$rou} =~  
  750.                     /^([^#]+)(#[0-9a-f]{6})s*,s*
  751.                      ([^#]+)(#[0-9a-f]{6})s*,s*
  752.                      ([^#]+)(#[0-9a-f]{6})s*,s*
  753.                      ([^#]+)(#[0-9a-f]{6})s*,s*
  754.                      ([^#]+)(#[0-9a-f]{6})/ix) {
  755.                     ($$rcfg{'col1'}{$rou}, $$rcfg{'rgb1'}{$rou},
  756.                      $$rcfg{'col2'}{$rou}, $$rcfg{'rgb2'}{$rou},
  757.                      $$rcfg{'col3'}{$rou}, $$rcfg{'rgb3'}{$rou},
  758.                      $$rcfg{'col4'}{$rou}, $$rcfg{'rgb4'}{$rou},
  759.                      $$rcfg{'col5'}{$rou}, $$rcfg{'rgb5'}{$rou}) = 
  760.                        ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
  761.                 } else {
  762.                     warn ("WARNING: "colours[$rou]" for colour definitionn".
  763.                           "       use the format: Name#hexcolour, Name#Hexcolour,...n",
  764.                           "       note, that dorelpercent requires 5 colours");
  765.                     $error="yes";
  766.                 }
  767.             } else {            
  768.                 if ($$rcfg{"colours"}{$rou} =~  
  769.                     /^([^#]+)(#[0-9a-f]{6})s*,s*
  770.                      ([^#]+)(#[0-9a-f]{6})s*,s*
  771.                      ([^#]+)(#[0-9a-f]{6})s*,s*
  772.                      ([^#]+)(#[0-9a-f]{6})/ix) {
  773.                     ($$rcfg{'col1'}{$rou}, $$rcfg{'rgb1'}{$rou},
  774.                      $$rcfg{'col2'}{$rou}, $$rcfg{'rgb2'}{$rou},
  775.                      $$rcfg{'col3'}{$rou}, $$rcfg{'rgb3'}{$rou},
  776.                      $$rcfg{'col4'}{$rou}, $$rcfg{'rgb4'}{$rou}) =
  777.                        ($1, $2, $3, $4, $5, $6, $7, $8);
  778.                 } else {
  779.                     warn "WARNING: "colours[$rou]" for colour definitionn".
  780.                           "       use the format: Name#hexcolour, Name#Hexcolour,...n";
  781.                     $error="yes";
  782.                 }
  783.             }
  784.         } else {            
  785.             if (defined $$rcfg{'options'}{'dorelpercent'}{$rou}) {
  786.                 ($$rcfg{'col1'}{$rou}, $$rcfg{'rgb1'}{$rou},
  787.                  $$rcfg{'col2'}{$rou}, $$rcfg{'rgb2'}{$rou},
  788.                  $$rcfg{'col3'}{$rou}, $$rcfg{'rgb3'}{$rou},
  789.                  $$rcfg{'col4'}{$rou}, $$rcfg{'rgb4'}{$rou},
  790.                  $$rcfg{'col5'}{$rou}, $$rcfg{'rgb5'}{$rou}) = 
  791.                    ("GREEN","#00cc00",
  792.                     "BLUE","#0000ff",
  793.                     "DARK GREEN","#006600",
  794.                     "MAGENTA","#ff00ff",
  795.                     "AMBER","#ef9f4f");
  796.             } else {            
  797.                 ($$rcfg{'col1'}{$rou}, $$rcfg{'rgb1'}{$rou},
  798.                  $$rcfg{'col2'}{$rou}, $$rcfg{'rgb2'}{$rou},
  799.                  $$rcfg{'col3'}{$rou}, $$rcfg{'rgb3'}{$rou},
  800.                  $$rcfg{'col4'}{$rou}, $$rcfg{'rgb4'}{$rou}) =
  801.                    ("GREEN","#00cc00",
  802.                     "BLUE","#0000ff",
  803.                     "DARK GREEN","#006600",
  804.                     "MAGENTA","#ff00ff");
  805.             }
  806.         }
  807.         # Background color, format: #rrggbb
  808.         if (! defined $$rcfg{'background'}{$rou}) {
  809.             $$rcfg{'background'}{$rou} = "#ffffff";
  810.         }
  811.         if ($$rcfg{'background'}{$rou} =~ /^(#[0-9a-f]{6})/i) {
  812.             $$rcfg{'backgc'}{$rou} = "$1";
  813.         } else {
  814.             warn "WARNING: "background[$rou]: ".
  815.                   "$$rcfg{'background'}{$rou}" for colour definitionn".
  816.                   "       use the format: #rrggbbn";
  817.             $error="yes";
  818.         }
  819.         if (! defined  $$rcfg{'kilo'}{$rou}) {
  820.             $$rcfg{'kilo'}{$rou} = 1000;
  821.         }
  822.         if (defined $$rcfg{'kmg'}{$rou}) {
  823.             $$rcfg{'kmg'}{$rou} =~ s/s+//g;
  824.         }
  825.         if (! defined $$rcfg{'xzoom'}{$rou}) {
  826.             $$rcfg{'xzoom'}{$rou} = 1.0;
  827.         }
  828.         if (! defined $$rcfg{'yzoom'}{$rou}) {
  829.             $$rcfg{'yzoom'}{$rou} = 1.0;
  830.         }
  831.         if (! defined $$rcfg{'xscale'}{$rou}) {
  832.             $$rcfg{'xscale'}{$rou} = 1.0;
  833.         }
  834.         if (! defined $$rcfg{'yscale'}{$rou}) {
  835.             $$rcfg{'yscale'}{$rou} = 1.0;
  836.         }
  837.         if (! defined $$rcfg{'timestrpos'}{$rou}) {
  838.             $$rcfg{'timestrpos'}{$rou} = 'NO';
  839.         }
  840.         if (! defined $$rcfg{'timestrfmt'}{$rou}) {
  841.             $$rcfg{'timestrfmt'}{$rou} = "%Y-%m-%d %H:%M";
  842.         }
  843.         if ($error eq "yes") {        
  844.             die "ERROR: Please fix the error(s) in your config filen";
  845.         }
  846.     }
  847. }
  848. # make sure string ends with a slash.
  849. sub ensureSL($) {
  850. #  return;
  851.   my $ref = shift;
  852.   return if $$ref eq "";
  853.   debug('dir',"ensure path IN:  '$$ref'");
  854.   if (${MRTG_lib::SL} eq '\'){
  855.      # two slashes at the start of the string are OK
  856.      $$ref =~ s/(.)Q${MRTG_lib::SL}E+/$1${MRTG_lib::SL}/g;
  857.   } else {
  858.      $$ref =~ s/Q${MRTG_lib::SL}E+/${MRTG_lib::SL}/g;
  859.   }
  860.   $$ref =~ s/Q${MRTG_lib::SL}E*$/${MRTG_lib::SL}/;
  861.   debug('dir',"ensure path OUT: '$$ref'");
  862. }
  863. # convert current supplied time into a nice date string
  864. sub datestr ($) {
  865.     my ($time) = shift || return 0;
  866.     my ($wday) = ('Sunday','Monday','Tuesday','Wednesday',
  867.                   'Thursday','Friday','Saturday')[(localtime($time))[6]];
  868.     my ($month) = ('January','February' ,'March' ,'April' ,
  869.                    'May' , 'June' , 'July' , 'August' , 'September' , 
  870.                    'October' ,
  871.                    'November' , 'December' )[(localtime($time))[4]];
  872.     my ($mday,$year,$hour,$min) = (localtime($time))[3,5,2,1];
  873.     if ($min<10) {
  874.         $min = "0$min";
  875.     }
  876.     return "$wday, $mday $month ".($year+1900)." at $hour:$min";
  877. }
  878. # create expire date for expiery in ARG Minutes
  879. sub expistr ($) {
  880.     my ($time) = time+$_[0]*60+5;
  881.     my ($wday) = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat')[(gmtime($time))[6]];
  882.     my ($month) = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep', 
  883.                    'Oct','Nov','Dec')[(gmtime($time))[4]];
  884.     my ($mday,$year,$hour,$min,$sec) = (gmtime($time))[3,5,2,1,0];
  885.     if ($mday<10) {
  886.         $mday = "0$mday";
  887.     }
  888.     ;
  889.     if ($hour<10) {
  890.         $hour = "0$hour";
  891.     }
  892.     ;
  893.     if ($min<10) {
  894.         $min = "0$min";
  895.     }
  896.     if ($sec<10) {
  897.         $sec = "0$sec";
  898.     }
  899.     return "$wday, $mday $month ".($year+1900)." $hour:$min:$sec GMT";
  900. }
  901. sub create_pid ($) {
  902.     my $pidfile = shift;
  903.     return if ($OS eq 'NT' );
  904.     return if -e $pidfile;
  905.     if ( open(PIDFILE,">$pidfile")) {
  906.          close PIDFILE;
  907.     } else {
  908.          warn "cannot write to $pidfile: $!n";
  909.     }
  910. }
  911. sub demonize_me ($) {
  912.     my $pidfile = shift;
  913.     my $cfgfile = shift;
  914.     print "Daemonizing MRTG ...n";
  915.     if ( $OS eq 'NT' ) {
  916.         print "Do Not close this window. Or MRTG will dien";
  917. #            require Win32::Console;
  918. #            my $CONSOLE = new Win32::Console;
  919.         #    detach process from Console
  920. #            $CONSOLE->Flush();
  921. #            $CONSOLE->Free();
  922. #            $CONSOLE->Alloc();
  923. #            $CONSOLE->Mode()
  924.     }
  925.     elsif( $OS eq 'OS2')
  926.     {
  927.      require OS2::Process;
  928.      if (my_type() eq 'VIO'){
  929.         $main::Cleanfile3 = $pidfile;
  930.         print "MRTG detached. PID=".system(P_DETACH,$^X." ".$0." ".$cfgfile);
  931.         exit;
  932.      }
  933.     } else {
  934.            # Check out if there is another mrtg running before forking
  935.            if (defined $pidfile && open(READPID, "<$pidfile")){
  936.                if (not eof READPID) {
  937.                    chomp(my $input = <READPID>);    # read process id in pidfile
  938.                    if ($input && kill 0 => $input) {# oops - the pid actually exists
  939.                         die "ERROR: I Quit! Another copy of mrtg seems to be running. Check $pidfilen";
  940.                    }
  941.                }
  942.                close READPID;
  943.            }
  944.            defined (my $pid = fork) or die "Can't fork: $!";
  945.            if ($pid) {
  946.               exit;
  947.             } else {
  948.                 if (defined $pidfile){
  949.                    $main::Cleanfile3 = $pidfile;
  950.                    if (open(PIDFILE,">$pidfile")) {
  951.                         print PIDFILE "$$n";
  952.                         close PIDFILE;
  953.                    } else {
  954.                         warn "cannot write to $pidfile: $!n";
  955.                    }
  956.               }
  957.               require 'POSIX.pm';
  958.               POSIX::setsid() or die "Can't start a new session: $!";
  959.               open STDOUT,'>/dev/null' or die "ERROR: Redirecting STDOUT to /dev/null: $!";
  960.               open STDIN, '</dev/null' or die "ERROR: Redirecting STDIN from /dev/null: $!";
  961.       }
  962.    }
  963. }
  964. # Create a new SNMP target entry for the @$target array and return a
  965. # reference to it
  966. sub newSnmpTarg( $$ ) {
  967. my $t = shift; # target string
  968. my $if = shift; # interface match strings
  969. my $targ = { }; # New target closure
  970. $targ->{ Methode } = 'SNMP';
  971. $targ->{ Community } = $if->{ComStr};
  972. $targ->{ Host } = ( defined $if->{HostIPv6} ) ? $if->{HostIPv6} : $if->{HostName};
  973. $targ->{ SnmpOpt } = $if->{SnmpInfo};
  974. $targ->{ snmpoptions}  = $if->{snmpoptions};
  975. $targ->{ Conversion } = ( defined $if->{ConvSub} ) ? $if->{ConvSub} : '';
  976. for my $i( 0..1 ) {
  977. die 'ERROR: Malformed ', $i ? 'output ' : 'input ', "ifSpec in '$t'n"
  978. if not defined $if->{OID}[$i] and not defined $if->{Alt}[$i];
  979. $targ->{OID}[$i] = $if->{OID}[$i];
  980. if( defined $if->{Alt}[$i] ) {
  981. if( defined $if->{Num}[$i] ) {
  982. $targ->{IfSel}[$i] = 'If';
  983. $targ->{Key}[$i] = $if->{Num}[$i];
  984. } elsif( defined $if->{IP}[$i] ) {
  985. $targ->{IfSel}[$i] = 'Ip';
  986. $targ->{Key}[$i] = $if->{IP}[$i];
  987. } elsif( defined $if->{Desc}[$i] ) {
  988. $targ->{IfSel}[$i] = 'Descr';
  989. $targ->{Key}[$i] = $if->{Desc}[$i];
  990. } elsif( defined $if->{Name}[$i] ) {
  991. $targ->{IfSel}[$i] = 'Name';
  992. $targ->{Key}[$i] = $if->{Name}[$i];
  993. } elsif( defined $if->{Eth}[$i] ) {
  994. $targ->{IfSel}[$i] = 'Eth';
  995. $targ->{Key}[$i] = join( '-', map( { sprintf '%02x', hex $_ } split( /-/, $if->{Eth}[$i] ) ) );
  996. } elsif( defined $if->{Type}[$i] ) {
  997. $targ->{IfSel}[$i] = 'Type';
  998. $targ->{Key}[$i] = $if->{Type}[$i];
  999. } else {
  1000. die "ERROR: Internal error parsing ifSpec in '$t'n";
  1001. }
  1002. } else {
  1003. $targ->{IfSel}[$i] = 'None';
  1004. $targ->{Key}[$i] = '';
  1005. }
  1006. # Remove escaped characters and trailing space from Descr or Name Key
  1007. $targ->{Key}[$i] =~ s/\([s:&@])/$1/g
  1008. if $targ->{IfSel}[$i] eq 'Descr' or $targ->{IfSel}[$i] eq 'Name';
  1009. $targ->{Key}[$i] =~ s/[- ]+$//;
  1010. }
  1011. # Remove escaped characters from community
  1012. $targ->{ Community } =~ s/\([ @])/$1/g;
  1013. return $targ; # Return new target closure
  1014. }
  1015. # ( $string, $unique ) = targparser( $string, $target, $targIndex, $ipv4only )
  1016. # Walk amd analyze the target string $string. $target is a reference to the
  1017. # array of targets being built. $targIndex is a reference to a hash of targets
  1018. # previously encountered indexed by target string. When $ipv4only is nonzero,
  1019. # only IPv4 is in use. Returns the modifed target string and the index of the
  1020. # @$target array to which the target refers if that index is unique. If the
  1021. # index is not unique, i.e. the target definition is a calculation involving
  1022. # two or more different targets, then the value -1 is returned for $unique.
  1023. # Targparser updates the target array avoiding duplicate targets. The goal is
  1024. # to substitute all target definitions with strings of the form
  1025. # "$t1$thisTarg$t2", where $thisTarg is the target index, and $t1 and $t2 are
  1026. # as defined below. The intended result is a target string that can be eval'ed
  1027. # in its entirety later on when monitoring data has been collected. This
  1028. # evaluation occurs in sub getcurrent in the main mrtg script.
  1029. # Note: In the regular expressions in &targparser, we have avoided m/.../i
  1030. # and the variables &`, $&, and $'. Use of these makes regex processing less
  1031. # efficient. See Friedl, J.E.F. Mastering Regular Expressions. O'Reilly.
  1032. # p. 273
  1033. sub targparser( $$$$$ ) {
  1034. # Target string (int:community@router, etc.)
  1035. my $string = shift;
  1036. # Reference to target array
  1037. my $target = shift;
  1038. # Reference to target index hash
  1039. my $targIndex = shift;
  1040. # Nonzero if only IPv4 is in use
  1041. my $ipv4only = shift;
  1042. # options passed per target.
  1043. my $snmpoptions = shift;
  1044. # Next available index in the @$target array
  1045. my $idx = @$target;
  1046. # Common match strings: pre-target, target, post-target
  1047. my( $pre, $t, $post );
  1048. # Portion of string already parsed
  1049. my $parsed = '';
  1050. # Initialize $unique to undefined. It will take on the $targIndex value
  1051. # of the first target encountered. $otherTargCount will count the
  1052. # number of other targets (targets with different values of $targIndex)
  1053. # encountered during the parse. $unique will be returned as undef
  1054. # unless $otherTargCount remains 0.
  1055. my $unique = -1;
  1056. my $otherTargCount = 0;
  1057. # Components of the target expression that are substituted into the
  1058. # target string each time a target is identified. The substitution
  1059. # string is the interpolated value of "$t1$targIndex$t2". At present
  1060. # $t1 and $t2 are set to create a new BigFloat object.
  1061. # my $t1 = ' Math::BigFloat->new($target->[';
  1062. # my $t2 = ']{$mode}) ';
  1063.         # this gives problems with perl 5.005 so bigfloat is introduces in mrtg itself
  1064. my $t1 = ' $target->[';
  1065. my $t2 = ']{$mode} ';
  1066. # Find and substitute all external program targets
  1067. while( ( $pre, $t, $post ) = $string =~ m<
  1068. ^(.*?) # capture pre-target string
  1069. ` # beginning of program target
  1070. ((?:\`|[^`])+) # capture target contents (` allowed)
  1071. ` # end of program target
  1072. (.*)$ # capture post-target string
  1073. >x ) { # Total of 3 captures
  1074. my $thisTarg;
  1075. if( exists $targIndex->{ $t } ) {
  1076. # This program target has been encountered previously
  1077. $thisTarg = $targIndex->{ $t };
  1078. debug( 'tarp', "Existing program target [$thisTarg]" );
  1079. } else {
  1080. # A new program target is needed
  1081. my $targ = { };
  1082. $targ->{ Methode } = 'EXEC';
  1083. $targ->{ Command } = $t;
  1084. # Remove escaped backticks
  1085. $targ->{ Command } =~ s/\`/`/g;
  1086. $target->[ $idx ] = $targ;
  1087. $thisTarg = $idx++;
  1088. $targIndex->{ $t } = $thisTarg;
  1089. debug( 'tarp', "New program target [$thisTarg] '$t'" );
  1090. }
  1091. $parsed .= "$pre$t1$thisTarg$t2";
  1092. $string = $post;
  1093. if( $unique < 0 ) {
  1094. $unique = $thisTarg;
  1095. } else {
  1096. $otherTargCount++ unless $thisTarg == $unique;
  1097. }
  1098. };
  1099. # Reset $string for new target type search
  1100. $string = $parsed . $string;
  1101. $parsed = '';
  1102. debug( 'tarp', "&targparser external done: '$string'" );
  1103. # Common interface specification regex components
  1104. # Simple interface specification regex component. Matches interface
  1105. # specification by IPv4 address, description, name, Ethernet address, or
  1106. # type.
  1107. my $ifSimple =
  1108. '       (d+)|' . # by number ($if->{Num})
  1109. '  /    (d+(?:.d+)+)|' . # by IPv4 address ($if->{IP})
  1110. '  \\ ((?:\\[s:&@]|[^s:&@])+)|' . # by description (allow   : & @) ($if->{Desc})
  1111. '  #   ((?:\\[s:&@]|[^s:&@])+)|' . # by name (allow   : & @) ($if->{Name})
  1112. '  !    ([a-fA-F0-9]+(?:-[a-fA-F0-9]+)+)|' . # by Ethernet address ($if->{Eth})
  1113. '  %    (d+)';  # by type ($if->{Type})
  1114. # Complex interface specification regex component. Note that a null string
  1115. # will match. Therefore the match must be postprocessed to check that
  1116. # $ifOID and $ifAlt are not both null.
  1117. my $ifComplex =
  1118. '([a-zA-Z0-9]*(?:.d+)*?)' . # OID possibly starting with a MIB name ($if->{OID})
  1119. '(' . # Interface specification alternatives: ($if->{Alt})
  1120. '.' . #  separator
  1121. $ifSimple . #  simple alternatives (6 variables)
  1122. ')?'; #  maybe none of the above
  1123. # Community-host interface specification regex component.
  1124. my $ifComHost =
  1125. '((?:\\[@ ]|[^s@])+)' . # community string ('@' and ' ' allowed) ($if->{ComStr})
  1126. '@' . # separator
  1127. '(?:([[a-fA-F0-9:]*])|' . # hostname as IPv6 address ($if->{HostIPv6})
  1128. '([-w]+(?:.[-w]+)*))' . # or DNS name ($if->{HostName})
  1129. '((?::[d.!]*)*)' . # SNMP session configuration ($if->{SnmpInfo})
  1130. '(?:|([a-zA-Z_][w]*))?'; # numeric conversion subroutine ($if->{ConvSub})
  1131. # Match strings for simple and complex interface specifications. Entries
  1132. # are of the form $if->{k1}[i], where k1 is OID, Alt, Num, IP, Desc,
  1133. # Name, Eth, or Type, and i is 0 or 1 (input or output). Entries may also
  1134. # have the form $if->{k1}, where k1 is Rev, ComStr, HostIPv6, HostName,
  1135. # SnmpInfo, or ConvSub, with no [i] in these cases.
  1136. my $if;
  1137. # Find and substitute all complex OID targets
  1138. while( ( $pre, $t, $if->{OID}[0], $if->{Alt}[0], $if->{Num}[0],
  1139. $if->{IP}[0], $if->{Desc}[0], $if->{Name}[0], $if->{Eth}[0],
  1140. $if->{Type}[0], $if->{OID}[1], $if->{Alt}[1], $if->{Num}[1],
  1141. $if->{IP}[1], $if->{Desc}[1], $if->{Name}[1], $if->{Eth}[1],
  1142. $if->{Type}[1], $if->{ComStr}, $if->{HostIPv6}, $if->{HostName},
  1143. $if->{SnmpInfo}, $if->{ConvSub}, $post ) = $string =~ m<
  1144. ^(.*?) # capture pre-target string
  1145. ( # capture entire target
  1146. ${ifComplex} # input interface specification (8 captures)
  1147. & # separator
  1148. ${ifComplex} # output interface specification (8 captures)
  1149. : # separator
  1150. ${ifComHost} # community-host specification (5 captures)
  1151. ) # end of entire target capture
  1152. (.*)$ # capture post-target string
  1153. >x ) { # Total of 24 captures
  1154. my $thisTarg;
  1155. # Exception: skip and try to parse later as a simple target if
  1156. # $if->{Desc}[0], $if->{Name}[0], $if->{Desc}[1], or $if->{Name}[1]
  1157. # ends with a backslash character
  1158. if( ( defined $if->{Desc}[0] and $if->{Desc}[0] =~ m<\$> ) or
  1159. ( defined $if->{Name}[0] and $if->{Name}[0] =~ m<\$> ) or
  1160. ( defined $if->{Desc}[1] and $if->{Desc}[1] =~ m<\$> ) or
  1161. ( defined $if->{Name}[1] and $if->{Name}[1] =~ m<\$> ) ) {
  1162. $parsed .= "$pre$t";
  1163. $string = $post;
  1164. next;
  1165. }
  1166. if( exists $targIndex->{ $t } ) {
  1167. # This complex target has been encountered previously
  1168. $thisTarg = $targIndex->{ $t };
  1169. debug( 'tarp', "Existing complex target [$thisTarg]" );
  1170. } else {
  1171. # A new complex target is needed
  1172. my $targ = newSnmpTarg( $t, $if );
  1173. $targ->{ ipv4only } = $ipv4only;
  1174. $targ->{ snmpoptions } = $snmpoptions;
  1175. $target->[ $idx ] = $targ;
  1176. $thisTarg = $idx++;
  1177. $targIndex->{ $t } = $thisTarg;
  1178. debug( 'tarp', "New complex target [$thisTarg] '$t':n" .
  1179. "  Comu:  $targ->{Community}, Host: $targ->{Host}n" .
  1180. "  Opt:   $targ->{SnmpOpt}, IPv4: $targ->{ipv4only}n" .
  1181. "  Conv:  $targ->{Conversion}n" .
  1182. "  OID:   $targ->{OID}[0], $targ->{OID}[1]n" .
  1183. "  IfSel: $targ->{IfSel}[0], $targ->{IfSel}[1]n" .
  1184. "  Key:   $targ->{Key}[0], $targ->{Key}[1]" );
  1185. }
  1186. $parsed .= "$pre$t1$thisTarg$t2";
  1187. $string = $post;
  1188. if( $unique < 0 ) {
  1189. $unique = $thisTarg;
  1190. } else {
  1191. $otherTargCount++ unless $thisTarg == $unique;
  1192. }
  1193. }
  1194. # Reset $string and $parsedfor new target type search
  1195. $string = $parsed . $string;
  1196. $parsed = '';
  1197. debug( 'tarp', "&targparser complex done: '$string'" );
  1198. # Find and substitute all simple targets
  1199. while( ( $pre, $t, $if->{Rev}, $if->{Num}[0], $if->{IP}[0],
  1200. $if->{Desc}[0], $if->{Name}[0], $if->{Eth}[0], $if->{Type}[0],
  1201. $if->{ComStr}, $if->{HostIPv6}, $if->{HostName}, $if->{SnmpInfo},
  1202. $if->{ConvSub}, $post ) = $string =~ m<
  1203. ^(.*?) # capture pre-target string
  1204. ( # capture entire target
  1205. (-)? # capture direction reversal
  1206. (?: ${ifSimple} ) # simple interface specification (6 captures)
  1207. : # separator
  1208. ${ifComHost} # community-host specification (5 captures)
  1209. ) # end of entire target capture
  1210. (.*)$ # capture post-target string
  1211. >x ) { # Total of 15 captures
  1212. my $thisTarg;
  1213. if( exists $targIndex->{ $t } ) {
  1214. # This simple target has been encountered previously
  1215. $thisTarg = $targIndex->{ $t };
  1216. debug( 'tarp', "Existing simple target [$thisTarg]" );
  1217. } else {
  1218. # A new simple target is needed
  1219. # Reverse interface directions if indicated by $if->{Rev}.
  1220. # The sense of $d1 and $d2 is 0 for input and 1 for output
  1221. my $d1 = ( defined $if->{Rev} and $if->{Rev} eq '-' ) ? 1 : 0;
  1222. my $d2 = 1 - $d1;
  1223. # Set the OIDs depending on whether SNMPv2 has been specified
  1224. # and on the direction
  1225. if( $if->{SnmpInfo} =~ m/(?::[^:]*){4}:[32][Cc]?/ ) {
  1226. $if->{OID}[$d1] = 'ifHCInOctets';
  1227. $if->{OID}[$d2] = 'ifHCOutOctets';
  1228. } else {
  1229. $if->{OID}[$d1] = 'ifInOctets';
  1230. $if->{OID}[$d2] = 'ifOutOctets';
  1231. }
  1232. # Give $if->{Alt}[i] an arbitrary defined value so that
  1233. # &newSnmpTarg works correctly
  1234. $if->{Alt}[0] = 1;
  1235. $if->{Alt}[1] = 1;
  1236. # Copy input specification to output
  1237. $if->{Num}[1] = $if->{Num}[0];
  1238. $if->{IP}[1] = $if->{IP}[0];
  1239. $if->{Desc}[1] = $if->{Desc}[0];
  1240. $if->{Name}[1] = $if->{Name}[0];
  1241. $if->{Eth}[1] = $if->{Eth}[0];
  1242. $if->{Type}[1] = $if->{Type}[0];
  1243. my $targ = newSnmpTarg( $t, $if );
  1244. $targ->{ snmpoptions} = $snmpoptions;
  1245. $targ->{ ipv4only } = $ipv4only;
  1246. $target->[ $idx ] = $targ;
  1247. $thisTarg = $idx++;
  1248. $targIndex->{ $t } = $thisTarg;
  1249. debug( 'tarp', "New simple target [$thisTarg] '$t':n" .
  1250. "  Comu:  $targ->{Community}, Host: $targ->{Host}n" .
  1251. "  Opt:   $targ->{SnmpOpt}, IPv4: $targ->{ipv4only}n" .
  1252. "  Conv:  $targ->{Conversion}n" .
  1253. "  OID:   $targ->{OID}[0], $targ->{OID}[1]n" .
  1254. "  IfSel: $targ->{IfSel}[0], $targ->{IfSel}[1]n" .
  1255. "  Key:   $targ->{Key}[0], $targ->{Key}[1]" );
  1256. }
  1257. $parsed .= "$pre$t1$thisTarg$t2";
  1258. $string = $post;
  1259. if( $unique < 0 ) {
  1260. $unique = $thisTarg;
  1261. } else {
  1262. $otherTargCount++ unless $thisTarg == $unique;
  1263. }
  1264. }
  1265. # Assemble string to be returned
  1266. $string = $parsed . $string;
  1267. # Set $unique undefined if more than one target is referred to in the
  1268. # target string
  1269. $unique = -1 if $otherTargCount;
  1270. debug( 'tarp', "&targparser simple done: '$string'" );
  1271. debug( 'tarp', "&targparser returning: unique = $unique" );
  1272. return ( $string, $unique );
  1273. }
  1274. # Display of &targparser intermediate values for debugging purposes. Call as
  1275. # showMatch( $string, $pre, $t, $post, $if ) from within &targparser.
  1276. sub showMatch( $$$$$ ) {
  1277. my( $string, $pre, $t, $post, $if ) = @_;
  1278. warn "# Matching on string '$string'n";
  1279. warn "# Prematch:  '$pre'n";
  1280. warn "# Target:    '$t'n";
  1281. warn "# Postmatch: '$post'n";
  1282. warn "# Captured:n";
  1283. foreach my $k( keys %$if ) {
  1284. if( ref( $if->{$k} ) eq 'ARRAY' ) {
  1285. warn "#  $if->{$k}[0,1]: '",
  1286. ( defined $if->{$k}[0] ) ? $if->{$k}[0] : 'undef', "', '",
  1287. ( defined $if->{$k}[1] ) ? $if->{$k}[1] : 'undef', "'n";
  1288. } else {
  1289. warn "#  $if->{$k}:      '",
  1290. ( defined $if->{$k} ) ? $if->{$k} : 'undef', "'n";
  1291. }
  1292. }
  1293. }
  1294. sub readconfcache ($) {
  1295.     my $cfgfile = shift;
  1296.     my %confcache;
  1297.     if (open (CFGOK,"<$cfgfile")) {
  1298.         while (<CFGOK>) {
  1299.             chomp;
  1300.             next unless /t/; #ignore odd lines
  1301.     next if /^S+:/; #ignore legacy lines
  1302.             my ($host,$method,$key,$if) = split (/t/, $_);
  1303.             $key =~ s/[- ]+$//; # no trailing whitespace in keys realy !
  1304.             $key =~ s/[- ]/ /g; # all else becomes a normal space ... get a life
  1305.             $confcache{$host}{$method}{$key} = $if;
  1306.         }
  1307.         close CFGOK;
  1308.     }
  1309.     return %confcache;
  1310. }
  1311. sub writeconfcache ($$) {
  1312.     my $confcache = shift;
  1313.     my $cfgfile = shift;
  1314.     if ($cfgfile ne '&STDOUT'){
  1315.       open (CFGOK,">$cfgfile") or die "ERROR: writing $cfgfile.ok: $!";
  1316.     }
  1317.     my @hosts;
  1318.     if (defined $$confcache{___updated}) {
  1319.         @hosts = @{$$confcache{___updated}} ;
  1320.         delete $$confcache{___updated};
  1321.     } else {
  1322.         @hosts = grep !/^___/, keys %{$confcache}
  1323.     }
  1324.     foreach my $host (sort @hosts) {
  1325.         foreach my $method (sort keys %{$$confcache{$host}}) {
  1326.             foreach my $key (sort keys %{$$confcache{$host}{$method}}) {
  1327.                 if ($cfgfile ne '&STDOUT'){
  1328.                         print CFGOK "$hostt$methodt$keyt".
  1329.                             $$confcache{$host}{$method}{$key},"n";
  1330.                 } else {
  1331.                          print "$hostt$methodt$keyt".
  1332.                             $$confcache{$host}{$method}{$key},"n";
  1333.                 }
  1334.             }
  1335.         }
  1336.     }
  1337.     close CFGOK;
  1338. }
  1339. sub cleanhostkey ($){
  1340.     my $host = shift;
  1341.     return undef unless defined $host;
  1342.     $host =~ s/(:d*)(?:(:d*)(?:(:d*)(?:(:d*)(?:(:d*)))))$/$1$5/
  1343.         or
  1344.     $host =~ s/(:d*)(?:(:d*)(?:(:d*)(?:(:d*)?)?)?)$/$1/;
  1345.     $host =~ s/:/_/g; # make sure that double invocations do not kill us
  1346.     return $host;
  1347. }
  1348. sub storeincache ($$$$$){
  1349.     my($confcache,$host,$method,$key,$value) = @_;
  1350.     $host = cleanhostkey $host;
  1351.     if (not defined $value ){
  1352.  $$confcache{$host}{$method}{$key} = undef;
  1353.  return;
  1354.     }
  1355.     $value =~ s/[- ]/ /g; # all else becomes a normal space ... get a life
  1356.     $value =~ s/ +$//; # no trailing spaces
  1357.     if (defined $$confcache{$host}{$method}{$key} and 
  1358. $$confcache{$host}{$method}{$key} ne $value) {
  1359.         $$confcache{$host}{$method}{$key} = "Dup";
  1360. debug('coca',"store in confcache $host $method $key --> $value (duplicate)");
  1361.     } else {
  1362.         $$confcache{$host}{$method}{$key} = $value;
  1363. debug('coca',"store in confcache $host $method $key --> $value");
  1364.     }
  1365. }
  1366. sub readfromcache ($$$$){
  1367.     my($confcache,$host,$method,$key) = @_;
  1368.     $host = cleanhostkey $host;
  1369.     return $$confcache{$host}{$method}{$key};
  1370. }
  1371. sub clearfromcache ($$){
  1372.     my($confcache,$host) = @_;
  1373.     $host = cleanhostkey $host;
  1374.     delete $$confcache{$host};
  1375.     debug('coca',"clear confcache $host");
  1376. }
  1377. sub populateconfcache ($$$$$) {
  1378.     my $confcache = shift;
  1379.     my $host = shift;
  1380.     my $ipv4only = shift;
  1381.     my $reread = shift;
  1382.     my $snmpoptions = shift || {};
  1383.     my $hostkey = cleanhostkey $host;    
  1384.     return if defined $$confcache{$hostkey} and not $reread;
  1385.     my $snmp_errlevel = $SNMP_Session::suppress_warnings;
  1386.     $SNMP_Session::suppress_warnings = 3;    
  1387.     debug('coca',"populate confcache $host");
  1388.     # clear confcache for host;
  1389.     delete $$confcache{$hostkey};
  1390.     my @ret;
  1391.     my %tables = ( ifDescr => 'Descr',
  1392.    ifName  => 'Name',
  1393.    ifType  => 'Type',
  1394.    ipAdEntIfIndex => 'Ip' );
  1395.     my @nodes = qw (ifName ifDescr ifType ipAdEntIfIndex);
  1396.     # it seems that some devices only give back sensible data if their tables
  1397.     # are walked in the right ordere ....
  1398.     foreach my $node (@nodes) {
  1399. next if $confcache->{___deadhosts}{$hostkey} and time - $confcache->{___deadhosts}{$hostkey} < 300;
  1400. $SNMP_Session::errmsg = undef;
  1401. @ret = snmpwalk(v4onlyifnecessary($host, $ipv4only), $snmpoptions, $node);
  1402. unless ( $SNMP_Session::errmsg){
  1403.     foreach my $ret (@ret)
  1404.       {
  1405.   my ($oid, $desc) = split(':', $ret, 2);
  1406.   if ($tables{$node} eq 'Ip') {
  1407.       storeincache($confcache,$host,$tables{$node},$oid,$desc);
  1408.   } else {
  1409.                       $desc =~ s/[- ]+$//; #trailing whitespace is too sick for us
  1410.                       $desc =~ s/[- ]/ /g; #whitespace is just whitespace
  1411.       storeincache($confcache,$host,$tables{$node},$desc,$oid);
  1412.   }
  1413.       };
  1414. } else {
  1415.        $confcache->{___deadhosts}{$hostkey} = time
  1416. if $SNMP_Session::errmsg =~ /no response received/;
  1417.     debug('coca',"Skipping $node scanning because $host does not seem to support it");
  1418. }
  1419.     }
  1420.     if ($confcache->{___deadhosts}{$hostkey} and time - $confcache->{___deadhosts}{$hostkey} < 300){
  1421. $SNMP_Session::suppress_warnings = $snmp_errlevel;
  1422. return;
  1423.     }
  1424.     $SNMP_Session::errmsg = undef;
  1425.     @ret = snmpwalk(v4onlyifnecessary($host, $ipv4only), $snmpoptions, "ifPhysAddress");
  1426.     unless ( $SNMP_Session::errmsg){
  1427. foreach my $ret (@ret)
  1428.   {
  1429.       my ($oid, $bin) = split(':', $ret, 2);
  1430.       my $eth = unpack 'H*', $bin; 
  1431.         my @eth;
  1432.       while ($eth =~ s/^..//){
  1433.         push @eth, $&;
  1434.       }
  1435.       my $phys=join '-', @eth;
  1436.       storeincache($confcache,$host,"Eth",$phys,$oid);
  1437.            }
  1438.      } else {
  1439.             debug('coca',"Skipping ifPhysAddress scanning because $host does not seem to support it");
  1440.      }
  1441.      if (ref $$confcache{___updated} ne 'ARRAY') {
  1442.         $$confcache{___updated} = []; #init to empty array
  1443.      }
  1444.      push @{$$confcache{___updated}}, $hostkey;
  1445.     $SNMP_Session::suppress_warnings = $snmp_errlevel;    
  1446. }
  1447. sub log2rrd ($$$) {
  1448.     my $router = shift;
  1449.     my $cfg = shift;
  1450.     my $rcfg = shift;
  1451.     my %mark;
  1452.     my %incomp;
  1453.     my %elapsed_time;
  1454.     my %rate;
  1455.     my %store;
  1456.     my %first_step;
  1457.     my %cur;
  1458.     my %next;
  1459.     my $rrd;    
  1460.     my @steps = qw(300 1800 7200 86400);
  1461.     my %sizes = ( 300 => 600, 1800 => 700, 7200 => 775, 86400 => 797);
  1462.     open R, "<$$cfg{logdir}$$rcfg{'directory'}{$router}$router.log" or 
  1463. die "ERROR: opening $$cfg{logdir}$$rcfg{'directory'}{$router}$router.log: $!";
  1464.     debug('rrd',"converting $$cfg{logdir}$$rcfg{'directory'}{$router}$router.log");
  1465.     my $latest_timestamp;
  1466.     my %latest_counter;
  1467.     chomp($_ = <R>);
  1468.     my $time;
  1469.     my $next_time;
  1470.     ($latest_timestamp,$latest_counter{in},$latest_counter{out}) = split /s+/;
  1471.     chomp($_ = <R>);  
  1472.     ($time,$cur{in},$cur{out},$cur{maxin},$cur{maxout}) = split /s+/;
  1473.     foreach my $s (@steps) {
  1474. $mark{$s} = $latest_timestamp - ($latest_timestamp % $s) + $s;
  1475. $first_step{$s} = $latest_timestamp - ($mark{$s} - $s);
  1476. $elapsed_time{$s} = $s - $first_step{$s};
  1477. $rate{in}{$s}=$cur{in};
  1478. $rate{out}{$s}=$cur{out};
  1479. $rate{maxin}{$s}=$cur{maxin};
  1480. $rate{maxout}{$s}=$cur{maxout};
  1481.     }
  1482.     while(<R>){
  1483. chomp;
  1484. ($next_time,$next{in},$next{out},$next{maxin},$next{maxout}) =
  1485.     split /s+/;
  1486.         foreach my $s (@steps) {
  1487.     # bail if we have enough entries
  1488.     next if ref $store{in}{$s} and
  1489. scalar @{$store{in}{$s}} > $sizes{$s};
  1490.    
  1491.     # ok we are still here. If next mark is before the next time
  1492.             # we take a short step, else we gobble up
  1493.     my $next_stop;
  1494.     do {
  1495. if ($elapsed_time{$s} + $time - $next_time > $s) {
  1496.     $next_stop = $mark{$s}-$s;
  1497. } else {
  1498.     $next_stop = $next_time;
  1499. }
  1500. my $time_diff = $time-$next_stop;
  1501. foreach my $d (qw(in out)) {     
  1502.     $rate{$d}{$s} = ($rate{$d}{$s} * $elapsed_time{$s}
  1503.      + $cur{$d} * $time_diff) /
  1504.        ($elapsed_time{$s} + $time_diff);
  1505. }
  1506. foreach my $d (qw(maxin maxout)){
  1507.     $rate{$d}{$s} = $cur{$d} if $rate{$d}{$s} < $cur{$d};
  1508. }
  1509. $elapsed_time{$s} += $time_diff;
  1510. # print "$time $next_stopn" if $s == 300;
  1511. if ($next_stop == $mark{$s}-$s) {
  1512.     foreach my $t (qw(in out maxin maxout)){
  1513.                        $rate{$t}{$s}/=3600
  1514.                            if (defined $$rcfg{'options'}{'perhour'}{$router});    
  1515.                        $rate{$t}{$s}/=60
  1516.                            if (defined $$rcfg{'options'}{'perminute'}{$router});
  1517.             push @{$store{$t}{$s}}, int($rate{$t}{$s});
  1518.     }
  1519.     $mark{$s} -= $s;
  1520.     $rate{maxin}{$s} = 0;
  1521.     $rate{maxout}{$s} = 0;
  1522.     $elapsed_time{$s} = 0;
  1523. }
  1524.             } while ($next_stop > $next_time );
  1525. }
  1526.         ($time,$cur{in},$cur{out},$cur{maxin},$cur{maxout}) = 
  1527.     ($next_time,$next{in},$next{out},$next{maxin},$next{maxout});
  1528.     }
  1529.     close R;
  1530.     # lets see if we have rrdtool 1.2 at our hands
  1531.     my $VERSION = '0001';
  1532.     if ($RRDs::VERSION >= 1.2){
  1533. $VERSION = '0003';
  1534.     }
  1535.     my $DST;
  1536.     my $pdprepin = (shift @{$store{in}{300}})*($first_step{300});
  1537.     my $pdprepout = (shift @{$store{out}{300}})*($first_step{300});
  1538.     if (defined $$rcfg{'options'}{'absolute'}{$router}) {
  1539. $DST = 'ABSOLUTE'
  1540.     } elsif (defined $$rcfg{'options'}{'gauge'}{$router}) {
  1541. $DST = 'GAUGE'
  1542.     } else {
  1543. $DST = 'COUNTER'
  1544.     }
  1545.     my $MHB = $$cfg{interval} * 60 * 2;
  1546.     my $MAX1 =
  1547.       $$rcfg{'absmax'}{$router}
  1548. || $$rcfg{'maxbytes1'}{$router} 
  1549.   || 'U';
  1550.     my $MAX2 =
  1551.       $$rcfg{'absmax'}{$router}
  1552. || $$rcfg{'maxbytes2'}{$router} 
  1553.   || 'U';
  1554.     
  1555.     $rrd = <<RRD;
  1556. <!-- MRTG Log converted to RRD -->
  1557. <rrd>
  1558. <version> $VERSION </version>
  1559. <step> 300 </step>
  1560. <lastupdate> $latest_timestamp </lastupdate>
  1561. <ds>
  1562. <name> ds0 </name>
  1563. <type> $DST </type>
  1564. <minimal_heartbeat> $MHB </minimal_heartbeat>
  1565. <min> 0 </min>
  1566. <max> $MAX1 </max>
  1567. <!-- PDP Status -->
  1568. <last_ds> $latest_counter{in} </last_ds>
  1569. <value> $pdprepin </value>
  1570. <unknown_sec> 0 </unknown_sec>
  1571. </ds>
  1572. <ds>
  1573. <name> ds1 </name>
  1574. <type> $DST </type>
  1575. <minimal_heartbeat> $MHB </minimal_heartbeat>
  1576. <min> 0 </min>
  1577. <max> $MAX2 </max>
  1578. <!-- PDP Status -->
  1579. <last_ds> $latest_counter{out} </last_ds>
  1580. <value> $pdprepout </value>
  1581. <unknown_sec> 0 </unknown_sec>
  1582. </ds>
  1583. RRD
  1584.     $first_step{300} = 0; # invalidate
  1585.     addarch(1,'AVERAGE','in','out',%store,%first_step,$rrd);
  1586.     addarch(6,'AVERAGE','in','out',%store,%first_step,$rrd);
  1587.     addarch(24,'AVERAGE','in','out',%store,%first_step,$rrd);
  1588.     addarch(288,'AVERAGE','in','out',%store,%first_step,$rrd);
  1589.     addarch(1,'MAX','maxin','maxout',%store,%first_step,$rrd);
  1590.     addarch(6,'MAX','maxin','maxout',%store,%first_step,$rrd);
  1591.     addarch(24,'MAX','maxin','maxout',%store,%first_step,$rrd);
  1592.     addarch(288,'MAX','maxin','maxout',%store,%first_step,$rrd);
  1593.     $rrd .= <<RRD;
  1594. </rrd>
  1595. RRD
  1596.         
  1597.     if ( $OS eq 'NT'  or $OS eq 'OS2') {
  1598.        open (R, "|$$cfg{rrdtool} restore - $$cfg{logdir}$$rcfg{'directory'}{$router}$router.rrd");
  1599.     } else {
  1600.        open (R, "|-") or exec "$$cfg{rrdtool}","restore","-","$$cfg{logdir}$$rcfg{'directory'}{$router}$router.rrd";
  1601.     }
  1602.     print R $rrd;
  1603.     close R;
  1604. }
  1605. sub addarch($$$$$$$){
  1606.     my $steps = shift;
  1607.     my $cons = shift;
  1608.     my $in = shift;
  1609.     my $out = shift;
  1610.     my $store = shift;
  1611.     my $first_step = shift;
  1612.     my $rrd = shift;
  1613.     my $cdpin = 'NaN';
  1614.     my $cdpout = 'NaN';
  1615.     my $param_start = '';
  1616.     my $param_end = '';
  1617.     my $extra_ds = '';
  1618.     if ($RRDs::VERSION >= 1.2){
  1619.         $param_start = '<params>';
  1620.         $param_end = '</params>';
  1621.         $extra_ds = '<primary_value> 0.0000000000e+00 </primary_value> <secondary_value> 0.0000000000e+00 </secondary_value>';
  1622.     }
  1623.     if ($steps != 300) {
  1624. $cdpin = shift @{$$store{$in}{300*$steps}};
  1625. $cdpout = shift @{$$store{$out}{300*$steps}};
  1626.     };
  1627.     $$rrd .= <<RRD;
  1628. <!-- Round Robin Archive -->
  1629. <rra>
  1630. <cf> $cons </cf>
  1631. <pdp_per_row> $steps </pdp_per_row>
  1632. $param_start <xff> 0.5 </xff> $param_end
  1633. <cdp_prep>
  1634. <ds>$extra_ds <value> $cdpin </value>  <unknown_datapoints> 0 </unknown_datapoints></ds>
  1635. <ds>$extra_ds <value> $cdpout </value>  <unknown_datapoints> 0 </unknown_datapoints></ds>
  1636. </cdp_prep>
  1637. <database>
  1638. RRD
  1639.     while (@{$$store{$in}{$steps*300}}){
  1640.         # we take zero as UNKNOWN
  1641. my $inr = pop @{$$store{$in}{$steps*300}} || 'NaN';
  1642. my $outr = pop @{$$store{$out}{$steps*300}} || 'NaN';
  1643. $$rrd .= <<RRD;
  1644.              <row><v> $inr </v><v> $outr </v></row>
  1645. RRD
  1646.     }
  1647.     $$rrd .= <<RRD;
  1648. </database>
  1649. </rra>
  1650. RRD
  1651. }
  1652. # debug if the relevant debug tag is active print the debug message
  1653. sub debug ($$) {
  1654.     return unless scalar @main::DEBUG;
  1655.     my $tag = shift;
  1656.     my $msg = shift;
  1657.     return unless grep {$_ eq $tag} @main::DEBUG;
  1658.     warn "--",$tag,": ",$msg,"n";
  1659.     return;
  1660. }
  1661. # timestamp
  1662. sub timestamp () {
  1663.     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
  1664.                                                 localtime(time);
  1665.     $year += 1900;
  1666.     $mon += 1;
  1667.     return sprintf "%4d-%02d-%02d %02d:%02d:%02d", $year,$mon,$mday,$hour,$min,$sec;
  1668. }
  1669. # configure __DIE__ and __WARN__
  1670.        
  1671. sub setup_loghandlers ($){
  1672.     $::global_logfile = $_[0];
  1673.     for($_[0]){
  1674. /^eventlog$/i && do {
  1675.     require Win32::EventLog;
  1676.     $SIG{__WARN__} = sub {
  1677. my $EventLog = Win32::EventLog->new('MRTG');
  1678. my $Type = ($_[0] =~ /warning/) ? 
  1679.   &Win32::EventLog::EVENTLOG_WARNING_TYPE : 
  1680.   &Win32::EventLog::EVENTLOG_INFORMATION_TYPE;
  1681. my $Msg = $_[0];
  1682. $Msg =~ s/n/rn/g;
  1683.                 $Msg =~ s/[nr]$//g;
  1684. $EventLog->Report({
  1685.         EventID => 1000,
  1686.                       Category => "WARN",
  1687.       EventType => $Type,
  1688.                       Data => '',                       
  1689.       Strings => $Msg });
  1690. $EventLog->Close;
  1691.     };
  1692.     $SIG{__DIE__} = sub {
  1693. my $EventLog = Win32::EventLog->new('MRTG');
  1694. my $Msg = $_[0];
  1695. $Msg =~ s/n/rn/g;
  1696.                 $Msg =~ s/[nr]$//g;
  1697. $EventLog->Report({
  1698.       EventID => 1000,
  1699.                       Category => "ERROR",
  1700.       EventType => &Win32::EventLog::EVENTLOG_ERROR_TYPE,
  1701.                       Data => '',
  1702.       Strings => $Msg });
  1703. $EventLog->Close;
  1704. exit 1;
  1705.     };
  1706.     last;
  1707. };
  1708. $SIG{__WARN__} = sub {
  1709.     
  1710.     if (open DEB, ">>$::global_logfile") {
  1711. print DEB timestamp." -- $_[0]";
  1712. close DEB;
  1713.     } else {
  1714. print STDERR timestamp." -- $_[0]" 
  1715.     }
  1716. };
  1717. $SIG{__DIE__} = sub {
  1718.     
  1719.     if ( open DEB, ">>$::global_logfile") {
  1720. print DEB timestamp." -- $_[0]";
  1721. close DEB;
  1722.     } else {
  1723. print STDERR timestamp." -- $_[0]" 
  1724.     }
  1725.     exit 1
  1726. };
  1727.     }
  1728. }    
  1729. # Adds the v4only attribute to a target if the caller requests it.
  1730. # (this includes targets specified using numeric IPv6 addresses...)
  1731. sub v4onlyifnecessary ($$) {
  1732.     my $target = shift;
  1733.     my $add = shift;
  1734.     my ($v6addr, $temptarget);
  1735.     if($add) {
  1736. # Catch numeric IPv6 addresses
  1737. if ( $target =~ /([[w:]*])(.*)/) {
  1738.     ($v6addr, $temptarget) = ($1,$2);
  1739. } else {
  1740.     $temptarget = $target;
  1741. }
  1742. return $target.(":" x (5 - ($temptarget =~ tr/://))).":v4only";
  1743.     } else {
  1744. return $target;
  1745.     }
  1746. }
  1747. __END__
  1748. =pod
  1749. =head1 NAME
  1750. MRTG_lib.pm - Library for MRTG and support scripts
  1751. =head1 SYNOPSIS
  1752.  use MRTG_lib;
  1753.  my ($configfile, @target_names, %globalcfg, %targetcfg);
  1754.  readcfg($configfile, @target_names, %globalcfg, %targetcfg);
  1755.  my (@parsed_targets);
  1756.  cfgcheck(@target_names, %globalcfg, %targetcfg, @parsed_targets);
  1757. =head1 DESCRIPTION
  1758. MRTG_lib is part of MRTG, the Multi Router Traffic Grapher. It was separated
  1759. from MRTG to allow other programs to easily use the same config files. The
  1760. main part of MRTG_lib is the config file parser but some other funcions are
  1761. there too.
  1762. =over 4
  1763. =item C<$MRTG_lib::OS>
  1764. Type of OS: WIN, UNIX, VMS
  1765. =item C<$MRTG_lib::SL>
  1766. I<Slash> in the current OS.
  1767. =item C<$MRTG_lib::PS>
  1768. Path separator in PATH variable
  1769. =item C<readcfg>
  1770. C<readcfg($file, @targets, %globalcfg, %targetcfg [, $prefix, %extrules])>
  1771. Reads a config file, parses it and fills some arrays and hashes. The
  1772. mandatory arguments are: the name of the config file, a ref to an array which
  1773. will be filled with a list of the target names, a hashref for the global
  1774. configuration, a hashref for the target configuration.
  1775. The configuration file syntax is:
  1776.  globaloption: value
  1777.  targetoption[targetname]: value
  1778.  aprefix*extglobal: value
  1779.  aprefix*exttarget[target2]: value
  1780. E.g.
  1781.  workdir: /var/stat/mrtg
  1782.  target[router1]: 2:public@router1.local.net
  1783.  14all*columns: 2
  1784. The global config hash has the structure
  1785.  $globalcfg{configoption} = 'value'
  1786. The target config hash has the structure
  1787.  $targetcfg{configoption}{targetname} = 'value'
  1788. See L<mrtg-reference> for more information about the MRTG configuration syntax.
  1789. C<readcfg> can take two additional arguments to extend the config file
  1790. syntax. This allows programs to put their configuration into the mrtg config
  1791. file. The fifth argument is the prefix of the extension, the sixth argument
  1792. is a hash with the checkrules for these extension settings. E.g. if the
  1793. prefix is "14all" C<readcfg> will check config lines that begin with
  1794. "14all*", i.e. all lines like
  1795.  14all*columns: 2
  1796.  14all*graphsize[target3]: 500 200
  1797. against the rules in %extrules. The format of this hash is:
  1798.  $extrules{option} = [sub{$_[0] =~ m/^d+$/}, sub{"Error message for $_[0]"}]
  1799.      i.e.
  1800.  $extrules{option}[0] -> a test expression
  1801.  $extrules{option}[1] -> error message if test fails
  1802. The first part of the array is a perl expression to test the value of the
  1803. option. The test can access this value in the variable "$arg". The second
  1804. part of the array is an error message to display when the test fails. The
  1805. failed value can be integrated by using the variable "$arg".
  1806. Config settings with an different prefix than the one given in the C<readcfg>
  1807. call are not checked but inserted into I<%globalcfg> and I<%targetcfg>.
  1808. Prefixed settings keep their prefix in the config hashes:
  1809.  $targetcfg{'14all*graphsize'}{'target3'} = '500 200'
  1810. =item C<cfgcheck>
  1811. C<cfgcheck(@target_names, %globalcfg, %targetcfg, @parsed_targets)>
  1812. Checks the configuration read by C<readcfg>. Checks the values in the config
  1813. for syntactical and/or semantical errors. Sets defaults for some options.
  1814. Parses the "target[...]" options and filles the array @parsed_targets ready
  1815. for mrtg functions.
  1816. The first three arguments are the same as for C<readcfg>. The fourth argument
  1817. is an arrayref which will be filled with the parsed target defs.
  1818. C<cfgcheck> converts the values of target settings I<options>, e.g.
  1819.  options[router1]: bits, growright
  1820. to a hash:
  1821.  $targetcfg{'option'}{'bits'}{'router1'} = 1
  1822.  $targetcfg{'option'}{'growright'}{'router1'} = 1
  1823. This is not done by C<readcfg> so if you don't use C<cfgcheck> you have to
  1824. check the scalar variable I<$targetcfg{'option'}{'router1'}> (MRTG allows
  1825. options to be separated by space or ',').
  1826. =item C<ensureSL>
  1827. C<ensureSL($pathname)>
  1828. Checks that the I<pathname> does not contain double path separators and ends
  1829. with a path separator. It uses $MRTG_lib::SL as path separator which will be /
  1830. or  depending on the OS.
  1831. =item C<log2rrd>
  1832. C<log2rrd ($router,%globalcfg,%targetcfg)>
  1833. Convert log file to rrd format. Needs rrdtool.
  1834. =item C<datestr>
  1835. C<datestr(time)>
  1836. Returns the time given in the argument as a nicely formated date string.
  1837. The argument has to be in UNIX time format (seconds since 1970-1-1).
  1838. =item C<timestamp>
  1839. C<timestamp()>
  1840. Return a string representing the current time.
  1841. =item C<setup_loghandlers>
  1842. C<setup_loghandlers(filename)>
  1843. Install signalhandlers for __DIE__ and __WARN__ making the errors
  1844. go the the specified destination. If filename is 'eventlog'
  1845. mrtg will log to the windows event logger.
  1846. =item C<expistr>
  1847. C<expistr(time)>
  1848. Returns the time given in the argument formatted suitable for HTTP
  1849. Expire-Headers.
  1850. =item C<create_pid> 
  1851. C<create_pid()> 
  1852. Creates a pid file for the mrtg daemon       
  1853. =item C<demonize_me>
  1854. C<demonize_me()>
  1855. Puts the running program into background, detaching it from the terminal.
  1856. =item C<populatecache>
  1857. C<populatecache(%confcache, $host, $reread, $snmpoptshash)>
  1858. Reads the SNMP variables I<ifDescr>, I<ipAdEntIfIndex>, I<ifPhysAddress>, I<ifName> from
  1859. the I<host> and stores the values in I<%confcache> as follows:
  1860.  $confcache{$host}{'Descr'}{ifDescr}{oid} = (ifDescr or 'Dup')
  1861.  $confcache{$host}{'IP'}{ipAdEntIfIndex}{oid} = (ipAdEntIfIndex or 'Dup')
  1862.  $confcache{$host}{'Eth'}{ifPhysAddress}{oid} = (ifPhysAddress or 'Dup')
  1863.  $confcache{$host}{'Name'}{ifName}{oid} = (ifName or 'Dup')
  1864.  $confcache{$host}{'Type'}{ifType}{oid} = (ifType or 'Dup')
  1865. The value (at the right side of =) is 'Dup' if a value was retrieved
  1866. muliple times, the retrieved value else.
  1867. =item C<readconfcache>
  1868. C<my $confcache = readconfcache($file)>
  1869. Preload the confcache from a file.
  1870. =item C<readfromconfcache>
  1871. C<writeconfcache($confcache,$file)>
  1872. Store the current confcache into a file.
  1873. =item C<writeconfcache>
  1874. C<writeconfcache($confcache,$file)>
  1875. Store the current confcache into a file.
  1876. =item C<storeincache>
  1877. C<storeincache($confcache,$host,$method,$key,$value)>
  1878. =item C<readfromcache>
  1879. C<readfromcache($confcache,$host,$method,$key)>
  1880. =item C<clearfromcache>
  1881. C<clearfromcache($confcache,$host)>
  1882. =item C<debug>
  1883. C<debug($type, $message)>
  1884. Prints the I<message> on STDERR if debugging is enabled for type I<type>.
  1885. A debug type is enabled if I<type> is in array @main::DEBUG.
  1886. =back
  1887. =head1 AUTHORS
  1888. Rainer Bawidamann E<lt>Rainer.Bawidamann@rz.uni-ulm.deE<gt>
  1889. (This Manpage)
  1890. =cut