mrtg
上传用户:shbosideng
上传日期:2013-05-04
资源大小:1555k
文件大小:89k
源码类别:

SNMP编程

开发平台:

C/C++

  1. #! /usr/bin/perl -w
  2. # -*- mode: cperl -*-
  3. ###################################################################
  4. # MRTG 2.13.2  Multi Router Traffic Grapher
  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. my @STARTARGS=($0,@ARGV);
  17. @main::DEBUG=qw();
  18. # DEBUG TARGETS
  19. # cfg  - watch the config file reading
  20. # dir  - directory mangeling
  21. # base - basic program flow
  22. # tarp - target parser
  23. # snpo - snmp polling
  24. # snpo2 - more snmp debug
  25. # coca - confcache operations
  26. # repo - track confcache repopulation
  27. # fork - forking view
  28. # time - some timing info
  29. # log  - logging of data via rateup or rrdtool
  30. $main::GRAPHFMT="png";
  31. # There older perls tend to behave peculiar with
  32. # large integers ...
  33. require 5.005;
  34. use strict;
  35. BEGIN {
  36.     # Automatic OS detection ... do NOT touch
  37.     if ( $^O =~ /^(ms)?(dos|win(32|nt)?)/i ) {
  38. $main::OS = 'NT';
  39. $main::SL = '\';
  40. $main::PS = ';';        
  41.     } elsif ( $^O =~ /^NetWare$/i ) {
  42. $main::OS = 'NW';
  43. $main::SL = '/';
  44. $main::PS = ';';
  45.     } elsif ( $^O =~ /^VMS$/i ) {
  46. $main::OS = 'VMS';
  47. $main::SL = '.';
  48. $main::PS = ':';
  49.     } elsif ( $^O =~ /^os2$/i ) {
  50. $main::OS = 'OS2';
  51. $main::SL = '/';
  52. $main::PS = ';';
  53.     } else {
  54. $main::OS = 'UNIX';
  55. $main::SL = '/';
  56. $main::PS = ':';
  57.     }
  58.     if ( $ENV{LANG} and $ENV{LANG} =~ /UTF.*8/i ){        
  59.         my $args = join " ", map { /s/ ? ""$_"" : $_ } @ARGV;
  60.         $args ||= "";
  61.         print <<ERR;
  62. -----------------------------------------------------------------------
  63. ERROR: Mrtg will most likely not work properly when the environment
  64.        variable LANG is set to UTF-8. Please run mrtg in an environment
  65.        where this is not the case. Try the following command to start:
  66.        env LANG=C ${0} $args 
  67. -----------------------------------------------------------------------
  68. ERR
  69.         exit 0;
  70.     }
  71. }
  72. use FindBin;
  73. use lib "${FindBin::Bin}";
  74. use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2";
  75. use Getopt::Long;
  76. use Math::BigFloat;
  77. # search for binaries in the bin and bin/../lib  directory
  78. use MRTG_lib "2.100015";
  79. # $SNMP_Session::suppress_warnings = 2;
  80. use locales_mrtg "0.07";
  81. # Import IPv6 libraries if available, as (unfortunately) they are
  82. # necessary for dead host detection (for IPv6 name lookups).
  83. BEGIN {
  84.     if (eval {require Socket6;})  {
  85. import Socket;
  86. import Socket6
  87.     }    
  88. }
  89. # Do not Flash Console Windows for the forked rateup process 
  90. BEGIN {
  91.     if ($^O eq 'MSWin32'){
  92. eval {require Win32; Win32::SetChildShowWindow(0)};
  93. warn "WARNING: $@n" if $@;
  94.     }    
  95. }
  96. $main::STARTTIME = time;
  97. if ($MRTG_lib::OS eq 'OS2') {
  98. # in daemon mode we will pause 3 seconds to be sure that parent died
  99.   require OS2::Process;
  100.   if (OS2::Process::my_type() eq 'DETACH') {sleep(3);}
  101. }
  102. if ($MRTG_lib::OS eq 'UNIX') {
  103.    $SIG{INT} = $SIG{TERM} = 
  104.            sub {   unlink ${main::Cleanfile} 
  105.                        if defined $main::Cleanfile;
  106.                    unlink ${main::Cleanfile2}
  107.                        if defined $main::Cleanfile2;
  108.                    unlink ${main::Cleanfile3}
  109.                        if defined $main::Cleanfile3;
  110.                    warn "ERROR: Bailout after SIG $_[0]n";
  111.                    exit 1;
  112.                 };
  113.   $SIG{HUP} = sub {
  114.                    unlink ${main::Cleanfile} 
  115.                        if defined $main::Cleanfile;
  116.                    unlink ${main::Cleanfile2}
  117.                        if defined $main::Cleanfile2;
  118.                    unlink ${main::Cleanfile3}
  119.                        if defined $main::Cleanfile3;
  120.                    die "ERROR: Bailout after SIG $_[0]n";
  121.                 };
  122. }
  123. END {
  124.     local($?, $!);
  125.     unlink ${main::Cleanfile} if defined $main::Cleanfile;
  126.     unlink ${main::Cleanfile2} if defined $main::Cleanfile2;
  127. }
  128. &main;
  129. exit(0);
  130. #### Functions ################################################
  131. sub main {
  132.     debug 'time', "prog start ".localtime(time);
  133.     
  134.     # read in the config file
  135.     my @routers;
  136.     my %cfg;
  137.     my %rcfg;
  138.     my %opts; 
  139.     my $EXITCODE = 0;
  140.     
  141.     GetOptions(%opts, 'user=s', 'group=s', 'lock-file=s','confcache-file=s','logging=s', 'check', 'fhs', 'daemon',  'pid-file=s','debug=s', 'log-only');
  142.     if (defined $opts{debug}){
  143.         @main::DEBUG = split /s*,s*/, $opts{debug};
  144. if (defined $SNMP_util::Debug){
  145.         $SNMP_util::Debug = 1 if grep /^snpo2$/, @main::DEBUG;
  146. }
  147.     }
  148.     my $uid = $<;
  149.     my $gid = $(;
  150.     if (defined $opts{group}) {
  151.         $gid = getgrnam($opts{group}) or die "ERROR: Unknown Group: $opts{group})n";
  152.     }
  153.     if (defined $opts{user}) {
  154.         $uid = getpwnam($opts{user}) or die "ERROR: Unknown User: $opts{user})n";
  155.     }
  156.     # If we've specified using FHS (http://www.pathname.com/fhs/) on the command line,
  157.     # use the relevant path definitions (can be overridden later):
  158.     my $confcachefile;
  159.     my $pidfile;
  160.     my $lockfile;
  161.     my $templock;
  162.     my $logfile;
  163.     if (defined $opts{"fhs"}) {
  164. $confcachefile = "/var/cache/mrtg/mrtg.ok";
  165. $pidfile = "/var/run/mrtg.pid";
  166. $lockfile = "/var/cache/mrtg/mrtg.lck";
  167. $templock = "/var/cache/mrtg/mrtg.lck.$$";
  168. $logfile = "/var/log/mrtg.log";
  169.     }
  170.     my $cfgfile = shift @ARGV;
  171.     if ( !defined $cfgfile and -r "/etc/mrtg.cfg" ) { $cfgfile = "/etc/mrtg.cfg"; }
  172.     printusage() unless defined $cfgfile;
  173.     # PID file code, used later if daemonizing...
  174.     if ( !defined($pidfile) ) {
  175.         $pidfile =  $cfgfile;
  176.         $pidfile =~ s/.[^./]+$//;
  177.         $pidfile .= '.pid';
  178.     }
  179.     $pidfile =  $opts{"pid-file"} || $pidfile;
  180.     # Run as a daemon, specified on command line (required for FHS compliant daemon)
  181.     if (defined $opts{"daemon"}) {
  182. # Create a pidfile, then chown it so we can use it once we change user
  183. &create_pid($pidfile);
  184. chown $uid, $gid, $pidfile;
  185.     }
  186.     ($(,$)) = ($gid,$gid) ;
  187.     ($<,$>) = ($uid,$uid) ;
  188.     die "ERROR failed to set UID to $uidn" unless ($< == $uid and  $> == $uid);
  189.     $logfile = $opts{logging} || $logfile;
  190.     if (defined $logfile){
  191. setup_loghandlers $logfile;
  192.         warn "Started mrtg with config '$cfgfile'n";
  193.     }
  194.     # lets make sure that there are not two mrtgs running in parallel.
  195.     # so we lock on the cfg file. Nothing fancy, just a lockfile
  196.     $lockfile = $opts{"lock-file"} || $lockfile;
  197.     if (! defined $lockfile) {
  198.         $lockfile = $cfgfile."_l";
  199.     }
  200.     if (! defined $templock) {
  201.         $templock = $lockfile."_" . $$ ;
  202.     }
  203.     debug('base', "Creating Lockfiles $lockfile,$templock");
  204.     &lockit($lockfile,$templock);
  205.     debug('base', "Reading Config File: $cfgfile");
  206.     readcfg($cfgfile,@routers,%cfg,%rcfg);
  207.     
  208.     
  209.     # Enable or disable snmpv3
  210.     if(defined $cfg{enablesnmpv3}) {
  211.         $cfg{enablesnmpv3} = lc($cfg{enablesnmpv3});
  212.     } else {
  213.         $cfg{enablesnmpv3} = 'no';
  214.     }
  215.     # Check we have the necessary libraries for IPv6 support
  216.     if ($cfg{enablesnmpv3} eq 'yes') {
  217.         if  (eval {require Net_SNMP_util;})   {
  218.     import Net_SNMP_util;
  219.             debug('base', "SNMP V3 libraries found, SNMP V3 enabled.");
  220.         } else {
  221.             warn "WARNING: SNMP V3 libraries not found, SNMP V3 disabled.n";
  222.             $cfg{enablesnmpv3} =  'no';
  223.     require SNMP_util;
  224.     import SNMP_util;
  225.         }
  226.     }
  227.     else { # load V1/V2 libraries 
  228. require SNMP_util;
  229. import SNMP_util;
  230.     }
  231.     
  232.     # Enable or disable IPv6
  233.     if(defined $cfg{enableipv6}) {
  234.         $cfg{enableipv6} = lc($cfg{enableipv6});
  235.     } else {
  236.         $cfg{enableipv6} = 'no';
  237.     }
  238.     # Check we have the necessary libraries for IPv6 support
  239.     if ($cfg{enableipv6} eq 'yes') {
  240.         if ( (eval {require Socket6;}) and (eval {require IO::Socket::INET6;}) ) {
  241.             debug('base', "IPv6 libraries found, IPv6 enabled.");
  242.         } else {
  243.             warn "WARNING: IPv6 libraries not found, IPv6 disabled.n";
  244.             $cfg{enableipv6} =  'no';
  245.         }
  246.     }
  247.     # from our last run we kept some info about
  248.     # the configuration of our devices around
  249.     debug('base', "Reading Interface Config cache");
  250.     $confcachefile =  $opts{"confcache-file"} || $confcachefile;
  251.     if ( !defined($confcachefile) ) {
  252.         $confcachefile = $cfgfile;
  253.         $confcachefile  =~ s/.[^./]+$//;
  254.         $confcachefile .= ".ok";
  255.     }
  256.     my $confcache = readconfcache($confcachefile);
  257.     # Check the config and create the target object
  258.     debug('base', "Checking Config File");
  259.     my @target;
  260.     cfgcheck(@routers, %cfg, %rcfg, @target);
  261.     # exit here if we only check the config file
  262.     # in case of an error, cfgcheck() already exited
  263.     if (defined $opts{check}) {
  264.         debug('base', "Remove Lock Files");
  265.         close LOCK; unlink ($templock, $lockfile);
  266.         debug('base', "Exit after successful config file check");
  267.         exit 0;
  268.     }
  269.     # postload rrdtool support
  270.     if ($cfg{logformat} eq 'rrdtool'){
  271.         debug('base', "Loading RRD support");
  272. require 'RRDs.pm';
  273.     }
  274.     # set the locale
  275.     my $LOC;
  276.     if ( $cfg{'language'} and defined($lang2tran::LOCALE{"L$cfg{'language'}E"})) {
  277. debug('base', "Loading Locale for ".$cfg{'language'});
  278. $LOC=$lang2tran::LOCALE{"L$cfg{'language'}E"};
  279.     } else {
  280. debug('base', "Loading default Locale");
  281. $LOC=$lang2tran::LOCALE{'default'};
  282.     }
  283.     # Daemon Code
  284.     my $last_time=0;
  285.     my $curent_time;
  286.     my $sleep_time;
  287.     if (defined $opts{"daemon"}) { $cfg{'runasdaemon'} = "yes"; }
  288.     &demonize_me($pidfile,$cfgfile) if defined $cfg{'runasdaemon'}  and $cfg{'runasdaemon'} =~ /y/i  and $MRTG_lib::OS ne 'VMS'
  289.      and not (defined $cfg{'nodetach'} and $cfg{'nodetach'} =~ /y/i);
  290.     # auto restart on die if running as demon
  291.     $SIG{__DIE__} = sub {
  292.         warn $_[0];
  293.         warn "*** Restarting after 10 seconds in an attempt to recover from the error aboven";
  294.         sleep 10;
  295.         exec @STARTARGS;
  296.     } if $cfg{'runasdaemon'};
  297.     debug('base', "Starting main Loop");
  298.     do {                        # Do this loop once for native mode and forever in daemon mode 
  299.         my $router;
  300.         debug 'time', "loop start ".localtime(time);
  301.         #if we run as daemon, we sleep in between collection cycles
  302.         $sleep_time=  ($cfg{interval}*60)-(time-$last_time);
  303.         if ($sleep_time > 0 ) { #If greater than 0 the sleep that amount of time
  304.     debug('time', "Sleep time $sleep_time seconds");
  305.             sleep ($sleep_time);
  306.         } elsif ($last_time > 0) {
  307.             warn "WARNING: data collection did not complete within interval!n";
  308.         }
  309.         $last_time=time;
  310.         # set meta expires if there is an index file
  311.         # 2000/05/03 Bill McGonigle <bill@zettabyte.net>
  312.         if (defined $cfg{'writeexpires'}) {
  313.            my $exp = &expistr($cfg{'interval'} ? $cfg{'interval'} : 5);
  314.            my $fil;
  315.            $fil = "$cfg{'htmldir'}index.html"  if -e "$cfg{'htmldir'}index.html";
  316.            $fil = "$cfg{'htmldir'}index.htm"  if -e "$cfg{'htmldir'}index.htm";
  317.             if (defined $fil) {
  318.                    open(META, ">$fil.meta");
  319.                    print META "Expires: $expn";
  320.                    close(META);
  321.             }
  322.         }
  323.         # Use SNMP to populate the target object
  324. debug('base', "Populate Target object by polling SNMP and".
  325.       " external Datasources");
  326.         debug 'time', "snmp read start ".localtime(time);
  327.         readtargets($confcache,@target, %cfg);
  328.         # collect data for each router or pseudo target (`executable`)
  329.         debug 'time', "target loop start ".localtime(time);
  330.         foreach $router (@routers) {
  331.     debug('base', "Act on Router/Target $router");
  332.             if (defined $rcfg{'setenv'}{$router}) {
  333.                 my $line = $rcfg{'setenv'}{$router};
  334.                 while ( $line =~ s/([^=]+)="([^"]*)"s*// ) # " - unconfuse the highliter
  335.   { 
  336.     $ENV{$1}=$2;
  337.                 }
  338.     }
  339.     my($savetz) = $ENV{'TZ'};
  340.     if (defined $rcfg{'timezone'}{$router}) {
  341.                 $ENV{'TZ'} = $rcfg{'timezone'}{$router};
  342. if ( $main::OS eq 'UNIX' ){
  343. require 'POSIX.pm';
  344. POSIX::tzset();
  345. }
  346.             }
  347.             my ($inlast, $outlast, $uptime, $name, $time) = 
  348.               getcurrent(@target, $router, %rcfg, %cfg);
  349.             if ( defined($inlast) and defined($outlast)) {
  350.               $EXITCODE = $EXITCODE | 1;
  351.             }
  352.             else {
  353.               $EXITCODE = $EXITCODE | 2;
  354.             }
  355.     debug('base', "Get Current values: in:".( defined $inlast ? $inlast : "undef").", out:".
  356.                                                  ( defined $outlast? $outlast : "undef").", up:".
  357.                                                  ( defined $uptime ? $uptime : "undef").", name:".
  358.                                                  ( defined $name ? $name : "undef").", time:".
  359.                                                  ( defined $time ? $time : "undef"));
  360.             #abort, if the router is not responding.
  361.             if ($cfg{'logformat'} ne 'rrdtool') {
  362.               # undefined values are ok for rrdtool !
  363.               #if ( not defined $inlast or not defined $outlast){
  364.               #  warn "WARNING: Skipping Update of $router, inlast is not definedn"
  365.               #          unless defined $inlast;
  366.               #  warn "WARNING: Skipping Update of $router, outlast is not definedn"
  367.               #          unless defined $outlast;
  368.               #  next;
  369.               #}
  370.               if (defined $inlast and $inlast < 0) {
  371.                 $inlast += 2**31;
  372.                 # this is likely to be a broken snmp counter ... lets compensate
  373.               }  
  374.               if (defined $outlast and $outlast < 0) {
  375.                 $outlast += 2**31;
  376.                 # this is likely to be a broken snmp counter ... lets compensate
  377.               }  
  378.             }
  379.     
  380.             my ($maxin, $maxout, $maxpercent, $avin, $avout, $avpercent,$avmxin, $avmxout,
  381.                 $cuin, $cuout, $cupercent);
  382.     debug('base', "Create Graphics");
  383.             if ($rcfg{'options'}{'dorelpercent'}{$router}) {
  384.                 ($maxin, $maxout, $maxpercent, $avin, $avout, $avpercent,
  385.                  $cuin, $cuout, $cupercent, $avmxin, $avmxout) =
  386.                   writegraphics($router, %cfg, %rcfg, $inlast, $outlast, $time,$LOC, %opts);
  387.             } else {
  388.                 ($maxin, $maxout ,$avin, $avout, $cuin, $cuout, $avmxin, $avmxout) =
  389.                   writegraphics($router, %cfg, %rcfg, $inlast, $outlast, $time,$LOC, %opts);
  390.             }
  391.             # skip this update if we did not get anything usefull out of
  392.             # writegraphics
  393.             next if not defined $maxin;
  394.     debug('base', "Check for Thresholds");
  395.             threshcheck(%cfg,%rcfg,$cfgfile,$router,$cuin,$cuout);
  396.     if (defined $opts{'log-only'}){
  397. debug('base', "Disable Graph and HTML generation");
  398.     }
  399.     if ($cfg{logformat} eq 'rateup' and not defined $opts{'log-only'} ){
  400. debug('base', "Check for Write HTML Pages");
  401. writehtml($router, %cfg, %rcfg,
  402.   $maxin, $maxout, $maxpercent, $avin, $avout, $avmxin, $avmxout, $avpercent,
  403.   $cuin, $cuout, $cupercent, $uptime, $name, $LOC)
  404.     }
  405.             #put TZ things back in shape ... 
  406.             if ($savetz) {
  407.                 $ENV{'TZ'} =  $savetz;
  408.             } else {
  409.                 delete $ENV{'TZ'};
  410.             }
  411.     if ( $main::OS eq 'UNIX' ){
  412. require 'POSIX.pm';
  413. POSIX::tzset();
  414.     };
  415.         }
  416.     } while ($cfg{'runasdaemon'} and $cfg{'runasdaemon'} =~ /y/i ); #In daemon mode run forever
  417.     debug('base', "Exit main Loop");
  418.     # OK we are done, remove the lock files ... 
  419.     debug('base', "Remove Lock Files");
  420.     close LOCK; unlink ($templock, $lockfile);
  421.     debug('base', "Store Interface Config Cache");
  422.     delete $$confcache{___updated} if exists $$confcache{___updated}; # make sure everything gets written out not only the updated entries
  423.     writeconfcache($confcache,$confcachefile);
  424.     if ( ! $cfg{'runasdaemon'} or $cfg{'runasdaemon'} !~ /y/i ) {
  425.       if ( ($EXITCODE & 1) and ($EXITCODE & 2) ) {
  426.         # At least one target was sucessful
  427.         exit 91;
  428.       }
  429.       elsif ( not ($EXITCODE & 1) and ($EXITCODE & 2) ) {
  430.         # All targets failed
  431.         exit 92;
  432.       }
  433.     }
  434. }
  435. # ( $inlast, $outlast, $uptime, $name, $time ) =
  436. #    &getcurrent( $target, $rou, $rcfg, $cfg )
  437. # Calculate monitored data for device $rou based on information in @$target
  438. # and referring to configuration data in %$rcfg and %$cfg. In the returned
  439. # list, $inlast and $outlast are the input and output monitored data values,
  440. # $uptime is the device uptime, $name is the device name, and $time is the
  441. # current time when the calculation was performed.
  442. sub getcurrent {
  443. my( $target, $rou, $rcfg, $cfg ) = @_;
  444. # Hash indexed by $mode for conveniently saving $inlast and $outlast
  445. my %last;
  446. # Initialize uptime, device name, and data collection time to empty strings
  447. my $uptime = '';
  448. my $name = '';
  449. my $time = '';
  450. # Calculate input and output monitored data
  451. foreach my $mode( qw( _IN_  _OUT_ ) ) {
  452. # Initialize monitored data, warning message, and death message
  453. # to empty strings
  454. my $data;
  455. my $warning;
  456. my $death;
  457. {
  458. # Code block used to calculate monitoring data
  459. # Localize warning and death exception handlers to capture
  460. # error message less any leading and trailing white space
  461. local $SIG{ __WARN__ } =
  462. sub { $_[0] =~ /^s*(.+?)s*$/; $warning = $1; };
  463. local $SIG{ __DIE__ } =
  464. sub { $_[0] =~ /^s*(.+?)s*$/; $death = $1; };
  465. # Calculate monitoring data. $rcfg->{ target }{ $rou } contains
  466. # a Perl expression for the calculation.
  467. $data = eval "$rcfg->{target}{$rou}";
  468.         }
  469. # Test for various exceptions occurring in the calculation
  470. if( $warning ) {
  471. warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (warn): $warningn";
  472. $data = undef;
  473. } elsif( $death ) {
  474. warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (kill): $deathn";
  475. $data = undef;
  476. } elsif( $@ ) {
  477. warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (eval): $@n";
  478. $data = undef;
  479. } elsif( not defined $data ) {
  480. warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' did not eval into defined datan";
  481. $data = undef;
  482. } elsif( $data and $data !~ /^[-+]?d+(.d*)?([eE][+-]?[0-9]+)?$/ ) {
  483. warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' evaluated to '$data' instead of a numbern";
  484. $data = undef;
  485. } elsif( length( $data ) > 190 ) {
  486. warn "ERROR: $mode value: '$data' is way to long ...n";
  487. $data = undef;
  488. } else {
  489. # At this point data is considered valid. Round to an integer
  490. # unless RRDTool is in use and this is a gauge
  491.                         if (not ( $cfg->{ logformat } eq 'rrdtool'
  492.                                       and  defined $rcfg->{ options }{ gauge }{ $rou })){
  493.                             if (ref $data and ref $data eq 'Math::BigFloat') {
  494.                  $data->ffround( 0 )
  495.                             } else {
  496.                                         $data = sprintf "%.0f", $data;
  497.                             }
  498.                         }
  499. # Remove any leading plus sign
  500. $data =~ s/^+//;
  501. }
  502. $last{ $mode } = $data;
  503. }
  504. # Set $u to the unique index of the @$target array referred to in the
  505. # monitored data calculation for the current device. $u will be set to
  506. # -1 if that index is not unique.
  507. my $u = $rcfg->{ uniqueTarget }{ $rou };
  508. # Get the uptime, device name, and data collection time from the @$target
  509. # array if the monitored data calculation refers only to one target.
  510. # Otherwise it doesn't make sense to do this.
  511. if( $rcfg->{ uniqueTarget }{ $rou } >= 0 ) {
  512. $uptime = $target->[ $u ]{ _UPTIME_ };
  513. $name = $target->[ $u ]{ _NAME_ };
  514. $time = $target->[ $u ]{ _TIME_ };
  515. }
  516. # Set the time to the current time if it was not set above
  517. $time = time unless $time;
  518.         
  519. # Get the uptime and device name from the alternate location
  520. # (community@host) that may have been specified with the RouterUptime
  521. # target keyword
  522. if( defined $rcfg->{ routeruptime }{ $rou } ) {
  523.          ( $uptime, $name ) = snmpget(
  524. v4onlyifnecessary( $rcfg->{ routeruptime }{ $rou },
  525. $rcfg->{ ipv4only }{ $rou } ),
  526. $rcfg->{ snmpoptions }{ $rou }, 'sysUptime', 'sysName');
  527. }
  528.   
  529. # Get the device name from the alternate location (OID or
  530. # OID:community@host) that may have been specified with the RouterName
  531. # target keyword
  532. if( defined $rcfg->{ routername }{ $rou } ) {
  533. my( $noid, $nloc ) = split( /:/, $rcfg->{ routername }{ $rou }, 2 );
  534. # If no location (community@host) was specified use values from the
  535. # unique target referred to in the monitored data calculation
  536. if( $u >= 0 and not $nloc ) {
  537. my $comm = $target->[ $u ]{ Community };
  538. my $host = $target->[ $u ]{ Host };
  539. my $opt = $target->[ $u ]{ SnmpOpt };
  540. $nloc = "$comm@$host$opt";
  541. }
  542. # Get the location from the RouterUptime keyword if that is defined
  543. # and $nloc has not otherwise been specified
  544. $nloc = $rcfg->{ routeruptime }{ $rou }
  545. if defined $rcfg->{ routeruptime }{ $rou } and not $nloc;
  546. # Get the device name if $nloc (community@host) has been specified
  547. # one way or the other
  548. ( $name ) = snmpget( $nloc, $$rcfg{snmpoptions}{ $rou }, $noid ) if $nloc;
  549. }
  550.   
  551. return ( $last{ _IN_ }, $last{ _OUT_ }, $uptime, $name, $time );
  552. }
  553. sub rateupcheck ($) {
  554.     my $router = shift;
  555.     if ($?) {
  556.         my $value = $?;
  557.         my $signal =  $? & 127; #ignore the most significant bit 
  558.                                 #as it is always one when it is a returning
  559.                                 #child says dave ...
  560.         if (($MRTG_lib::OS ne 'UNIX') || ($signal != 127)) {
  561.             my $exitval = $? >> 8;
  562.             warn "WARNING: rateup died from Signal $signaln".
  563.               " with Exit Value $exitval when doing router '$router'n".
  564.                 " Signal was $signal, Returncode was $exitvaln"
  565.         }
  566.     }
  567. }
  568. sub writegraphics {
  569.     my($router, $cfg, $rcfg, $inlast, $outlast, $time,$LOC, $opts) = @_;
  570.   
  571.     my($absmax,$maxv, $maxvi, $maxvo, $i, $period, $res);
  572.     my(@exec, @mxvls, @metas);
  573.     my(%maxin, %maxout, %maxpercent, %avin, %avout, %avmxin, %avmxout,  %avpercent, %cuin, %cuout, %cupercent);
  574.     @metas = ();
  575.     $maxvi = $$rcfg{'maxbytes1'}{$router};
  576.     $maxvo = $$rcfg{'maxbytes2'}{$router};
  577.     if ($maxvi > $maxvo) {
  578.         $maxv = $maxvi;
  579.     } else {
  580.         $maxv = $maxvo;
  581.     }
  582.     $absmax = $$rcfg{'absmax'}{$router};
  583.     $absmax = $maxv unless defined $absmax;
  584.     if ($absmax < $maxv) {
  585.         die "ERROR: AbsMax: $absmax is smaller than MaxBytes: $maxvn";
  586.     }
  587.     # select whether the datasource gives relative or absolute return values.
  588.     my $up_abs="u";
  589.     $up_abs='m' if defined $$rcfg{'options'}{'perminute'}{$router};
  590.     $up_abs='h' if defined $$rcfg{'options'}{'perhour'}{$router};
  591.     $up_abs='d' if defined $$rcfg{'options'}{'derive'}{$router};
  592.     $up_abs='a' if defined $$rcfg{'options'}{'absolute'}{$router};
  593.     $up_abs='g' if defined $$rcfg{'options'}{'gauge'}{$router};
  594.     my $dotrrd = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.rrd";
  595.     my $dotlog = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.log";
  596.     my $reallog = $$cfg{logformat} eq 'rrdtool' ? $dotrrd : $dotlog;
  597.     if (defined $$cfg{maxage} and -e $reallog and time()-$$cfg{maxage} > (stat($reallog))[9]){
  598.          warn "ERROR: skipping update of $router. As $reallog is older than MaxAge ($$cfg{maxage} s)n";
  599.          return undef;
  600.     }
  601.     if ($$cfg{logformat} eq 'rrdtool') {
  602.         debug('base',"start RRDtool section");
  603.         # make sure we got some sane default here
  604.         my %dstype = qw/u COUNTER a ABSOLUTE g GAUGE h COUNTER m COUNTER d DERIVE/;
  605.         $up_abs = $dstype{$up_abs};
  606.         # update the database.
  607.         # set minimum/maximum values. use 'U' if we cannot get good values
  608.         # the lower bound is hardcoded to 0
  609.         my $absi = $maxvi;
  610.         my $abso = $maxvo;
  611.         $absi = $abso = $$rcfg{'absmax'}{$router}
  612.             if defined $$rcfg{'absmax'}{$router};
  613.         debug('base',"maxi:$absi, maxo:$abso");
  614.         $absi = 'U' if $absi == 0;
  615.         $abso = 'U' if $abso == 0;
  616.         # maybe we can convert an .log file to the new rrd format
  617.         if (-e $dotlog and not -e $dotrrd) {
  618.              debug('base',"converting $dotlog to RRD format");
  619.              if(defined $RRDs::VERSION and $RRDs::VERSION < 1.000271){
  620.                 die "ERROR: RRDtool version 1.0.27 or later required to perform log2rrd conversionn";
  621.              }
  622.              log2rrd($router,$cfg,$rcfg);
  623.         } elsif (! -e $dotrrd) {
  624.             #nope it seems we have to create a new one
  625.             debug('base',"create $dotrrd");
  626.             # create the rrd if it doesn't exist
  627.             # don't fail if interval is not set
  628.             my $interval = $$cfg{interval} || 5;
  629.             my $minhb = int($$cfg{interval} * 60)*2;
  630.             $minhb = 600 if ($minhb <600); 
  631.             my $rows = $$rcfg{'rrdrowcount'}{$router} || ( 4000 / $interval);
  632.             my @args = ($dotrrd, '-b', $time-10, '-s', int($interval * 60),
  633.                          "DS:ds0:$up_abs:$minhb:0:$absi",
  634.                          "DS:ds1:$up_abs:$minhb:0:$abso",
  635.                          "RRA:AVERAGE:0.5:1:$rows",
  636.                          ( $interval < 30  ? ("RRA:AVERAGE:0.5:".int(30/$interval).":800"):()),
  637.                          "RRA:AVERAGE:0.5:".int(120/$interval).":800",
  638.                          "RRA:AVERAGE:0.5:".int(1440/$interval).":800",
  639.                          "RRA:MAX:0.5:1:$rows",
  640.                          ( $interval < 30  ? ("RRA:MAX:0.5:".int(30/$interval).":800"):()),
  641.                          "RRA:MAX:0.5:".int(120/$interval).":800",
  642.                          "RRA:MAX:0.5:".int(1440/$interval).":800");
  643.             RRDs::create(@args);
  644.             my $e = RRDs::error();
  645.             die "ERROR: Cannot create logfile: $en" if $e;
  646.             debug('log', "Called: RRDs::create(@args)");
  647.         } else {
  648.             # update the minimum/maximum according to maxbytes/absmax
  649.             # and (re)set the data-source-type to reflect cfg changes
  650.             # cost: 1 read/write cycle, but update will reuse the buffered data
  651.             my @args = ($dotrrd, '-a', "ds0:$absi", '-a', "ds1:$abso",
  652.                        '-d', "ds0:$up_abs", '-d', "ds1:$up_abs");
  653.             RRDs::tune(@args);
  654.             my $e = RRDs::error();
  655.             warn "ERROR: Cannot tune logfile: $en" if $e;
  656.             debug('log', "Called: RRDs::tune(@args)");
  657.         }
  658.         # update the rrd
  659.         $inlast  = 'U' unless defined $inlast  and $inlast  =~ /S/ and $inlast  ne '##UNDEF##';
  660.         $outlast = 'U' unless defined $outlast and $outlast =~ /S/ and $outlast ne '##UNDEF##';
  661.         debug('log', "Calling: RRDs::update($dotrrd, '$time:$inlast:$outlast')");
  662.         RRDs::update("$dotrrd", "$time:$inlast:$outlast");
  663.         my $e = RRDs::error(); 
  664.         warn "ERROR: Cannot update $dotrrd with '$time:$inlast:$outlast' $en" if ($e);
  665.         # get the rrdtool-processed values back from rrdtool
  666.         # for the threshold checks (we cannot use the fetched data)
  667.         my $info =  RRDs::info($dotrrd);
  668.         my $lasttime =  $info->{last_update} - $info->{last_update} % $info->{step};        
  669.         debug('log', "Called: RRDs::info($dotrrd)");
  670.         $e = RRDs::error(); 
  671.         warn "ERROR: Cannot 'info' $dotrrd: $en" if ($e);
  672.         my $fetch = (RRDs::fetch($dotrrd,'AVERAGE','-s',$lasttime-1,'-e',$lasttime))[3];
  673.         $e = RRDs::error(); 
  674.         warn "ERROR: Cannot 'fetch' $dotrrd: $en" if ($e);
  675.         debug('log', "Called: RRDs::fetch($dotrrd,'AVERAGE','-s',$lasttime,'-e',$lasttime)");        
  676.         my $in = $fetch->[0][0] ?  $fetch->[0][0] : "NaN" ;
  677.         my $out = $fetch->[0][1] ? $fetch->[0][1] : "NaN" ;
  678.         debug('log', "  got: $in/$out");
  679.         $cuin{'d'}{$router} = $fetch->[0][0];
  680.         $cuout{'d'}{$router} = $fetch->[0][1];
  681.         # the html pages and the graphics are created at "call time" so that's it!
  682.         # (the returned hashes are empty, it's just to minimize the changes to mrtg)
  683.         if ($$rcfg{'options'}{'dorelpercent'}{$router}) {  
  684.             return (%maxin, %maxout, %maxpercent, %avin, %avout, %avpercent, %cuin, %cuout, %cupercent,  %avmxin, %avmxout);
  685.         }
  686.         return (%maxin, %maxout, %avin, %avout, %cuin, %cuout, %avmxin, %avmxout );
  687.     }                 
  688.     ########## rrdtool users have left here ###############
  689.     ((($MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2') and (-e "${FindBin::Bin}${MRTG_lib::SL}rateup.exe")) or
  690.      (($MRTG_lib::OS eq 'NW') and (-e "SYS:/Mrtg/bin/rateup.nlm")) or
  691.      (-x "${FindBin::Bin}${MRTG_lib::SL}rateup")) or 
  692.        die "ERROR: Can't Execute '${FindBin::Bin}${MRTG_lib::SL}rateup'n";
  693.     # rateup does not know about undef so we make inlast and outlast ready for rateup
  694.     #warn "ERROR: inlast is undefined. Skipping $routern" unless defined $inlast;
  695.     #warn "ERROR: outlast is undefined. Skipping $routern" unless defined $outlast;
  696.     #return undef unless defined $inlast and defined $outlast;
  697.     # set values to -1 to tell rateup about unknown values
  698.     $inlast = -1 unless defined $inlast;
  699.     $outlast = -1 unless defined $outlast;
  700.     
  701.     if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
  702.         @exec = ("${FindBin::Bin}${MRTG_lib::SL}rateup", 
  703.                  "$$cfg{'logdir'}$$rcfg{'directory'}{$router}","$router",
  704.                  $time, $$rcfg{'options'}{'unknaszero'}{$router} ? '-z':'-Z',
  705.                  "$up_abs"."p", $inlast, $outlast, $absmax,
  706.                  "C", $$rcfg{'rgb1'}{$router},$$rcfg{'rgb2'}{$router},
  707.                  $$rcfg{'rgb3'}{$router},$$rcfg{'rgb4'}{$router},
  708.                  $$rcfg{'rgb5'}{$router});
  709.     } else { 
  710.         @exec = ("${FindBin::Bin}${MRTG_lib::SL}rateup", 
  711.                  "$$cfg{'logdir'}$$rcfg{'directory'}{$router}","$router",
  712.                  $time, $$rcfg{'options'}{'unknaszero'}{$router} ? '-z':'-Z',
  713.                  "$up_abs", $inlast, $outlast, $absmax,
  714.                  "c", $$rcfg{'rgb1'}{$router},$$rcfg{'rgb2'}{$router},
  715.                  $$rcfg{'rgb3'}{$router},$$rcfg{'rgb4'}{$router});
  716.     }
  717.     # If this list grows anymore would it be more efficient to have an
  718.     # array to look up the command line option to send to rateup rather
  719.     # than have a long list to check?
  720.     push (@exec, '-t') if defined $$rcfg{'options'}{'transparent'}{$router};
  721.     push (@exec, '-0') if defined $$rcfg{'options'}{'withzeroes'}{$router};
  722.     push (@exec, '-b') if defined $$rcfg{'options'}{'noborder'}{$router};
  723.     push (@exec, '-a') if defined $$rcfg{'options'}{'noarrow'}{$router};
  724.     push (@exec, '-i') if defined $$rcfg{'options'}{'noi'}{$router};
  725.     push (@exec, '-o') if defined $$rcfg{'options'}{'noo'}{$router};
  726.     push (@exec, '-l') if defined $$rcfg{'options'}{'logscale'}{$router};
  727.     push (@exec, '-m') if defined $$rcfg{'options'}{'secondmean'}{$router};
  728.     push (@exec, '-d') if defined $$rcfg{'options'}{'pngdate'}{$router};
  729.     push (@exec, '-p') if defined $$rcfg{'options'}{'printrouter'}{$router};
  730.     my $maxx = $$rcfg{'xsize'}{$router}; 
  731.     my $maxy = $$rcfg{'ysize'}{$router};
  732.     my $xscale = $$rcfg{'xscale'}{$router}; 
  733.     my $yscale = $$rcfg{'yscale'}{$router}; 
  734.     my $growright = 0+($$rcfg{'options'}{'growright'}{$router} or 0);
  735.     my $bits = 0+($$rcfg{'options'}{'bits'}{$router} or 0);
  736.     my $integer = 0+($$rcfg{'options'}{'integer'}{$router} or 0);
  737.     my $step = 5*60; 
  738.     my $rop;
  739.     my $ytics = $$rcfg{'ytics'}{$router};
  740.     my $yticsf= $$rcfg{'yticsfactor'}{$router};
  741.     my $timestrfmt = $$rcfg{'timestrfmt'}{$router};
  742.     my $timestrpos = ${MRTG_lib::timestrpospattern}{uc $$rcfg{'timestrpos'}{$router}};
  743.     if (not defined $$rcfg{'ylegend'}{$router}){
  744. if ($bits){
  745.     $$rcfg{'ylegend'}{$router} = &$LOC("Bits per minute")
  746. if defined $$rcfg{'options'}{'perminute'}{$router};
  747.     $$rcfg{'ylegend'}{$router} = &$LOC("Bits per hour")     
  748. if defined $$rcfg{'options'}{'perhour'}{$router};
  749. } else {
  750.     $$rcfg{'ylegend'}{$router} = &$LOC("Bytes per minute")
  751. if defined $$rcfg{'options'}{'perminute'}{$router};
  752.     $$rcfg{'ylegend'}{$router} = &$LOC("Bytes per hour")     
  753. if defined $$rcfg{'options'}{'perhour'}{$router};
  754. }
  755.     }
  756.     if ($$rcfg{'ylegend'}{$router}) {
  757.         push (@exec, "l", "[$$rcfg{'ylegend'}{$router}]");
  758.     }
  759.     my $sign = ($$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /d/) ? 1 : -1;
  760.     if ($$rcfg{'pngtitle'}{$router}) {
  761.         push (@exec, "T", "[$$rcfg{'pngtitle'}{$router}]");
  762.     }
  763.   
  764.     if ($$rcfg{'timezone'}{$router}) {
  765.         push (@exec, "Z", "$$rcfg{'timezone'}{$router}");
  766.     }
  767.   
  768.     if ($$rcfg{'kilo'}{$router}) {
  769.         push (@exec, "k", $$rcfg{'kilo'}{$router});
  770.     }
  771.     if ($$rcfg{'kmg'}{$router}) { 
  772.         push (@exec, "K", $$rcfg{'kmg'}{$router});
  773.     }
  774.     if ($$rcfg{'weekformat'}{$router}) {
  775.         push (@exec, "W", $$rcfg{'weekformat'}{$router});
  776.     }
  777.     my $SAGE = (time - $main::STARTTIME) / 3600 / 24; # current script age 
  778.     if (not defined $$opts{'log-only'}){
  779.       if (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~ /d/) {
  780.         # VMS: should work for both now
  781.         push (@exec, "i", "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-day.${main::GRAPHFMT}",
  782.               $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, ,$xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos);
  783.         @mxvls = ("d");
  784.         push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-day.${main::GRAPHFMT}",
  785.               $$cfg{'interval'} ? $$cfg{'interval'} : 5);
  786.        }
  787.   
  788.     if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}") or
  789.          ((-M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}") + $SAGE  >= 0.5/24)) and
  790.         (not defined $$rcfg{'suppress'}{$router}  or $$rcfg{'suppress'}{$router} !~/w/)
  791.        ) {
  792.         $step=30*60;
  793.         $sign = (defined $$rcfg{'unscaled'}{$router}  and $$rcfg{'unscaled'}{$router} =~ /w/) ? 1 : -1;
  794.         push (@mxvls , "w");
  795.         $rop =(defined $$rcfg{'withpeak'}{$router}  and $$rcfg{'withpeak'}{$router} =~ /w/) ? "p" : "i"; 
  796.         push (@exec, $rop ,"$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}",
  797.               $sign*$maxvi, $sign*$maxvo,  $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos);
  798.         push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}", 30);
  799.     }
  800.   
  801.     if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}") or
  802.          (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}") + $SAGE >= 2/24))  and
  803.         (not defined  $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~ /m/)) {
  804.         $step=2*60*60;
  805.         $sign = (defined $$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /m/) ? 1 : -1;
  806.         push (@mxvls , "m");
  807.         $rop =(defined $$rcfg{'withpeak'}{$router} and $$rcfg{'withpeak'}{$router} =~ /m/) ? "p" : "i"; 
  808.         push (@exec, $rop ,"$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}",
  809.               $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos);
  810.         push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}", 120);
  811.     }
  812.   
  813.     if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}") or
  814.          (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}") + $SAGE  >= 1)) and
  815.         (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~/y/)) {
  816.         $step=24*60*60;
  817.         $sign = (defined $$rcfg{'unscaled'}{$router}  and $$rcfg{'unscaled'}{$router} =~ /y/) ? 1 : -1;
  818.         push (@mxvls , "y");
  819.         $rop =(defined $$rcfg{'withpeak'}{$router}  and $$rcfg{'withpeak'}{$router} =~ /y/) ? "p" : "i"; 
  820.         push (@exec, $rop, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}",
  821.               $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos);
  822.         push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}", 1440);
  823.     }
  824.   }
  825.     # VMS: this might work now ... or does VMS NOT know about pipes?
  826.     # NT doesn't have fork() so an open(xxx,"-|") won't work
  827.     # OS2 fork() have bug with socket handles. In RunAsDaemon mode it fail
  828.     # after first loop (with "socket operation on non socket" message.
  829.     if ($MRTG_lib::OS eq 'VMS' or $MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2'){
  830.         map { s/"/\"/; $_ = '"'.$_.'"' if /s/ } @exec;
  831.         open (RATEUP, join (" ", @exec)."|") or
  832.            do {
  833.                 warn "WARNING: rateup (".(join " ", @exec ).
  834.                           ") did not work: $!n";
  835.                 return;
  836.            }
  837.     } elsif ($MRTG_lib::OS eq 'NW'){
  838.         map { s/"/\"/; $_ = '"'.$_.'"' if /s/ } @exec;
  839.         # Stuff around because of Perl problems.
  840.         open (NWPARMS, ">"."$$cfg{'imagedir'}$router.dat") or
  841.            do {
  842.                 warn "WARNING: Rateup parameters [$$cfg{'imagedir'}$router.dat] [open] failed.n";
  843.                 return;
  844.            };
  845.         print NWPARMS join (" ", @exec);
  846.         close NWPARMS;
  847.         # Now run Rateup with path to Parameters.
  848.         open (RATEUP, "SYS:/Mrtg/bin/rateup -f $$cfg{'imagedir'}$router.dat"."|") or
  849.            do {
  850.                 warn "WARNING: SYS:/Mrtg/bin/rateup -f $$cfg{'imagedir'}$router.dat did NOT work.n";
  851.                 return;
  852.            }
  853.     } else {
  854.         $! = undef;
  855.         open (RATEUP,"-|") or  
  856.                 do { exec @exec or
  857.                      warn "WARNING: rateup (".(join " ", @exec ).
  858.                           ") did not work: $!n";
  859.                 };
  860.     }
  861.     
  862.     debug('log', "Called @exec");
  863.     imggen($$cfg{icondir} || $$cfg{imagedir});
  864.     if (open (HTML,"<$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.$$rcfg{'extension'}{$router}")) {
  865.         for ($i=0 ; $i<200 ; $i++) {
  866.             last if eof(HTML);
  867.             $_= <HTML>;
  868.             if (/<!-- maxin ([dwmy]) (d*)/) {
  869.                 $maxin{$1}{$router}=$2 || 0;
  870.             }
  871.             if (/<!-- maxout ([dwmy]) (d*)/) {
  872.                 $maxout{$1}{$router}=$2 || 0;
  873.             }
  874.             if (/<!-- maxpercent ([dwmy]) (d*)/) {
  875.                 $maxpercent{$1}{$router}=$2 || 0;
  876.             }
  877.             if (/<!-- avin ([dwmy]) (d*)/) {
  878.                 $avin{$1}{$router}=$2 || 0;
  879.             }
  880.             if (/<!-- avout ([dwmy]) (d*)/) {
  881.                 $avout{$1}{$router}=$2 || 0;
  882.             }
  883.             if (/<!-- avpercent ([dwmy]) (d*)/) {
  884.                 $avpercent{$1}{$router}=$2 || 0;
  885.             }
  886.             if (/<!-- cuin ([dwmy]) (d*)/) {
  887.                 $cuin{$1}{$router}=$2 || 0;
  888.             }
  889.             if (/<!-- cuout ([dwmy]) (d+)/) {
  890.                 $cuout{$1}{$router}=$2 || 0;
  891.             }
  892.      
  893.             if (/<!-- cupercent ([dwmy]) (d+)/) {
  894.                 $cupercent{$1}{$router}=$2 || 0;
  895.             }
  896.             if (/<!-- avmxin ([dwmy]) (d*)/) {
  897.                 $avmxin{$1}{$router}=$2 || 0;
  898.             }
  899.             if (/<!-- avmxout ([dwmy]) (d*)/) {
  900.                 $avmxout{$1}{$router}=$2 || 0;
  901.             }
  902.         }
  903.         close HTML;
  904.     }
  905.   
  906.     foreach $period (@mxvls) {
  907.         $res = <RATEUP>; 
  908.         if (not defined $res and eof(RATEUP)){
  909.             warn "ERROR: Skipping webupdates because rateup did not return anything sensiblen";
  910.             close RATEUP;
  911.             rateupcheck $router;
  912.             return;
  913.         };
  914.         chomp $res;
  915.         $maxin{$period}{$router}=sprintf("%.0f",$res || 0);
  916.         chomp($res = <RATEUP>); 
  917.         $maxout{$period}{$router}=sprintf("%.0f",$res || 0);
  918.         if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
  919.             chomp($res = <RATEUP>); 
  920.             $maxpercent{$period}{$router}=sprintf("%.0f",$res || 0);
  921.         }
  922.         chomp($res = <RATEUP>); 
  923.         $avin{$period}{$router}=sprintf("%.0f",$res || 0);
  924.         chomp($res = <RATEUP>); 
  925.         $avout{$period}{$router}=sprintf("%.0f",$res || 0);
  926.         if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
  927.             chomp($res = <RATEUP>); 
  928.             $avpercent{$period}{$router}=sprintf("%.0f",$res || 0);
  929.         }
  930.         chomp($res = <RATEUP>); 
  931.         $cuin{$period}{$router}=sprintf("%.0f",$res || 0);
  932.         chomp($res = <RATEUP>); 
  933.         $cuout{$period}{$router}=sprintf("%.0f",$res || 0);
  934.         if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
  935.             chomp($res = <RATEUP>); 
  936.             $cupercent{$period}{$router}=sprintf("%.0f",$res || 0);
  937.         }
  938.         chomp($res = <RATEUP>);
  939.         debug('avmx',"avmxin  $res");        
  940.         $avmxin{$period}{$router}=sprintf("%.0f",$res || 0);     
  941.         chomp($res = <RATEUP>);
  942.         debug('avmx',"avmxout $res");        
  943.         $avmxout{$period}{$router}=sprintf("%.0f",$res || 0);     
  944.         
  945.     }
  946.     close(RATEUP);
  947.     rateupcheck $router;
  948.     if ( defined $$cfg{'writeexpires'}  and $$cfg{'writeexpires'} =~ /^y/i ) {
  949.         my($fil,$exp);
  950.         while ( $fil = shift(@metas) ) {
  951.             $exp = &expistr(shift(@metas));
  952.             open(META, ">$fil.meta");
  953.             print META "Expires: $expn";
  954.             close(META);
  955.         }
  956.     }
  957.     if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
  958.         return (%maxin, %maxout, %maxpercent, %avin, %avout, %avpercent, %cuin, %cuout, %cupercent, %avmxin, %avmxout);
  959.     } else {
  960.         return (%maxin, %maxout, %avin, %avout, %cuin, %cuout, %avmxin, %avmxout);
  961.     }
  962. }
  963. #format 10*$kilo to 10 kB/s
  964. sub fmi {
  965.     my($number, $maxbytes, $router, @foo) = @_;
  966.     return "?????" unless defined $number;
  967.     my($rcfg,$LOC)=@foo;
  968.     my @short= (&$LOC(""),&$LOC(""),&$LOC(""),&$LOC(""));
  969.     my $mul = 1;
  970.     if ($$rcfg{'kmg'}{$router}) {
  971.         my($i);
  972.         if ($$rcfg{'options'}{'bits'}{$router}) {
  973.             foreach $i (split(/,/, $$rcfg{'kmg'}{$router})) {
  974.                 if ($$rcfg{'options'}{'perminute'}{$router}) {
  975.                     $short[$#short+1] = "$i".&$LOC("b/min");
  976.                 } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
  977.                     $short[$#short+1] = "$i".&$LOC("b/h");
  978.                 } else {
  979.                     $short[$#short+1] = "$i".&$LOC("b/s");
  980.                 }
  981.             }
  982.             $mul= 8;
  983.         } else {
  984.             foreach $i (split(/,/, $$rcfg{'kmg'}{$router})) {
  985.                 if ($$rcfg{'options'}{'perminute'}{$router}) {
  986.                     $short[$#short+1] = "$i".&$LOC ("B/min");
  987.                 } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
  988.                     $short[$#short+1] = "$i".&$LOC("B/h");
  989.                 } else {
  990.                     $short[$#short+1] = "$i".&$LOC("B/s");
  991.                 }
  992.             }
  993.             $mul= 1;
  994.         }
  995.         if (defined $$rcfg{'shortlegend'}{$router}) {
  996.             foreach $i (split(/,/, $$rcfg{'kmg'}{$router})) {
  997.                 $short[$#short+1] = "$i"."$$rcfg{'shortlegend'}{$router}";
  998.             }
  999.         }
  1000.     } else {
  1001.         if (defined $$rcfg{'options'}{'bits'}{$router}) {
  1002.             if ($$rcfg{'options'}{'perminute'}{$router}) {
  1003.                 @short = (&$LOC("b/min"),&$LOC("kb/min"),&$LOC("Mb/min"),&$LOC("Gb/min"));
  1004.             } elsif (defined $$rcfg{'options'}{'perhour'}{$router}) {
  1005.                 @short = (&$LOC("b/h"),&$LOC("kb/h"),&$LOC("Mb/h"),&$LOC("Gb/h"));
  1006.             } else {
  1007.                 @short = (&$LOC("b/s"),&$LOC("kb/s"),&$LOC("Mb/s"),&$LOC("Gb/s"));
  1008.             }
  1009.             $mul= 8;
  1010.         } else {
  1011.             if ($$rcfg{'options'}{'perminute'}{$router}) {
  1012.                 @short = (&$LOC("B/min"),&$LOC("kB/min"),&$LOC("MB/min"),&$LOC("GB/min"));
  1013.             } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
  1014.                 @short = (&$LOC("B/h"),&$LOC("kB/h"),&$LOC("MB/h"),&$LOC("GB/h"));
  1015.             } else {
  1016.                 @short = (&$LOC("B/s"),&$LOC("kB/s"),&$LOC("MB/s"),&$LOC("GB/s"));
  1017.             }
  1018.             $mul= 1;
  1019.         }
  1020.         if ($$rcfg{'shortlegend'}{$router}) {
  1021.             @short = ("$$rcfg{'shortlegend'}{$router}",
  1022.                       "k$$rcfg{'shortlegend'}{$router}",
  1023.                       "M$$rcfg{'shortlegend'}{$router}",
  1024.                       "G$$rcfg{'shortlegend'}{$router}");
  1025.         }
  1026.     }
  1027.     my $digits=length("".$number*$mul);
  1028.     my $divm=0;
  1029.     #
  1030.     #  while ($digits-$divm*3 > 4) { $divm++; }
  1031.     #  my $divnum = $number*$mul/10**($divm*3);
  1032.     my $divnum=$number*$mul*$$rcfg{'factor'}{$router};
  1033.     #  while ($divnum/$$rcfg{'kilo'}{$router} >= 10*$$rcfg{'kilo'}{$router} and $divnum<$#short) {
  1034.     while ($divnum >= 10*$$rcfg{'kilo'}{$router} and $divm<$#short) {
  1035.         $divm++;
  1036.         $divnum /= $$rcfg{'kilo'}{$router};
  1037.     }
  1038.     my $perc;
  1039.     if ($number == 0 || $maxbytes == 0) {
  1040.         $perc = 0;
  1041.     } else {
  1042.         $perc = 100/$maxbytes*$number;
  1043.     }
  1044.     if (defined $$rcfg{'options'}{'integer'}{$router}) {
  1045.         if ($$rcfg{'options'}{'nopercent'}{$router}) {
  1046.             return sprintf("%.0f %s",$divnum,$short[$divm]);
  1047.         } else {
  1048.             return sprintf("%.0f %s (%2.1f%%)",$divnum,$short[$divm],$perc);
  1049.         }
  1050.     } else {
  1051.         if (defined $$rcfg{'options'}{'nopercent'}{$router}) {
  1052.             return sprintf("%.1f %s",$divnum,$short[$divm]); # Added: FvW
  1053.         } else {
  1054.             return sprintf("%.1f %s (%2.1f%%)",$divnum,$short[$divm],$perc);
  1055.         }
  1056.         return sprintf("%.1f %s (%2.1f%%)",$divnum,$short[$divm],$perc);
  1057.     }
  1058. }
  1059. sub writehtml {
  1060.     my($router, $cfg, $rcfg, $maxin, $maxout, $maxpercent,
  1061.        $avin, $avout, $avmxin, $avmxout, $avpercent, 
  1062.        $cuin, $cuout, $cupercent, $uptime, $name, $LOC) = @_;
  1063.   
  1064.     my($VERSION,$Today,$peri);
  1065.   
  1066.     my($persec);
  1067.     if (defined $$rcfg{'options'}{'bits'}{$router}) {
  1068.         $persec = &$LOC("Bits");
  1069.     } else {
  1070.         $persec = &$LOC("Bytes");
  1071.     }
  1072.     #  Work out the Colour legend
  1073.     my($leg1, $leg2, $leg3, $leg4, $leg5);
  1074.     if ($$rcfg{'legend1'}{$router}) {
  1075.         $leg1 = $$rcfg{'legend1'}{$router};
  1076.     } else {
  1077.         if ($$rcfg{'options'}{'perminute'}{$router}) {
  1078.             $leg1=&$LOC("Incoming Traffic in $persec per Minute");
  1079.         } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
  1080.             $leg1=&$LOC("Incoming Traffic in $persec per Hour");
  1081.         } else {
  1082.             $leg1=&$LOC("Incoming Traffic in $persec per Second");
  1083.         }
  1084.     }
  1085.     if ($$rcfg{'legend2'}{$router}) {
  1086.         $leg2 = $$rcfg{'legend2'}{$router};
  1087.     } else {
  1088.         if ($$rcfg{'options'}{'perminute'}{$router}) {
  1089.             $leg2=&$LOC("Outgoing Traffic in $persec per Minute");
  1090.         } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
  1091.             $leg2=&$LOC("Outgoing Traffic in $persec per Hour");
  1092.         } else {
  1093.             $leg2=&$LOC("Outgoing Traffic in $persec per Second");
  1094.         }
  1095.     }
  1096.     if ($$rcfg{'legend3'}{$router}) {
  1097.         $leg3 = $$rcfg{'legend3'}{$router};
  1098.     } else {
  1099.         $leg3 = &$LOC("Maximal 5 Minute Incoming Traffic");
  1100.     }
  1101.     if ($$rcfg{'legend4'}{$router}) {
  1102.         $leg4 = $$rcfg{'legend4'}{$router};
  1103.     } else {
  1104.         $leg4 = &$LOC("Maximal 5 Minute Outgoing Traffic");
  1105.     }
  1106.     if ($$rcfg{'legend5'}{$router}) {
  1107.         $leg5 = $$rcfg{'legend5'}{$router};
  1108.     } else {
  1109.         $leg5 = "(($leg1)/($leg2))*100";
  1110.     }
  1111.     # Translate the color names
  1112.     $$rcfg{'col1'}{$router}=&$LOC($$rcfg{'col1'}{$router});
  1113.     $$rcfg{'col2'}{$router}=&$LOC($$rcfg{'col2'}{$router});
  1114.     $$rcfg{'col3'}{$router}=&$LOC($$rcfg{'col3'}{$router});
  1115.     $$rcfg{'col4'}{$router}=&$LOC($$rcfg{'col4'}{$router});
  1116.     $$rcfg{'col5'}{$router}=&$LOC($$rcfg{'col5'}{$router});
  1117.     my $dirrel = "../" x ($$rcfg{'directory_web'}{$router} =~ tr|/|/|);
  1118.     $Today=&$LOC(datestr(time));
  1119.     $VERSION = "2.13.2";
  1120.     open (HTML,">$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.$$rcfg{'extension'}{$router}") || 
  1121.       do { warn ("WARNING: Writing $router.$$rcfg{'extension'}{$router}: $!");
  1122.           next };
  1123.     # this unforutnately confuses IE greatly ... so we have to comment
  1124.     # it out for now ... :-(
  1125.     # print HTML '<?xml version="1.0" encoding="' . &$LOC('iso-8859-1') . '"?>' . "n";
  1126.     print HTML '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/dtd/xhtml11.dtd">' . "n";
  1127.     print HTML "<html>n";
  1128.     my $interval =$$cfg{'interval'} ? $$cfg{'interval'} : 5;
  1129.     my $expiration = &expistr($interval);
  1130.     my $refresh =  $$cfg{'refresh'} ? $$cfg{'refresh'} : 300;
  1131.     my $namestring = &$LOC("the device");  
  1132.     print HTML "<!-- Begin Head -->n";
  1133.     print HTML <<"TEXT";    
  1134. <head>
  1135. <title>$$rcfg{'title'}{$router}</title>
  1136. <meta http-equiv="refresh" content="$refresh" />
  1137. <meta http-equiv="pragma" content="no-cache" />
  1138. <meta http-equiv="cache-control" content="no-cache" />
  1139. <meta http-equiv="expires" content="$expiration" />
  1140. <meta http-equiv="generator" content="MRTG $VERSION" />
  1141. <meta http-equiv="date" content="$expiration" />
  1142. TEXT
  1143.     print HTML "tt" . '<meta http-equiv="content-type" content="text/html; charset='.&$LOC('iso-8859-1') . "" />n";
  1144.     foreach $peri (qw(d w m y)) {
  1145.         print HTML <<"TEXT";
  1146. <!-- maxin $peri $$maxin{$peri}{$router} -->
  1147. <!-- maxout $peri $$maxout{$peri}{$router} -->
  1148. TEXT
  1149.         if ($$rcfg{'options'}{'dorelpercent'}{$router} and defined $$maxpercent{$peri}{$router}) {
  1150.             print HTML <<"TEXT";
  1151. <!-- maxpercent $peri $$maxpercent{$peri}{$router} -->
  1152. TEXT
  1153.         }
  1154.         print HTML <<"TEXT";
  1155. <!-- avin $peri $$avin{$peri}{$router} -->
  1156. <!-- avout $peri $$avout{$peri}{$router} -->
  1157. TEXT
  1158.         if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
  1159.             print HTML <<"TEXT";
  1160. <!-- avpercent $peri $$avpercent{$peri}{$router} -->
  1161. TEXT
  1162.         }
  1163.         
  1164.         print HTML "<!-- cuin $peri $$cuin{$peri}{$router} -->n"
  1165.            if defined $$cuin{$peri}{$router};
  1166.         print HTML "<!-- cuout $peri $$cuout{$peri}{$router} -->n"
  1167.            if defined $$cuout{$peri}{$router};
  1168.         if ($$rcfg{'options'}{'dorelpercent'}{$router} and $$cupercent{$peri}{$router} ) {
  1169.             print HTML <<"TEXT";
  1170. <!-- cupercent $peri $$cupercent{$peri}{$router} -->
  1171. TEXT
  1172.         }
  1173.         print HTML <<"TEXT" if  $$avmxin{$peri}{$router} and $$avmxout{$peri}{$router};
  1174. <!-- avmxin $peri $$avmxin{$peri}{$router} -->
  1175. <!-- avmxout $peri $$avmxout{$peri}{$router} -->
  1176. TEXT
  1177.     }
  1178.     $namestring = "<strong>'$name'</strong>" if $name;
  1179.     defined $$rcfg{backgc}{$router} or $$rcfg{backgc}{$router} = "#fff";
  1180.     $$rcfg{'rgb1'}{$router} = "" unless defined $$rcfg{'rgb1'}{$router};
  1181.     $$rcfg{'rgb2'}{$router} = "" unless defined $$rcfg{'rgb2'}{$router};
  1182.     $$rcfg{'rgb3'}{$router} = "" unless defined $$rcfg{'rgb3'}{$router};
  1183.     $$rcfg{'rgb4'}{$router} = "" unless defined $$rcfg{'rgb4'}{$router};
  1184.     $$rcfg{'rgb5'}{$router} = "" unless defined $$rcfg{'rgb5'}{$router};
  1185.     $$rcfg{'rgb6'}{$router} = "" unless defined $$rcfg{'rgb6'}{$router};
  1186.     print HTML "
  1187. <style type="text/css">
  1188. body {
  1189. background-color: $$rcfg{'backgc'}{$router};
  1190. }
  1191. div {
  1192. border-bottom: 2px solid #aaa;
  1193. padding-bottom: 10px;
  1194. margin-bottom: 5px;
  1195. }
  1196. div h2 {
  1197. font-size: 1.2em;
  1198. }
  1199. div.graph img {
  1200. margin: 5px 0;
  1201. }
  1202. div.graph table, div#legend table {
  1203. font-size: .8em;
  1204. }
  1205. div.graph table td {
  1206. padding: 0 10px;
  1207. text-align: right;
  1208. }
  1209. div table .in th, div table td span.in {
  1210. color: $$rcfg{'rgb1'}{$router};
  1211. }
  1212. div table .out th, div table td span.out {
  1213. color: $$rcfg{'rgb2'}{$router};
  1214. }";
  1215.  print HTML "
  1216. div table .inpeak th {
  1217. color: $$rcfg{'rgb3'}{$router};
  1218. }
  1219. div table .outpeak th {
  1220. color: $$rcfg{'rgb4'}{$router};
  1221. } " if defined $rcfg->{withpeak}{$router};
  1222.     print HTML "
  1223. div table .relpercent th {
  1224. color: $$rcfg{'rgb5'}{$router};
  1225. }" if ( $$rcfg{'options'}{'dorelpercent'}{$router} );
  1226.  
  1227.     print HTML "
  1228. div#legend th {
  1229. text-align: right;
  1230. }
  1231. div#footer {
  1232. border: none;
  1233. font-size: .8em;
  1234. font-family: Arial, Helvetica, sans-serif;
  1235. width: 476px;
  1236. }
  1237. div#footer img {
  1238. border: none;
  1239. height: 25px;
  1240. }
  1241. div#footer address {
  1242. text-align: right;
  1243. }
  1244. div#footer #version {
  1245. margin: 0;
  1246. padding: 0;
  1247. float: left;
  1248. width: 88px;
  1249. text-align: right;
  1250. }
  1251. </style>";
  1252.     # allow for n in addhead
  1253.     defined $$rcfg{addhead}{$router} or $$rcfg{addhead}{$router} = "";
  1254.     defined $$rcfg{pagetop}{$router} or $$rcfg{pagetop}{$router} = "";
  1255.     if (defined $$rcfg{bodytag}{$router}) {
  1256.         if ($$rcfg{bodytag}{$router} !~ /<body/i) {
  1257.                 $$rcfg{bodytag}{$router} = "<body $$rcfg{bodytag}{$router}>";
  1258.         }
  1259.     } else {
  1260.         $$rcfg{bodytag}{$router} = "<body>";
  1261.     }
  1262.     $$rcfg{addhead}{$router} =~ s/\n/n/g if defined $$rcfg{addhead}{$router};
  1263.     print HTML "
  1264. $$rcfg{'addhead'}{$router}
  1265. </head>
  1266. $$rcfg{bodytag}{$router}
  1267. $$rcfg{'pagetop'}{$router}";
  1268.     if (defined $$rcfg{'timezone'}{$router}){    
  1269.     print HTML     
  1270.       &$LOC("The statistics were last updated <strong>$Today $$rcfg{'timezone'}{$router}</strong>");
  1271.     } else {
  1272.     print HTML     
  1273.       &$LOC("The statistics were last updated <strong>$Today</strong>");
  1274.     }
  1275.     if ($uptime and ! $$rcfg{options}{noinfo}{$router}) {
  1276.         print HTML
  1277.           ",<br />n".
  1278.         &$LOC("at which time $namestring had been up for <strong>$uptime</strong>.").
  1279.         "n<!-- End Head -->";
  1280.     }
  1281.     my %sample= ('d' => "`Daily' Graph (".$interval.' Minute',
  1282.                  'w' => "`Weekly' Graph (30 Minute",
  1283.                  'm' => "`Monthly' Graph (2 Hour",
  1284.                  'y' => "`Yearly' Graph (1 Day");
  1285.   
  1286.     my %full = ('d' => 'day',
  1287.                 'w' => 'week',
  1288.                 'm' => 'month',
  1289.                 'y' => 'year');
  1290.   
  1291.     my $InCo;
  1292.     if (!(defined $$rcfg{'options'}{'noi'}{$router})) {
  1293.     if (exists $$rcfg{'legendi'}{$router}) {
  1294.         if ($$rcfg{'legendi'}{$router} ne "") {
  1295.             $InCo=$$rcfg{'legendi'}{$router};
  1296.         }
  1297.     } else {
  1298.         $InCo=&$LOC("In");
  1299.     }
  1300.     }
  1301.     
  1302.     my $OutCo;
  1303.     if (!(defined $$rcfg{'options'}{'noo'}{$router})) {
  1304.     if (exists $$rcfg{'legendo'}{$router}) {
  1305.         if ($$rcfg{'legendo'}{$router} ne "") {
  1306.             $OutCo=$$rcfg{'legendo'}{$router};
  1307.         }
  1308.     } else {
  1309.         $OutCo=&$LOC("Out");
  1310.     }
  1311.     }
  1312.     my $PercentCo;
  1313.     if (defined $$rcfg{'legend5'}{$router}) {
  1314.         if ($$rcfg{'legend5'}{$router} ne "") {
  1315.             $PercentCo=$$rcfg{'legend5'}{$router};
  1316.         }
  1317.     } else {
  1318.         $PercentCo=&$LOC("Percentage");
  1319.     }
  1320.   
  1321.     foreach $peri (qw(d w m y)) {
  1322.         next if defined $$rcfg{'suppress'}{$router} and $$rcfg{'suppress'}{$router} =~/$peri/;
  1323.         my $gifw;
  1324.         if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
  1325.             $gifw=sprintf("%.0f",($$rcfg{'xsize'}{$router}*$$rcfg{'xscale'}{$router}+
  1326.                                   +100+30) *$$rcfg{'xzoom'}{$router});
  1327.         } else {
  1328.             $gifw=sprintf("%.0f",($$rcfg{'xsize'}{$router}*$$rcfg{'xscale'}{$router}
  1329.                                   +100) *$$rcfg{'xzoom'}{$router});
  1330.         }
  1331.         my $gifh=sprintf("%.0f",($$rcfg{'ysize'}{$router}*$$rcfg{'yscale'}{$router}+35)
  1332.                          *$$rcfg{'yzoom'}{$router});
  1333.                  
  1334.         # take the image directory away from the html directory to give us relative links
  1335. my $imagepath = ( $cfg->{htmldir} ne $cfg->{imagedir} ) ? "$dirrel$$cfg{imagehtml}$$rcfg{directory_web}{$router}" : "";
  1336.         print HTML "
  1337. <!-- Begin $sample{$peri} -->
  1338. <div class="graph">
  1339. <h2>".&$LOC("$sample{$peri}")." ".&$LOC("Average)")."</h2>
  1340. <img src="$imagepath$router-$full{$peri}.${main::GRAPHFMT}" title="$full{$peri}" alt="$full{$peri}" />
  1341. <table>
  1342. <tr>
  1343. <th></th>
  1344. <th scope="col">" . &$LOC("Max") . "</th>
  1345. <th scope="col">" . &$LOC("Average") . "</th>
  1346. <th scope="col">" . &$LOC("Current") . "</th>
  1347. </tr>";
  1348.         my(@foo)=($rcfg,$LOC);
  1349.         print HTML "
  1350. <tr class="in">
  1351. <th scope="row">" . $InCo . "</th>
  1352. <td>".&fmi($$maxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)."</td>
  1353. <td>".&fmi($$avin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)." </td>
  1354. <td>".&fmi($$cuin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)." </td>
  1355. </tr>" if $InCo;
  1356.         print HTML "
  1357. <tr class="out">
  1358. <th scope="row">" . $OutCo . "</th>
  1359. <td>".&fmi($$maxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)." </td>
  1360. <td>".&fmi($$avout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)." </td>
  1361. <td>".&fmi($$cuout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)." </td>
  1362. </tr>" if $OutCo;
  1363.         print HTML "
  1364. <tr class="relpercent">
  1365. <th scope="row">" . $PercentCo . "</th>
  1366. <td>".sprintf("%0.1f %%",($$maxpercent{$peri}{$router} || 0))." </td>
  1367. <td>".sprintf("%0.1f %%",($$avpercent{$peri}{$router} || 0 ))." </td>
  1368. <td>".sprintf("%0.1f %%",($$cupercent{$peri}{$router} || 0 ))." </td>
  1369. </tr>" if ($$rcfg{'options'}{'dorelpercent'}{$router} and $PercentCo);
  1370. print HTML "
  1371. <tr>
  1372. <td colspan="8">
  1373. " . &$LOC("Average max 5 min values for $sample{$peri} interval):") . "
  1374. <span class="in">$InCo</span> " . &fmi($$avmxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo) . "/
  1375. <span class="out">$OutCo</span> " . &fmi($$avmxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo) . "
  1376. </td>
  1377. </tr>" if ($$rcfg{'options'}{'avgpeak'}{$router} and $InCo and $OutCo);
  1378.         print HTML "
  1379. </table>
  1380. </div>
  1381. <!-- End $sample{$peri} -->n";
  1382. }
  1383.     if (!(defined $$rcfg{'options'}{'nolegend'}{$router})) {
  1384.     print HTML "
  1385. <!-- Begin Legend -->
  1386. <div id="legend">
  1387. <table>";
  1388.     print HTML "
  1389. <tr class="in">
  1390. <th scope="row">$$rcfg{'col1'}{$router} ###</th>
  1391. <td>$leg1</td>
  1392. </tr>" if $InCo;
  1393.     print HTML "
  1394. <tr class="out">
  1395. <th scope="row">$$rcfg{'col2'}{$router} ###</th>
  1396. <td>$leg2</td>
  1397. </tr>" if $OutCo;
  1398.     if ($$rcfg{'withpeak'}{$router}) {
  1399.         print HTML "
  1400. <tr class="inpeak">
  1401. <th scope="row">$$rcfg{'col3'}{$router} ###</th>
  1402. <td>$leg3</td>
  1403. </tr>" if $InCo;
  1404.         print HTML "
  1405. <tr class="outpeak">
  1406. <th scope="row">$$rcfg{'col4'}{$router} ###</th>
  1407. <td>$leg4</td>
  1408. </tr>" if $OutCo;
  1409.     }
  1410.     if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
  1411.         print HTML "
  1412. <tr class="relpercent">
  1413. <th scope="row">$$rcfg{'col5'}{$router} ###</th>
  1414. <td>$leg5</td>
  1415. </tr>";
  1416.     }
  1417.         print HTML "
  1418. </table>
  1419. </div>
  1420. <!-- End Legend -->";
  1421.     }
  1422.     if (!(defined $$rcfg{'options'}{'nobanner'}{$router})) {
  1423.     my $gifPath;
  1424.     if (defined $$cfg{icondir}) {
  1425.         $gifPath = $$cfg{icondir};
  1426.         #lets make sure there is a trailing path separator
  1427.         $gifPath =~ s|/*$|/|;
  1428.     } else {
  1429. $gifPath = "$dirrel$$cfg{imagehtml}";
  1430.     }
  1431.     print HTML<<TEXT;
  1432. <!-- Begin MRTG Block -->
  1433. <div id="footer">
  1434. <a href="http://people.ee.ethz.ch/~oetiker/webtools/mrtg/"><img src="${gifPath}mrtg-l.${main::GRAPHFMT}" width="63" title="MRTG" alt="MRTG" /><img src="${gifPath}mrtg-m.${main::GRAPHFMT}" width="25" title="MRTG" alt="MRTG" /><img src="${gifPath}mrtg-r.${main::GRAPHFMT}" width="388" title="Multi Router Traffic Grapher" alt="Multi Router Traffic Grapher" /></a>
  1435. <p id="version">$VERSION</p>
  1436. <address>
  1437. <a href="http://people.ee.ethz.ch/~oetiker/">Tobias Oetiker</a>
  1438. <a href="mailto:oetiker@ee.ethz.ch">&lt;oetiker@ee.ethz.ch&gt;</a><br />
  1439. TEXT
  1440.     print HTML &$LOC("and");
  1441.     print HTML<<TEXT;
  1442. <a href="http://www.bungi.com/">Dave Rand</a>
  1443. <a href="mailto:dlr@bungi.com">&lt;dlr@bungi.com&gt;</a>
  1444. TEXT
  1445.     # We don't need this any more.
  1446.     undef $gifPath;
  1447.     if ($MRTG_lib::OS eq 'VMS') {
  1448.         print HTML "<br />
  1449. ".&$LOC("Ported to OpenVMS Alpha by")." <a href="http://www.cerberus.ch/">Werner Berger</a>
  1450. <a href="mailto:werner.berger@cch.cerberus.ch">&lt;werner.berger@cch.cerberus.ch&gt;</a>";
  1451.     }
  1452. # There is not realy any significant portion of code from Studard left and
  1453. # none of his addresses work anymore. -- Tobi  2001-06-04
  1454. #    if ($MRTG_lib::OS eq 'NT') {
  1455. #        print HTML 
  1456. #          "<div>
  1457. #  ".&$LOC("Ported to WindowsNT by")."
  1458. #  <NOBR><small><A HREF="http://www.testlab.orst.edu/">Stuart Schneider</A>
  1459. #  <A HREF="mailto:schneis@testlab.orst.edu">
  1460. #  &lt;schneis@testlab.orst.edu&gt;</A></NOBR></div>
  1461. # ";
  1462. #    }
  1463.     if ( 
  1464.         $$cfg{'language'} and 
  1465.         defined($lang2tran::LOCALE{"L$$cfg{'language'}E"}) and
  1466.         ($LOC != $lang2tran::LOCALE{"default"})) 
  1467.     {
  1468.         if (defined($credits::LOCALE{"L$$cfg{'language'}E"})) {
  1469.             print HTML "<br />
  1470. ".$credits::LOCALE{"L$$cfg{'language'}E"};
  1471.         } else {
  1472.             print HTML "<br />
  1473. ".$credits::LOCALE{'default'};
  1474.         }
  1475.         ;
  1476.     }
  1477.     print HTML <<TEXT
  1478. </address>
  1479. </div>
  1480. <!-- End MRTG Block -->
  1481. TEXT
  1482.     }
  1483.     print HTML $$rcfg{'pagefoot'}{$router} if defined $$rcfg{'pagefoot'}{$router};
  1484.     print HTML <<TEXT;
  1485. </body>
  1486. </html>
  1487. TEXT
  1488.     close HTML;
  1489.     if (defined $$cfg{'writeexpires'}  and $$cfg{'writeexpires'} =~ /^y/i) {
  1490.         open(HTMLG, ">$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.".
  1491.      "$$rcfg{'extension'}{$router}.meta") ||
  1492.        do {
  1493.    warn "WARNING: Writing $$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.".
  1494.      "$$rcfg{'extension'}{$router}.meta: $!n";
  1495.    next
  1496.        };
  1497.         print HTMLG "Expires: $expirationn";
  1498.         close(HTMLG);
  1499.     }
  1500. }
  1501. sub printusage {
  1502.     print <<USAGEDESC;
  1503. Usage: mrtg <config-file>
  1504. mrtg-2.13.2 is the Multi Router Traffic Grapher.
  1505. If you want to know more about this tool, you might want
  1506. to read the docs. They came together with mrtg! 
  1507. Home: http://people.ee.ethz.ch/~oetiker/webtools/mrtg/
  1508. USAGEDESC
  1509.     exit(1);
  1510. }
  1511. sub lockit {
  1512.     my ($lockfile,$templock) = @_;
  1513.     if ($MRTG_lib::OS eq 'VMS' or $MRTG_lib::OS eq 'NT'  or $MRTG_lib::OS eq 'OS2') {
  1514.         # too sad NT and VMS can't do links we'll do the diletants lock
  1515.         if (-e $lockfile and not unlink $lockfile) {
  1516.             my($lockage) = time()-(stat($lockfile))[9];
  1517.             die "ERROR: I guess another mrtg is running. A lockfile ($lockfile)n".
  1518.                  "       aged $lockage seconds is hanging around and I can't removen".
  1519.                  "       it because another process is still using it.";
  1520.         }
  1521.       
  1522.         open (LOCK, ">$lockfile") or 
  1523.           die "ERROR: Creating lockfile $lockfile: $!n";
  1524.         print LOCK "$$n";
  1525.         close LOCK;
  1526.         open (LOCK, "<$lockfile") or 
  1527.           die "ERROR: Reading lockfile $lockfile for owner check: $!n";
  1528.         my($read)=<LOCK>;
  1529.         chomp($read);
  1530.         die "ERROR: Someone else just got the lockfile $lockfilen" 
  1531.           unless  $$ == $read;
  1532.     } else {
  1533.         # now, lets do it the UNIX way ... Daves work ...
  1534.         open(LOCK,">$templock") or die "ERROR: Creating templock $templock: $!";
  1535.         $main::Cleanfile = $templock;
  1536.         if (!link($templock,$lockfile)) { # Lock file exists - deal with it.
  1537.             my($nlink,$lockage) = (stat($lockfile))[3,9]; 
  1538.             $lockage = time() - $lockage;
  1539.             if ($nlink < 2 or $lockage > 30*60) { #lockfile is alone and old
  1540.                 unlink($lockfile) 
  1541.                   || do{ unlink $templock; 
  1542.                          die "ERROR: Can't unlink stale lockfile ($lockfile). Permissions?n"};
  1543.                 link($templock,$lockfile) 
  1544.                   || do{ unlink $templock; 
  1545.                          die "ERROR: Can't create lockfile ($lockfile).n".
  1546.                            "Permission problem or another mrtg locking succesfully?n"};
  1547.             } else {
  1548.                 unlink $templock;
  1549.                 die "ERROR: It looks as if you are running two copies of mrtg in parallel onn".
  1550.                     "       the same config file. There is a lockfile ($lockfile) and it isn".
  1551.                     "       is only $lockage seconds old ... Check your crontab.n".
  1552.                     "       (/etc/crontab and /var/spool/cron/root) n"
  1553.                         if $lockage < 4;
  1554.       
  1555.                 die  "ERROR: I guess another mrtg is running. A lockfile ($lockfile) agedn".
  1556.                      "$lockage seconds is hanging around. If you are sure that no other mrtgn".
  1557.                      "is running you can remove the lockfilen";
  1558.           
  1559.             }
  1560.         
  1561.         }
  1562.     }
  1563. }
  1564. sub threshcheck {
  1565.     # threshold checking by Tom Muggli
  1566.     # ... fsck'd up but fixed by Juha Laine
  1567.     my ($cfg,$rcfg,$cfgfile,$router,$cuin,$cuout) = @_;
  1568.     my $threshfile;
  1569.     my %threshval;
  1570.     $threshval{i} = $$cuin{'d'}{$router};
  1571.     $threshval{o} = $$cuout{'d'}{$router};
  1572.     # are we going to keep state ?
  1573.     if (defined $$cfg{'threshdir'}){
  1574.         ensureSL($$cfg{'threshdir'});
  1575.         $threshfile = $$cfg{'threshdir'}.(split /Q$MRTG_lib::SLE/, $cfgfile)[-1].".$router";
  1576.     }
  1577.     # setup environment for external scripts
  1578.     if (defined $$rcfg{'threshdesc'}{$router}) {
  1579.         $ENV{THRESH_DESC}=$$rcfg{'threshdesc'}{$router};
  1580.     } else {
  1581.         delete $ENV{THRESH_DESC};
  1582.     }
  1583.     foreach my $dir (qw(i o)){ # in and out
  1584.         foreach my $bound (qw(min max)){
  1585.     # skip unless a threshold is defined for this "$router"
  1586.             next unless defined $$rcfg{'thresh'.$bound.$dir}{$router};
  1587.             # we can not deal with unknown data here
  1588.             $threshval{$dir} = 0 if not defined $threshval{$dir} and $$rcfg{'options'}{'unknaszero'}{$router};            
  1589.             warn "WARNING: can't do threshold checking for target $router($dir) -- No valid current datan"
  1590.                 unless defined $threshval{$dir};
  1591.       my $value = $threshval{$dir};
  1592.             next unless defined $value;
  1593.             my $boundval = $$rcfg{'thresh'.$bound.$dir}{$router};
  1594.             next unless defined $boundval;
  1595.             if ($boundval =~ s/%$//) { # defined in % of maxbytes
  1596.                 # 2 decimals in %
  1597.                     $value = int($value / $$rcfg{"maxbytes".($dir eq 'i' ? 1 : 2)}{$router} * 10000.0) / 100; # the new code
  1598.             }
  1599.             if (($bound eq 'min' and $boundval > $value) or
  1600.                 ($bound eq 'max' and $boundval < $value)) {
  1601. # threshold was broken...
  1602.                 my @exec = ( $$rcfg{'threshprog'.$dir}{$router}, $router,
  1603.   $$rcfg{'thresh'.$bound.$dir}{$router}, $threshval{$dir},($$rcfg{'threshdesc'}{$router} ||"No Description"), $value);
  1604. # Check if we use the status file or not...
  1605. if ( defined $threshfile ) {
  1606.     if ( not -e $threshfile.".".$bound.uc($dir) ) {
  1607. # Create a file to indicate a threshold problem for the time after the problem
  1608. open THRESHTOUCH, ">".$threshfile.".".$bound.uc($dir)
  1609.     or warn "WARNING: Creating $threshfile.".$bound.uc($dir).": $!n";
  1610. close THRESHTOUCH;
  1611.                         debug('base',"run treshprog$dir: ".(join ",",@exec));
  1612.                    system @exec if defined $$rcfg{'threshprog'.$dir}{$router};
  1613.     }
  1614. } else {
  1615.     # no threshold dir so run on every 'break'
  1616.     system @exec if defined $$rcfg{'threshprog'.$dir}{$router};
  1617. }
  1618.     } else {
  1619. # no threshold broken ...
  1620. my @exec = ( $$rcfg{'threshprogok'.$dir}{$router}, $router,
  1621.   $$rcfg{'thresh'.$bound.$dir}{$router}, $threshval{$dir});
  1622. # Check if we use the status file or not...
  1623. if ( defined $threshfile ) {
  1624.     if ( -e $threshfile.".".$bound.uc($dir) ){
  1625. unlink "$threshfile.".$bound.uc($dir);
  1626. system  @exec if defined $$rcfg{'threshprogok'.$dir}{$router};
  1627.     }
  1628. }
  1629.     }
  1630.         } # foreach my $bound ...
  1631.     } # foreach my $dir
  1632. }
  1633. sub getexternal ($) {
  1634.     my $command = shift;
  1635.     my $in=undef;
  1636.     my $out=undef;
  1637.     my $uptime="unknown";
  1638.     my $name="unknown";
  1639.     open (EXTERNAL , $command."|")
  1640. or warn "WARNING: Running '$command': $!n";
  1641.     warn "WARNING: Could not get any data from external command ".
  1642. "'".$command.
  1643.     "'nMaybe the external command did not even start. ($!)nn" if eof EXTERNAL;
  1644.     chomp( $in=<EXTERNAL>) unless eof EXTERNAL;
  1645.     chomp( $out=<EXTERNAL>) unless eof EXTERNAL;
  1646.     chomp( $uptime=<EXTERNAL>) unless eof EXTERNAL;
  1647.     chomp( $name=<EXTERNAL>) unless eof EXTERNAL;
  1648.     close EXTERNAL;
  1649.     # strip returned date
  1650.     $uptime  =~ s/^s*(.*?)s*/$1/;
  1651.     $name  =~ s/^s*(.*?)s*/$1/;
  1652.     # do we have numbers in the external programs answer ?
  1653.     if ( not defined $in ) {
  1654. warn "WARNING: Problem with External get '$command':n".
  1655.     "   Expected a Number for 'in' but nothing'nn";        
  1656.     } elsif ( $in eq 'UNKNOWN' ) {
  1657.         $in = undef;
  1658.     } elsif ( $in !~ /([-+]?d+(.d+)?)/ ) {
  1659. warn "WARNING: Problem with External get '$command':n".
  1660.     "   Expected a Number for 'in' but got '$in'nn";
  1661. $in = undef;
  1662.     } else {
  1663.         $in = $1;
  1664.     }
  1665.     if ( not defined $out ) {
  1666. warn "WARNING: Problem with External get '$command':n".
  1667.     "   Expected a Number for 'out' but nothing'nn";
  1668.     } elsif ( $out eq 'UNKNOWN' ) {
  1669.         $out = undef;
  1670.     } elsif ( $out !~ /([-+]?d+(.d+)?)/ ) {
  1671. warn "WARNING: Problem with Externale get '$command':n".
  1672.     "   Expected a Number for 'out' but got '$out'nn";
  1673. $out = undef;
  1674.     } else {
  1675.         $out = $1;
  1676.     }
  1677.     debug('snpo',"External result:".($in||"undef")." out:".($out||"undef")." uptime:".($uptime||"undef")." name:".($name||"undef"));
  1678.     return ($in,$out,time,$uptime,$name);
  1679. }
  1680. sub getsnmparg ($$$){
  1681.     my $confcache = shift;
  1682.     my $target = shift;
  1683.     my $cfg = shift;
  1684.     my $retry = 0;
  1685.     my $hostname = $$target{Host};
  1686.     my $hostkey = "$$target{Community}@$$target{Host}$$target{SnmpOpt}";
  1687.     if ($$target{ipv4only}) {
  1688.         if (not ( $hostname =~ /^d+.d+.d+.d+$/ or gethostbyname $hostname) ){
  1689.             warn "WARNING: Skipping host $hostname as it does not resolve to an IPv4 addressn";
  1690.             return 'DEADHOST';
  1691.         }
  1692.     } else {
  1693.          if($hostname =~ /^[(.*)]$/) {
  1694.             # Numeric IPv6 address. Check that it's valid
  1695.             $hostname = substr($hostname, 1);
  1696.             chop $hostname;
  1697.             if(! inet_pton(AF_INET6(), $hostname)) {
  1698.                 warn "WARNING: Skipping host $hostname: invalid IPv6 addressn";
  1699.                 return 'DEADHOST';
  1700.             }
  1701.         } else {
  1702.             # Hostname. Look it up
  1703.             my @res;
  1704.             my ($too,$port,$otheropts) = split(':', $$target{SnmpOpt}, 3);
  1705.             $port = 161 unless defined $port;
  1706.             @res = getaddrinfo($hostname, $port, AF_UNSPEC(), SOCK_DGRAM());
  1707.             if (scalar (@res) < 5) {
  1708.                 warn "WARNING: Skipping host $hostname as it does not resolve to an IPv4 or IPv6 addressn";
  1709.                 return 'DEADHOST';
  1710.             }
  1711.         }
  1712.     }
  1713.   RETRY:
  1714.     my @ifnum = ();
  1715.     my @OID = ();
  1716.     # Find apropriate Interface to poll from
  1717.     for my $i (0..1) {
  1718. if ($$target{IfSel}[$i] eq 'If') {
  1719.     $ifnum[$i] = ".".$$target{Key}[$i];
  1720.     debug('snpo',"simple If: $ifnum[$i]");
  1721. } elsif($$target{IfSel}[$i] eq 'None') {
  1722.             $ifnum[$i] = "";
  1723.         } else {
  1724.             $$target{Key}[$i] =~ s/s+$//; # no trainling whitespace in keys ...
  1725.     if (not defined readfromcache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i])) {
  1726. debug('snpo',"($i) Populate ConfCache for $$target{Host}$$target{SnmpOpt}");
  1727. populateconfcache($confcache,"$$target{Community}@$$target{Host}$$target{SnmpOpt}",$$target{ipv4only},1,$$target{snmpoptions});
  1728.     }
  1729.     if (not defined readfromcache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i])) {
  1730. warn "WARNING: Could not match host:'$$target{Community}@$$target{Host}$$target{SnmpOpt}' ref:'$$target{IfSel}[$i]' key:'$$target{Key}[$i]'n";
  1731. return 'NOMATCH';
  1732.     } else {
  1733. $ifnum[$i] = ".".readfromcache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i]);
  1734. debug('snpo',"($i) Confcache Match $$target{Key}[$i] -> $ifnum[$i]");
  1735.     }
  1736. }
  1737. if ($ifnum[$i] !~ /^$|^.d+$/) {
  1738.     warn "WARNING: Can not determine".
  1739.       " ifNumber for $$target{Community}@$$target{Host}$$target{SnmpOpt} tref: '$$target{IfSel}[$i]' tkey: '$$target{Key}[$i]'n";
  1740.     return 'NOMATCH';
  1741. }
  1742.     }
  1743.     for my $i (0..1) {
  1744. # add ifget methodes call for a cross check;
  1745. for ($$target{IfSel}[$i]) {
  1746.     /^Eth$/ && do {
  1747. push @OID, "ifPhysAddress".$ifnum[$i]; last
  1748.     };
  1749.     /^Ip$/ && do {
  1750. push @OID, "ipAdEntIfIndex".".".$$target{Key}[$i];last
  1751.     };
  1752.     /^Descr$/ && do {
  1753. push @OID, "ifDescr".$ifnum[$i]; last
  1754.     };
  1755.     /^Type$/ && do {
  1756. push @OID, "ifType".$ifnum[$i]; last
  1757.     };
  1758.     /^Name$/ && do {
  1759. push @OID, "ifName".$ifnum[$i]; last
  1760.     };
  1761. }
  1762. push @OID ,$$target{OID}[$i].$ifnum[$i];
  1763.     }
  1764.     # we also want to know uptime and system name unless we are
  1765.     if ( not defined $$cfg{nomib2} and $$cfg{logformat} ne 'rrdtool' ) {
  1766.       if ( $OID[0] !~ /^cache.+$/ and
  1767.            $OID[0] !~ /^Q1.3.6.1.4.1.3495.1E/ ) {
  1768.            push @OID, qw(sysUptime sysName);
  1769.       } else {
  1770.            push @OID, qw(cacheUptime cacheSoftware cacheVersionId)
  1771.       }
  1772.     }
  1773.     # pull that data
  1774.     debug('snpo',"SNMPGet from $$target{Community}@$$target{Host}$$target{SnmpOpt} -- ".(join ",", @OID));
  1775.     my @ret;
  1776.     
  1777.     # make sure we have no error messages hanging round.
  1778.     
  1779.     $SNMP_Session::errmsg = '';
  1780.     my $targtemp = $$target{Community}.'@'.$$target{Host}.$$target{SnmpOpt};
  1781.     $targtemp = v4onlyifnecessary($targtemp, $$target{ipv4only});
  1782.     
  1783.     my @snmpoids = grep !/^(Pseudo|WaLK|GeTNEXT)/, @OID;
  1784.         
  1785.     if (defined $$cfg{singlerequest}){
  1786. #LH        local $BER::pretty_print_timeticks = 0;
  1787. foreach my $oid (@snmpoids){
  1788.             push @ret, snmpget($targtemp,$$target{snmpoptions},$oid);
  1789. }
  1790.     } else {
  1791. @ret = snmpget($targtemp,$$target{snmpoptions},@snmpoids);
  1792.     }
  1793.     my @newret;
  1794.     for (@OID) {
  1795.         /^PseudoZero$/ && do { push @newret, 0; next; };
  1796.         /^PseudoOne$/ && do { push @newret, 1; next; };
  1797.         s/^WaLK(d*)// && do { my $idx = $1 || 0; my $oid=$_;push @newret, (split /:/, (snmpwalk($targtemp,$$target{snmpoptions},$oid))[$idx],2)[1]; 
  1798.                           debug('snpo',"snmpwalk '$oid' -> ".($newret[-1]||'UNDEF'));next};
  1799.         s/^GeTNEXT// && do { my $oid=$_;push @newret, (split /:/, snmpgetnext($targtemp,$$target{snmpoptions},$oid),2)[1]; 
  1800.                           debug('snpo',"snmpgetnext '$oid' -> ".($newret[-1]||'UNDEF'));next};
  1801.         push @newret, shift @ret;
  1802.     }
  1803.     @ret = @newret;
  1804.     debug('snpo',"SNMPfound -- ".(join ", ", map {"'".($_||"undef")."'"}  @ret));
  1805.     $ret[-2] = $ret[-2].' '.$ret[-1] if $OID[-1] and $OID[-1] eq 'cacheVersionId';
  1806.     my $time = time;
  1807.     my @final;
  1808.     # lets do some reality check
  1809.     for my $i (0..1) {
  1810. # some ifget methodes call for a cross check;
  1811. for ($$target{IfSel}[$i]) {
  1812.     /^Eth$/ && do {
  1813. my $bin = shift @ret || 0xff;
  1814. my $eth = unpack 'H*', $bin;
  1815. my @eth;
  1816. while ($eth =~ s/^..//){
  1817.     push @eth, $&;
  1818. }
  1819. my $phys=join '-', @eth;
  1820. if ($phys ne $$target{Key}[$i]) {
  1821.     debug('snpo', "($i) eth if crosscheck got $phys expected $$target{Key}[$i]");
  1822.     if (not $retry) {
  1823. $retry=1;
  1824. # remove broken entry
  1825. storeincache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i],undef);
  1826. debug('repo',"($i) goto RETRY force if cache repopulation");
  1827. goto RETRY;
  1828.     } else {
  1829. warn "WARNING: could not match&get".
  1830.     " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for Eth $$target{Key}[$i]n";
  1831. return 'NOMATCH';
  1832.     }
  1833. };
  1834. debug ('snpo',"($i) Eth crosscheck OK");
  1835.     };
  1836.     /^Ip$/ && do {
  1837. my $if = shift @ret || 'none';
  1838. if ($ifnum[$i] ne '.'.$if) {
  1839.     debug('repo', "($i) IP if crosscheck got .$if expected $ifnum[$i]");
  1840.     if (not $retry) {
  1841. $retry=1;
  1842. # remove broken entry
  1843. storeincache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i],undef);   
  1844. debug('repo',"($i) goto RETRY force if cache repopulation");
  1845. goto RETRY;
  1846.     } else {
  1847. warn "WARNING: could not match&get".
  1848.     " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for IP $$target{Key}[$i]n";
  1849. return 'NOMATCH';
  1850.     }
  1851. }
  1852. debug ('snpo',"($i) IP crosscheck OK");
  1853.     };
  1854.     /^(Descr|Name|Type)$/ && do {
  1855. my $descr = shift @ret || 'Empty';
  1856.                 $descr =~ s/[- ]+$//; # remove excess spaces and stuff
  1857. if ($descr ne $$target{Key}[$i]) {
  1858.     debug('repo', "($i) $_ if crosscheck got $descr expected $$target{Key}[$i]");
  1859.     if (not $retry) {
  1860. $retry=1;
  1861. # remove broken entry
  1862. storeincache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i],undef);   
  1863. debug('repo',"($i) goto RETRY force if cache repopulation");
  1864. goto RETRY;
  1865.     } else {
  1866. warn "WARNING: could not match&get".
  1867.     " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for $_ '$$target{Key}[$i]'n";
  1868. return 'NOMATCH';
  1869.     }
  1870. debug ('snpo',"($i) $_ crosscheck OK");
  1871.     };
  1872. }
  1873. # no sense continuing here ... if there is no data ...      
  1874.   if (defined $SNMP_Session::errmsg and $SNMP_Session::errmsg =~ /no response received/){
  1875.             $SNMP_Session::errmsg = '';
  1876.     warn "WARNING: skipping because at least the query for $OID[0] on  $$target{Host} did not succeedn";
  1877.     return 'DEADHOST';
  1878. }
  1879. if ($$target{OID}[$i] =~ /if(Admin|Oper)Hack/) {
  1880.     push @final, ((shift @ret) == 1) ? 1:0;
  1881. } else {
  1882.     push @final, shift @ret;
  1883. }
  1884.     }
  1885.     
  1886.     my @res = ( @final,$time, @ret);
  1887.     
  1888.     # Convert in and out values to integers with a user-defined subroutine
  1889.     # specified by the Conversion target key
  1890.     if( $target->{ Conversion } ) {
  1891.         foreach my $ri( 0..1 ) {
  1892.             next unless defined $res[ $ri ];
  1893.             my $exp = "&MRTGConversion::$target->{ Conversion }( '$res[ $ri ]' )";
  1894.             $res[ $ri ] = eval $exp;
  1895.             warn "WARNING: evaluation of "$exp" failedn$@n" if $@;
  1896.         }
  1897.     }
  1898.     # have some cleanup first, it seems that some agents
  1899.     # are adding newlines to what they return
  1900.     map{ $_ =~ s/n|r//g if defined $_ } @res;
  1901.     map{ $_ =~ s/^s+//g if defined $_ } @res;
  1902.     map{ $_ =~ s/s+$//g if defined $_ } @res;
  1903.     
  1904.     # in and out should be numbers only
  1905. for my $ri (0..1){
  1906.     # for folks using rrdtool I am allowing numbers 
  1907.     # with decimals here
  1908.     if ( defined $res[$ri] and $res[$ri] !~ /^[-+]?d+(.d+)?$/ ) {
  1909. warn "WARNING: Expected a number but got '$res[$ri]'n";
  1910. $res[$ri] = undef;
  1911.     }
  1912. }
  1913. return @res;
  1914.     }
  1915. # read target function ...
  1916. sub readtargets ($$$) {
  1917.     my ($confcache,$target,$cfg) = @_;
  1918.     my $forks = $$cfg{forks};
  1919.     my $trgnum = $#{$target}+1;
  1920.     if (defined $forks and $forks > 1  and $trgnum > 1){
  1921.         $forks = $trgnum if $forks > $trgnum;
  1922.         my $split = int($trgnum / $forks) + 1;       
  1923. my @hand;
  1924. # get them forks to work ... 
  1925. for (my $i = 0; $i < $forks;$i++) {
  1926.     local *D;
  1927.             my $sleep_count=0;
  1928.             my $pid;
  1929.             do {
  1930.                $pid = open(D, "-|");
  1931.                 unless (defined $pid) {
  1932.                     warn "WARNING cannot fork: $!n";
  1933.                     die "ERROR bailing out after 6 faild forkattempts"
  1934.                          if $sleep_count++ > 6;
  1935.                     sleep 10;
  1936.                 }
  1937.             } until defined $pid;
  1938.     if ($pid) { # parent
  1939.       $hand[$i] = *D; # funky file handle magic ... 
  1940. debug ('fork',"Parent $$ after fork of child $i");
  1941.     } else {  # child
  1942. debug ('fork',"Child $i ($$) after fork");
  1943. my $res = "";
  1944.                 my %deadhost;
  1945. for (my $ii = $i * $split; 
  1946.      $ii < ($i+1) * $split and $ii < $trgnum;
  1947.      $ii++){
  1948.     my $targ = $$target[$ii];
  1949.     my @res;
  1950.     if ($$targ{Methode} eq 'EXEC') {
  1951. @res = getexternal($$targ{Command});
  1952.     } else { # parent
  1953.                         if (not $deadhost{$$targ{Community}.$$targ{Host}}) {      
  1954.                 @res = getsnmparg($confcache,$targ,$cfg);
  1955.                             if ( $res[0] and $res[0] eq 'DEADHOST') {
  1956.                                 # guess we got a blank here
  1957.                                 @res = ( undef,undef,time,undef,undef);
  1958.                                 $deadhost{$$targ{Community}.$$targ{Host}} = 1;
  1959.                                 warn "WARNING: no data for $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}@$$targ{Host}. Skipping further queries for Host $$targ{Host} in this round.n"
  1960.                             } elsif ($res[0] and $res[0] eq 'NOMATCH'){
  1961.                                 @res = (undef,undef,time,undef,undef);
  1962.                             }
  1963.                         } else {
  1964.                             @res = ( undef,undef,time,undef,undef);
  1965.                         }
  1966.     }
  1967.     for (my $iii=0;$iii<5;$iii++){
  1968. if (defined $res[$iii]){
  1969.                             $res .= "$res[$iii]n";
  1970. } else {
  1971.                             $res .= "##UNDEF##n";
  1972. }
  1973.     }
  1974. }
  1975. debug ('fork',"Child $i ($$) waiting to deliver");
  1976. print $res; # we only talk after the work has been done to
  1977.                        # otherwhise we might get blocked 
  1978.                 # return updated hosts from confcache
  1979.                 writeconfcache($confcache,'&STDOUT')
  1980.                         if defined $$confcache{___updated};
  1981. exit 0;
  1982.     }
  1983.     
  1984. }
  1985. # happy reaping ... 
  1986.         my $vin =''; # vector of pipe file-descriptors from children
  1987. for (my $i = 0; $i < $forks;$i++) {
  1988.             vec($vin, fileno($hand[$i]), 1) = 1;
  1989.         }
  1990.         my $left = $forks;
  1991.         while ($left) {
  1992.             my $rout = $vin; # read vector
  1993.             my $eout = $vin; # exception vector
  1994.             my $nfound = select($rout, undef, $eout, undef); # no timeout
  1995.             if (1 > $nfound) {
  1996.                die sprintf("ERROR: select returned %d: $!n", $nfound);
  1997.             }
  1998.     for (my $i = 0; $i < $forks; $i++) {
  1999.                 next unless defined $hand[$i] and defined fileno($hand[$i]);
  2000. # this does not seem to work reliably
  2001. #                if (vec($eout, fileno($hand[$i]), 1)) {
  2002. #    die "ERROR: fork $i has died ahead of time?n";
  2003. #                }
  2004.                 next unless vec($rout, fileno($hand[$i]), 1);
  2005.                 vec($vin, fileno($hand[$i]), 1) = 0; # remove this child fd
  2006.         debug ('fork',"Parent reading child $i");
  2007.         my $h = $hand[$i];
  2008.         for (my $ii = $i * $split; 
  2009.      $ii < ($i+1) * $split and $ii < $trgnum;
  2010.      $ii++){ 
  2011.     my $targ = $$target[$ii];
  2012.     my @res;
  2013.     for (0..4){
  2014.         my $line = <$h>; # must be a simple scalar here else it wont work
  2015.         die "ERROR: fork $i has died ahead of time ...n" if not defined $line;
  2016.         chomp $line;
  2017.     #                    debug ('fork',"reading for $ii $line");
  2018.         $line = undef if $line eq "##UNDEF##";                
  2019.         push @res,$line;
  2020.     };
  2021.     
  2022.     ($$targ{_IN_},
  2023.      $$targ{_OUT_},
  2024.      $$targ{_TIME_},
  2025.      $$targ{_UPTIME_},
  2026.      $$targ{_NAME_}) = @res; 
  2027.                      if ($] >= 5.0061){
  2028.                          $$targ{_IN_} = Math::BigFloat->new($$targ{_IN_}) if $$targ{_IN_};
  2029.                          $$targ{_OUT_} = Math::BigFloat->new($$targ{_OUT_}) if $$targ{_OUT_};
  2030.                      }
  2031.                 }     
  2032.                 # feed confcache entries
  2033.                 my $lasthost ="";
  2034.                 while (<$h>){
  2035.                     chomp;
  2036.                     my ($host,$method,$key,$value) = split (/t/, $_);
  2037.                     if ($host ne $lasthost){
  2038.                   debug ('fork',"start undef for $host");
  2039.                          clearfromcache($confcache,$host);
  2040.                   debug ('fork',"end undef for $host");
  2041.                     }
  2042.                     $lasthost = $host;
  2043.                     storeincache($confcache,$host,$method,$key,$value);
  2044.                  }
  2045.                  close $h;
  2046.                  --$left;
  2047.             }
  2048.         }
  2049.             
  2050.     } else {
  2051.         my %deadhost;
  2052. foreach my $targ (@$target) {
  2053.     if ($$targ{Methode} eq 'EXEC') {
  2054. debug('snpo', "run external $$targ{Command}");
  2055. ($$targ{_IN_},
  2056.  $$targ{_OUT_},
  2057.  $$targ{_TIME_},
  2058.  $$targ{_UPTIME_},
  2059.  $$targ{_NAME_}) = getexternal($$targ{Command});
  2060.     } elsif ($$targ{Methode} eq 'SNMP' and not $deadhost{$$targ{Host}}) {
  2061. debug('snpo', "run snmpget from $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}@$$targ{Host}");
  2062. ($$targ{_IN_},
  2063.  $$targ{_OUT_},
  2064.  $$targ{_TIME_},
  2065.  $$targ{_UPTIME_},
  2066.  $$targ{_NAME_}) = getsnmparg($confcache,$targ,$cfg);
  2067.                if ( $$targ{_IN_} and $$targ{_IN_} eq 'DEADHOST') {
  2068.                    $$targ{_IN_} = undef;
  2069.                    $$targ{_TIME_} =time;
  2070.                    # guess we got a blank here
  2071.                    $deadhost{$$targ{Host}} = 1;
  2072.                    warn "WARNING: no data for $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}@$$targ{Host}. Skipping further queries for Host $$targ{Host} in this round.n"
  2073.                } 
  2074.                if (  $$targ{_IN_} and $$targ{_IN_} eq 'NOMATCH') {   
  2075.                    $$targ{_IN_} = undef;
  2076.                    $$targ{_TIME_} =time;
  2077.                }
  2078.        
  2079.     } else {
  2080.                  $$targ{_IN_} = undef;
  2081.                  $$targ{_OUT_} = undef;
  2082.                  $$targ{_TIME_} = time;
  2083.                  $$targ{_UPTIME_} = undef;
  2084.                  $$targ{_NAME_} = undef;
  2085.             }
  2086.             if ($] >= 5.008 ){
  2087.                     $$targ{_IN_} = new Math::BigFloat "$$targ{_IN_}" if $$targ{_IN_};
  2088.                     $$targ{_OUT_} = new Math::BigFloat "$$targ{_OUT_}" if $$targ{_OUT_};
  2089.             }
  2090.                 
  2091. }       
  2092.     }
  2093.         
  2094. }
  2095. sub imggen ($) {
  2096.         my $dir = shift;
  2097.         if ( ! -r "$dir${main::SL}mrtg-l.png" and  open W, ">$dir${main::SL}mrtg-l.png" ){
  2098.                 binmode W;
  2099.                 print W unpack ('u', <<'UUENC');
  2100. MB5!.1PT*&@H    -24A$4@   #    9! ,   !TA.O'    &%!,5$5]9GTT
  2101. M79A?8HB(9WFR;6G_=DWM=%35<5R_M[A2     6)+1T0'%F&(ZP   1%)1$%4
  2102. M>-JMDKUOPD ,Q:E4]CH?S0PMG8-.$6L1A*YMJ,D<T>Q%5?[_ON>$Y (2$R>=
  2103. MY;-^OF=;GNCMSFY#_#GW$IU0WMP/#O5HSGNW8"9R)/92$NQZ:GUDG/@.@!
  2104. M)CP#4H >LT>)NB!A0]8,"]&0.(#WY92V<$=FM4<P7$:Q,JN^BGRV+WOX2
  2105. MX.<2+4VH!U01B-LYX#V3B*U(1N 5[K,/?(G,)1!!9-%WX0.HYX7 :0!"]0&4
  2106. MMV*/Z"/N@&8$P,N8!:FD[!4 #7EN$G1 <M+"0*0B0&$!#:MQ@#P#?WIO@,^
  2107. M+KI@K$9V#B"P03V,!5)T]1T#*A,8P"P.%JZ1USGL%%IPTC.#<ONMK2W@']'
  2108. M6*MJXQ-D&    $-T15AT4V]F='=A<F4 0"@C*4EM86=E36%G:6-K(#0N,BXY
  2109. M(#DY+S Y+S Q(&-R:7-T>4!M>7-T:6,N97,N9'5P;VYT+F-O;>WHV?     J
  2110. M=$58=%-I9VYA='5R90!D,C(W8S<T.3AA-3 Q93=F830U,#@V8SEF9C0T8F(Y
  2111. K8ULTB'X    .=$58=%!A9V4 -C-X,C4K,"LP&!)XE     !)14Y$KD)@@F(Y
  2112. UUENC
  2113. close W;
  2114.         }
  2115.         if ( ! -r "$dir${main::SL}mrtg-m.png" and  open W, ">$dir${main::SL}mrtg-m.png" ){
  2116.                 binmode W;
  2117.                 print W unpack ('u', <<'UUENC');
  2118. MB5!.1PT*&@H    -24A$4@   !D    9! ,    VQYA0    &%!,5$5]9GTT
  2119. M79A588QN9(*7:73_=DWL=%31<F'8YWD&     6)+1T0'%F&(ZP   (!)1$%4
  2120. M>-IC"$4"!0P$>"E&2B:I,%Z((!"(I$)X88H@GJ :A)<$Y@@*I8)YAA">("N(
  2121. M%PYD"#L+"IJ#Y4!FN(86N4',# )J"4TO*RO!_$"@::'@HTEQ /K _&$0+P 
  2122. M(.T:6@A2@62?H#B*6TRQN#,<Q0^AP<C^ _K=&,GO1(89 ,3!45'T;[0_    
  2123. M0W1%6'13;V9T=V%R90! *",I26UA9V5-86=I8VL@-"XR+CD@.3DO,#DO,#$@
  2124. M8W)I<W1Y0&UY<W1I8RYE<RYD=7!O;G0N8V]M[>C9    "IT15AT4VEG;F%T
  2125. M=7)E #1E,S8X-S$P,38Q-S)A96%B.3,Y8SEA,F5D-31B86(U@DWZ,@    ET
  2126. M15AT1&5L87D ,C4P(RC.$P    YT15AT4&%G90 R-7@R-2LP*S"#D2 ?    
  2127. ) $E%3D2N0F""
  2128. UUENC
  2129. close W;
  2130.         }
  2131.         if ( ! -r "$dir${main::SL}mrtg-r.png" and  open W, ">$dir${main::SL}mrtg-r.png" ){
  2132.                 binmode W;
  2133.                 print W unpack ('u', <<'UUENC');
  2134. MB5!.1PT*&@H    -24A$4@   80    9! ,   ##'$3)    &%!,5$4T79@
  2135. M8YQ5=JAPC;>:KS_?[5WNFZR-PB%CO!     6)+1T0'%F&(ZP  !=5)1$%4
  2136. M>-KM6$ESVE@0;BW 58: K_)"YDH(.%=YL/"511)7O""N3AS!WY_^NM^3Q)+,
  2137. M4C55DZJ1*2&]U]O7Z,4T"]__0_A;US.O7$$#H*PR=R[_'@#? A9X3KLW,K
  2138. MNRX_*L=MGK^#PQ)[$USW+J@'U)E,!JI<V>GS9"+RG0^B!(NDM,ZMB'G,M_?6
  2139. MF@^[_#7D[U)*!V_N!#+4$)>M@+Z(.J/1)PNAI4]=5K# 0_,)'Z(4UXH*A<#/
  2140. M2SQ3+?[=!F4Q&M?Z!8@2-?4X+O*L^Q$PPQ;MA.R= 65RRWR/:[U&#PTVR_
  2141. MR4*JI#3?9#E-$U)#W)5*GE&3[Y&!T%P+?QR2)U;UUOB(0@A0"-Z*.AOVC)=^
  2142. MY=L&-T/L"$G !,KBW"V-3PU[.8W@5N7J)W(?L'&WB5LP6_*X10OP+2L21G/
  2143. MH7OA=#:1&M)F-1FI^A+8B&H %;A+U>LP8A)HN^RZOC9^BZY#=#_&10NA8"
  2144. M.5E !^R7H/2R=0G!6PH%1%-!W@<"GU+PN4Q.Z2D6-K!2-;D]-=J"-2H?&C>
  2145. M7"B$ABCW."HV-PX@%"6$YHS<3:@.K!$K2;N"4-0A)X'.R[?%R4$MXQ"TT9,
  2146. M7*Y:IP)!8N0L=Z0IVUJH(9!51H%B1)9A]$%,S3QB[_P0 EA;B W*KM/%?%Q
  2147. M%*@8'$*0K?Y@54'0*+ !XZ@&P;C'2N$W?SU&BX"Z"YKH08ATBB,^WCHW[U5
  2148. MCK6U$!U'H3$WA9<<1%+2I8R"H:]@ZT=Q8%9LK1%Z!1AA< XH902XAM@X#<;
  2149. MA?;J) HL-F[!KJ*U%KC:D<XFTOAKZ35^+8FU7-S5.8Z!(N;U7P)]B93.@G!
  2150. M4XWWZ^$U)]+-]? "M9#5YI*_+%^LE''@S[5Q]&951J&PYL0-FJ],-<4J>DD
  2151. M_NQ.9M$RD9?TE!AXHJ8E,+IJE^'Q6+.H1XK#A;VFI.B6F=)&B$+E>'9T
  2152. M51>-EPD:$VVE;#C>'CJ2:=.FJ?9>1_'20-CQ'_ESAN"5Y?Q4LT'=SFS?^.7*
  2153. MYFU%;!(IFTP>!K!Q%=8AL.)7D@8/AL;K:#3A#LW#*>0H> <0(-"]YF!:*>X2
  2154. MUG&GD;D08Z0] ,((4XWG0O;)0-@2UTPW8@V^G0NG46BO^FNB,@I.7A$?EO.W
  2155. MK+1)V<4R&H<$E]MR-K7@GD!HB;DL!4G%N>VP;?YJCPE?''#<:4C!?S7?-IH
  2156. MXG%'2N"B.&2Y/^M(*ZE!6PM.]L..5#4997<P;JG(SP]:JKLCZP&03+3&=[<
  2157. M+B$% [N;Y?E&PQ<80[RJG)_Z(@Q;TERV&)DG48AJY>R@5W:5"SWE* JV([U5
  2158. M*6W8H>EBM=_O>9B8CF2C0)M:.1L)@-*(!$_^+C?S8TF.FJJO;6?& AP20*A
  2159. MB6DR[*FS':G[7)5<<U$1VXYD6)RRO9L]:$)C@=3CT19'%00G53RMF96R55J+
  2160. M^! "2],!%$B88CAS9?S S>_,&2F1-N>:+LBJ2^+#Z1Q1?WV02(3PR2/<JV<D
  2161. M8Q.O/]<@&#R<B4S6-.SK+HS<Z&W4 @@Z!9<VSE0D)SWSI^1H"+^*H" Q!(?
  2162. M3V<3WH#'^=H&(1Z-F<D4PNA"F+#Y+TKRSC#,!DG/8=#*D;C73LC60B>'*IX
  2163. M=,_1!P+ %2,A]5Q'TBSR49QN_$P5?$9Z>2 P=;DXN8B..I(+ 2/'76SBU,P
  2164. MC9=6BD28R^O)=+1&2E2"#/C%CX7M++)J&#SFMP*GD^F,_SCC+.'42'H+;&)
  2165. M0LH]YTU8>HM#"!3_GI@4.:R%*_3UEC:M+I8_I]-,T&9*3H0:4U/RYG:-+9
  2166. M):7)9D,JX$_9*=,TW>)74"X0F@ Y50B83W?'H!$"]80*XF;"P2P>%NUJ&2_
  2167. M?-51V7N3)7)?E!]B_$V:OMMJ^,+CZMYPMK??9;'SXBG"J;4C%PBPVX5^T]1"
  2168. M6U-M/;K_[#>U>Q4<$?_I=?&SS>NP_(5X=Z<<ITR.Y5YP7_B/QC_Z/"?]4_
  2169. MO3U!QLJ>FT="M4^    0W1%6'13;V9T=V%R90! *",I26UA9V5-86=I8VL@
  2170. M-"XR+CD@.3DO,#DO,#$@8W)I<W1Y0&UY<W1I8RYE<RYD=7!O;G0N8V]M[>C9
  2171. M    "IT15AT4VEG;F%T=7)E #$R,3<U939B,S$X,S,S-V0S.#%F,#%C-C-C
  2172. M9F,T,S9ELPXRP0    ]T15AT4&%G90 S.#AX,C4K,"LP4K-IB0    !)14Y$
  2173. $KD)@@C9E
  2174. UUENC
  2175. close W;
  2176.         }
  2177. }
  2178. # Local Variables:
  2179. # mode: cperl
  2180. # eval: (cperl-set-style "PerlStyle")
  2181. # mode: flyspell
  2182. # mode: flyspell-prog
  2183. # End:
  2184. #
  2185. # vi: sw=4