#! /usr/bin/perl -w
# -*- mode: cperl -*-

###################################################################
# MRTG 2.10.11  Multi Router Traffic Grapher
###################################################################
# Created by Tobias Oetiker <oetiker@ee.ethz.ch>
#            and Dave Rand <dlr@bungi.com>
#
# For individual Contributers check the CHANGES file
#
###################################################################
#
# Distributed under the GNU General Public License
#
###################################################################
my @STARTARGS=($0,@ARGV);

@main::DEBUG=qw();
# DEBUG TARGETS
# cfg  - watch the config file reading
# dir  - directory mangeling
# base - basic program flow
# tarp - target parser
# snpo - snmp polling
# repo - track confcache repopulation
# fork - forking view
# time - some timing info
# log  - logging of data via rateup or rrdtool
$main::GRAPHFMT="png";
# There older perls tend to behave peculiar with
# large integers ...
require 5.005;

use strict;

BEGIN {
    # Automatic OS detection ... do NOT touch
    if ( $^O =~ /^(ms)?(dos|win(32|nt)?)/i ) {
	$main::OS = 'NT';
	$main::SL = '\\';
	$main::PS = ';';
    } elsif ( $^O =~ /^VMS$/i ) {
	$main::OS = 'VMS';
	$main::SL = '.';
	$main::PS = ':';
    } elsif ( $^O =~ /^os2$/i ) {
	$main::OS = 'OS2';
	$main::SL = '/';
	$main::PS = ';';
    } else {
	$main::OS = 'UNIX';
	$main::SL = '/';
	$main::PS = ':';
    }
    if ( $ENV{LANG} and $ENV{LANG} =~ /UTF-8/i ){        
        print <<ERR;
-----------------------------------------------------------------------
ERROR: Mrtg will most likely not work propperly when the environment
       variable LANG is set to UTF-8. Please run mrtg in an environment
       where this is not the case:

       env LANG=C ${0} ...
-----------------------------------------------------------------------
ERR
        exit 0;
    }
}


use FindBin;
use lib "${FindBin::Bin}";
use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2";
use Getopt::Long;
use Math::BigFloat;

# search for binaries in the bin and bin/../lib  directory
use MRTG_lib "2.100001";
use SNMP_util '0.97';
# $SNMP_Session::suppress_warnings = 2;
use locales_mrtg "0.07";

# Import IPv6 libraries if available, as (unfortunately) they are
# necessary for dead host detection (for IPv6 name lookups).
BEGIN {
    if (eval {require Socket6;})  {
	import Socket;
	import Socket6
    }
}

$main::STARTTIME = time;

if ($MRTG_lib::OS eq 'OS2') {
# in daemon mode we will pause 3 seconds to be sure that parent died
  require OS2::Process;
  if (OS2::Process::my_type() eq 'DETACH') {sleep(3);}
}

if ($MRTG_lib::OS eq 'UNIX') {
   $SIG{INT} = $SIG{TERM} = 
           sub {   unlink ${main::Cleanfile} 
                       if defined $main::Cleanfile;
                   unlink ${main::Cleanfile2}
                       if defined $main::Cleanfile2;
                   unlink ${main::Cleanfile3}
                       if defined $main::Cleanfile3;
                   warn "ERROR: Bailout after SIG $_[0]\n";
                   exit 1;
                };
  $SIG{HUP} = sub {
                   unlink ${main::Cleanfile} 
                       if defined $main::Cleanfile;
                   unlink ${main::Cleanfile2}
                       if defined $main::Cleanfile2;
                   unlink ${main::Cleanfile3}
                       if defined $main::Cleanfile3;
                   die "ERROR: Bailout after SIG $_[0]\n";
                };
}


END {
    local($?, $!);
    unlink ${main::Cleanfile} if defined $main::Cleanfile;
    unlink ${main::Cleanfile2} if defined $main::Cleanfile2;
}

&main;

exit(0);

#### Functions ################################################

sub main {

    debug 'time', "prog start ".localtime(time);
    
    # read in the config file
    my @routers;
    my %cfg;
    my %rcfg;
    my %opts;
    GetOptions(\%opts, 'user=s', 'group=s', 'lock-file=s','confcache-file=s','logging=s', 'check', 'fhs', 'daemon',  'pid-file=s','debug=s');

    if (defined $opts{debug}){
        @main::DEBUG = split /\s*,\s*/, $opts{debug}
    }

    my $uid = $<;
    my $gid = $(;

    if (defined $opts{group}) {
        $gid = getgrnam($opts{group}) or die "ERROR: Unknown Group: $opts{group})\n";
    }

    if (defined $opts{user}) {
        $uid = getpwnam($opts{user}) or die "ERROR: Unknown User: $opts{user})\n";
    }

    # If we've specified using FHS (http://www.pathname.com/fhs/) on the command line,
    # use the relevant path definitions (can be overridden later):

    my $confcachefile;
    my $pidfile;
    my $lockfile;
    my $templock;
    my $logfile;

    if (defined $opts{"fhs"}) {
	$confcachefile = "/var/cache/mrtg/mrtg.ok";
	$pidfile = "/var/run/mrtg.pid";
	$lockfile = "/var/cache/mrtg/mrtg.lck";
	$templock = "/var/cache/mrtg/mrtg.lck.$$";
	$logfile = "/var/log/mrtg.log";
    }	

    my $cfgfile = shift @ARGV;

    if ( !defined $cfgfile && -r "/etc/mrtg.cfg" ) { $cfgfile = "/etc/mrtg.cfg"; }

    printusage() unless defined $cfgfile;

    # PID file code, used later if daemonizing...
    if ( !defined($pidfile) ) {
        $pidfile =  $cfgfile;
        $pidfile =~ s/\.[^.\/]+$//;
        $pidfile .= '.pid';
    }
    $pidfile =  $opts{"pid-file"} || $pidfile;

    # Run as a daemon, specified on command line (required for FHS compliant daemon)
    if (defined $opts{"daemon"}) {
	# Create a pidfile, then chown it so we can use it once we change user
	&create_pid($pidfile);
	chown $uid, $gid, $pidfile;
    }

    ($(,$)) = ($gid,$gid) ;
    ($<,$>) = ($uid,$uid) ;
    die "ERROR failed to set UID to $uid\n" unless ($< == $uid and  $> == $uid);

    $logfile = $opts{logging} || $logfile;
    if (defined $logfile){
	setup_loghandlers $logfile;
        warn "Started mrtg\n";
    }	

    # lets make sure that there are not two mrtgs running in parallel.
    # so we lock on the cfg file. Nothing fancy, just a lockfile

    $lockfile = $opts{"lock-file"} || $lockfile;

    if (! defined $lockfile) {
        $lockfile = $cfgfile."_l";
    }
    if (! defined $templock) {
        $templock = $lockfile."_" . $$ ;
    }

    debug('base', "Creating Lockfiles $lockfile,$templock");
    &lockit($lockfile,$templock);

    debug('base', "Reading Config File: $cfgfile");
    readcfg($cfgfile,\@routers,\%cfg,\%rcfg);

    # Enable or disable IPv6
    if(defined $cfg{enableipv6}) {
        $cfg{enableipv6} = lc($cfg{enableipv6});
    } else {
        $cfg{enableipv6} = 'no';
    }

    # Check we have the necessary libraries for IPv6 support
    if ($cfg{enableipv6} eq 'yes') {
        if ( (eval {require Socket6;}) && (eval {require IO::Socket::INET6;}) ) {
            debug('base', "IPv6 libraries found, IPv6 enabled.");
        } else {
            warn "WARNING: IPv6 libraries not found, IPv6 disabled.\n";
            $cfg{enableipv6} =  'no';
        }
    }

    # from our last run we kept some info about
    # the configuration of our devices around
    debug('base', "Reading Interface Config cache");
    $confcachefile =  $opts{"confcache-file"} || $confcachefile;
    if ( !defined($confcachefile) ) {
        $confcachefile = $cfgfile;
        $confcachefile  =~ s/\.[^.\/]+$//;
        $confcachefile .= ".ok";
    }
    my $confcache = readconfcache($confcachefile);

    # Check the config and create the target object
    debug('base', "Checking Config File");
    my @target;
    cfgcheck(\@routers, \%cfg, \%rcfg, \@target);

    # exit here if we only check the config file
    # in case of an error, cfgcheck() already exited
    if (defined $opts{check}) {
        debug('base', "Remove Lock Files");
        close LOCK; unlink ($templock, $lockfile);
        debug('base', "Exit after successful config file check");
        exit 0;
    }

    # postload rrdtool support
    if ($cfg{logformat} eq 'rrdtool'){
        debug('base', "Loading RRD support");
	require 'RRDs.pm';
    }

    # set the locale
    my $LOC;
    if ( $cfg{'language'} && defined($lang2tran::LOCALE{"\L$cfg{'language'}\E"})) {
	debug('base', "Loading Locale for ".$cfg{'language'});
	$LOC=$lang2tran::LOCALE{"\L$cfg{'language'}\E"};
    } else {
	debug('base', "Loading default Locale");
	$LOC=$lang2tran::LOCALE{'default'};
    }

    # Daemon Code
    my $last_time=0;
    my $curent_time;
    my $sleep_time;
    if (defined $opts{"daemon"}) { $cfg{'runasdaemon'} = "yes"; }
    &demonize_me($pidfile,$cfgfile) if defined $cfg{'runasdaemon'}  and $cfg{'runasdaemon'} =~ /y/i  and $MRTG_lib::OS ne 'VMS';
    # auto restart on die if running as demon

    $SIG{__DIE__} = sub {
        warn $_[0];
        warn "*** Restarting after 10 seconds in an attempt to recover from the error above\n";
        sleep 10;
        exec @STARTARGS;
    } if $cfg{'runasdaemon'};

    debug('base', "Starting main Loop");
    do {                        # Do this loop once for native mode and forever in daemon mode 
        my $router;
        debug 'time', "loop start ".localtime(time);

        #if we run as daemon, we sleep in between collection cycles
        $sleep_time=  ($cfg{interval}*60)-(time-$last_time);
        if ($sleep_time > 0 ) { #If greater than 0 the sleep that amount of time
	    debug('time', "Sleep time $sleep_time seconds");
            sleep ($sleep_time);
        } elsif ($last_time > 0) {
            warn "WARNING: data collection did not complete within interval!\n";
        }
        $last_time=time;

        # set meta expires if there is an index file
        # 2000/05/03 Bill McGonigle <bill@zettabyte.net>
        if (defined $cfg{'writeexpires'}) {
           my $exp = &expistr($cfg{'interval'} ? $cfg{'interval'} : 5);
           my $fil;
           $fil = "$cfg{'htmldir'}index.html"  if -e "$cfg{'htmldir'}index.html";
           $fil = "$cfg{'htmldir'}index.htm"  if -e "$cfg{'htmldir'}index.htm";
            if (defined $fil) {
                   open(META, ">$fil.meta");
                   print META "Expires: $exp\n";
                   close(META);
            }
        }


        # Use SNMP to populate the target object
	debug('base', "Populate Target object by polling SNMP and".
	      " external Datasources");
        debug 'time', "snmp read start ".localtime(time);
        readtargets($confcache,\@target, \%cfg);

        # collect data for each router or pseudo target (`executable`)
        debug 'time', "target loop start ".localtime(time);
        foreach $router (@routers) {
	    debug('base', "Act on Router/Target $router");
            if (defined $rcfg{'setenv'}{$router}) {
                my $line = $rcfg{'setenv'}{$router};
                while ( $line =~ s/([^=]+)=\"([^\"]*)\"\s*// ) 
 		{ 
		    $ENV{$1}=$2;
                }
	    }
	    my($savetz) = $ENV{'TZ'};
	    if (defined $rcfg{'timezone'}{$router}) {
                $ENV{'TZ'} = $rcfg{'timezone'}{$router}
            }

            my ($inlast, $outlast, $uptime, $name, $time) = 
              getcurrent(\@target, $router, \%rcfg, \%cfg);
	    debug('base', "Get Current values: in:".( defined $inlast ? $inlast : "undef").", out:".
                                                 ( defined $outlast? $outlast : "undef").", up:".
                                                 ( defined $uptime ? $uptime : "undef").", name:".
                                                 ( defined $name ? $name : "undef").", time:".
                                                 ( defined $time ? $time : "undef"));

            #abort, if the router is not responding.
            if ($cfg{'logformat'} ne 'rrdtool') {
              # undefined values are ok for rrdtool !
              #if ( not defined $inlast or not defined $outlast){
              #  warn "WARNING: Skipping Update of $router, inlast is not defined\n"
              #          unless defined $inlast;
              #  warn "WARNING: Skipping Update of $router, outlast is not defined\n"
              #          unless defined $outlast;
              #  next;
              #}

              if ($inlast < 0) {
                $inlast += 2**31;
                # this is likely to be a broken snmp counter ... lets compensate
              }  
              if ($outlast < 0) {
                $outlast += 2**31;
                # this is likely to be a broken snmp counter ... lets compensate
              }  
            }
	    
            my ($maxin, $maxout, $maxpercent, $avin, $avout, $avpercent,$avmxin, $avmxout,
                $cuin, $cuout, $cupercent);
	    debug('base', "Create Graphics");
            if ($rcfg{'options'}{'dorelpercent'}{$router}) {
                ($maxin, $maxout, $maxpercent, $avin, $avout, $avpercent,
                 $cuin, $cuout, $cupercent, $avmxin, $avmxout) =
                  writegraphics($router, \%cfg, \%rcfg, $inlast, $outlast, $time,$LOC);
            } else {
                ($maxin, $maxout ,$avin, $avout, $cuin, $cuout, $avmxin, $avmxout) =
                  writegraphics($router, \%cfg, \%rcfg, $inlast, $outlast, $time,$LOC);
            }
            # skip this update if we did not get anything usefull out of
            # writegraphics
            next if not defined $maxin;

	    debug('base', "Check for Thresholds");
            threshcheck(\%cfg,\%rcfg,$cfgfile,$router,$cuin,$cuout);

	    if ($cfg{logformat} eq 'rateup'){
		debug('base', "Check for Write HTML Pages");
		writehtml($router, \%cfg, \%rcfg,
			  $maxin, $maxout, $maxpercent, $avin, $avout, $avmxin, $avmxout, $avpercent,
			  $cuin, $cuout, $cupercent, $uptime, $name, $LOC)
	    }

            #put TZ things back in shape ... 
            if ($savetz) {
                $ENV{'TZ'} =  $savetz;
            } else {
                delete $ENV{'TZ'};
            }
            ;
        }
    } while ($cfg{'runasdaemon'} and $cfg{'runasdaemon'} =~ /y/i ); #In daemon mode run forever
    debug('base', "Exit main Loop");
    # OK we are done, remove the lock files ... 

    debug('base', "Remove Lock Files");
    close LOCK; unlink ($templock, $lockfile);

    debug('base', "Store Interface Config Cache");
    delete $$confcache{___updated} if exists $$confcache{___updated}; # make sure everything gets written out not only the updated entries
    writeconfcache($confcache,$confcachefile);

}

# ( $inlast, $outlast, $uptime, $name, $time ) =
#    &getcurrent( $target, $rou, $rcfg, $cfg )
# Calculate monitored data for device $rou based on information in @$target
# and referring to configuration data in %$rcfg and %$cfg. In the returned
# list, $inlast and $outlast are the input and output monitored data values,
# $uptime is the device uptime, $name is the device name, and $time is the
# current time when the calculation was performed.
sub getcurrent {
	my( $target, $rou, $rcfg, $cfg ) = @_;
	# Hash indexed by $mode for conveniently saving $inlast and $outlast
	my %last;
	# Initialize uptime, device name, and data collection time to empty strings
	my $uptime = '';
	my $name = '';
	my $time = '';

	# Calculate input and output monitored data
	foreach my $mode( qw( _IN_  _OUT_ ) ) {
		# Initialize monitored data, warning message, and death message
		# to empty strings
		my $data;
		my $warning;
		my $death;
		{
			# Code block used to calculate monitoring data
			# Localize warning and death exception handlers to capture
			# error message less any leading and trailing white space
			local $SIG{ __WARN__ } =
				sub { $_[0] =~ /^\s*(.+?)\s*$/; $warning = $1; };
			local $SIG{ __DIE__ } =
				sub { $_[0] =~ /^\s*(.+?)\s*$/; $death = $1; };
			# Calculate monitoring data. $rcfg->{ target }{ $rou } contains
			# a Perl expression for the calculation.
			$data = eval "$rcfg->{target}{$rou}";
       		}
		# Test for various exceptions occurring in the calculation
		if( $warning ) {
			warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (warn): $warning\n";
			$data = undef;
		} elsif( $death ) {
			warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (kill): $death\n";
			$data = undef;
		} elsif( $@ ) {
			warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (eval): $@\n";
			$data = undef;
		} elsif( not defined $data ) {
			warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' did not eval into defined data\n";
			$data = undef;
		} elsif( $data && $data !~ /^[-+]?\d+(\.\d*)?([eE][+-]?[0-9]+)?$/ ) {
			warn "ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' evaluated to '$data' instead of a number\n";
			$data = undef;
		} elsif( length( $data ) > 190 ) {
			warn "ERROR: $mode value: '$data' is way to long ...\n";
			$data = undef;
		} else {
			# At this point data is considered valid. Round to an integer
			# unless RRDTool is in use and this is a gauge
                        if (not ( $cfg->{ logformat } eq 'rrdtool'
                                      and  defined $rcfg->{ options }{ gauge }{ $rou })){
                            if (ref $data and ref $data eq 'Math::BigFloat') {
                			$data->ffround( 0 )
                            } else {
                                        $data = sprintf "%.0f", $data;
                            }
                        }
			# Remove any leading plus sign
			$data =~ s/^\+//;
		}
		$last{ $mode } = $data;
	}

	# Set $u to the unique index of the @$target array referred to in the
	# monitored data calculation for the current device. $u will be set to
	# -1 if that index is not unique.
	my $u = $rcfg->{ uniqueTarget }{ $rou };

	# Get the uptime, device name, and data collection time from the @$target
	# array if the monitored data calculation refers only to one target.
	# Otherwise it doesn't make sense to do this.
	if( $rcfg->{ uniqueTarget }{ $rou } >= 0 ) {
		$uptime = $target->[ $u ]{ _UPTIME_ };
		$name = $target->[ $u ]{ _NAME_ };
		$time = $target->[ $u ]{ _TIME_ };
	}

	# Set the time to the current time if it was not set above
	$time = time unless $time;
        
	# Get the uptime and device name from the alternate location
	# (community@host) that may have been specified with the RouterUptime
	# target keyword
	if( defined $rcfg->{ routeruptime }{ $rou } ) {
		( $uptime, $name ) = snmpget(
			v4onlyifnecessary( $rcfg->{ routeruptime }{ $rou },
				$rcfg->{ ipv4only }{ $rou } ),
			$cfg->{ snmpoptions }, 'sysUptime', 'sysName');
	}
  
	# Get the device name from the alternate location (OID or
	# OID:community@host) that may have been specified with the RouterName
	# target keyword
	if( defined $rcfg->{ routername }{ $rou } ) {
		my( $noid, $nloc ) = split( /:/, $rcfg->{ routername }{ $rou }, 2 );
		# If no location (community@host) was specified use values from the
		# unique target referred to in the monitored data calculation
		if( $u >= 0 and not $nloc ) {
			my $comm = $target->[ $u ]{ Community };
			my $host = $target->[ $u ]{ Host };
			my $opt = $target->[ $u ]{ SnmpOpt };
			$nloc = "$comm\@$host$opt";
		}
		# Get the location from the RouterUptime keyword if that is defined
		# and $nloc has not otherwise been specified
		$nloc = $rcfg->{ routeruptime }{ $rou }
			if defined $rcfg->{ routeruptime }{ $rou } and not $nloc;
		# Get the device name if $nloc (community@host) has been specified
		# one way or the other
		( $name ) = snmpget( $nloc, $$cfg{snmpoptions}, $noid ) if $nloc;
	}
  
	return ( $last{ _IN_ }, $last{ _OUT_ }, $uptime, $name, $time );
}

sub rateupcheck ($) {
    my $router = shift;
    if ($?) {
        my $value = $?;
        my $signal =  $? & 127; #ignore the most significant bit 
                                #as it is always one when it is a returning
                                #child says dave ...
        if (($MRTG_lib::OS ne 'UNIX') || ($signal != 127)) {
            my $exitval = $? >> 8;
            warn "WARNING: rateup died from Signal $signal\n".
              " with Exit Value $exitval when doing router '$router'\n".
                " Signal was $signal, Returncode was $exitval\n"
        }
    }
}

sub writegraphics {
    my($router, $cfg, $rcfg, $inlast, $outlast, $time,$LOC) = @_;
  
    my($absmax,$maxv, $maxvi, $maxvo, $i, $period, $res);
    my(@exec, @mxvls, @metas);
    my(%maxin, %maxout, %maxpercent, %avin, %avout, %avmxin, %avmxout,  %avpercent, %cuin, %cuout, %cupercent);

    @metas = ();
    $maxvi = $$rcfg{'maxbytes1'}{$router};
    $maxvo = $$rcfg{'maxbytes2'}{$router};
    if ($maxvi > $maxvo) {
        $maxv = $maxvi;
    } else {
        $maxv = $maxvo;
    }
    $absmax = $$rcfg{'absmax'}{$router};
    $absmax = $maxv unless defined $absmax;
    if ($absmax < $maxv) {
        die "ERROR: AbsMax: $absmax is smaller than MaxBytes: $maxv\n";
    }


    # select whether the datasource gives relative or absolte return values.
    my $up_abs="u";
    $up_abs='a' if defined $$rcfg{'options'}{'absolute'}{$router};
    $up_abs='g' if defined $$rcfg{'options'}{'gauge'}{$router};
    $up_abs='h' if defined $$rcfg{'options'}{'perhour'}{$router};
    $up_abs='m' if defined $$rcfg{'options'}{'perminute'}{$router};

    if ($$cfg{logformat} eq 'rrdtool') {
        debug('base',"start RRDtool section");
        # make sure we got some sane default here
        my %dstype = qw/u COUNTER a ABSOLUTE g GAUGE h COUNTER m COUNTER/;
        $up_abs = $dstype{$up_abs};
        # update the database.
        my $rrd = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.rrd";
        # set minimum/maximum values. use 'U' if we cannot get good values
        # the lower bound is hardcoded to 0
        my $absi = $maxvi;
        my $abso = $maxvo;
        $absi = $abso = $$rcfg{'absmax'}{$router}
            if defined $$rcfg{'absmax'}{$router};
        debug('base',"maxi:$absi, maxo:$abso");
        $absi = 'U' if $absi == 0;
        $abso = 'U' if $abso == 0;
        # maybe we can convert an .log file to the new rrd format
        if (-e "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.log"
            and not -e "$rrd") {
             debug('base',"converting $$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.log to RRD format");
             if(defined $RRDs::VERSION and $RRDs::VERSION < 1.000271){
                die "ERROR: RRDtool version 1.0.27 or later required to perform log2rrd conversion\n";
             }
             log2rrd($router,$cfg,$rcfg);
        } elsif (! -e $rrd) {
            #nope it seems we have to create a new one
            debug('base',"create $rrd");
            # create the rrd if it doesn't exist
            # don't fail if interval is not set
            my $interval = $$cfg{interval} || 5;
            my $minhb = int($$cfg{interval} * 60)*2;
            $minhb = 600 if ($minhb <600); 
            my $rows = $$rcfg{'rrdrowcount'}{$router} || ( 4000 / $interval);
            my @args = ($rrd, '-b', $time-10, '-s', int($interval * 60),
                         "DS:ds0:$up_abs:$minhb:0:$absi",
                         "DS:ds1:$up_abs:$minhb:0:$abso",
                         "RRA:AVERAGE:0.5:1:$rows",
                         ( $interval < 30  ? ("RRA:AVERAGE:0.5:".int(30/$interval).":800"):()),
                         "RRA:AVERAGE:0.5:".int(120/$interval).":800",
                         "RRA:AVERAGE:0.5:".int(1440/$interval).":800",
                         "RRA:MAX:0.5:1:$rows",
                         ( $interval < 30  ? ("RRA:MAX:0.5:".int(30/$interval).":800"):()),
                         "RRA:MAX:0.5:".int(120/$interval).":800",
                         "RRA:MAX:0.5:".int(1440/$interval).":800");
            RRDs::create(@args);
            my $e = RRDs::error();
            die "ERROR: Cannot create logfile: $e\n" if $e;
            debug('log', "Called: RRDs::create(@args)");
        } else {
            # update the minimum/maximum according to maxbytes/absmax
            # and (re)set the data-source-type to reflect cfg changes
            # cost: 1 read/write cycle, but update will reuse the buffered data
            my @args = ($rrd, '-a', "ds0:$absi", '-a', "ds1:$abso",
                       '-d', "ds0:$up_abs", '-d', "ds1:$up_abs");
            RRDs::tune(@args);
            my $e = RRDs::error();
            die "ERROR: Cannot tune logfile: $e\n" if $e;
            debug('log', "Called: RRDs::tune(@args)");
        }
        # update the rrd
        $inlast  = 'U' unless defined $inlast  && $inlast  =~ /\S/ && $inlast  ne '##UNDEF##';
        $outlast = 'U' unless defined $outlast && $outlast =~ /\S/ && $outlast ne '##UNDEF##';
        debug('log', "Calling: RRDs::update($rrd, '$time:$inlast:$outlast')");
        RRDs::update("$rrd", "$time:$inlast:$outlast");
        my $e = RRDs::error(); 
        warn "ERROR: Cannot update $rrd with '$time:$inlast:$outlast' $e\n" if ($e);

        # get the rrdtool-processed values back from rrdtool
        # for the threshold checks (we cannot use the fetched data)
        my $lasttime = RRDs::last($rrd);
        debug('log', "Called: RRDs::last()");
        $e = RRDs::error(); 
        warn "ERROR: Cannot 'last' $rrd: $e\n" if ($e);
        my $fetch = (RRDs::fetch($rrd,'AVERAGE','-s',$lasttime,'-e',$lasttime))[3];
        $e = RRDs::error(); 
        die "ERROR: Cannot 'fetch' $rrd: $e\n" if ($e);
        debug('log', "Called: RRDs::fetch($rrd,'AVERAGE','-s',$lasttime,'-e',$lasttime)");        
        my $in = $fetch->[0]->[0] ?  $fetch->[0]->[0] : "NaN" ;
        my $out = $fetch->[0]->[1] ? $fetch->[0]->[1] : "NaN" ;
        debug('log', "  got: $in/$out");
        $cuin{'d'}{$router} = $fetch->[0]->[0];
        $cuout{'d'}{$router} = $fetch->[0]->[1];
        # the html pages and the graphics are created at "call time" so that's it!
        # (the returned hashes are empty, it's just to minimize the changes to mrtg)
        if ($$rcfg{'options'}{'dorelpercent'}{$router}) {  
            return (\%maxin, \%maxout, \%maxpercent, \%avin, \%avout, \%avpercent, \%cuin, \%cuout, \%cupercent,  \%avmxin, \%avmxout);
        }
        return (\%maxin, \%maxout, \%avin, \%avout, \%cuin, \%cuout, \%avmxin, \%avmxout );

    }                 
    ########## rrdtool users have left here ###############

    ((($MRTG_lib::OS eq 'NT' || $MRTG_lib::OS eq 'OS2') && (-e "${FindBin::Bin}${MRTG_lib::SL}rateup.exe")) ||
     (-x "${FindBin::Bin}${MRTG_lib::SL}rateup")) || 
       die "ERROR: Can't Execute '${FindBin::Bin}${MRTG_lib::SL}rateup'\n";

    # rateup does not know about undef so we make inlast and outlast ready for rateup
    #warn "ERROR: inlast is undefined. Skipping $router\n" unless defined $inlast;
    #warn "ERROR: outlast is undefined. Skipping $router\n" unless defined $outlast;
    #return undef unless defined $inlast and defined $outlast;

    # set values to -1 to tell rateup about unknown values
    $inlast = -1 unless defined $inlast;
    $outlast = -1 unless defined $outlast;
    
    if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
        @exec = ("${FindBin::Bin}${MRTG_lib::SL}rateup", 
                 "$$cfg{'logdir'}$$rcfg{'directory'}{$router}","$router",
                 $time, $$rcfg{'options'}{'unknaszero'}{$router} ? '-z':'-Z',
                 "$up_abs"."p", $inlast, $outlast, $absmax,
                 "C", $$rcfg{'rgb1'}{$router},$$rcfg{'rgb2'}{$router},
                 $$rcfg{'rgb3'}{$router},$$rcfg{'rgb4'}{$router},
                 $$rcfg{'rgb5'}{$router});
    } else { 

        @exec = ("${FindBin::Bin}${MRTG_lib::SL}rateup", 
                 "$$cfg{'logdir'}$$rcfg{'directory'}{$router}","$router",
                 $time, $$rcfg{'options'}{'unknaszero'}{$router} ? '-z':'-Z',
                 "$up_abs", $inlast, $outlast, $absmax,
                 "c", $$rcfg{'rgb1'}{$router},$$rcfg{'rgb2'}{$router},
                 $$rcfg{'rgb3'}{$router},$$rcfg{'rgb4'}{$router});
    }

    # If this list grows anymore would it be more efficient to have an
    # array to look up the command line option to send to rateup rather
    # than have a long list to check?
    push (@exec, '-t') if defined $$rcfg{'options'}{'transparent'}{$router};
    push (@exec, '-0') if defined $$rcfg{'options'}{'withzeroes'}{$router};
    push (@exec, '-b') if defined $$rcfg{'options'}{'noborder'}{$router};
    push (@exec, '-a') if defined $$rcfg{'options'}{'noarrow'}{$router};
    push (@exec, '-i') if defined $$rcfg{'options'}{'noi'}{$router};
    push (@exec, '-o') if defined $$rcfg{'options'}{'noo'}{$router};
    push (@exec, '-d') if defined $$rcfg{'options'}{'pngdate'}{$router};
    push (@exec, '-p') if defined $$rcfg{'options'}{'printrouter'}{$router};

    my $maxx = $$rcfg{'xsize'}{$router}; 
    my $maxy = $$rcfg{'ysize'}{$router};
    my $xscale = $$rcfg{'xscale'}{$router}; 
    my $yscale = $$rcfg{'yscale'}{$router}; 
    my $growright = 0+($$rcfg{'options'}{'growright'}{$router} or 0);
    my $bits = 0+($$rcfg{'options'}{'bits'}{$router} or 0);
    my $integer = 0+($$rcfg{'options'}{'integer'}{$router} or 0);
    my $step = 5*60; 
    my $rop;
    my $ytics = $$rcfg{'ytics'}{$router};
    my $yticsf= $$rcfg{'yticsfactor'}{$router};

    if (not defined $$rcfg{'ylegend'}{$router}){
	if ($bits){
	    $$rcfg{'ylegend'}{$router} = &$LOC("Bits per minute")
		if defined $$rcfg{'options'}{'perminute'}{$router};
	    $$rcfg{'ylegend'}{$router} = &$LOC("Bits per hour")	    
		if defined $$rcfg{'options'}{'perhour'}{$router};
	} else {
	    $$rcfg{'ylegend'}{$router} = &$LOC("Bytes per minute")
		if defined $$rcfg{'options'}{'perminute'}{$router};
	    $$rcfg{'ylegend'}{$router} = &$LOC("Bytes per hour")	    
		if defined $$rcfg{'options'}{'perhour'}{$router};
	}
    }
	
    if ($$rcfg{'ylegend'}{$router}) {
        push (@exec, "l", "[$$rcfg{'ylegend'}{$router}]");
    }
    my $sign = ($$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /d/) ? 1 : -1;

    if ($$rcfg{'pngtitle'}{$router}) {
        push (@exec, "T", "[$$rcfg{'pngtitle'}{$router}]");
    }
  
    if ($$rcfg{'timezone'}{$router}) {
        push (@exec, "Z", "$$rcfg{'timezone'}{$router}");
    }
  
    if ($$rcfg{'kilo'}{$router}) {
        push (@exec, "k", $$rcfg{'kilo'}{$router});
    }
    if ($$rcfg{'kmg'}{$router}) { 
        push (@exec, "K", $$rcfg{'kmg'}{$router});
    }
    if ($$rcfg{'weekformat'}{$router}) {
        push (@exec, "W", $$rcfg{'weekformat'}{$router});
    }
    if (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~ /d/) {
        # VMS: should work for both now
        push (@exec, "i", "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-day.${main::GRAPHFMT}",
              $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, ,$xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf);
        @mxvls = ("d");
        push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-day.${main::GRAPHFMT}",
              $$cfg{'interval'} ? $$cfg{'interval'} : 5);
    }
    my $SAGE = (time - $main::STARTTIME) / 3600 / 24; # current script age 

    if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}") ||
         ((-M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}") + $SAGE  >= 0.5/24)) &&
        (not defined $$rcfg{'suppress'}{$router}  or $$rcfg{'suppress'}{$router} !~/w/)
       ) {
        $step=30*60;
        $sign = (defined $$rcfg{'unscaled'}{$router}  and $$rcfg{'unscaled'}{$router} =~ /w/) ? 1 : -1;
        push (@mxvls , "w");
        $rop =(defined $$rcfg{'withpeak'}{$router}  and $$rcfg{'withpeak'}{$router} =~ /w/) ? "p" : "i"; 
        push (@exec, $rop ,"$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}",
              $sign*$maxvi, $sign*$maxvo,  $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf);
        push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}", 30);
    }
  

    if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}") ||
         (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}") + $SAGE >= 2/24))  &&
        (not defined  $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~ /m/)) {
        $step=2*60*60;
        $sign = (defined $$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /m/) ? 1 : -1;
        push (@mxvls , "m");
        $rop =(defined $$rcfg{'withpeak'}{$router} and $$rcfg{'withpeak'}{$router} =~ /m/) ? "p" : "i"; 
        push (@exec, $rop ,"$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}",
              $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf);
        push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}", 120);
    }
  
    if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}") ||
         (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}") + $SAGE  >= 1)) &&
        (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~/y/)) {
        $step=24*60*60;
        $sign = (defined $$rcfg{'unscaled'}{$router}  and $$rcfg{'unscaled'}{$router} =~ /y/) ? 1 : -1;
        push (@mxvls , "y");
        $rop =(defined $$rcfg{'withpeak'}{$router}  and $$rcfg{'withpeak'}{$router} =~ /y/) ? "p" : "i"; 
        push (@exec, $rop, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}",
              $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf) ;
        push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}", 1440);
    }

    # VMS: this might work now ... or does VMS NOT know about pipes?
    # NT doesn't have fork() so an open(xxx,"-|") won't work
    # OS2 fork() have bug with socket handles. In RunAsDaemon mode it fail
    # after first loop (with "socket operation on non socket" message.

    if ($MRTG_lib::OS eq 'VMS' || $MRTG_lib::OS eq 'NT' || $MRTG_lib::OS eq 'OS2'){
        map { s/"/\\"/; $_ = '"'.$_.'"' if /\s/ } @exec;
        open (RATEUP, join (" ", @exec)."|") or
           do {
                warn "WARNING: rateup (".(join " ", @exec ).
                          ") did not work: $!\n";
                return;
           }
    } else {
        $! = undef;
        open (RATEUP,"-|") ||  
                do { exec @exec or
                     warn "WARNING: rateup (".(join " ", @exec ).
                          ") did not work: $!\n";
                };

    }
    
    debug('log', "Called @exec");

    imggen($$cfg{icondir} || $$cfg{imagedir});

    if (open (HTML,"<$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.$$rcfg{'extension'}{$router}")) {
        for ($i=0 ; $i<200 ; $i++) {
            last if eof(HTML);
            $_= <HTML>;
            if (/<!-- maxin ([dwmy]) (\d*)/) {
                $maxin{$1}{$router}=$2 || 0;
            }

            if (/<!-- maxout ([dwmy]) (\d*)/) {
                $maxout{$1}{$router}=$2 || 0;
            }

            if (/<!-- maxpercent ([dwmy]) (\d*)/) {
                $maxpercent{$1}{$router}=$2 || 0;
            }

            if (/<!-- avin ([dwmy]) (\d*)/) {
                $avin{$1}{$router}=$2 || 0;
            }

            if (/<!-- avout ([dwmy]) (\d*)/) {
                $avout{$1}{$router}=$2 || 0;
            }

            if (/<!-- avpercent ([dwmy]) (\d*)/) {
                $avpercent{$1}{$router}=$2 || 0;
            }

            if (/<!-- cuin ([dwmy]) (\d*)/) {
                $cuin{$1}{$router}=$2 || 0;
            }

            if (/<!-- cuout ([dwmy]) (\d+)/) {
                $cuout{$1}{$router}=$2 || 0;
            }
     
            if (/<!-- cupercent ([dwmy]) (\d+)/) {
                $cupercent{$1}{$router}=$2 || 0;
            }
            if (/<!-- avmxin ([dwmy]) (\d*)/) {
                $avmxin{$1}{$router}=$2 || 0;
            }

            if (/<!-- avmxout ([dwmy]) (\d*)/) {
                $avmxout{$1}{$router}=$2 || 0;
            }
        }
        close HTML;
    }
  
    foreach $period (@mxvls) {
        $res = <RATEUP>; 
        if (not defined $res and eof(RATEUP)){
            warn "ERROR: Skipping webupdates because rateup did not return anything sensible\n";
            close RATEUP;
            rateupcheck $router;
            return;
        };
        chomp $res;
        $maxin{$period}{$router}=sprintf("%.0f",$res || 0);
        chomp($res = <RATEUP>); 
        $maxout{$period}{$router}=sprintf("%.0f",$res || 0);

        if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
            chomp($res = <RATEUP>); 
            $maxpercent{$period}{$router}=sprintf("%.0f",$res || 0);
        }

        chomp($res = <RATEUP>); 
        $avin{$period}{$router}=sprintf("%.0f",$res || 0);
        chomp($res = <RATEUP>); 
        $avout{$period}{$router}=sprintf("%.0f",$res || 0);

        if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
            chomp($res = <RATEUP>); 
            $avpercent{$period}{$router}=sprintf("%.0f",$res || 0);
        }

        chomp($res = <RATEUP>); 
        $cuin{$period}{$router}=sprintf("%.0f",$res || 0);
        chomp($res = <RATEUP>); 
        $cuout{$period}{$router}=sprintf("%.0f",$res || 0);

        if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
            chomp($res = <RATEUP>); 
            $cupercent{$period}{$router}=sprintf("%.0f",$res || 0);
        }

        chomp($res = <RATEUP>);
        debug('avmx',"avmxin  $res");        
        $avmxin{$period}{$router}=sprintf("%.0f",$res || 0);     
        chomp($res = <RATEUP>);
        debug('avmx',"avmxout $res");        
        $avmxout{$period}{$router}=sprintf("%.0f",$res || 0);     
        
    }
    close(RATEUP);
    rateupcheck $router;
    if ( defined $$cfg{'writeexpires'}  and $$cfg{'writeexpires'} =~ /^y/i ) {
        my($fil,$exp);
        while ( $fil = shift(@metas) ) {
            $exp = &expistr(shift(@metas));
            open(META, ">$fil.meta");
            print META "Expires: $exp\n";
            close(META);
        }
    }

    if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
        return (\%maxin, \%maxout, \%maxpercent, \%avin, \%avout, \%avpercent, \%cuin, \%cuout, \%cupercent, \%avmxin, \%avmxout);
    } else {
        return (\%maxin, \%maxout, \%avin, \%avout, \%cuin, \%cuout, \%avmxin, \%avmxout);
    }
}

#format 10*$kilo to 10 kB/s
sub fmi {
    my($number, $maxbytes, $router, @foo) = @_;
    return "?????" unless defined $number;
    my($rcfg,$LOC)=@foo;
    my @short;
    my $mul = 1;
    if ($$rcfg{'kmg'}{$router}) {
        my($i);
        if ($$rcfg{'options'}{'bits'}{$router}) {
            @short = ();
            foreach $i (split(/,/, $$rcfg{'kmg'}{$router})) {
                if ($$rcfg{'options'}{'perminute'}{$router}) {
                    $short[$#short+1] = "$i".&$LOC("b/min");
                } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
                    $short[$#short+1] = "$i".&$LOC("b/h");
                } else {
                    $short[$#short+1] = "$i".&$LOC("b/s");
                }
            }
            $mul= 8;
        } else {
            @short = ();
            foreach $i (split(/,/, $$rcfg{'kmg'}{$router})) {
                if ($$rcfg{'options'}{'perminute'}{$router}) {
                    $short[$#short+1] = "$i".&$LOC ("B/min");
                } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
                    $short[$#short+1] = "$i".&$LOC("B/h");
                } else {
                    $short[$#short+1] = "$i".&$LOC("B/s");
                }
            }
            $mul= 1;
        }
        if (defined $$rcfg{'shortlegend'}{$router}) {
            @short = ();
            foreach $i (split(/,/, $$rcfg{'kmg'}{$router})) {
                $short[$#short+1] = "$i"." $$rcfg{'shortlegend'}{$router}";
            }
        }
    } else {
        if (defined $$rcfg{'options'}{'bits'}{$router}) {
            if ($$rcfg{'options'}{'perminute'}{$router}) {
                @short = (&$LOC("b/min"),&$LOC("kb/min"),&$LOC("Mb/min"),&$LOC("Gb/min"));
            } elsif (defined $$rcfg{'options'}{'perhour'}{$router}) {
                @short = (&$LOC("b/h"),&$LOC("kb/h"),&$LOC("Mb/h"),&$LOC("Gb/h"));
            } else {
                @short = (&$LOC("b/s"),&$LOC("kb/s"),&$LOC("Mb/s"),&$LOC("Gb/s"));
            }
            $mul= 8;
        } else {
            if ($$rcfg{'options'}{'perminute'}{$router}) {
                @short = (&$LOC("B/min"),&$LOC("kB/min"),&$LOC("MB/min"),&$LOC("GB/min"));
            } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
                @short = (&$LOC("B/h"),&$LOC("kB/h"),&$LOC("MB/h"),&$LOC("GB/h"));
            } else {
                @short = (&$LOC("B/s"),&$LOC("kB/s"),&$LOC("MB/s"),&$LOC("GB/s"));
            }
            $mul= 1;
        }
        if ($$rcfg{'shortlegend'}{$router}) {
            @short = ("$$rcfg{'shortlegend'}{$router}",
                      "k$$rcfg{'shortlegend'}{$router}",
                      "M$$rcfg{'shortlegend'}{$router}",
                      "G$$rcfg{'shortlegend'}{$router}");
        }
    }
    my $digits=length("".$number*$mul);
    my $divm=0;
    #
    #  while ($digits-$divm*3 > 4) { $divm++; }
    #  my $divnum = $number*$mul/10**($divm*3);
    my $divnum=$number*$mul*$$rcfg{'factor'}{$router};
    #  while ($divnum/$$rcfg{'kilo'}{$router} >= 10*$$rcfg{'kilo'}{$router} && $divnum<$#short) {
    while ($divnum >= 10*$$rcfg{'kilo'}{$router} && $divm<$#short) {
        $divm++;
        $divnum /= $$rcfg{'kilo'}{$router};
    }
    my $perc;
    if ($number == 0 || $maxbytes == 0) {
        $perc = 0;
    } else {
        $perc = 100/$maxbytes*$number;
    }
    if (defined $$rcfg{'options'}{'integer'}{$router}) {
        if ($$rcfg{'options'}{'nopercent'}{$router}) {
            return sprintf("%.0f %s",$divnum,$short[$divm]);
        } else {
            return sprintf("%.0f %s (%2.1f%%)",$divnum,$short[$divm],$perc);
        }
    } else {
        if (defined $$rcfg{'options'}{'nopercent'}{$router}) {
            return sprintf("%.1f %s",$divnum,$short[$divm]); # Added: FvW
        } else {
            return sprintf("%.1f %s (%2.1f%%)",$divnum,$short[$divm],$perc);
        }
        return sprintf("%.1f %s (%2.1f%%)",$divnum,$short[$divm],$perc);
    }
}


sub writehtml {
    my($router, $cfg, $rcfg, $maxin, $maxout, $maxpercent,
       $avin, $avout, $avmxin, $avmxout, $avpercent, 
       $cuin, $cuout, $cupercent, $uptime, $name, $LOC) = @_;
  
    my($VERSION,$Today,$peri);
  
    my($persec);

    if (defined $$rcfg{'options'}{'bits'}{$router}) {
        $persec = &$LOC("Bits");
    } else {
        $persec = &$LOC("Bytes");
    }

    #  Work out the Colour legend
    my($leg1, $leg2, $leg3, $leg4, $leg5);
    if ($$rcfg{'legend1'}{$router}) {
        $leg1 = $$rcfg{'legend1'}{$router};
    } else {
        if ($$rcfg{'options'}{'perminute'}{$router}) {
            $leg1=&$LOC("Incoming Traffic in $persec per Minute");
        } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
            $leg1=&$LOC("Incoming Traffic in $persec per Hour");
        } else {
            $leg1=&$LOC("Incoming Traffic in $persec per Second");
        }
    }
    if ($$rcfg{'legend2'}{$router}) {
        $leg2 = $$rcfg{'legend2'}{$router};
    } else {
        if ($$rcfg{'options'}{'perminute'}{$router}) {
            $leg2=&$LOC("Outgoing Traffic in $persec per Minute");
        } elsif ($$rcfg{'options'}{'perhour'}{$router}) {
            $leg2=&$LOC("Outgoing Traffic in $persec per Hour");
        } else {
            $leg2=&$LOC("Outgoing Traffic in $persec per Second");
        }	
    }
    if ($$rcfg{'legend3'}{$router}) {
        $leg3 = $$rcfg{'legend3'}{$router};
    } else {
        $leg3 = &$LOC("Maximal 5 Minute Incoming Traffic");
    }
    if ($$rcfg{'legend4'}{$router}) {
        $leg4 = $$rcfg{'legend4'}{$router};
    } else {
        $leg4 = &$LOC("Maximal 5 Minute Outgoing Traffic");
    }
    if ($$rcfg{'legend5'}{$router}) {
        $leg5 = $$rcfg{'legend5'}{$router};
    } else {
        $leg5 = "(($leg1)/($leg2))*100";
    }
    # Translate the color names
    $$rcfg{'col1'}{$router}=&$LOC($$rcfg{'col1'}{$router});
    $$rcfg{'col2'}{$router}=&$LOC($$rcfg{'col2'}{$router});
    $$rcfg{'col3'}{$router}=&$LOC($$rcfg{'col3'}{$router});
    $$rcfg{'col4'}{$router}=&$LOC($$rcfg{'col4'}{$router});
    $$rcfg{'col5'}{$router}=&$LOC($$rcfg{'col5'}{$router});

    my $dirrel = "../" x ($$rcfg{'directory_web'}{$router} =~ tr|/|/|);

    $Today=&$LOC(datestr(time));
    $VERSION = "2.10.11";
    open (HTML,">$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.$$rcfg{'extension'}{$router}") || 
      do { warn ("WARNING: Writing $router.$$rcfg{'extension'}{$router}: $!");
      	   next };
    print HTML "<!-- Begin Head -->\n";
    print HTML '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">' . "\n";
    print HTML "<HTML>\n";
    my $interval =$$cfg{'interval'} ? $$cfg{'interval'} : 5;
    my $expiration = &expistr($interval);
    my $refresh =  $$cfg{'refresh'} ? $$cfg{'refresh'} : 300;
    my $namestring = &$LOC("the device");  
    print HTML <<"TEXT";    
<HEAD>
<TITLE>$$rcfg{'title'}{$router}</TITLE>
<META HTTP-EQUIV="Refresh" CONTENT="$refresh">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" content="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="$expiration">
<META HTTP-EQUIV="Generator" CONTENT="MRTG $VERSION">
<META HTTP-EQUIV="Date" CONTENT="$expiration">

TEXT

    print HTML 
      '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; '.&$LOC('charset=iso-8859-1')."\">\n";

    foreach $peri (qw(d w m y)) {
        print HTML <<"TEXT";
<!-- maxin $peri $$maxin{$peri}{$router} -->
<!-- maxout $peri $$maxout{$peri}{$router} -->
TEXT
        if ($$rcfg{'options'}{'dorelpercent'}{$router} and defined $$maxpercent{$peri}{$router}) {
            print HTML <<"TEXT";
<!-- maxpercent $peri $$maxpercent{$peri}{$router} -->
TEXT
        }
        print HTML <<"TEXT";
<!-- avin $peri $$avin{$peri}{$router} -->
<!-- avout $peri $$avout{$peri}{$router} -->
TEXT
        if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
            print HTML <<"TEXT";
<!-- avpercent $peri $$avpercent{$peri}{$router} -->
TEXT
        }
        
        print HTML "<!-- cuin $peri $$cuin{$peri}{$router} -->"
           if defined $$cuin{$peri}{$router};
        print HTML "<!-- cuout $peri $$cuout{$peri}{$router} -->"
           if defined $$cuout{$peri}{$router};

        if ($$rcfg{'options'}{'dorelpercent'}{$router} and $$cupercent{$peri}{$router} ) {
            print HTML <<"TEXT";
<!-- cupercent $peri $$cupercent{$peri}{$router} -->
TEXT
        }
        print HTML <<"TEXT" if  $$avmxin{$peri}{$router} and $$avmxout{$peri}{$router};
<!-- avmxin $peri $$avmxin{$peri}{$router} -->
<!-- avmxout $peri $$avmxout{$peri}{$router} -->
TEXT

    }

    $namestring = "<B>'$name'</B>" if $name;

    # allow for \n in addhead
    defined $$rcfg{addhead}{$router} or $$rcfg{addhead}{$router} = "";
    defined $$rcfg{backgc}{$router} or $$rcfg{backgc}{$router} = "";
    defined $$rcfg{pagetop}{$router} or $$rcfg{pagetop}{$router} = "";
    if (defined $$rcfg{bodytag}{$router}) {
        if (not defined $$rcfg{bodytag}{$router} or $$rcfg{bodytag}{$router} !~ /<body/i) {
                $$rcfg{bodytag}{$router} = "<BODY $$rcfg{bodytag}{$router}>";
        }
    } else {
        $$rcfg{bodytag}{$router} = "<BODY $$rcfg{backgc}{$router}>";
    }

    $$rcfg{addhead}{$router} =~ s/\\n/\n/g if defined $$rcfg{addhead}{$router};

    print HTML <<"TEXT";    
$$rcfg{'addhead'}{$router}
</HEAD>
$$rcfg{bodytag}{$router}
$$rcfg{'pagetop'}{$router}
<HR>
TEXT

    if (defined $$rcfg{'timezone'}{$router}){    
    print HTML     
      &$LOC("The statistics were last updated <B>$Today $$rcfg{'timezone'}{$router}</B>");
    } else {
    print HTML     
      &$LOC("The statistics were last updated <B>$Today</B>");
    }
    if ($uptime && ! $$rcfg{options}{noinfo}{$router}) {
        print HTML
          ",<BR>\n".
        &$LOC("at which time $namestring had been up for <B>$uptime</B>.").
        "\n<!-- End Head -->";
    }

    my %sample= ('d' => "`Daily' Graph (".$interval.' Minute',
                 'w' => "`Weekly' Graph (30 Minute",
                 'm' => "`Monthly' Graph (2 Hour",
                 'y' => "`Yearly' Graph (1 Day");
  
    my %full = ('d' => 'day',
                'w' => 'week',
                'm' => 'month',
                'y' => 'year');
  
    $$rcfg{'rgb1'}{$router} = "" unless defined $$rcfg{'rgb1'}{$router};
    $$rcfg{'rgb2'}{$router} = "" unless defined $$rcfg{'rgb2'}{$router};
    $$rcfg{'rgb3'}{$router} = "" unless defined $$rcfg{'rgb3'}{$router};
    $$rcfg{'rgb4'}{$router} = "" unless defined $$rcfg{'rgb4'}{$router};
    $$rcfg{'rgb5'}{$router} = "" unless defined $$rcfg{'rgb5'}{$router};
    $$rcfg{'rgb6'}{$router} = "" unless defined $$rcfg{'rgb6'}{$router};

    my $InCo;
    if (!(defined $$rcfg{'options'}{'noi'}{$router})) {
    if (exists $$rcfg{'legendi'}{$router}) {
        if ($$rcfg{'legendi'}{$router} ne "") {
            $InCo="<FONT COLOR=\"$$rcfg{'rgb1'}{$router}\">".
              "$$rcfg{'legendi'}{$router}&nbsp;</FONT>";
        }
    } else {
        $InCo="<FONT COLOR=\"$$rcfg{'rgb1'}{$router}\">".
          &$LOC("&nbsp;In:</FONT>");
    }
    }
    
    my $OutCo;
    if (!(defined $$rcfg{'options'}{'noo'}{$router})) {
    if (exists $$rcfg{'legendo'}{$router}) {
        if ($$rcfg{'legendo'}{$router} ne "") {
            $OutCo="<FONT COLOR=\"$$rcfg{'rgb2'}{$router}\">".
              "$$rcfg{'legendo'}{$router}&nbsp;</FONT>";
        }
    } else {
        $OutCo="<FONT COLOR=\"$$rcfg{'rgb2'}{$router}\">".
          &$LOC("&nbsp;Out:</FONT>");
    }
    }
    my $PercentCo;
    if (defined $$rcfg{'legend5'}{$router}) {
        if ($$rcfg{'legend5'}{$router} ne "") {
            $PercentCo="<FONT COLOR=\"$$rcfg{'rgb5'}{$router}\">".
              "$$rcfg{'legend5'}{$router}</FONT>";
        }
    } else {
        $PercentCo="<FONT COLOR=\"$$rcfg{'rgb5'}{$router}\">".
          &$LOC("Percentage&nbsp;</FONT>");
    }
  
    foreach $peri (qw(d w m y)) {
        next if defined $$rcfg{'suppress'}{$router} and $$rcfg{'suppress'}{$router} =~/$peri/;
        my $gifw;
        if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
            $gifw=sprintf("%.0f",($$rcfg{'xsize'}{$router}*$$rcfg{'xscale'}{$router}+
                                  +100+30) *$$rcfg{'xzoom'}{$router});
        } else {
            $gifw=sprintf("%.0f",($$rcfg{'xsize'}{$router}*$$rcfg{'xscale'}{$router}
                                  +100) *$$rcfg{'xzoom'}{$router});
        }
        my $gifh=sprintf("%.0f",($$rcfg{'ysize'}{$router}*$$rcfg{'yscale'}{$router}+35)
                         *$$rcfg{'yzoom'}{$router});
                 
        # take the image directory away from the html directory to give us relative links


        print HTML "
<!-- Begin $sample{$peri} -->
<HR>
".&$LOC("<B>$sample{$peri}").&$LOC(" Average)</B><BR>")."
<IMG VSPACE=10 WIDTH=$gifw HEIGHT=$gifh ALIGN=TOP 
     SRC=\"$dirrel$$cfg{imagehtml}$$rcfg{directory_web}{$router}$router-$full{$peri}.${main::GRAPHFMT}\" ALT=\"$full{$peri}\">
 <TABLE CELLPADDING=0 CELLSPACING=0>
";
        my(@foo)=($rcfg,$LOC);
        print HTML "<TR>
  ".&$LOC("<TD ALIGN=right><SMALL>Max $InCo</SMALL></TD>")."
  <TD ALIGN=left><SMALL>".&fmi($$maxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)."
   </SMALL></TD>
  <TD WIDTH=5></TD>
  ".&$LOC("<TD ALIGN=right><SMALL>Average $InCo</SMALL></TD>")."
  <TD ALIGN=left><SMALL>".&fmi($$avin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)."
  </SMALL></TD>
  <TD WIDTH=5></TD>
  ".&$LOC("<TD ALIGN=right><SMALL>Current $InCo</SMALL></TD>")."
  <TD ALIGN=left><SMALL>".&fmi($$cuin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)."
  </SMALL></TD>
 </TR>
" if $InCo;
        print HTML "
 <TR>
  ".&$LOC("<TD ALIGN=right><SMALL>Max $OutCo</SMALL></TD>")."
  <TD ALIGN=left><SMALL>".&fmi($$maxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)."
  </SMALL></TD>
  <TD WIDTH=5></TD>
  ".&$LOC("<TD ALIGN=right><SMALL>Average $OutCo</SMALL></TD>")."
  <TD ALIGN=left><SMALL>".&fmi($$avout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)."
  </SMALL></TD>
  <TD WIDTH=5></TD>
  ".&$LOC("<TD ALIGN=right><SMALL>Current $OutCo</SMALL></TD>")."
  <TD ALIGN=left><SMALL>".&fmi($$cuout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)."
 </SMALL></TD>
 </TR> " if $OutCo;
        print HTML "
 <TR>
  ".&$LOC("<TD ALIGN=right><SMALL>Max $PercentCo</SMALL></TD>")."
  <TD ALIGN=left><SMALL>".sprintf("%0.1f %%",($$maxpercent{$peri}{$router} || 0))."
  </SMALL></TD>
  <TD WIDTH=5></TD>
  ".&$LOC("<TD ALIGN=right><SMALL>Average $PercentCo</SMALL></TD>")."
  <TD ALIGN=left><SMALL>".sprintf("%0.1f %%",($$avpercent{$peri}{$router} || 0 ))."
  </SMALL></TD>
  <TD WIDTH=5></TD>
  ".&$LOC("<TD ALIGN=right><SMALL>Current $PercentCo</SMALL></TD>")."
  <TD ALIGN=left><SMALL>".sprintf("%0.1f %%",($$cupercent{$peri}{$router} || 0 ))."
 </SMALL></TD>
 </TR> " if ($$rcfg{'options'}{'dorelpercent'}{$router} && $PercentCo);

print HTML '<TD colspan="8"><small>',&$LOC("Average max 5 min values for $sample{$peri} interval):&nbsp;&nbsp;"),
  "$InCo ", &fmi($$avmxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo), "/",
  "$OutCo ", &fmi($$avmxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo),
  "</small></TD></TR>" if ($$rcfg{'options'}{'avgpeak'}{$router} && $InCo && $OutCo);
  
print HTML "
</TABLE>
<!-- End $sample{$peri} -->\n";

}

    if (!(defined $$rcfg{'options'}{'nolegend'}{$router})) {
    print HTML "
<!-- Begin Legend -->
  <HR><BR>
  <TABLE WIDTH=500 BORDER=0 CELLPADDING=4 CELLSPACING=0>";
    print HTML "
   <TR><TD ALIGN=RIGHT><FONT SIZE=-1 COLOR=\"$$rcfg{'rgb1'}{$router}\">
      <B>$$rcfg{'col1'}{$router} ###</B></FONT></TD>
      <TD><FONT SIZE=-1>$leg1</FONT></TD></TR> " if $InCo;
    print HTML "
   <TR><TD ALIGN=RIGHT><FONT SIZE=-1 COLOR=\"$$rcfg{'rgb2'}{$router}\">
      <B>$$rcfg{'col2'}{$router} ###</B></FONT></TD>
      <TD><FONT SIZE=-1>$leg2</FONT></TD></TR> " if $OutCo;
  
    if ($$rcfg{'withpeak'}{$router}) {
        print HTML "
   <TR><TD ALIGN=RIGHT><FONT SIZE=-1 COLOR=\"$$rcfg{'rgb3'}{$router}\">
                        <B>$$rcfg{'col3'}{$router}###</B></FONT></TD>
       <TD><FONT SIZE=-1>$leg3</FONT></TD></TR> " if $InCo;
        print HTML "
   <TR><TD ALIGN=RIGHT><FONT SIZE=-1 COLOR=\"$$rcfg{'rgb4'}{$router}\">
                        <B>$$rcfg{'col4'}{$router}###</B></FONT></TD>
       <TD><FONT SIZE=-1>$leg4</FONT></TD></TR> "if $OutCo;
    }

    if ($$rcfg{'options'}{'dorelpercent'}{$router}) {
        print HTML "
   <TR><TD ALIGN=RIGHT><FONT SIZE=-1 COLOR=\"$$rcfg{'rgb5'}{$router}\">
                        <B>$$rcfg{'col5'}{$router}###</B></FONT></TD>
       <TD><FONT SIZE=-1>$leg5</FONT></TD></TR> ";
    }

        print HTML "
  </TABLE>
<!-- End Legend -->";
    }

    if (!(defined $$rcfg{'options'}{'nobanner'}{$router})) {
    my $gifPath;

    if (defined $$cfg{icondir}) {
        $gifPath = $$cfg{icondir};
        #lets make sure there is a trailing path separator
        $gifPath =~ s|/*$|/|;
    } else {
	$gifPath = "$dirrel$$cfg{imagehtml}";
    }

    print HTML<<TEXT;
<!-- Begin MRTG Block -->
<BR><HR><BR>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0>
  <TR>
    <TD WIDTH=63><A
    HREF="http://people.ee.ethz.ch/~oetiker/webtools/mrtg/"><IMG
    BORDER=0 SRC="${gifPath}mrtg-l.${main::GRAPHFMT}" WIDTH=63 HEIGHT=25 ALT="MRTG"></A></TD>
    <TD WIDTH=25><A
    HREF="http://people.ee.ethz.ch/~oetiker/webtools/mrtg/"><IMG
    BORDER=0 SRC="${gifPath}mrtg-m.${main::GRAPHFMT}" WIDTH=25 HEIGHT=25 ALT=""></A></TD>
    <TD WIDTH=388><A
    HREF="http://people.ee.ethz.ch/~oetiker/webtools/mrtg/"><IMG
    BORDER=0 SRC="${gifPath}mrtg-r.${main::GRAPHFMT}" WIDTH=388 HEIGHT=25
    ALT="Multi Router Traffic Grapher"></A></TD>
  </TR>
</TABLE>
<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0>
  <TR VALIGN=top>
  <TD WIDTH=88 ALIGN=RIGHT><FONT FACE="Arial,Helvetica" SIZE=2>$VERSION</FONT></TD>
  <TD WIDTH=388 ALIGN=RIGHT><FONT FACE="Arial,Helvetica" SIZE=2>
  <A HREF="http://people.ee.ethz.ch/~oetiker/">Tobias Oetiker</A>
  <A HREF="mailto:oetiker\@ee.ethz.ch">&lt;oetiker\@ee.ethz.ch&gt;</A> 
TEXT

    print HTML &$LOC("and");
    print HTML '&nbsp;<A HREF="http://www.bungi.com/">Dave&nbsp;Rand</A>&nbsp;<A HREF="mailto:dlr@bungi.com">&lt;dlr@bungi.com&gt;</A></FONT>';
    print HTML<<TEXT;
  </TD>
</TR>
</TABLE>
TEXT

    # We don't need this any more.
    undef $gifPath;

    if ($MRTG_lib::OS eq 'VMS') {
        print HTML 
          "<div> 
".&$LOC("Ported to OpenVMS Alpha by").".
  <NOBR><A HREF=\"http://www.cerberus.ch/\">Werner Berger</A>
  <A href=\"mailto:werner.berger\@cch.cerberus.ch\">
  &lt;werner.berger\@cch.cerberus.ch&gt;</A></NOBR></div>
";

    }
# There is not realy any significant portion of code from Studard left and
# none of his addresses work anymore. -- Tobi  2001-06-04
#    if ($MRTG_lib::OS eq 'NT') {
#        print HTML 
#          "<div>
#  ".&$LOC("Ported to WindowsNT by")."
#  <NOBR><small><A HREF=\"http://www.testlab.orst.edu/\">Stuart Schneider</A>
#  <A HREF=\"mailto:schneis\@testlab.orst.edu\">
#  &lt;schneis\@testlab.orst.edu&gt;</A></NOBR></div>
# ";
#    }
    if ( 
        $$cfg{'language'} && 
        defined($lang2tran::LOCALE{"\L$$cfg{'language'}\E"}) &&
        ($LOC != $lang2tran::LOCALE{"default"})) 
    {
        if (defined($credits::LOCALE{"\L$$cfg{'language'}\E"})) {
            print HTML "<div><small>".$credits::LOCALE{"\L$$cfg{'language'}\E"}."</small></div>";
        } else {
            print HTML "<div><small>".$credits::LOCALE{'default'}."<small></div>";
        }
        ;
    }

    print HTML "<!-- End MRTG Block -->\n";
    }

    print HTML $$rcfg{'pagefoot'}{$router} if defined $$rcfg{'pagefoot'}{$router};
    print HTML <<TEXT;
</BODY>
</HTML>

TEXT
    close HTML;

    if (defined $$cfg{'writeexpires'}  and $$cfg{'writeexpires'} =~ /^y/i) {
        open(HTMLG, ">$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.".
	     "$$rcfg{'extension'}{$router}.meta") ||
	       do {
		   warn "WARNING: Writing $$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.".
		     "$$rcfg{'extension'}{$router}.meta: $!\n";
		   next
	       };

        print HTMLG "Expires: $expiration\n";
        close(HTMLG);
    }
}


sub printusage {
    print <<USAGEDESC;
Usage: mrtg <config-file>

mrtg-2.10.11 is the Multi Router Traffic Grapher.

If you want to know more about this tool, you might want
to read the docs. They came together with mrtg! 

Home: http://people.ee.ethz.ch/~oetiker/webtools/mrtg/

USAGEDESC
    exit(1);
}


sub lockit {
    my ($lockfile,$templock) = @_;
    if ($MRTG_lib::OS eq 'VMS' || $MRTG_lib::OS eq 'NT'  || $MRTG_lib::OS eq 'OS2') {
        # too sad NT and VMS can't do links we'll do the diletants lock
        if (-e $lockfile && not unlink $lockfile) {
            my($lockage) = time()-(stat($lockfile))[9];
            die "ERROR: I guess another mrtg is running. A lockfile ($lockfile)\n".
                 "       aged $lockage seconds is hanging around and I can't remove\n".
                 "       it because another process is still using it.";
        }
      
        open (LOCK, ">$lockfile") or 
          die "ERROR: Creating lockfile $lockfile: $!\n";
        print LOCK "$$\n";
        close LOCK;
        open (LOCK, "<$lockfile") or 
          die "ERROR: Reading lockfile $lockfile for owner check: $!\n";
        my($read)=<LOCK>;
        chomp($read);
        die "ERROR: Someone else just got the lockfile $lockfile\n" 
          unless  $$ == $read;
    } else {
        # now, lets do it the UNIX way ... Daves work ...
        open(LOCK,">$templock") || die "ERROR: Creating templock $templock: $!";
        $main::Cleanfile = $templock;
        if (!link($templock,$lockfile)) { # Lock file exists - deal with it.
            my($nlink,$lockage) = (stat($lockfile))[3,9]; 
            $lockage = time() - $lockage;
            if ($nlink < 2 || $lockage > 30*60) { #lockfile is alone and old
                unlink($lockfile) 
                  || do{ unlink $templock; 
                         die "ERROR: Can't unlink stale lockfile ($lockfile). Permissions?\n"};
                link($templock,$lockfile) 
                  || do{ unlink $templock; 
                         die "ERROR: Can't create lockfile ($lockfile).\n".
                           "Permission problem or another mrtg locking succesfully?\n"};
            } else {
                unlink $templock;
                die "ERROR: It looks as if you are running two copies of mrtg in parallel on\n".
                    "       the same config file. There is a lockfile ($lockfile) and it is\n".
                    "       is only $lockage seconds old ... Check your crontab.\n".
                    "       (/etc/crontab and /var/spool/cron/root) \n"
                        if $lockage < 4;
      
                die  "ERROR: I guess another mrtg is running. A lockfile ($lockfile) aged\n".
                     "$lockage seconds is hanging around. If you are sure that no other mrtg\n".
                     "is running you can remove the lockfile\n";
          
            }
        
        }
    }
}

sub threshcheck {
    # threshold checking by Tom Muggli
    # ... fsck'd up but fixed by Juha Laine
    my ($cfg,$rcfg,$cfgfile,$router,$cuin,$cuout) = @_;
    my $threshfile;
    my %threshval;

    $threshval{i} = $$cuin{'d'}{$router};
    $threshval{o} = $$cuout{'d'}{$router};

    # are we going to keep state ?
    if (defined $$cfg{'threshdir'}){
        ensureSL(\$$cfg{'threshdir'});
        $threshfile = $$cfg{'threshdir'}.(split /\Q$MRTG_lib::SL\E/, $cfgfile)[-1].".$router";
    }

    # setup environment for external scripts
    if (defined $$rcfg{'threshdesc'}{$router}) {
        $ENV{THRESH_DESC}=$$rcfg{'threshdesc'}{$router};
    } else {
        delete $ENV{THRESH_DESC};
    }

    foreach my $dir (qw(i o)){ # in and out
        foreach my $bound (qw(min max)){
	    # skip unless a threshold is defined for this "$router"
            next unless defined $$rcfg{'thresh'.$bound.$dir}{$router};
            # we can not deal with unknown data here
            warn "WARNING: can't do threshold checking for target $router($dir) -- No valid current data\n"
                unless defined $threshval{$dir};
 	    my $value = $threshval{$dir};
            next unless defined $value;
            my $boundval = $$rcfg{'thresh'.$bound.$dir}{$router};
            next unless defined $boundval;
            if ($boundval =~ s/%$//) { # defined in % of maxbytes
                # 2 decimals in %
                    $value = int($value / $$rcfg{maxbytes}{$router} * 10000.0) / 100; # the new code
            }

            if (($bound eq 'min' and $boundval > $value) or
                ($bound eq 'max' and $boundval < $value)) {

		# threshold was broken...
                my @exec = ( $$rcfg{'threshprog'.$dir}{$router}, $router,
			  $$rcfg{'thresh'.$bound.$dir}{$router}, $threshval{$dir},($$rcfg{'threshdesc'}{$router}||"No Description"), $value);

		# Check if we use the status file or not...
		if ( defined $threshfile ) {
		    if ( not -e $threshfile.".".$bound.uc($dir) ) {
			# Create a file to indicate a threshold problem for the time after the problem
			open THRESHTOUCH, ">".$threshfile.".".$bound.uc($dir)
			    or warn "WARNING: Creating $threshfile.".$bound.uc($dir).": $!\n";
			close THRESHTOUCH;
                        debug('base',"run treshprog$dir: ".(join ",",@exec));
                  	system @exec if defined $$rcfg{'threshprog'.$dir}{$router};
		    }
		} else {
		    # no threshold dir so run on every 'break'
		    system @exec if defined $$rcfg{'threshprog'.$dir}{$router};
		}
	    } else {

		# no threshold broken ...
		my @exec = ( $$rcfg{'threshprogok'.$dir}{$router}, $router,
			  $$rcfg{'thresh'.$bound.$dir}{$router}, $threshval{$dir});

		# Check if we use the status file or not...
		if ( defined $threshfile ) {
		    if ( -e $threshfile.".".$bound.uc($dir) ){
			unlink "$threshfile.".$bound.uc($dir);
			system  @exec if defined $$rcfg{'threshprogok'.$dir}{$router};
		    }
		}
	    }

        } # foreach my $bound ...
    } # foreach my $dir
}

sub getexternal ($) {
    my $command = shift;
    my $in=undef;
    my $out=undef;
    my $uptime="unknown";
    my $name="unknown";

    open (EXTERNAL , $command."|")
	|| warn "WARNING: Running '$command': $!\n";

    warn "WARNING: Could not get any data from external command ".
	"'".$command.
	    "'\nMaybe the external command did not even start. ($!)\n\n" if eof EXTERNAL;

    chomp( $in=<EXTERNAL>) unless eof EXTERNAL;
    chomp( $out=<EXTERNAL>) unless eof EXTERNAL;
    chomp( $uptime=<EXTERNAL>) unless eof EXTERNAL;
    chomp( $name=<EXTERNAL>) unless eof EXTERNAL;

    close EXTERNAL;

    # strip returned date
    $uptime  =~ s/^\s*(.*?)\s*/$1/;
    $name  =~ s/^\s*(.*?)\s*/$1/;

    # do we have numbers in the external programs answer ?
    if ( not defined $in ) {
	warn "WARNING: Problem with External get '$command':\n".
	    "   Expected a Number for 'in' but nothing'\n\n";        
    } elsif ( $in eq 'UNKNOWN' ) {
        $in = undef;
    } elsif ( $in !~ /([-+]?\d+(.\d+)?)/ ) {
	warn "WARNING: Problem with External get '$command':\n".
	    "   Expected a Number for 'in' but got '$in'\n\n";
	$in = undef;
    } else {
        $in = $1;
    }

    if ( not defined $out ) {
	warn "WARNING: Problem with External get '$command':\n".
	    "   Expected a Number for 'out' but nothing'\n\n";
    } elsif ( $out eq 'UNKNOWN' ) {
        $out = undef;
    } elsif ( $out !~ /([-+]?\d+(.\d+)?)/ ) {
	warn "WARNING: Problem with Externale get '$command':\n".
	    "   Expected a Number for 'out' but got '$out'\n\n";
	$out = undef;
    } else {
        $out = $1;
    }
    debug('snpo',"External result:$in out:$out uptime:$uptime name:$name");
    return ($in,$out,time,$uptime,$name);
}

sub getsnmparg ($$$){
    my $confcache = shift;
    my $target = shift;
    my $cfg = shift;
    my $retry = 0;

    my $hostname = $$target{Host};

    if ($$target{ipv4only}) {
        if (not ( $hostname =~ /^\d+\.\d+\.\d+\.\d+$/ or gethostbyname $hostname) ){
            warn "WARNING: Skipping host $hostname as it does not resolve to an IPv4 address\n";
            return 'DEADHOST';
        }
    } else {
         if($hostname =~ /^\[(.*)\]$/) {
            # Numeric IPv6 address. Check that it's valid
            $hostname = substr($hostname, 1);
            chop $hostname;
            if(! inet_pton(AF_INET6(), $hostname)) {
                warn "WARNING: Skipping host $hostname: invalid IPv6 address\n";
                return 'DEADHOST';
            }
        } else {
            # Hostname. Look it up
            my @res;
            my ($too,$port,$otheropts) = split(':', $$target{SnmpOpt}, 3);
            $port = 161 unless defined $port;
            @res = getaddrinfo($hostname, $port, AF_UNSPEC(), SOCK_DGRAM());
            if (scalar (@res) < 5) {
                warn "WARNING: Skipping host $hostname as it does not resolve to an IPv4 or IPv6 address\n";
                return 'DEADHOST';
            }
        }
    }
  RETRY:
    my @ifnum = ();
    my @OID = ();
    # Find apropriate Interface to poll from
    for my $i (0..1) {
	if ($$target{IfSel}[$i] eq 'If') {
	    $ifnum[$i] = ".".$$target{Key}[$i];
	    debug('snpo',"simple If: $ifnum[$i]");
	} elsif($$target{IfSel}[$i] eq 'None') {
            $ifnum[$i] = "";
        } else {
            $$target{Key}[$i] =~ s/\s+$//; # no trainling whitespace in keys ...
	    if (not defined $$confcache{"$$target{Community}\@$$target{Host}$$target{SnmpOpt}"}{$$target{IfSel}[$i]}{$$target{Key}[$i]}) {
		debug('snpo',"($i) Populate ConfCache for $$target{Host}$$target{SnmpOpt}");
		populateconfcache($confcache,"$$target{Community}\@$$target{Host}$$target{SnmpOpt}",$$target{ipv4only},1,$$cfg{snmpoptions});
	    }
	    if (not defined $$confcache{"$$target{Community}\@$$target{Host}$$target{SnmpOpt}"}{$$target{IfSel}[$i]}{$$target{Key}[$i]}) {
		warn "WARNING: Could not match host:'$$target{Community}\@$$target{Host}$$target{SnmpOpt}' ref:'$$target{IfSel}[$i]' key:'$$target{Key}[$i]'\n";
		return undef;
	    } else {
		$ifnum[$i] = ".".$$confcache{"$$target{Community}\@$$target{Host}$$target{SnmpOpt}"}{$$target{IfSel}[$i]}{$$target{Key}[$i]};
		debug('snpo',"($i) Confcache Match $$target{Key}[$i] -> $ifnum[$i]");
	    }
	}
	if ($ifnum[$i] !~ /^$|^\.\d+$/) {
	    warn "WARNING: Can not determine".
	      " ifNumber for $$target{Community}\@$$target{Host}$$target{SnmpOpt} \tref: '$$target{IfSel}[$i]' \tkey: '$$target{Key}[$i]'\n";
	    return undef;
	}
    }
    for my $i (0..1) {
	# add ifget methodes call for a cross check;
	for ($$target{IfSel}[$i]) {
	    /^Eth$/ && do {
		push @OID, "ifPhysAddress".$ifnum[$i]; last
	    };
	    /^Ip$/ && do {
		push @OID, "ipAdEntIfIndex".".".$$target{Key}[$i];last
	    };
	    /^Descr$/ && do {
		push @OID, "ifDescr".$ifnum[$i]; last
	    };
	    /^Type$/ && do {
		push @OID, "ifType".$ifnum[$i]; last
	    };
	    /^Name$/ && do {
		push @OID, "ifName".$ifnum[$i]; last
	    };
	}
	push @OID ,$$target{OID}[$i].$ifnum[$i];
    }
    # we also want to know uptime and system name unless we are
    if ( not defined $$cfg{nomib2} and $$cfg{logformat} ne 'rrdtool' ) {
      if ( $OID[0] !~ /^cache.+$/ and
           $OID[0] !~ /^\Q1.3.6.1.4.1.3495.1\E/ ) {
           push @OID, qw(sysUptime sysName);
      } else {
           push @OID, qw(cacheUptime cacheSoftware cacheVersionId)
      }
    }

    # pull that data
    debug('snpo',"SNMPGet from $$target{Community}\@$$target{Host}$$target{SnmpOpt} -- ".(join ",", @OID));
    my @ret;
    
    # make sure we have no error messages hanging round.
    
    $SNMP_Session::errmsg = '';

    my $targtemp = $$target{Community}.'@'.$$target{Host}.$$target{SnmpOpt};
    $targtemp = v4onlyifnecessary($targtemp, $$target{ipv4only});
    
    my @snmpoids = grep !/^Pseudo/, @OID;
    if (defined $$cfg{singlerequest}){
        local $BER::pretty_print_timeticks = 0;
	foreach my $oid (@snmpoids){
            push @ret, snmpget($targtemp,$$cfg{snmpoptions},$oid);
	}
    } else {
	@ret = snmpget($targtemp,$$cfg{snmpoptions},@snmpoids);
    }
    my @newret;
    for (@OID) {
        /^PseudoZero$/ && do { push @newret, 0; next; };
        /^PseudoOne$/ && do { push @newret, 1; next; };
        push @newret, shift @ret;
    }
    @ret = @newret;
    debug('snpo',"SNMPfound -- ".(join ", ", map {"'".($_||"undef")."'"}  @ret));
    $ret[-2] = $ret[-2].' '.$ret[-1] if $OID[-1] and $OID[-1] eq 'cacheVersionId';
    my $time = time;
    my @final;
    # lets do some reality check
    for my $i (0..1) {
	# some ifget methodes call for a cross check;
	for ($$target{IfSel}[$i]) {
	    /^Eth$/ && do {
		my $bin = shift @ret || 0xff;
		my $eth = unpack 'H*', $bin;
		my @eth;
		while ($eth =~ s/^..//){
		    push @eth, $&;
		}
		my $phys=join '-', @eth;
		if ($phys ne $$target{Key}[$i]) {
		    debug('snpo', "($i) eth if crosscheck got $phys expected $$target{Key}[$i]");
		    if (not $retry) {
			$retry=1;
			# remove broken entry
			$$confcache{$$target{Community}.'@'.$$target{Host}.
				    $$target{SnmpOpt}}{$$target{IfSel}[$i]}{$$target{Key}[$i]} = undef;
			debug('repo',"($i) goto RETRY force if cache repopulation");
			goto RETRY;
		    } else {
			warn "WARNING: could not match&get".
			    " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for Eth $$target{Key}[$i]\n";
			return undef;
		    }
		};
		debug ('snpo',"($i) Eth crosscheck OK");
	    };
	    /^Ip$/ && do {
		my $if = shift @ret || 'none';
		if ($ifnum[$i] ne '.'.$if) {
		    debug('repo', "($i) IP if crosscheck got .$if expected $ifnum[$i]");
		    if (not $retry) {
			$retry=1;
			# remove broken entry
			$$confcache{$$target{Community}.'@'.$$target{Host}.
				    $$target{SnmpOpt}}{$$target{IfSel}[$i]}{$$target{Key}[$i]} = undef;
			debug('repo',"($i) goto RETRY force if cache repopulation");
			goto RETRY;
		    } else {
			warn "WARNING: could not match&get".
			    " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for IP $$target{Key}[$i]\n";
			return undef;
		    }
		}
		debug ('snpo',"($i) IP crosscheck OK");
	    };
	    /^(Descr|Name|Type)$/ && do {
		my $descr = shift @ret || 'Empty';
                $descr =~ s/[\0- ]+$//; # remove excess spaces and stuff
		if ($descr ne $$target{Key}[$i]) {
		    debug('repo', "($i) $_ if crosscheck got $descr expected $$target{Key}[$i]");
		    if (not $retry) {
			$retry=1;
			# remove broken entry
			$$confcache{$$target{Community}.'@'.$$target{Host}.
				    $$target{SnmpOpt}}{$$target{IfSel}[$i]}{$$target{Key}[$i]} = undef;
			debug('repo',"($i) goto RETRY force if cache repopulation");
			goto RETRY;
		    } else {
			warn "WARNING: could not match&get".
			    " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for $_ '$$target{Key}[$i]'\n";
			return undef;
		    }
		} 
		debug ('snpo',"($i) $_ crosscheck OK");
	    };
	}
	# we no sense continuing here ... if there is no data ...      
 	if (defined $SNMP_Session::errmsg and $SNMP_Session::errmsg =~ /no response received/){
            $SNMP_Session::errmsg = '';
	    warn "WARNING: skipping because at least the query for $OID[0] on  $$target{Host} did not succeed\n";
	    return 'DEADHOST';
	}
	if ($$target{OID}[$i] =~ /if(Admin|Oper)Hack/) {
	    push @final, ((shift @ret) == 1) ? 1:0;
	} else {
	    push @final, shift @ret;
	}
    }
    
    my @res = ( @final,$time, @ret);
    
    # have some cleanup first, it seems that some agents
    # are adding newlines to what they return
    map{ $_ =~ s/\n|\r//g if defined $_ } @res;
    map{ $_ =~ s/^\s+//g if defined $_ } @res;
    map{ $_ =~ s/\s+$//g if defined $_ } @res;
    
    # Convert in and out values to integers with a user-defined subroutine
    # specified by the Conversion target key
    if( $target->{ Conversion } ) {
        foreach my $ri( 0..1 ) {
            next unless defined $res[ $ri ];
            my $exp = "&MRTGConversion::$target->{ Conversion }( '$res[ $ri ]' )";
            $res[ $ri ] = eval $exp;
            warn "WARNING: evaluation of \"$exp\" failed\n$@\n" if $@;
        }
    }

    # in and out should be numbers only
	for my $ri (0..1){
	    # for folks using rrdtool I am allowing numbers 
	    # with decimals here
	    if ( defined $res[$ri] && $res[$ri] !~ /^[-+]?\d+(.\d+)?$/ ) {
		warn "WARNING: Expected a number but got '$res[$ri]'\n";
		$res[$ri] = undef;
		
	    }
	}
	return @res;
    }


# read target function ...
sub readtargets ($$$) {
    my ($confcache,$target,$cfg) = @_;
    my $forks = $$cfg{forks};
    my $trgnum = $#{$target}+1;
    if (defined $forks and $forks > 1  and $trgnum > 1){
        $forks = $trgnum if $forks > $trgnum;
        my $split = int($trgnum / $forks) + 1;       
	my @hand;
	# get them forks to work ... 
	for (my $i = 0; $i < $forks;$i++) {
	    local *D;
            my $sleep_count=0;
            my $pid;
            do {
               $pid = open(D, "-|");
                unless (defined $pid) {
                    warn "WARNING cannot fork: $!\n";
                    die "ERROR bailing out after 6 faild forkattempts"
                         if $sleep_count++ > 6;
                    sleep 10;
                }
            } until defined $pid;
	    if ($pid) { # parent
     		$hand[$i] = *D; # funky file handle magic ... 
		debug ('fork',"Parent $$ after fork of child $i");
	    } else {  # child
		debug ('fork',"Child $i ($$) after fork");
		my $res = "";
                my %deadhost;
		for (my $ii = $i * $split; 
		     $ii < ($i+1) * $split and $ii < $trgnum;
		     $ii++){
		    my $targ = $$target[$ii];
		    my @res;
		    if ($$targ{Methode} eq 'EXEC') {
			@res = getexternal($$targ{Command});
		    } else { # parent
                        if (not $deadhost{$$targ{Community}.$$targ{Host}}) {      
        	  	    @res = getsnmparg($confcache,$targ,$cfg);
                            if ( $res[0] and $res[0] eq 'DEADHOST') {
                                # guess we got a blank here
                                @res = ( undef,undef,time,undef,undef);
                                $deadhost{$$targ{Community}.$$targ{Host}} = 1;
                                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"
                            } elsif ($#res != 4){
                                @res = (undef,undef,time,undef,undef);
                            }
                        } else {
                            @res = ( undef,undef,time,undef,undef);
                        }
		    }
		
		    for (my $iii=0;$iii<5;$iii++){
			if (defined $res[$iii]){
                            $res .= "$res[$iii]\n";
			} else {
                            $res .= "##UNDEF##\n";
			}
		    }
		}
		debug ('fork',"Child $i ($$) waiting to deliver");
		print $res; # we only talk after the work has been done to
                  	    # otherwhise we might get blocked 
                # return updated hosts from confcache
                writeconfcache($confcache,'&STDOUT')
                        if defined $$confcache{___updated};
		exit 0;
	    }
	    
	}
	# happy reaping ... 
        my $vin =''; # vector of pipe file-descriptors from children
	for (my $i = 0; $i < $forks;$i++) {
            vec($vin, fileno($hand[$i]), 1) = 1;
        }
        my $left = $forks;
        while ($left) {
            my $rout = $vin; # read vector
            my $eout = $vin; # exception vector
            my $nfound = select($rout, undef, $eout, undef); # no timeout
            if (1 > $nfound) {
               die sprintf("ERROR: select returned %d: $!\n", $nfound);
            }
	    for (my $i = 0; $i < $forks; $i++) {
                next unless defined $hand[$i] and defined fileno($hand[$i]);
# this does not seem to work reliably
#                if (vec($eout, fileno($hand[$i]), 1)) {
#		   die "ERROR: fork $i has died ahead of time?\n";
#                }
                next unless vec($rout, fileno($hand[$i]), 1);

                vec($vin, fileno($hand[$i]), 1) = 0; # remove this child fd

	        debug ('fork',"Parent reading child $i");
	        my $h = $hand[$i];
	        for (my $ii = $i * $split; 
		     $ii < ($i+1) * $split and $ii < $trgnum;
		     $ii++){ 
		    my $targ = $$target[$ii];
		    my @res;
		    for (0..4){
		        my $line = <$h>; # must be a simple scalar here else it wont work
		        die "ERROR: fork $i has died ahead of time ...\n" if not defined $line;
		        chomp $line;
    #                    debug ('fork',"reading for $ii $line");
		        $line = undef if $line eq "##UNDEF##";                
		        push @res,$line;
		    };
    
		    ($$targ{_IN_},
		     $$targ{_OUT_},
		     $$targ{_TIME_},
		     $$targ{_UPTIME_},
		     $$targ{_NAME_}) = @res; 
                     if ($] >= 5.0061){
                         $$targ{_IN_} = Math::BigFloat->new($$targ{_IN_}) if $$targ{_IN_};
                         $$targ{_OUT_} = Math::BigFloat->new($$targ{_OUT_}) if $$targ{_OUT_};
                     }
                }     
                # feed confcache entries
                my $lasthost ="";
                while (<$h>){
                    chomp;
                    my ($host,$method,$key,$value) = split (/\t/, $_);
                    if ($host ne $lasthost){
        	         debug ('fork',"start undef for $host");
                         $$confcache{$host} = undef;
        	         debug ('fork',"end undef for $host");
                    }
                    $lasthost = $host;
                    storeincache($confcache,$host,$method,$key,$value);
                 }
                 close $h;
                 --$left;
            }
        }
	            
    } else {
        my %deadhost;
	foreach my $targ (@$target) {
	    if ($$targ{Methode} eq 'EXEC') {
		debug('snpo', "run external $$targ{Command}");
		($$targ{_IN_},
		 $$targ{_OUT_},
		 $$targ{_TIME_},
		 $$targ{_UPTIME_},
		 $$targ{_NAME_}) = getexternal($$targ{Command});
	    } elsif ($$targ{Methode} eq 'SNMP' and not $deadhost{$$targ{Host}}) {
		debug('snpo', "run snmpget from $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}\@$$targ{Host}");
		($$targ{_IN_},
		 $$targ{_OUT_},
		 $$targ{_TIME_},
		 $$targ{_UPTIME_},
		 $$targ{_NAME_}) = getsnmparg($confcache,$targ,$cfg);
               if ( $$targ{_IN_} and $$targ{_IN_} eq 'DEADHOST') {
                   $$targ{_IN_} = undef;
                   # guess we got a blank here
                   $deadhost{$$targ{Host}} = 1;
                   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"
               }        
	    } else {
                 $$targ{_IN_} = undef;
                 $$targ{_OUT_} = undef;
                 $$targ{_TIME_} = time;
                 $$targ{_UPTIME_} = undef;
                 $$targ{_NAME_} = undef;
            }
            if ($] >= 5.008 ){
                    $$targ{_IN_} = new Math::BigFloat "$$targ{_IN_}" if $$targ{_IN_};
                    $$targ{_OUT_} = new Math::BigFloat "$$targ{_OUT_}" if $$targ{_OUT_};
            }
                
	}       
    }
        
}

sub imggen ($) {
        my $dir = shift;
        if ( ! -r "$dir${main::SL}mrtg-l.png" and  open W, ">$dir${main::SL}mrtg-l.png" ){
                binmode W;
                print W unpack ('u', <<'UUENC');
MB5!.1PT*&@H    -24A$4@   #\    9! ,   !TA.O'    &%!,5$5]9GTT
M79A?8HB(9WFR;6G_=DWM=%35<5R_M[A2     6)+1T0'%F&(ZP   1%)1$%4
M>-JMDKUOPD ,Q:E4]CH?S0PMG8-.$6L1A*YMJ,D<T>Q%5?[_ON>$Y (2$R>=
MY;-^OF=;GNCM\SFY#_#GW$IU0WMP/#O5HSGNW8"9R)/92$NQ\Z:GUDG/@.@!
M)CP#4H\ >LT>)NB!A0]8,"]&0.(#WY92V<\$=FM4<P7$:Q,JN\^BGRV+WOX2
MX.<2+4VH!U01B-LYX#V3B*U(1N 5[K,/?(G,)1!!9-%WX0.HYX7 :0!"]0&4
MMV*/Z"/N@&8$P,N8!:FD[!4\ #7EN$G1 <M+"0*0B0&$!#:MQ@#P#?WIO@,^
M+KI@K$9V#B"P03V,!\5)T]1T#*A,8P"P.%JZ1USGL%%IPTC.#<ONMK2W@']'
M6*MJXQ-D&    $-T15AT4V]F='=A<F4 0"@C*4EM86=E36%G:6-K(#0N,BXY
M(#DY+S Y+S Q(&-R:7-T>4!M>7-T:6,N97,N9'5P;VYT+F-O;>WHV?     J
M=$58=%-I9VYA='5R90!D,C(W8S<T.3AA-3 Q93=F830U,#@V8SEF9C0T8F(Y
K8ULTB'X    .=$58=%!A9V4 -C-X,C4K,"LP&!)XE     !)14Y$KD)@@F(Y
UUENC
close W;
        }
        if ( ! -r "$dir${main::SL}mrtg-m.png" and  open W, ">$dir${main::SL}mrtg-m.png" ){
                binmode W;
                print W unpack ('u', <<'UUENC');
MB5!.1PT*&@H    -24A$4@   !D    9! ,    VQYA0    &%!,5$5]9GTT
M79A588QN9(*7:73_=DWL=%31<F'8YWD&     6)+1T0'%F&(ZP   (!)1$%4
M>-IC"$4"!0P$>"E&2B:I,%Z((!"(I$)X88H@GJ :A)<$Y@@*I8)YAA">("N(
M%PYD"#L+"IJ#Y4!FN(86N4',# )J"4TO*R\O!_$"@::'@HTEQ /K _&$0+P 
M(.T:6@A2@62?H#B*6TRQN#,<Q0^AP<C^ _K=&,GO1(89 ,3!45'T;[0_    
M0W1%6'13;V9T=V%R90! *",I26UA9V5-86=I8VL@-"XR+CD@.3DO,#DO,#$@
M8W)I<W1Y0&UY<W1I8RYE<RYD=7!O;G0N8V]M[>C9\    "IT15AT4VEG;F%T
M=7)E #1E,S8X-S$P,38Q-S)A96%B.3,Y8SEA,F5D-31B86(U@DWZ,@    ET
M15AT1&5L87D ,C4P(RC.$P    YT15AT4&%G90 R-7@R-2LP*S"#D2 ?    
) $E%3D2N0F""
UUENC
close W;
        }
        if ( ! -r "$dir${main::SL}mrtg-r.png" and  open W, ">$dir${main::SL}mrtg-r.png" ){
                binmode W;
                print W unpack ('u', <<'UUENC');
MB5!.1PT*&@H    -24A$4@   80    9! ,   ##'$3)    &%!,5$4T79@\
M8YQ5=JAPC;>:K\S\_?[5WNFZR-PB%CO!     6)+1T0'%F&(ZP  !=5)1$%4
M>-KM6$ESVE@0;BW 58: K_)"YDH(.%=YL/"511)7O""N3AS!WY_^NM^3Q)+,
M4C55DZJ1*2&]U]O7Z\,4T"]__0_A;US.O\7$$#H*PR=R[_'@#? A9X3KLW,K
MNRX_*L=MGK^#PQ)[$USW+J@'U)E,!JI<V>GS9"+RG0^B!(NDM,ZMB'G,M_?6
MF@^[_#7D[U)*!V_N!#+4$)>M@+Z(.J/1)PNAI4]=5K# 0_,)'Z(4UXH*A<#/
M2SQ\3+?[=!F4Q&M?Z!8@2-?4X+O*L^Q\$PPQ;MA.R= 65RRWR/:[U&#PTVR_
MR4*JI#3?9#E-$U)#W)5*GE&3[Y&!T%P+?QR2)U;UUOB(0@A0"-Z*.AOVC)=^
MY=L&-T/L"$G !,KBW"V-3PU[.\8W@5N7J)W(?L'&WB5LP6_*X10OP+2L21G/
MH7OA=#:1&M)F-1FI\^A+8B&H %;A+\U>LP8A)HN^RZOC9^BZY#=#_&10NA8"
M.5E !^R7H/2R=0G!6PH%1\%-!W@<"GU+PN4Q.Z2D6-K!2-;D]-=J"-2H?&C>
M7"B$ABCW."HV-PX@%"6$YHS<3:@.K!$K2;N"4-0A\)X'.R[?%R4$MXQ"TT9,
M7*Y:IP)!8N0L=Z0IVUJH(9!51H%B1)9A]$%,S3QB[_P0 EA;B\ W*KM/%?%Q
M%*@8'$*0K?Y@54'0*+ !XZ@&P;C'2N$W?SU&\BX"Z"YKH08ATBB,^WCHW[U5
MCK6U$!U'H3$WA9<<1\%+2I8R"H:]@ZT=Q8%9LK1%Z!1AA< XH902XAM@X#<;
MA?;J) HL-F[!KJ*U%KC:D<XFTOAKZ35^+8FU7-S5\.8Z!(N;U7P)]B93.@G!
M4XWWZ^$U)]+-]? "M9#5YI*_+%^LE''@S[5Q]&951\J&PYL0-FJ],-<4J>DD
M_NQ\.9M$RD9?TE!AX\HJ8E,+IJE^'Q6+.H1X\K#A;V\FI.B6F=)&B$+E>'9T
M51>-EPD:$VVE;#C>'CJ2:=.FJ?9>1_'20-CQ'_ESAN"5Y?Q4LT'=SFS?^.7*
MYFU%;!(IFTP>!K!Q%=8AL.)7D@8/AL;K:#3A#LW#*>0H> <0(-"]YF!:*>X2
MUG&GD;D08Z0] ,((4XWG0O;)0-@2UTPW8@V^G0NG46BO^FNB,@I.7A$?EO.W
MK+1)V<4R&H<$E]MR-K7@GD!HB;DL!4G%N>VP;?YJCPE?\''#<:4C!?S7?-IH
MXG%'2N"B.&2Y/^M(*ZE!6PM.]L..5#4997<P;JG(\SP]:JKLCZP&03+3&=[<
M+B$% [N;Y?E&PQ<80[RJG)_Z(@Q;TERV&)DG48AJY>R@5W:5"SWE* JV([U5
M*6W8H>EBM=_O>9B8CF2C0)M:.1L)@-*(!$_\^+C?S8TF.FJJO;6?& AP20*A
MB6DR[*FS':G[7)5<<U$1VXYD6)RRO9L]:$)C@=3CT19'%00G53RMF96R55J+
M^! "2],!%$B88CAS9?S S>_,&2F1-N>:+LBJ2^+#Z1Q1?WV02(3PR2/<JV<D
M8Q.O/]<@&#R<B4S6-\.SK+HS<Z&W4 @@Z!9<VSE0D)SWSI^1H"+^*H" Q!(?
M3V<3W\H#'^=H&(1Z-F<D4PNA"F+#Y+TKRSC#,!DG/8=#*D;C73LC60B>'*IX
M=,_1!P+ %2,A]5Q'TBSR49QN_$P5\?$9Z>2 P=;DXN8B..I(+ 2/'76SBU,P
MC9=6BD28R\^O)=+1&2E2"#/C%CX7M++)J&#SFMP*GD^F,_SCC+.'42'H+;&)
M0LH]YTU8>HM#"!3_GI@4.:R%*_3\U\EC:M+I8_I]-,T&9*3H0:4U/RYG:-+9
M):7)9D,JX$_9*=,TW>)74"X0F@ Y50B83W?\'H!$"]80*XF;"P2P>%NUJ&2_
M?-51V7N3)7)?E!]B_$V:OMMJ^,+CZMYPMK??9;'SXBG"J;4C%PBPVX5^T]1"
M6U-M/;K_[#>U>Q4<$?_I=?&SS>NP_(5\X=Z<<ITR.Y5YP7_B/QC_Z/\"?]4_
MO\3U!QLJ>FT="M4^    0W1%6'13;V9T=V%R90! *",I26UA9V5-86=I8VL@
M-"XR+CD@.3DO,#DO,#$@8W)I<W1Y0&UY<W1I8RYE<RYD=7!O;G0N8V]M[>C9
M\    "IT15AT4VEG;F%T=7)E #$R,3<U939B,S$X,S,S-V0S.#%F,#%C-C-C
M9F,T,S9ELPXRP0    ]T15AT4&%G90 S.#AX,C4K,"LP4K-IB0    !)14Y$
$KD)@@C9E
UUENC
close W;
        }

}

# Local Variables:
# mode: cperl
# eval: (cperl-set-style "PerlStyle")
# mode: flyspell
# mode: flyspell-prog
# End:
#
# vi: sw=4
