#! /usr/bin/perl -w # -*- mode: cperl -*- ################################################################### # MRTG 2.10.13 Multi Router Traffic Grapher ################################################################### # Created by Tobias Oetiker # and Dave Rand # # For individual Contributers check the CHANGES file # ################################################################### # # Distributed under the GNU General Public License # ################################################################### # Adaptation SambaEdu Philippe Chadefaux # $Id: mrtg-se3 6654 2011-11-27 16:35:54Z dbo $ # 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 <) = ($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; my @cfgfile = split(/\//, $cfgfile); if (! defined $lockfile) { #DEB $lockfile = $cfgfile."_l"; $lockfile = "/var/lock/mrtg/" . join('_', @cfgfile) . "_l"; } if (! defined $templock) { $templock = $lockfile."_" . $$ ; #DEB $templock = "/var/lock/mrtg/" . join('_', @cfgfile) . "_l_" . $$ ; } 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;}) and (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 @cachefile = split(/\//, $cfgfile); $confcachefile = "/var/lib/mrtg/" . join('_', @cachefile); } 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'} and 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 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 (defined $inlast and $inlast < 0) { $inlast += 2**31; # this is likely to be a broken snmp counter ... lets compensate } if (defined $outlast and $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 and $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}; my $dotrrd = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.rrd"; my $dotlog = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.log"; my $reallog = $$cfg{logformat} eq 'rrdtool' ? $dotrrd : $dotlog; if (defined $$cfg{maxage} and -e $reallog and time()-$$cfg{maxage} > (stat($reallog))[9]){ warn "ERROR: skipping update of $router. As $reallog is older than MaxAge ($$cfg{maxage} s)\n"; return undef; } 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. # 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 $dotlog and not -e $dotrrd) { debug('base',"converting $dotlog 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 $dotrrd) { #nope it seems we have to create a new one debug('base',"create $dotrrd"); # 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 = ($dotrrd, '-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 = ($dotrrd, '-a', "ds0:$absi", '-a', "ds1:$abso", '-d', "ds0:$up_abs", '-d', "ds1:$up_abs"); RRDs::tune(@args); my $e = RRDs::error(); warn "ERROR: Cannot tune logfile: $e\n" if $e; debug('log', "Called: RRDs::tune(@args)"); } # update the rrd $inlast = 'U' unless defined $inlast and $inlast =~ /\S/ and $inlast ne '##UNDEF##'; $outlast = 'U' unless defined $outlast and $outlast =~ /\S/ and $outlast ne '##UNDEF##'; debug('log', "Calling: RRDs::update($dotrrd, '$time:$inlast:$outlast')"); RRDs::update("$dotrrd", "$time:$inlast:$outlast"); my $e = RRDs::error(); warn "ERROR: Cannot update $dotrrd 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($dotrrd); debug('log', "Called: RRDs::last()"); $e = RRDs::error(); warn "ERROR: Cannot 'last' $dotrrd: $e\n" if ($e); my $fetch = (RRDs::fetch($dotrrd,'AVERAGE','-s',$lasttime,'-e',$lasttime))[3]; $e = RRDs::error(); die "ERROR: Cannot 'fetch' $dotrrd: $e\n" if ($e); debug('log', "Called: RRDs::fetch($dotrrd,'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' or $MRTG_lib::OS eq 'OS2') and (-e "${FindBin::Bin}${MRTG_lib::SL}rateup.exe")) or (-x "${FindBin::Bin}${MRTG_lib::SL}rateup")) or 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}") or ((-M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}") + $SAGE >= 0.5/24)) and (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}") or (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}") + $SAGE >= 2/24)) and (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}") or (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}") + $SAGE >= 1)) and (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' or $MRTG_lib::OS eq 'NT' or $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,"-|") or do { exec @exec or warn "WARNING: rateup (".(join " ", @exec ). ") did not work: $!\n"; }; } debug('log', "Called @exec"); imggen($$cfg{icondir} || $$cfg{imagedir}); # SE3 my $fichier="$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router".".php"; if (open (HTML,"<$fichier")) { # if (open (HTML,"<$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.$$rcfg{'extension'}{$router}")) { for ($i=0 ; $i<200 ; $i++) { last if eof(HTML); $_= ; if (/\n"; print HTML "\n"; print HTML '' . "\n"; print 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"; $$rcfg{'title'}{$router} TEXT print HTML '\n"; foreach $peri (qw(d w m y)) { print HTML <<"TEXT"; TEXT if ($$rcfg{'options'}{'dorelpercent'}{$router} and defined $$maxpercent{$peri}{$router}) { print HTML <<"TEXT"; TEXT } print HTML <<"TEXT"; TEXT if ($$rcfg{'options'}{'dorelpercent'}{$router}) { print HTML <<"TEXT"; TEXT } print HTML "" if defined $$cuin{$peri}{$router}; print HTML "" if defined $$cuout{$peri}{$router}; if ($$rcfg{'options'}{'dorelpercent'}{$router} and $$cupercent{$peri}{$router} ) { print HTML <<"TEXT"; TEXT } print HTML <<"TEXT" if $$avmxin{$peri}{$router} and $$avmxout{$peri}{$router}; TEXT } $namestring = "'$name'" 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} !~ / $$rcfg{bodytag}{$router} $$rcfg{'pagetop'}{$router}
TEXT if (defined $$rcfg{'timezone'}{$router}){ print HTML &$LOC("The statistics were last updated $Today $$rcfg{'timezone'}{$router}"); } else { print HTML &$LOC("The statistics were last updated $Today"); } if ($uptime and ! $$rcfg{options}{noinfo}{$router}) { print HTML ",
\n". &$LOC("at which time $namestring had been up for $uptime."). "\n"; } 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="". "$$rcfg{'legendi'}{$router} "; } } else { $InCo="". &$LOC(" In:"); } } my $OutCo; if (!(defined $$rcfg{'options'}{'noo'}{$router})) { if (exists $$rcfg{'legendo'}{$router}) { if ($$rcfg{'legendo'}{$router} ne "") { $OutCo="". "$$rcfg{'legendo'}{$router} "; } } else { $OutCo="". &$LOC(" Out:"); } } my $PercentCo; if (defined $$rcfg{'legend5'}{$router}) { if ($$rcfg{'legend5'}{$router} ne "") { $PercentCo="". "$$rcfg{'legend5'}{$router}"; } } else { $PercentCo="". &$LOC("Percentage "); } 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 "
".&$LOC("$sample{$peri}").&$LOC(" Average)
")." \"$full{$peri}\" "; my(@foo)=($rcfg,$LOC); print HTML " ".&$LOC("")." ".&$LOC("")." ".&$LOC("")." " if $InCo; print HTML " ".&$LOC("")." ".&$LOC("")." ".&$LOC("")." " if $OutCo; print HTML " ".&$LOC("")." ".&$LOC("")." ".&$LOC("")." " if ($$rcfg{'options'}{'dorelpercent'}{$router} and $PercentCo); print HTML '" if ($$rcfg{'options'}{'avgpeak'}{$router} and $InCo and $OutCo); print HTML "
Max $InCo".&fmi($$maxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)." Average $InCo".&fmi($$avin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)." Current $InCo".&fmi($$cuin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)."
Max $OutCo".&fmi($$maxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)." Average $OutCo".&fmi($$avout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)." Current $OutCo".&fmi($$cuout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)."
Max $PercentCo".sprintf("%0.1f %%",($$maxpercent{$peri}{$router} || 0))." Average $PercentCo".sprintf("%0.1f %%",($$avpercent{$peri}{$router} || 0 ))." Current $PercentCo".sprintf("%0.1f %%",($$cupercent{$peri}{$router} || 0 ))."
',&$LOC("Average max 5 min values for $sample{$peri} interval):  "), "$InCo ", &fmi($$avmxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo), "/", "$OutCo ", &fmi($$avmxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo), "
\n"; } if (!(defined $$rcfg{'options'}{'nolegend'}{$router})) { print HTML "

"; print HTML " " if $InCo; print HTML " " if $OutCo; if ($$rcfg{'withpeak'}{$router}) { print HTML " " if $InCo; print HTML " "if $OutCo; } if ($$rcfg{'options'}{'dorelpercent'}{$router}) { print HTML " "; } print HTML "
$$rcfg{'col1'}{$router} ### $leg1
$$rcfg{'col2'}{$router} ### $leg2
$$rcfg{'col3'}{$router}### $leg3
$$rcfg{'col4'}{$router}### $leg4
$$rcfg{'col5'}{$router}### $leg5
"; } # SE3 # 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< #


# # # # # # #
MRTGMulti Router Traffic Grapher
# # # # #
$VERSION # Tobias Oetiker # <oetiker\@ee.ethz.ch> #TEXT # print HTML &$LOC("and"); # print HTML ' Dave Rand <dlr@bungi.com>'; # print HTML< #
#TEXT # We don't need this any more. # undef $gifPath; # if ($MRTG_lib::OS eq 'VMS') { # print HTML # "
#".&$LOC("Ported to OpenVMS Alpha by").". # Werner Berger # # <werner.berger\@cch.cerberus.ch>
#"; } # 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 # "
# ".&$LOC("Ported to WindowsNT by")." # Stuart Schneider # # <schneis\@testlab.orst.edu>
# "; # } # if ( # $$cfg{'language'} and # defined($lang2tran::LOCALE{"\L$$cfg{'language'}\E"}) and # ($LOC != $lang2tran::LOCALE{"default"})) # { # if (defined($credits::LOCALE{"\L$$cfg{'language'}\E"})) { # print HTML "
".$credits::LOCALE{"\L$$cfg{'language'}\E"}."
"; # } else { # print HTML "
".$credits::LOCALE{'default'}."
"; # } # ; # } # print HTML "\n"; # } # print HTML $$rcfg{'pagefoot'}{$router} if defined $$rcfg{'pagefoot'}{$router}; # print 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 < mrtg-2.10.13 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' or $MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2') { # too sad NT and VMS can't do links we'll do the diletants lock if (-e $lockfile and 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)=; 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") or 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 or $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."|") or 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=) unless eof EXTERNAL; chomp( $out=) unless eof EXTERNAL; chomp( $uptime=) unless eof EXTERNAL; chomp( $name=) 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||"undef")." out:".($out||"undef")." uptime:".($uptime||"undef")." name:".($name||"undef")); 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 'NOMATCH'; } 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 'NOMATCH'; } } 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 'NOMATCH'; } }; 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 'NOMATCH'; } } 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 'NOMATCH'; } } 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] and $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[0] and $res[0] eq 'NOMATCH'){ @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; $$targ{_TIME_} =time; # 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" } if ( $$targ{_IN_} and $$targ{_IN_} eq 'NOMATCH') { $$targ{_IN_} = undef; $$targ{_TIME_} =time; } } 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,DQ%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<\$=FM4WHV? J M=$58=%-I9VYA='5R90!D,C(W8S$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-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#,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,!JIGS9"+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:DDD 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#4997R@5W:5"SWE* JV([U5 M*6W8H>EBM=_O>9B8CF2C0)M:.1L)@-*(!$_\^+C?S8TF.FJJO;6?& AP20*A MB6DR[*FS':G[7)5<_,&2F1-N>:+LBJ2^+#Z1Q1?WV02(3PR2/'*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<FT="M4^ 0W1%6'13;V9T=V%R90! *",I26UA9V5-86=I8VL@ M-"XR+CD@.3DO,#DO,#$@8W)IC9 M\ "IT15AT4VEG;F%T=7)E #$R,3