1#!/usr/local/bin/perl
2##!/usr/bin/speedy
3# vim:ts=4
4# trend.cgi
5#    Generate a trending graph, based on yearly data, for the specified
6#    mrtg target.  A module to be called from routers.cgi Extension interface
7#
8#  Copyright S Shipway 2003
9#  version 0.1: first working attempt, released with routers.cgi v2.13
10#  version 0.2: fix bugs (routers.cgi v2.13a)
11#  version 0.3: Directory[] directive was broken
12#  version 0.4: some reporting issues
13#  version 0.5: error checking, more comments etc
14#  version 0.6: not much
15#  version 0.7: more configuration in routers2.conf, better error messages
16#  version 1.0: Removed external rrdtool exe call and XML stage.  Now all
17#               processing of the temporary rrd is done internally. Also,
18#               appears to be Windows compatible.
19#  version 1.1: added different base options
20#  version 1.2: added decolon function for RRD v1.2.x compatibility
21#  version 1.3: added stylesheet support
22#  version 1.4: added MAX/AVG option
23#  version 1.5: more RRD1.2.x support
24#  version 2.0: more RRD version compatibility stuff
25#          2.1: better libadd support
26#
27##########################################################################
28# To install this script:
29#   Change the #! line at the beginning to give correct Perl location
30#   Set correct Temporary working directory in Constants
31#      $TMPPATH
32#   Copy trend.cgi into web server cgi-bin, correct permissions
33#   Define an Extension in the .cfg file for a particular target
34#      routers.cgi*Extension[targetname]: "Trending analysis" /cgi-bin/trend.cgi graph-sm.gif
35#   Try out different decay factors
36#
37#  You can put 'trendurl = /cgi-bin/trend.cgi' into your routers2.conf
38#  to automatically add trending analysis to all targets (undocumented)
39#
40##########################################################################
41
42use strict;
43use CGI;
44use FileHandle;
45use Text::ParseWords;
46use File::Basename;
47
48# Constants
49#########################################################################
50###################### CHANGE THESE TO MAKE IT WORK ####################
51#########################################################################
52# This is the temporary working directory.  Must have write permissions.
53my( $TMPPATH ) = "/tmp"; # eg: "C:\\temp" for windows
54#########################################################################
55##################### END OF SECTION YOU HAVE TO CHANGE #################
56#########################################################################
57my( $DEFPREDICT ) = 50;
58my( $VERSION ) = "2.1";
59# decay factor can be between 1.0 and 0.  0=history unimportant, 1.0=all
60# data equally important.
61my( @decays ) = ( 1.0, 0.99, 0.95, 0.9, 0.8, 0.66, 0.5 );
62
63###GLOBAL#START########################################################
64# Global Variables (change to our() for speedycgi and perl 5.8)
65# To log errors and progress, set debug=1 and set LOG
66my( $debug ) = 0; # override in routers2.conf 'debug = 1'
67my( $LOG   ) = "$TMPPATH/trend.log"; # this must be writeable! override with
68                                     # routers2.conf 'logfile = ...'
69my( $DECAY   ) = 0.95;
70my( $PREDICT ) = $DEFPREDICT;
71my( $BASE    ) = 0; # 0=current, 1=average
72my( $device, $community, $targetwindow, $target, $file, $backurl )
73	= ( "","public","graph","","","");
74my( $conffile ) = "/u01/etc/routers2.conf"; # overridden by passed parameters
75my( $routersurl ) = '';
76my( $q ) = new CGI;
77my( %headeropts ) = ();
78my( %config ) = ();
79my( $authuser ) = '';
80my( $pathsep ) = '/';
81my( %target ) = ();
82my( $tempfile ) = '/tmp/trend.foo';
83my( $ds, $rrddata , $starttime, $endtime, $interval) = ('','','','','');
84my( $trendstart, $trenddelta ); # array references
85my( $dwmy ) = 'm';
86my( %interfaces );
87my( $workdir ) = '';
88my( $monthlylabel ) = '%W';
89my( $dailylabel ) = '%H';
90my( @params ) = ();
91my( $graphsuffix ) = 'png';
92my( $lastupdate ) = 0;
93my( $gstyle ) = 'l2';
94my( $ksym, $k, $M, $G ) = ( 'K', 1024,1024000, 1024000000);
95my( @info ) = ();
96my( $cfile );
97my( $fgcolour, $bgcolour, $linkcolour ) = ( "#000000", "#ffffff", "#000080" );
98my( $consolidation, $conspfx ) = ("AVERAGE","Avg");
99#my( $consolidation, $conspfx ) = ("MAX","Max");
100my( $myurl ) = $q->url();
101###GLOBAL#END##########################################################
102
103
104#################################
105# For RRD v1.2 compatibility: remove colons for COMMENT directive if
106# we are in v1.2 or later, else leave them there
107sub decolon($) {
108    my($s) = $_[0];
109    return $s if($RRDs::VERSION < 1.002 );
110    $s =~ s/:/\\:/g;
111    return $s;
112}
113
114#######################################################################
115sub errlog($)
116{
117	return if(!$debug);
118	open LOG, ">>$LOG" or return;
119	LOG->autoflush(1);
120	print LOG "".localtime(time).": ".(join " ",@_)."\n";
121	close LOG;
122}
123#######################################################################
124sub inlist($@)
125{
126	my($pat) = shift @_;
127	return 0 if(!defined $pat or !$pat or !@_);
128	foreach (@_) { return 1 if( $_ and /$pat/i ); }
129	return 0;
130}
131
132######################
133# calculate short date string from given time index
134
135sub shortdate($)
136{
137	my( $dformat ) = "%c"; # windows perl doesnt have %R
138	my( $datestr, $fmttime ) = ("",0);
139	return "DATE ERROR 1" if(!$_[0]);
140	$fmttime = $_[0];
141	$fmttime = time if(!$fmttime);
142	my( $sec, $min, $hour, $mday, $mon, $year ) = localtime($fmttime);
143	# try to get local formatting
144	$dformat = $config{'web-shortdateformat'}
145		if(defined $config{'web-shortdateformat'});
146	$dformat =~ s/&nbsp;/ /g;
147	eval { require POSIX; };
148	if($@) {
149		$datestr = $mday."/".($mon+1)."/".($year-100);
150	} else {
151		$datestr = POSIX::strftime($dformat,
152			0,$min,$hour,$mday,$mon,$year);
153	}
154	return "DATE ERROR 2" if(!$datestr);
155	return $datestr;
156}
157
158#################################
159# For string trims.  Remove leading and trailing blanks
160sub trim($)
161{
162	my($x)=$_[0];
163	$x=~s/\s*$//;
164	$x=~s/^\s*//;
165	$x;
166}
167#############################################################################
168# reformat to look nice
169# params -- number, fix flag, integer flag
170sub doformat($$$)
171{
172	my( $sufx ) = "";
173	my( $val, $fix, $intf ) = @_;
174
175	return "???" if(!defined $val or $val !~ /\d/ );
176	return $val if( $val == 0 );
177
178	if(!$fix) {
179		if( $val >= $G ) {
180			$val /= $G; $sufx = "G";
181		} elsif( $val >= $M  ) {
182			$val /= $M; $sufx = "M";
183		} elsif( $val >= $k ) {
184			$val /= $k; $sufx = $ksym;
185		}
186	}
187
188	return sprintf "%.0f %s",$val,$sufx
189		if( $intf or ( int($val*100) == (100*int($val)) ) );
190	return sprintf "%.2f %s",$val,$sufx;
191}
192# Round the number to a set no of decimal places
193sub dp($$) {
194	my($num,$dcp) =@_;
195	my($rv);
196	return '0' if(!$num);
197	$rv = sprintf '%.'.$dcp.'f',$num;
198	$rv =~ s/\.0+$//; # remove trailing .0
199	return $rv;
200}
201
202#######################################################################
203# Read in configuration file
204
205# readconf: pass it a list of section names
206sub readconf(@)
207{
208	my ($inlist, $i, @secs, $sec, $usersec);
209	@secs = @_;
210	%config = ();
211	$usersec = "\177";
212	$usersec = "user-".(lc $authuser) if( $authuser );
213
214	# set defaults
215	%config = (
216		'routers.cgi-confpath' => ".",
217		'routers.cgi-cfgfiles' => "*.conf *.cfg",
218		'web-png' => 0
219	);
220
221	( open CFH, "<".$conffile ) || do {
222		print $q->header({-expires=>"now"});
223		start_html_ss({ -title => "Error", -bgcolor => "#ffd0d0",
224			-class=>'error'  });
225		print $q->h1("Error").$q->p("Cannot read config file $conffile.");
226		print $q->end_html();
227		exit(0);
228	};
229
230	$inlist=0;
231	$sec = "";
232	while( <CFH> ) {
233		/^\s*#/ && next;
234		/\[(.*)\]/ && do {
235			$sec = lc $1;
236			$inlist=0;
237			foreach $i ( @secs ) {
238				if ( (lc $i) eq $sec ) { $inlist=1; last; }
239			}
240			# override for additional sections
241			# put it here so people cant break things easily
242			next if($inlist);
243			if( ( $sec eq $usersec ) or ( $sec eq 'routers2.cgi' ) ) {
244				$sec = 'routers.cgi'; $inlist = 1;
245			}
246			next;
247		};
248		# note final \s* to strip all trailing spaces (which works because
249		# the *? operator is non-greedy!)  This should also take care of
250		# stripping trailing CR if file created in DOS mode (yeuchk).
251		if ( $inlist ) { /(\S+)\s*=\s*(\S.*?)\s*$/ and $config{"$sec-$1"}=$2; }
252	}
253	close CFH;
254
255	# Activate NT compatibility options.
256	# $^O is the OS name, NT usually produces 'MSWin32'.  By checking for 'Win'
257	# we should be able to cover most possibilities.
258	if ( (defined $config{'web-NT'} and $config{'web-NT'}=~/[1y]/i)
259		or $^O =~ /Win/ or $^O =~ /DOS/i  ) {
260		$pathsep = "\\";
261	}
262
263	# backwards compatibility for old v1.x users
264	$config{'routers.cgi-iconurl'} = $config{'routers.cgi-iconpath'}
265		if( !defined $config{'routers.cgi-iconurl'}
266			and defined $config{'routers.cgi-iconpath'} );
267
268	# some path corrections: remove trailing path separators on f/s paths
269	foreach ( qw/dbpath confpath graphpath graphurl/ ) {
270		$config{"routers.cgi-$_"} =~ s/[\/\\]$//;
271	}
272	# and add a trailing path separator on URL paths...
273	$config{'routers.cgi-iconurl'} = "/rrdicons/"
274		if(!defined $config{'routers.cgi-iconurl'} );
275	$config{'routers.cgi-iconurl'} .= "/"
276		if( $config{'routers.cgi-iconurl'} !~ /\/$/ );
277
278	# allow [routers.cgi] section to override [web] section for some
279	# parameters
280	$config{'web-backurl'} = $config{'routers.cgi-backurl'}
281		if(defined $config{'routers.cgi-backurl'});
282
283	# For broken web servers (eg thttpd)
284	$myurl = $config{'routers.cgi-trendurl'}
285		if(defined $config{'routers.cgi-trendurl'} );
286	$myurl = $config{'trend.cgi-myurl'}
287		if(defined $config{'trend.cgi-myurl'} );
288}
289#######################################################################
290# stylesheet start_html
291sub start_html_ss
292{
293    my($opts,$bgopt) = @_;
294    my($ssheet) = "";
295
296    $opts->{-encoding} = $config{'web-charset'} if($config{'web-charset'});
297
298#    if(!defined $opts->{'-link'}) {
299#        $opts->{'-link'}=$linkcolour;
300#        $opts->{'-vlink'}=$linkcolour;
301#        $opts->{'-alink'}=$linkcolour;
302#    }
303#    $opts->{'-text'}=$deffgcolour if(!defined $opts->{'-text'});
304#    $opts->{'-bgcolor'}=$defbgcolour if(!defined $opts->{'-bgcolor'});
305
306    # If we have overridden things, then put it into the sheet here.
307    # overriding style sheet using mrtg .cfg file options
308    if( $bgopt and $opts->{-class}) {
309        $ssheet .= "body.".$opts->{'-class'}." { background: $bgopt }\n";
310    }
311    # overriding style sheet using routers2.conf options
312    # default pages
313    if( $config{"routers.cgi-bgcolour"} or $config{"routers.cgi-fgcolour"} ) {
314        $ssheet .= "body { ";
315        $ssheet .= " color: ".$config{"routers.cgi-fgcolour"}."; "
316            if($config{"routers.cgi-fgcolour"});
317        $ssheet .= " background: ".$config{"routers.cgi-bgcolour"}
318            if($config{"routers.cgi-bgcolour"});
319        $ssheet .= "}\n";
320    }
321    # links
322    $ssheet .=  "A:link { color: ".$config{'routers.cgi-linkcolour'}. " }\n "
323        ."A:visited { color: ".$config{'routers.cgi-linkcolour'}. " }\n "
324        ."A:hover { color: ".$config{'routers.cgi-linkcolour'}. " } \n"
325        if($config{'routers.cgi-linkcolour'});
326
327    if($config{'routers.cgi-stylesheet'}) {
328        $opts->{'-style'} = { -src=>$config{'routers.cgi-stylesheet'}, -code=>$ssheet };
329    }
330    print $q->start_html($opts)."\n";
331}
332
333#######################################################################
334# possible MODEs: interface, cpu, memory, generic (more to come)
335sub identify($) {
336	my( $key, %identify, $k, @d, $mode );
337	my($unit,$totunit,$okfile);
338	my($timel, $times);
339
340	$k = $_[0];
341
342	# description defaults
343	if(defined $config{"targetnames-$k"}) {
344		$interfaces{$k}{shdesc} = $config{"targetnames-$k"};
345	}
346	if(defined $config{"targettitles-$k"}) {
347		$interfaces{$k}{desc} = $config{"targettitles-$k"};
348	}
349	if(!defined $interfaces{$k}{shdesc}) {
350		if(!defined $config{'targetnames-ifdefault'}
351			or $config{'targetnames-ifdefault'} !~ /target/ ) {
352			if(defined $interfaces{$k}{ipaddress}) {
353				$interfaces{$k}{shdesc} = $interfaces{$k}{ipaddress};
354			} elsif(defined $interfaces{$k}{ifdesc}) {
355				$interfaces{$k}{shdesc} = $interfaces{$k}{ifdesc};
356			} elsif(defined $interfaces{$k}{ifno}) {
357				$interfaces{$k}{shdesc} = "#".$interfaces{$k}{ifno};
358			} else {
359#				$interfaces{$k}{desc} =~ /^(\S+)/;
360#				$interfaces{$k}{shdesc} = $1;
361				$interfaces{$k}{shdesc} = $interfaces{$k}{desc};
362			}
363		}
364		$interfaces{$k}{shdesc} = $k if(!defined $interfaces{$k}{shdesc});
365	}
366
367	# try and identify the interface
368	@d = ( $k, $interfaces{$k}{desc}, $interfaces{$k}{shdesc} );
369	$mode = "";
370	$mode = $interfaces{$k}{mode} if(defined $interfaces{$k}{mode});
371	%identify = ();
372	if(! $mode) {
373		if( inlist( "cpu", @d ) and ($interfaces{$k}{maxbytes}==100) )
374			{ $mode = "cpu"; }
375		elsif( defined $interfaces{$k}{ifno} or defined $interfaces{$k}{ifdesc}
376			or $interfaces{$k}{isif} or  defined $interfaces{$k}{ipaddress} )
377			{ $mode = "interface"; $interfaces{$k}{isif} = 1; }
378		elsif( inlist( "interface", @d ) or inlist("serial",@d)
379			or inlist( "ATM", @d )  or inlist( "[^x]port", @d ))
380			{ $mode = "interface"; }
381		elsif( inlist( "mem", @d ) ) { $mode = "memory"; }
382		else { $mode = "generic"; }
383		$interfaces{$k}{mode} = $mode;
384	}
385
386	# set appropriate defaults for thismode
387	if(!defined $interfaces{$k}{mult}) {
388		if($mode eq "interface" and
389			(!defined $config{'routers.cgi-bytes'}
390			or $config{'routers.cgi-bytes'} !~ /y/ )
391		) { $interfaces{$k}{mult} = 8; }
392		else { $interfaces{$k}{mult} = 1; }
393	}
394
395	# defaults for everything...
396	$timel = "second"; $times = "s"; $unit = ""; $totunit = "";
397	if($mode eq "interface") { $totunit = "bytes"; }
398	if($interfaces{$k}{mult} > 3599 ) {
399		$timel = "hour"; $times = "hr";
400		if($interfaces{$k}{mult} > 3600) { $totunit = "bits"; }
401	} elsif($interfaces{$k}{mult} >59 ) {
402		$timel = "minute"; $times = "min";
403		if($interfaces{$k}{mult} > 60) { $totunit = "bits"; }
404	} elsif($interfaces{$k}{mult} > 1) { $totunit = "bits"; }
405	$unit = "$totunit/$times";
406	$unit = "bps" if($unit eq "bits/s");
407	$identify{ylegend} = "per $timel";
408	$identify{background} = $bgcolour;
409	$identify{legendi} = "In: ";
410	$identify{legendo} = "Out:";
411	$identify{legend1} = "Incoming" ;
412	$identify{legend2} = "Outgoing";
413	$identify{legend3} = "Peak inbound";
414	$identify{legend4} = "Peak outbound";
415	$identify{total} = 1;
416	$identify{percentile} = 1;
417	$identify{percent} = 1;
418	$identify{unit} = $unit;
419	$identify{totunit} = $totunit;
420	$identify{unscaled} = "";
421
422	if($mode eq "interface") {
423		$identify{ylegend} = "traffic in $unit";
424		$identify{legendi} = "In: ";
425		$identify{legendo} = "Out:";
426		$identify{legend1} = "Incoming traffic";
427		$identify{legend2} = "Outgoing traffic";
428		$identify{legend3} = "Peak inbound traffic";
429		$identify{legend4} = "Peak outbound traffic";
430		$identify{icon} = "interface-sm.gif";
431#		$identify{background} = "#ffffff";
432		$identify{unscaled} = "6dwmy";
433		$identify{total} = 1;
434	} elsif( $mode eq "cpu" ) {
435		$identify{ylegend} = "Percentage use";
436		$identify{legendi} = "CPU";
437		$identify{unit} = "%";
438		$identify{fixunits} = 1;
439		$identify{totunit} = "";
440		$identify{legend1} = "CPU usage";
441		$identify{legend3} = "Peak CPU usage";
442		$identify{legend2} = "";
443		$identify{legend4} = "";
444		$identify{icon} = "cpu-sm.gif";
445#		$identify{background} = "#ffffd0";
446		$identify{unscaled} = "6dwmy";
447		$identify{percent} = 0;
448		$identify{total} = 0;
449	} elsif( $mode eq "memory" ) {
450		$identify{ylegend} = "Bytes used";
451		$identify{legendi} = "MEM";
452		$identify{legendo} = "MEM";
453		$identify{legend1} = "Memory usage";
454		$identify{legend3} = "Peak memory usage";
455		$identify{legend2} = "Sec. memory usage";
456		$identify{legend4} = "Peak sec memory usage";
457		$identify{icon} = "cpu-sm.gif";
458#		$identify{background} = "#d0d0ff";
459		$identify{total} = 0;
460		$identify{unit} = "bytes";
461		$identify{unit} = "bits" if($interfaces{$k}{bits});
462		$identify{totunit} = "";
463	} elsif( $mode eq "ping" ) {
464		$identify{totunit} = "";
465		$identify{unit} = "ms";
466		$identify{fixunit} = 1;
467		$identify{ylegend} = "milliseconds";
468		$identify{legendi} = "High:";
469		$identify{legendo} = "Low:";
470		$identify{legend1} = "Round trip time range";
471		$identify{legend2} = "Round trip time range";
472		$identify{legend3} = "High peak 5min RTT";
473		$identify{legend4} = "Low peak 5min RTT";
474		$identify{icon} = "clock-sm.gif";
475#		$identify{background} = "#ffffdd";
476		$identify{total} = 0;
477		$identify{percent} = 0;
478		$identify{percentile} = 0;
479		$identify{unscaled} = "";
480	}
481
482	# unscaled default option
483	if( defined $config{'routers.cgi-unscaled'} ) {
484		if( $config{'routers.cgi-unscaled'} =~ /y/i ) {
485			$identify{unscaled} = "6dwmy" ;
486		} else {
487			$identify{unscaled} = "" ;
488		}
489	}
490
491	# set icon
492	$identify{icon} = guess_icon( 0, $k, $interfaces{$k}{desc}, $interfaces{$k}{shdesc} ) if(!defined $identify{icon});
493
494	# different default for totunit
495	# if we have a custom 'unit' but no custom 'totunit', then try to be
496	# a bit more clever.
497	if( defined $interfaces{$k}{unit} ) {
498		my( $u ) = $interfaces{$k}{unit};
499		if( $u =~ /^(.*)\// ) {
500			$identify{totunit} = $1;
501		} elsif( $u =~ /^(.*)ps$/ ) {
502			$identify{totunit} = $1;
503		} else {
504			$identify{totunit} = $u;
505		}
506	}
507
508	# set the defaults
509	foreach $key ( keys %identify ) {
510		$interfaces{$k}{$key} = $identify{$key}
511			if(!defined $interfaces{$k}{$key} );
512	}
513
514	$interfaces{$k}{mult} = 1 if(!defined $interfaces{$k}{mult});
515	$interfaces{$k}{maxbytes} = 0 if(!defined $interfaces{$k}{maxbytes});
516	$interfaces{$k}{max} = $interfaces{$k}{maxbytes} * $interfaces{$k}{mult};
517	$interfaces{$k}{max1} = $interfaces{$k}{maxbytes1} * $interfaces{$k}{mult}
518		if(defined $interfaces{$k}{maxbytes1});
519	$interfaces{$k}{max2} = $interfaces{$k}{maxbytes2} * $interfaces{$k}{mult}
520		if(defined $interfaces{$k}{maxbytes2});
521	$interfaces{$k}{max} = $interfaces{$k}{max1} if(defined $interfaces{$k}{max1} and $interfaces{$k}{max1} > $interfaces{$k}{max} );
522	$interfaces{$k}{max} = $interfaces{$k}{max2} if(defined $interfaces{$k}{max2} and $interfaces{$k}{max2} > $interfaces{$k}{max} );
523	$interfaces{$k}{absmax}
524		= $interfaces{$k}{absmaxbytes} * $interfaces{$k}{mult}
525		if(defined $interfaces{$k}{absmaxbytes});
526	if(defined $interfaces{$k}{factor} ) {
527		$interfaces{$k}{max} *= $interfaces{$k}{factor};
528		$interfaces{$k}{absmax} *= $interfaces{$k}{factor}
529			if(defined $interfaces{$k}{absmax});
530		$interfaces{$k}{max1} *= $interfaces{$k}{factor}
531			if(defined $interfaces{$k}{max1});
532		$interfaces{$k}{max2} *= $interfaces{$k}{factor}
533			if(defined $interfaces{$k}{max2});
534	}
535	$interfaces{$k}{noo} = 1 if(!$interfaces{$k}{legend2});
536	$interfaces{$k}{noi} = 1 if(!$interfaces{$k}{legend1});
537
538	# catch the stupid people
539	if($interfaces{$k}{noo} and $interfaces{$k}{noi}) {
540		$interfaces{$k}{inmenu} = 0;
541		$interfaces{$k}{insummary} = 0;
542		$interfaces{$k}{inout} = 0;
543	}
544
545}
546# guess an appropriate icon.  1st param is 1 for devices menu, 0 for targets
547# other parameters are a list of attributes to check
548sub guess_icon($@)
549{
550	my($m) = shift @_;
551
552	if($m) {
553		# these tests for devices menu only
554		return "cisco-sm.gif" if( inlist "cisco",@_ );
555		return "3com-sm.gif" if( inlist "3com",@_ );
556		return "intel-sm.gif" if( inlist "intel",@_ );
557		return "router-sm.gif" if( inlist "router",@_ );
558		return "switch-sm.gif" if( inlist "switch",@_ );
559		return "firewall-sm.gif" if( inlist "firewall",@_ );
560		return "ibm-sm.gif" if( inlist "ibm",@_ );
561		return "linux-sm.gif" if( inlist "linux",@_ );
562		return "freebsd-sm.gif" if( inlist "bsd",@_ );
563		return "novell-sm.gif" if( inlist "novell",@_ );
564# these commented out as patterns are too short to be reliable
565#		return "mac-sm.gif" if( inlist "mac|apple",@_ );
566#		return "sun-sm.gif" if( inlist "sun",@_ );
567#		return "hp-sm.gif" if( inlist "hp",@_ );
568		return "win-sm.gif" if( inlist "windows",@_ );
569	}
570	return "mail-sm.gif"    if( inlist 'mail|messages',@_ );
571	return "web-sm.gif"     if( inlist 'internet',@_  or inlist 'proxy',@_ );
572	return "phone-sm.gif"   if( inlist 'phone',@_ );
573	return "modem-sm.gif"   if( inlist 'modem',@_ );
574	return "disk-sm.gif"    if( inlist 'nfs\w',@_ or inlist 'dsk',@_ );
575	return "globe-sm.gif"   if( inlist 'dns\w',@_ );
576	return "people-sm.gif"  if( inlist 'user[s ]',@_ );
577	return "server-sm.gif"  if( inlist 'server|host',@_ );
578	return "web-sm.gif"     if( inlist 'web',@_ );
579	return "traffic-sm.gif" if( inlist 'traffic',@_ );
580	return "chip-sm.gif"    if( inlist 'memory|cpu',@_ );
581	return "interface-sm.gif" if(!$m and  inlist 'interface|serial',@_ );
582	return "disk-sm.gif"    if( inlist 'dis[kc]|filesystem',@_ );
583	return "clock-sm.gif"   if( inlist 'time|rtt|ping',@_ );
584	return "temp-sm.gif"    if( inlist 'temp|climate|environment|heat',@_ );
585	return "menu-sm.gif"    if( inlist '\wlog',@_ );
586	return "interface-sm.gif" if(!$m and  inlist 'BRI|eth|tok|ATM|hme',@_ );
587	return "load-sm.gif"    if( inlist 'load|weight',@_ );
588	return "web-sm.gif"     if( inlist 'www',@_ );
589
590	if($m) {
591		# last chance with these less reliable ones
592		return "mac-sm.gif" if( inlist "mac|apple",@_ );
593		return "sun-sm.gif" if( inlist "sun",@_ );
594		return "hp-sm.gif"  if( inlist "hp",@_ );
595		return "win-sm.gif" if( inlist "win|pdc|bdc",@_ );
596	}
597
598	if($m) {
599		return $config{'targeticons-filedefault'}
600			if(defined $config{'targeticons-filedefault'});
601		return "menu-sm.gif";
602	} else {
603		return $config{'targeticons-ifdefault'}
604			if(defined $config{'targeticons-ifdefault'});
605		return "target-sm.gif";
606	}
607}
608
609#######################################################################
610# read in a specified cfg file (default to current router file)
611
612# interfaces hash: key= targetname
613#            data: hash:
614#            keys: lots.
615
616sub read_cfg_file
617{
618	my($cfgfile) = $_[0];
619	my($opts, $graph, $key, $k, $fd, $buf, $curif, @myifs, $arg,$rrd);
620	my($ifcnt, @ifarr, $t, $desc, $url, $icon, $targ, $newfile, $targfile);
621	my( $lasthostname, $lastcommunity ) = ("","");
622
623	my( $inpagetop, $inpagefoot ) = (0,0);
624
625	return if(!$cfgfile);
626
627	$fd = new FileHandle ;
628
629	if(! $fd->open( "<$cfgfile" )) {
630		return;
631	}
632
633	$key = ""; $curif = ""; @myifs = ();
634	while ( $buf = <$fd> ) {
635		next if( $buf =~ /^\s*#/ );
636		next if( $buf =~ /^\s*$/ ); # bit more efficient
637		if( $inpagefoot ) {
638			if( $curif and $buf =~ /^\s+\S/ ) {
639				$interfaces{$curif}{pagefoot} .= $buf;
640				next;
641			}
642			$inpagefoot = 0;
643		}
644		if( $inpagetop ) {
645			if( $curif and $buf =~ /^\s+\S/ ) {
646				$interfaces{$curif}{pagetop} .= $buf;
647				next;
648			}
649			$inpagetop = 0;
650		}
651		if( $buf =~ /^\s*Target\[(.+?)\]\s*:\s*(.+)/i ) {
652			$curif = $1; $arg = $2;
653			next if(defined $interfaces{$curif});
654			push @myifs, $curif;
655			$interfaces{$curif} = { file=>$cfgfile, target=>$curif,
656					insummary=>1, incompact=>1, inmenu=>1, isif=>0,
657					interval=>$interval, nomax=>0, noabsmax=>0  };
658			$interfaces{$curif}{rrd} = $workdir.$pathsep.(lc $curif).".rrd";
659			if( $arg =~ /^-?(\d+):([^\@:\s]+)\@([\w\-\.]+)/ ) {
660				# interface number
661				$interfaces{$curif}{isif} = 1;
662				$interfaces{$curif}{ifno} = $1;
663				$interfaces{$curif}{community} = $2;
664				$interfaces{$curif}{hostname} = $3;
665				$interfaces{$curif}{mode} = "interface";
666			} elsif( $arg =~ /^-?\/(\d+\.\d+\.\d+\.\d+):([^\@:\s]+)\@([\w\-\.]+)/ ) {
667				# IP address
668				$interfaces{$curif}{isif} = 1;
669				$interfaces{$curif}{ipaddress} = $1;
670				$interfaces{$curif}{community} = $2;
671				$interfaces{$curif}{hostname} = $3;
672				$interfaces{$curif}{mode} = "interface";
673			} elsif( $arg =~ /^-?[\\#!](\S.*?):([^\@:\s]+)\@([\w\-\.]+)/ ) {
674				$interfaces{$curif}{isif} = 1;
675				$interfaces{$curif}{ifdesc} = $1;
676				$interfaces{$curif}{community} = $2;
677				$interfaces{$curif}{hostname} = $3;
678				$interfaces{$curif}{mode} = "interface";
679				$interfaces{$curif}{ifdesc} =~ s/\\(.)/$1/g ;
680			} elsif( $arg =~ /&\w*[\d\.]+:(\S+)\@([\w\-\.]+)/ ) {
681				# explicit OIDs
682				$interfaces{$curif}{community} = $1;
683				$interfaces{$curif}{hostname} = $2;
684			} elsif( $arg =~ /mrtg.ping.probe/ ) {
685				# special for the mrtg-ping-probe.pl
686				$interfaces{$curif}{mode} = "ping";
687				$interfaces{$curif}{graphstyle} = "range";
688				$interfaces{$curif}{incompact} = 1;
689				$interfaces{$curif}{ifdesc} = "Response time" ;
690			} elsif( $arg =~ /`/ ) {
691				# external program
692				$interfaces{$curif}{insummary} = 1;
693				$interfaces{$curif}{incompact} = 1;
694			} else { # a target of some sort we dont yet know
695				$interfaces{$curif}{insummary} = 0;
696				$interfaces{$curif}{incompact} = 0;
697			}
698			$interfaces{$curif}{inout} = $interfaces{$curif}{isif};
699			foreach $k ( qw/isif inout incompact insummary inmenu/ ) {
700				$interfaces{$curif}{$k} = $interfaces{'_'}{$k}
701					if(defined $interfaces{'_'}{$k});
702			}
703			$lasthostname = $interfaces{$curif}{hostname}
704				if(defined $interfaces{$curif}{hostname});
705			$lastcommunity= $interfaces{$curif}{community}
706				if(defined $interfaces{$curif}{community});
707			next;
708		}
709		if( $buf =~ /^\s*(routers\.cgi\*)?(Title|Descr?)\[(.+?)\]\s*:\s*(\S.*)/i ) {
710			$curif = $3; $arg = $4;
711			if(!defined $interfaces{$curif}) {
712				$curif = "_$curif";
713				next if(!defined $interfaces{$curif});
714			}
715			$interfaces{$curif}{desc} = $arg;
716			next;
717		}
718		if( $buf =~ /^\s*Options\[(.+?)\]\s*:\s*(\S.*)/i ) {
719			$curif = $1;
720			next if(!defined $interfaces{$curif});
721			$interfaces{$curif}{options} = "" if(!$interfaces{$curif}{options});
722			$interfaces{$curif}{options} .= ' '.$2;
723			next;
724		}
725		if( $buf =~ /^\s*(routers\.cgi\*)?PageTop\[(.+?)\]\s*:\s*(\S.*)/i ) {
726			$curif = $2;  $arg = $3;
727			next if(!defined $interfaces{$curif});
728			$interfaces{$curif}{pagetop} = $arg;
729			$inpagetop = 1;
730			next;
731		}
732		if( $buf =~ /^\s*(routers\.cgi\*)?PageFoot\[(.+?)\]\s*:\s*(\S.*)/i ) {
733			$curif = $2;  $arg = $3;
734			next if(!defined $interfaces{$curif});
735			$interfaces{$curif}{pagefoot} = $arg;
736			$inpagefoot = 1;
737			next;
738		}
739		if( $buf =~ /^\s*SetEnv\[(.+?)\]\s*:\s*(\S.*)/i ) {
740			$curif = $1; $arg = $2;
741			next if(!defined $interfaces{$curif});
742			foreach $k ( quotewords('\s+',0,$arg) ) {
743				if( $k =~ /MRTG_INT_IP=\s*(\d+\.\d+\.\d+\.\d+)/ ) {
744					$interfaces{$curif}{ipaddress}=$1
745					if(!defined $interfaces{$curif}{ipaddress});
746					next;
747				}
748				if( $k =~ /MRTG_INT_DESCR?=\s*(\S.*)/ ) {
749					$interfaces{$curif}{shdesc}=$1
750					if(!defined $interfaces{$curif}{shdesc});
751					next;
752				}
753			}
754			next;
755		}
756		if( $buf =~ /^\s*routers\.cgi\*Short(Name|Descr?)\[(.+?)\]\s*:\s*(\S.*)/i ) {
757			$curif = $2; $arg = $3;
758			$curif = "_".$curif if(!defined $interfaces{$curif});
759			next if(!defined $interfaces{$curif});
760			$interfaces{$curif}{shdesc} = $arg if($arg);
761			next;
762		}
763		if( $buf =~ /^\s*routers\.cgi\*Options\[(.+?)\]\s*:\s*(\S.*)/i ) {
764			next if(!defined $interfaces{$1});
765			$interfaces{$1}{cgioptions}="" if(!$interfaces{$1}{cgioptions});
766			$interfaces{$1}{cgioptions} .= " ".$2;
767			next;
768		}
769		if( $buf =~ /^\s*(routers\.cgi\*)?MaxBytes\[(.+?)\]\s*:\s*(\d+)/i ) {
770			next if(!defined $interfaces{$2});
771			$interfaces{$2}{maxbytes} = $3;
772			next;
773		}
774		if($buf=~ /^\s*(routers\.cgi\*)?Unscaled\[(.+?)\]\s*:\s*([6dwmy]*)/i){
775			next if(!defined $interfaces{$2});
776			$interfaces{$2}{unscaled} = $3;
777			next;
778		}
779		if($buf=~ /^\s*(routers\.cgi\*)?WithPeaks?\[(.+?)\]\s*:\s*([dwmy]*)/i) {
780			next if(!defined $interfaces{$2});
781			$interfaces{$2}{withpeak} = $3;
782			next;
783		}
784		if( $buf =~ /^\s*(routers\.cgi\*)?YLegend\[(.+?)\]\s*:\s*(\S.*)/i ) {
785			$curif = $2;
786			$curif = "_$curif" if(!defined $interfaces{$curif});
787			next if(!defined $interfaces{$curif});
788			$interfaces{$curif}{ylegend} = $3;
789			next;
790		}
791		if($buf=~ /^\s*(routers\.cgi\*)?ShortLegend\[(.+?)\]\s*:\s*(.*)/i){
792			next if(!defined $interfaces{$2});
793			$interfaces{$2}{unit} = $3;
794			$interfaces{$2}{unit} =~ s/&nbsp;/ /g;
795			next;
796		}
797		if($buf=~ /^\s*routers\.cgi\*TotalLegend\[(.+?)\]\s*:\s*(.*)/i){
798			$curif = $1; $arg = $2;
799			next if(!defined $interfaces{$curif});
800			$arg =~ s/&nbsp;/ /g;
801			$interfaces{$curif}{totunit} = $arg;
802			next;
803		}
804		if( $buf =~ /^\s*(routers\.cgi\*)?(Legend[IO1234TA][IO]?)\[(.+?)\]\s*:\s*(\S.*)/i ) {
805			$curif = $3; $key = lc $2; $arg = $4;
806			$curif = "_$curif" if(!defined $interfaces{$curif});
807			next if(!defined $interfaces{$curif});
808			$arg =~ s/&nbsp;/ /;
809			$interfaces{$curif}{$key} = $arg;
810			next;
811		}
812		if( $buf =~ /^\s*routers\.cgi\*Mode\[(.+?)\]\s*:\s*(\S+)/i ) {
813			next if(!defined $interfaces{$1});
814			$interfaces{$1}{mode} = $2;
815			next;
816		}
817		if( $buf =~ /^\s*routers\.cgi\*Graph\[(.+?)\]\s*:\s*(\S.*)/i ) {
818			$curif = $1; $arg = $2;
819			if( $arg =~ /^"/ ) {
820				$arg =~ /^"([^"]+)"\s*:?(.*)/;
821				$opts = $2; $graph = $1;
822			} else {
823				$arg =~ /^(\S+)\s*:?(.*)/;
824				$opts = $2; $graph = $1;
825			}
826				next if(!$graph);
827			$interfaces{$curif}{usergraphs} = []
828				if(!defined $interfaces{$curif}{usergraphs});
829			push @{$interfaces{$curif}{usergraphs}}, $graph;
830			if( defined $interfaces{"_$graph"} ) {
831				push @{$interfaces{"_$graph"}{targets}}, $curif;
832				$interfaces{"_$graph"}{cgioptions} .= " $opts";
833			} else {
834				$interfaces{"_$graph"} = {
835					shdesc=>$graph,  targets=>[$curif],
836					cgioptions=>$opts, mode=>"\177_USER",
837					usergraph=>1, icon=>"cog-sm.gif", inout=>0, incompact=>0,
838					insummary=>0, inmenu=>1, desc=>"User defined graph $graph",
839					withtotal=>0, withaverage=>0
840				};
841				$interfaces{"_$graph"}{withtotal} = 1
842					if( defined $config{'routers.cgi-showtotal'}
843						and $config{'routers.cgi-showtotal'}=~/y/i);
844				push @myifs, "_$graph";
845			}
846			next;
847		}
848		if( $buf =~ /^\s*routers.cgi\*Icon\[(.+?)\]\s*:\s*(\S+)/i ) {
849			$curif = $1; $arg = $2;
850			if(!defined $interfaces{$curif}) {
851				$curif = "_$curif";
852				next if(!defined $interfaces{$curif});
853			}
854			next if(!defined $interfaces{$curif});
855			$interfaces{$curif}{icon} = $arg;
856			next;
857		}
858		if( $buf =~ /^\s*routers.cgi\*Ignore\[(.+?)\]\s*:\s*(\S+)/i ) {
859			$curif = $1; $arg = $2;
860			next if(!defined $interfaces{$curif});
861			if( $arg =~ /y/i ) {
862				$interfaces{$curif}{insummary} = 0;
863				$interfaces{$curif}{inmenu} = 0;
864				$interfaces{$curif}{inout} = 0;
865				$interfaces{$curif}{isif} = 0;
866			}
867			next;
868		}
869		if( $buf =~ /^\s*routers.cgi\*InSummary\[(.+?)\]\s*:\s*(\S+)/i ) {
870			$curif = $1; $arg = $2;
871			next if(!defined $interfaces{$curif});
872			if( $arg =~ /[1y]/i ) {  $interfaces{$curif}{insummary} = 1; }
873			else { $interfaces{$curif}{insummary} = 0; }
874			next;
875		}
876		if( $buf =~ /^\s*routers.cgi\*InMenu\[(.+?)\]\s*:\s*(\S+)/i ) {
877			$curif = $1; $arg = $2;
878			next if(!defined $interfaces{$curif});
879			if( $arg =~ /[1y]/i ) {  $interfaces{$curif}{inmenu} = 1; }
880			else { $interfaces{$curif}{inmenu} = 0; }
881			next;
882		}
883		if( $buf =~ /^\s*routers.cgi\*InOut\[(.+?)\]\s*:\s*(\S+)/i ) {
884			$curif = $1; $arg = $2;
885			next if(!defined $interfaces{$curif});
886			if( $arg =~ /[1y]/i ) {  $interfaces{$curif}{inout} = 1; }
887			else { $interfaces{$curif}{inout} = 0; }
888			next;
889		}
890		if( $buf =~ /^\s*routers.cgi\*InCompact\[(.+?)\]\s*:\s*(\S+)/i ) {
891			$curif = $1; $arg = $2;
892			next if(!defined $interfaces{$curif});
893			if( $arg =~ /[1y]/i ) {  $interfaces{$curif}{incompact} = 2; }
894			else { $interfaces{$curif}{incompact} = 0; }
895			next;
896		}
897		if( $buf =~ /^\s*Background\[(.+?)\]\s*:\s*(#[a-f\d]+)/i ) {
898			next if(!defined $interfaces{$1});
899			$interfaces{$1}{background} = $2;
900			next;
901		}
902		if( $buf =~ /^\s*Timezone\[(.+?)\]\s*:\s*(\S.*)/i ) {
903			next if(!defined $interfaces{$1});
904			$interfaces{$1}{timezone} = $2;
905			next;
906		}
907		if( $buf =~ /^\s*Directory\[(.+?)\]\s*:\s*(\S.*)/i ) {
908			$curif = $1; $arg = $2;
909			next if(!defined $interfaces{$curif});
910			$arg =~ s/[\s\\\/]+$//; # trim trailing spaces and path separators!
911			$interfaces{$curif}{directory} = $arg;
912			$interfaces{$curif}{rrd} =
913				$workdir.$pathsep.$arg.$pathsep.(lc $curif).".rrd";
914			next;
915		}
916		if( $buf =~ /^\s*Workdir\s*:\s*(\S+)/i ) {
917			$workdir = $1; $workdir =~ s/[\\\/]+$//; next; }
918		if( $buf =~ /^\s*Interval\s*:\s*(\d+)/i ) { $interval = $1; next; }
919		if( $buf =~ /^\s*Include\s*:\s*(\S+)/i ) {
920			$newfile = $1;
921			$newfile = (dirname $cfgfile).$pathsep.$newfile
922				if( $newfile !~ /^([a-zA-Z]:)?[\/\\]/ );
923			read_cfg_file($newfile);
924			next;
925		}
926		if( $buf =~ /^\s*LibAdd\s*:\s*(\S+)/i ) { unshift @INC, $1; next; }
927		if($buf=~ /^\s*(routers\.cgi\*)?MaxBytes(\d)\[(\S+)\]\s*:\s*(\d+)/i ){
928			$curif = $3; $arg = $4;
929			next if(!defined $interfaces{$curif});
930			$interfaces{$curif}{"maxbytes$2"} = $arg;
931			$interfaces{$curif}{maxbytes} = $arg
932				if(!$interfaces{$curif}{maxbytes});
933			next;
934		}
935		# the regexp from hell
936		if( $buf =~ /^\s*(routers\.cgi\*)?Colou?rs\[(.+?)\]\s*:\s*[^#]*(#[\da-f]{6})[\s,]+[^#]*(#[\da-f]{6})[\s,]+[^#]*(#[\da-f]{6})[\s,]+[^#]*(#[\da-f]{6})/i ) {
937			$curif = $2;
938			$curif = "_$curif" if(!defined $interfaces{$curif});
939			next if(!defined $interfaces{$curif});
940			$interfaces{$curif}{colours} = [ $3,$4,$5,$6 ];
941			next;
942		}
943		if( $buf =~ /^\s*routers\.cgi\*MBLegend\[(.+?)\]\s*:\s*(\S.*)/i ) {
944			$curif = $1;
945			$curif = "_$curif" if(!defined $interfaces{$curif});
946			$interfaces{$curif}{mblegend} = $2;
947			next;
948		}
949		if( $buf =~ /^\s*routers\.cgi\*AMLegend\[(.+?)\]\s*:\s*(\S.*)/i ) {
950			$curif = $1;
951			$curif = "_$curif" if(!defined $interfaces{$curif});
952			$interfaces{$curif}{amlegend} = $2;
953			next;
954		}
955		if( $buf =~ /^\s*(routers\.cgi\*)?AbsMax\[(.+?)\]\s*:\s*(\d+)/i ) {
956			next if(!defined $interfaces{$2});
957			$interfaces{$2}{absmaxbytes} = $3;
958			next;
959		}
960		if( $buf =~ /^\s*WeekFormat(\[.+?\])?\s*:\s*%?([UVW])/i ) {
961			# yes I know this is ugly, it is being retrofitted
962			$monthlylabel = "%".$2;
963			next;
964		}
965		if( $buf =~ /^\s*routers\.cgi\*GraphStyle\[(.+?)\]\s*:\s*(\S+)/i ) {
966			$curif = $1; $arg = $2;
967			$curif = "_$curif" if(!defined $interfaces{$curif});
968			next if(!defined $interfaces{$curif});
969			$interfaces{$curif}{graphstyle} = $arg;
970			next;
971		}
972		if( $buf =~ /^\s*(routers\.cgi\*)?Factor\[(.+?)\]\s*:\s*([\d\.]+)/i ) {
973			$curif = $2; $arg = $3;
974			next if(!defined $interfaces{$curif});
975			$interfaces{$curif}{factor} = $arg if($arg > 0);
976			next;
977		}
978		if( $buf =~ /^\s*(routers\.cgi\*)?Supp?ress?\[(.+?)\]\s*:\s*(\S+)/i ) {
979			$curif = $2; $arg = $3;
980			$curif = "_$curif" if(!defined $interfaces{$curif});
981			next if(!defined $interfaces{$curif});
982			$interfaces{$curif}{suppress} = $arg;
983			next;
984		}
985	}
986	$fd->close;
987
988	# now take the current file defaults
989	foreach $key ( keys %{$interfaces{'_'}} ) {
990		foreach $curif ( @myifs ) {
991			$interfaces{$curif}{$key} = $interfaces{'_'}{$key}
992				if(!defined $interfaces{$curif}{$key});
993		}
994	}
995	foreach $key ( keys %{$interfaces{'^'}} ) {
996		foreach $curif ( @myifs ) {
997			$interfaces{$curif}{$key} = $interfaces{'^'}{$key}.' '.$interfaces{$curif}{$key};
998		}
999	}
1000	foreach $key ( keys %{$interfaces{'$'}} ) {
1001		foreach $curif ( @myifs ) {
1002			$interfaces{$curif}{$key} .= ' '.$interfaces{'$'}{$key};
1003		}
1004	}
1005
1006	# now process the options
1007	foreach $curif ( @myifs ) {
1008		next if(!$curif);
1009		if(defined $interfaces{$curif}{options} ) {
1010		foreach $k ( split /[\s,]+/,$interfaces{$curif}{options} ) {
1011			$interfaces{$curif}{noo} = 1 if( $k eq "noo");
1012			$interfaces{$curif}{noi} = 1 if( $k eq "noi");
1013			if( $k eq "bits") { $interfaces{$curif}{bits} = 1; }
1014			if( $k eq "perminute") {
1015				$interfaces{$curif}{perminute} = 1
1016					if(!defined $interfaces{$curif}{perhour}
1017						and !defined $interfaces{$curif}{perminute});
1018			}
1019			if( $k eq "perhour") {
1020				$interfaces{$curif}{perhour} = 1
1021					if(!defined $interfaces{$curif}{perhour}
1022						and !defined $interfaces{$curif}{perminute});
1023			}
1024			if( $k eq "nopercent") {
1025				$interfaces{$curif}{percent} = 0 ;
1026				# default incompact to NO if target has nopercent set
1027				$interfaces{$curif}{incompact} = 0
1028					if($interfaces{$curif}{incompact} == 1);
1029			}
1030			$interfaces{$curif}{integer} = 1 if( $k eq "integer");
1031		} }
1032		if ( defined $interfaces{$curif}{cgioptions} ) {
1033		  foreach $k ( split /[\s,]+/,$interfaces{$curif}{cgioptions} ) {
1034			$interfaces{$curif}{available} = 1 if( $k eq "available");
1035			$interfaces{$curif}{available} = 0 if( $k eq "noavailable");
1036			$interfaces{$curif}{noo} = 1 if( $k eq "noo");
1037			$interfaces{$curif}{noi} = 1 if( $k eq "noi");
1038#			$interfaces{$curif}{mult} = 8 if( $k eq "bits");
1039#			$interfaces{$curif}{mult} = 1 if( $k eq "bytes");
1040			$interfaces{$curif}{noi} = 1 if( $k eq "noi");
1041			if( $k eq "bytes") { $interfaces{$curif}{bytes} = 1;
1042				$interfaces{$curif}{bits} = 0; }
1043			if( $k eq "bits") { $interfaces{$curif}{bits} = 1;
1044				$interfaces{$curif}{bytes} = 0;  }
1045			if( $k eq "perminute") {
1046				$interfaces{$curif}{perminute} = 1
1047					if(!defined $interfaces{$curif}{perhour}
1048						and !defined $interfaces{$curif}{perminute});
1049			}
1050			if( $k eq "perhour") {
1051				$interfaces{$curif}{perhour} = 1
1052					if(!defined $interfaces{$curif}{perhour}
1053						and !defined $interfaces{$curif}{perminute});
1054			}
1055			$interfaces{$curif}{isif} = 1 if($k eq "interface");
1056			if( $k eq "ignore") {
1057				$interfaces{$curif}{inmenu} = 0 ;
1058				$interfaces{$curif}{insummary} = 0 ;
1059				$interfaces{$curif}{inout} = 0 ;
1060				$interfaces{$curif}{incompact} = 0 ;
1061			}
1062			$interfaces{$curif}{unscaled} = "" if( $k eq "scaled");
1063			$interfaces{$curif}{total} = 0 if( $k eq "nototal");
1064			$interfaces{$curif}{percentile} = 0 if( $k eq "nopercentile");
1065			if( $k eq "summary" ) {
1066				$interfaces{$curif}{summary} = 1;
1067				$interfaces{$curif}{compact} = 0;
1068				$interfaces{$curif}{withtotal} = 0;
1069				$interfaces{$curif}{withaverage} = 0;
1070				$interfaces{$curif}{insummary} = 0 ;
1071				$interfaces{$curif}{incompact} = 0 ;
1072			}
1073			if( $k eq "compact" ) {
1074				$interfaces{$curif}{summary} = 0;
1075				$interfaces{$curif}{compact} = 1;
1076				$interfaces{$curif}{withtotal} = 0;
1077				$interfaces{$curif}{withaverage} = 0;
1078				$interfaces{$curif}{insummary} = 0 ;
1079				$interfaces{$curif}{incompact} = 0 ;
1080			}
1081			if( $k eq "total") {
1082				 $interfaces{$curif}{withtotal} = 1 ;
1083				 $interfaces{$curif}{total} = 1 ;
1084			}
1085			$interfaces{$curif}{withaverage} = 1 if( $k eq "average");
1086			$interfaces{$curif}{nolegend} = 1 if( $k eq "nolegend");
1087			$interfaces{$curif}{nodetails} = 1 if( $k eq "nodetails");
1088			$interfaces{$curif}{nomax} = 1 if( $k eq "nomax");
1089			$interfaces{$curif}{noabsmax} = 1 if( $k eq "noabsmax");
1090			$interfaces{$curif}{percent} = 0 if( $k eq "nopercent");
1091			$interfaces{$curif}{integer} = 1 if( $k eq "integer");
1092			if( $k =~ /^#[\da-fA-F]{6}$/ ) {
1093				$interfaces{$curif}{colours} = []
1094					if(!defined $interfaces{$curif}{colours});
1095				push @{$interfaces{$curif}{colours}}, $k;
1096			}
1097			$interfaces{$curif}{fixunits} = 1
1098				if( $k =~ /^fixunits?/i or $k =~ /^nounits?/i );
1099		  }
1100		}
1101		# fix the mult
1102		if($interfaces{$curif}{bytes}) {
1103			$interfaces{$curif}{mult} = 1;
1104		} elsif($interfaces{$curif}{bits} ) {
1105			$interfaces{$curif}{mult} = 8;
1106		}
1107		if($interfaces{$curif}{perminute}) {
1108			$interfaces{$curif}{mult}=1 if(!$interfaces{$curif}{mult});
1109			$interfaces{$curif}{mult} *= 60;
1110		} elsif($interfaces{$curif}{perhour}) {
1111			$interfaces{$curif}{mult}=1 if(!$interfaces{$curif}{mult});
1112			$interfaces{$curif}{mult} *= 3600;
1113		}
1114		# sanity check
1115		if( $interfaces{$curif}{incompact} and !$interfaces{$curif}{maxbytes}){
1116			$interfaces{$curif}{incompact} = 0;
1117		}
1118		# RRD file name
1119		if(! $interfaces{$curif}{usergraph} ) {
1120			$rrd = $workdir;
1121			$rrd .= $pathsep.$interfaces{$curif}{directory}
1122				if($interfaces{$curif}{directory});
1123			$rrd .= $pathsep.(lc $curif).".rrd";
1124			$interfaces{$curif}{rrd} = $rrd;
1125		}
1126	}
1127
1128	# now read the corresponding .ok file, if it exists
1129	$cfgfile =~ s/\.conf$/.ok/;
1130	$cfgfile =~ s/\.cfg$/.ok/;
1131	if( open OK, "<$cfgfile" )  {
1132		my( %ifdesc ) = ();
1133		my( %ifip ) = ();
1134		while( <OK> ) {
1135			if( /\tDescr\t(.+)\t(\d+)/ ) {
1136				$ifdesc{$2} = $1; $ifdesc{$1} = $2;
1137			}
1138			if( /\tIp\t(.+)\t(\d+)/ ) {
1139				$ifip{$2} = $1; $ifip{$1} = $2;
1140			}
1141		}
1142		close OK;
1143
1144		foreach $curif ( @myifs ) {
1145		if(!defined $interfaces{$curif}{ifno}) {
1146			$interfaces{$curif}{ifno} = $ifdesc{$interfaces{$curif}{ifdesc}}
1147			if(defined $interfaces{$curif}{ifdesc}
1148				and defined $ifdesc{$interfaces{$curif}{ifdesc}});
1149			$interfaces{$curif}{ifno} = $ifip{$interfaces{$curif}{ipaddress}}
1150			if(defined $interfaces{$curif}{ipaddress}
1151				and defined $ifip{$interfaces{$curif}{ipaddress}});
1152		}
1153		if(defined $interfaces{$curif}{ifno}) {
1154			$key = $interfaces{$curif}{ifno};
1155			$interfaces{$curif}{ifdesc} = $ifdesc{$key}
1156			if(defined $ifdesc{$key} and !defined $interfaces{$curif}{ifdesc});
1157			$interfaces{$curif}{ipaddress} = $ifip{$key}
1158			if(defined $ifip{$key} and !defined $interfaces{$curif}{ipaddress});
1159		}
1160		}
1161	} # ok file exists
1162
1163	# now set up userdefined graphs for Incoming and Outgoing, if it is
1164	# necessary.
1165	$ifcnt = 0; @ifarr = (); $curif="";
1166	foreach ( @myifs ) {
1167		$curif = $_ if(!$curif and $interfaces{$_}{community}
1168				and $interfaces{$_}{hostname} );
1169		if($interfaces{$_}{inout}) {
1170			$ifcnt++;
1171			push @ifarr, $_;
1172		}
1173	}
1174	if($ifcnt) {
1175		$t = "";
1176		if( defined $interfaces{'_incoming'} ) {
1177			push @{$interfaces{'_incoming'}{targets}},@ifarr
1178				if( $interfaces{'_incoming'}{mode} =~ /_AUTO/ );
1179		} else {
1180			$interfaces{'_incoming'} = {
1181			usergraph=>1, insummary=>0, inmenu=>1, inout=>0, incompact=>0,
1182			shdesc=>"Incoming",  targets=>[@ifarr], noo=>1, mult=>8,
1183			icon=>"incoming-sm.gif", mode=>"\177_AUTO",
1184			desc=>$t."Incoming traffic",
1185			withtotal=>0, withaverage=>0
1186			};
1187			if(defined $config{'routers.cgi-showtotal'}
1188				and $config{'routers.cgi-showtotal'}=~ /y/i ) {
1189				$interfaces{'_incoming'}{withtotal} = 1;
1190			}
1191		}
1192		if( defined $interfaces{'_outgoing'}  ) {
1193			push @{$interfaces{'_outgoing'}{targets}},@ifarr
1194				if( $interfaces{'_outgoing'}{mode} =~ /_AUTO/ );
1195		} else {
1196			$interfaces{'_outgoing'} = {
1197			usergraph=>1, insummary=>0, inmenu=>1, inout=>0, incompact=>0,
1198			shdesc=>"Outgoing",  targets=>[@ifarr], noi=>1, mult=>8,
1199			icon=>"outgoing-sm.gif", mode=>"\177_AUTO",
1200			desc=>$t."Outgoing traffic",
1201			withtotal=>0, withaverage=>0
1202			};
1203			if(defined $config{'routers.cgi-showtotal'}
1204				and $config{'routers.cgi-showtotal'}=~ /[1y]/i ) {
1205				$interfaces{'_outgoing'}{withtotal} = 1;
1206			}
1207		}
1208	}
1209}
1210
1211sub read_cfg($)
1212{
1213	my($cfgfile) = $_[0];
1214	my($l,$key,$k);
1215
1216	return if (!$cfgfile);
1217
1218	%interfaces = ( '_'=>{x=>0}, '^'=>{x=>0}, '$'=>{x=>0} );
1219	$interval = 5;
1220	$workdir = $config{'routers.cgi-dbpath'};
1221
1222	read_cfg_file($cfgfile); # recursive
1223
1224	# zap defaults
1225	delete $interfaces{'_'};
1226	delete $interfaces{'$'};
1227	delete $interfaces{'^'};
1228	delete $interfaces{''} if(defined $interfaces{''});
1229
1230	# first pass for interfaces
1231	foreach $key ( keys %interfaces ) {
1232		next if($interfaces{$key}{usergraph});
1233		identify $key;
1234	}
1235	# second pass for user graphs
1236	foreach $key ( keys %interfaces ) {
1237		next if(!$interfaces{$key}{usergraph});
1238		$k = $key; $k=~ s/^_//; # chop off initial _ prefix
1239		$interfaces{$key}{shdesc} = $config{"targetnames-$k"}
1240		if(defined $config{"targetnames-$k"});
1241		$interfaces{$key}{desc} = $config{"targettitles-$k"}
1242		if(defined $config{"targettitles-$k"});
1243		$interfaces{$key}{icon} = $config{"targeticons-$k"}
1244		if(defined $config{"targeticons-$k"});
1245		foreach $k ( keys %{$interfaces{$interfaces{$key}{targets}->[0]}} ) {
1246			$interfaces{$key}{$k}
1247				= $interfaces{$interfaces{$key}{targets}->[0]}{$k}
1248				if(!defined $interfaces{$key}{$k});
1249		}
1250	}
1251}
1252
1253#######################################################################
1254# Generate the page
1255sub mypage()
1256{
1257	my( $imgurl );
1258	my( $ahead );
1259	my( $javascript ) = "function RefreshMenu()
1260	{
1261	var mwin; var uopts;
1262	if( parent.menu ) {
1263	mwin = parent.menu;
1264	uopts = 'T';
1265	if( parent.menub ) { mwin = parent.menub; uopts = 't'; }
1266	mwin.location = '".$routersurl."?if=__none&rtr="
1267		.$q->escape($file)."&page=menu&xmtype=options&uopts='+uopts;
1268	}
1269	}";
1270
1271	$imgurl = $myurl."?fi=".$q->escape($file)
1272		."&ta=".$q->escape($target)
1273		."&dwmy=$dwmy"
1274		."&dk=$DECAY"
1275		."&pr=$PREDICT"
1276		."&ba=$BASE"
1277		."&t=$targetwindow&conf=".$q->escape($conffile)
1278		."&img=1";
1279
1280	start_html_ss({-title=>"Trend Analysis",
1281		-script=>$javascript, -onLoad=>"RefreshMenu()",
1282		-text=>$fgcolour, -bgcolor=>$bgcolour, -class=>'trend',
1283		-link=>$linkcolour, -alink=>$linkcolour, -vlink=>$linkcolour
1284	});
1285
1286	print $q->h1("Trend Analysis");
1287
1288	print $q->img({ alt=>'Trend graph', src=>$imgurl }).$q->br."\n";
1289#	print $q->a({href=>$imgurl},$imgurl).$q->br;
1290
1291	print "<DIV class=icons>";
1292	print $q->hr."\n<TABLE border=0 cellpadding=0 width=100% align=center><TR>";
1293	print "<TD valign=top>See alternative predictions:\n<UL>";
1294	foreach ( qw/daily weekly monthly yearly/ ) {
1295		if(substr($_,0,1) eq $dwmy) {
1296			print $q->li("Trending $_ predictions")."\n";
1297		} else {
1298		print $q->li($q->a({href=>(
1299				$myurl."?dwmy=".(substr($_,0,1))
1300				."&fi=".$q->escape($file)
1301				."&ta=".$q->escape($target)
1302				."&b=".$q->escape($backurl)
1303				."&t=".$q->escape($targetwindow)
1304				."&url=".$q->escape($routersurl)
1305				."&conf=".$q->escape($conffile)
1306				."&pr=$PREDICT"
1307				."&dk=$DECAY"
1308				."&ba=$BASE"
1309			)},
1310			"Trending $_ predictions" )
1311		)."\n";
1312		}
1313	}
1314	print "</UL></TD>\n";
1315	print "<TD valign=top>See different historical weightings:\n<UL>";
1316	foreach ( @decays ) {
1317		if($_ eq $DECAY ) {
1318			print $q->li("Decay factor $_" )."\n";
1319		} else {
1320		print $q->li($q->a({href=>(
1321				$myurl."?dk=$_"
1322				."&fi=".$q->escape($file)
1323				."&ta=".$q->escape($target)
1324				."&b=".$q->escape($backurl)
1325				."&t=".$q->escape($targetwindow)
1326				."&url=".$q->escape($routersurl)
1327				."&conf=".$q->escape($conffile)
1328				."&pr=$PREDICT"
1329				."&dwmy=$dwmy"
1330				."&ba=$BASE"
1331			)},
1332			"Decay factor $_" )
1333		)."\n";
1334		}
1335	}
1336	print "</UL></TD>\n";
1337	print "<TD valign=top>Different future distances:\n<UL>";
1338	foreach ( 50,100,150,200 ) {
1339		if( $dwmy eq "d" ) { $ahead = int(5 * $_ / 60)." hours"; }
1340		elsif( $dwmy eq "w" ) { $ahead = int( $_ / 48 )." days"; }
1341		elsif( $dwmy eq "m" ) { $ahead = int( $_ / 12 )." days"; }
1342		else { $ahead =  $_." days"; }
1343		if($_ eq $PREDICT ) {
1344			print $q->li("Look ahead $ahead")."\n";
1345		} else {
1346		print $q->li($q->a({href=>(
1347				$myurl."?pr=$_"
1348				."&fi=".$q->escape($file)
1349				."&ta=".$q->escape($target)
1350				."&b=".$q->escape($backurl)
1351				."&t=".$q->escape($targetwindow)
1352				."&url=".$q->escape($routersurl)
1353				."&conf=".$q->escape($conffile)
1354				."&dk=$DECAY"
1355				."&dwmy=$dwmy"
1356				."&ba=$BASE"
1357			)},
1358			"Look ahead $ahead" )
1359		)."\n";
1360		}
1361	}
1362	print "</UL></TD>\n";
1363	print "</TR></TABLE>\n";
1364	print "Base predicted trend on ";
1365	print $q->a({href=>(
1366				$myurl."?pr=$PREDICT"
1367				."&fi=".$q->escape($file)
1368				."&ta=".$q->escape($target)
1369				."&b=".$q->escape($backurl)
1370				."&t=".$q->escape($targetwindow)
1371				."&url=".$q->escape($routersurl)
1372				."&conf=".$q->escape($conffile)
1373				."&dk=$DECAY"
1374				."&dwmy=$dwmy"
1375				."&ba=0"
1376		)},"current value");
1377	print " or on ";
1378	print $q->a({href=>(
1379				$myurl."?pr=$PREDICT"
1380				."&fi=".$q->escape($file)
1381				."&ta=".$q->escape($target)
1382				."&b=".$q->escape($backurl)
1383				."&t=".$q->escape($targetwindow)
1384				."&url=".$q->escape($routersurl)
1385				."&conf=".$q->escape($conffile)
1386				."&dk=$DECAY"
1387				."&dwmy=$dwmy"
1388				."&ba=1"
1389		)},"weighted average value");
1390	print $q->hr."\n";
1391	print "Trend Decay: $DECAY".$q->br."\n";
1392	print "(smaller numbers mean pay less attention to historical data)<br>\n";
1393	print "See error log ".$q->a({href=>"$myurl?log=1",target=>"_new"},
1394		"here")."<BR>\n" if($debug);
1395	print "</DIV><DIV class=footer>";
1396	print $q->hr.$q->small("S Shipway: RRD trending analysis v$VERSION")."\n";
1397	print "</DIV>";
1398
1399	print $q->end_html();
1400}
1401#######################################################################
1402sub create_rrd($) {
1403	my( $rrdfile ) = @_;
1404	my( $dblint, $curtime, $curvala, $curvalb, $i );
1405	my( $rv, $line, $t, $v, $e, $entry);
1406	my( $hita, $hitb ) = (0,0);
1407	my(@args) = ();
1408
1409	# Create the empty RRD file with initial params
1410
1411	unlink $rrdfile if(-f $rrdfile);
1412	@args = ( $rrdfile,'-b',$starttime-1,'-s',$interval );
1413	foreach ( @$ds, 't0', 't1' ) {
1414		push @args, "DS:$_:GAUGE:".($interval*2).":U:U";
1415	}
1416	push @args, "RRA:AVERAGE:0.5:1:800";
1417	push @args, "RRA:MAX:0.5:1:800";
1418	RRDs::create(@args);
1419	$e = RRDs::error();
1420	if( $e ) { errlog("RRDCreate ".(join " ",@args).": $e"); return; }
1421
1422	# Output existing data ($rrddata,$starttine,$endtime,$interval)
1423	$t = $starttime;
1424	foreach $line ( @$rrddata ) {
1425		$entry = "$t";
1426		foreach $v ( @$line ) {
1427			if(defined $v) { $entry .= ":$v"; }
1428			else { $entry .= ":U"; }
1429		}
1430		$entry .= ":U:U";
1431		RRDs::update($rrdfile,$entry);
1432#		errlog($entry);
1433		$t += $interval;
1434	}
1435
1436	# output predicted data (@$trendstart, @$trenddelta)
1437	$i = 0; ( $curvala, $curvalb ) = @$trendstart;
1438	while ( $i < $PREDICT ) {
1439		$entry = "$t:U:U:$curvala:$curvalb";
1440		RRDs::update($rrdfile,$entry);
1441		$t += $interval;
1442		$curvala += $$trenddelta[0]; $curvalb += $$trenddelta[1];
1443		if($curvala < 0 and !$hita and !$interfaces{$target   }{noi}) {
1444			$curvala = 0; $hita = 1; $$trenddelta[0]=0;
1445			push @info, "Zero for ".$interfaces{$target}{legendi}
1446				." ".localtime($t)."\\l";
1447		}
1448		if($curvala > $interfaces{$target   }{maxbytes} and !$hita
1449			and !$interfaces{$target   }{noi}) {
1450			$curvala = $interfaces{$target   }{maxbytes}; $hita = 1;
1451			$$trenddelta[0]=0;
1452			push @info, "Max for ".$interfaces{$target   }{legendi}
1453				." ".localtime($t)."\\l";
1454		}
1455		if($curvalb < 0 and !$hitb and !$interfaces{$target   }{noo}) {
1456			$curvalb = 0; $hitb = 1; $$trenddelta[1]=0;
1457			push @info, "Zero for ".$interfaces{$target   }{legendo}
1458				." ".localtime($t)."\\l";
1459		}
1460		if($curvalb > $interfaces{$target   }{maxbytes} and !$hitb
1461			and !$interfaces{$target   }{noo}) {
1462			$curvalb = $interfaces{$target   }{maxbytes}; $hitb = 1;
1463			$$trenddelta[1]=0;
1464			push @info, "Max for ".$interfaces{$target   }{legendo}
1465				." ".localtime($t)."\\l";
1466		}
1467		$i++;
1468	}
1469}
1470#######################################################################
1471# calculate wieghted SD for line $idx at given delta
1472# weighted sd = square root of weighted average of squares of difference
1473sub wsd($$) {
1474	my( $idx,  $delta ) = @_;
1475	my( $c, $tot, $w, $row, $v ) = (0,0,0,0,0);
1476	my( $fx );
1477
1478	$fx  = $trendstart->[$idx];
1479	$w = 1;
1480	foreach $row ( 0 .. $#$rrddata ) {
1481		$v = $rrddata->[$#$rrddata - $row]->[$idx];
1482		if($v) { $tot += ($v-$fx)*($v-$fx) * $w; $c += $w;  }
1483		$w *= $DECAY; # Next value is less significant
1484		$fx -= $delta;
1485	}
1486	if($c) {
1487		$tot = sqrt($tot / $c);
1488	} elsif( $tot ) {
1489		$tot = sqrt($tot); # should not be possible for tot>0 and c=0
1490	}
1491
1492	return $tot;
1493}
1494#######################################################################
1495# This function will retrieve the RRD data, calculate the value and
1496# slope of the trend line, then call the outputxml function and rrdtool
1497# to create XML and the RRDTool database.
1498sub do_trending {
1499	my( $timeframe ) = "1d";
1500	my( $answer, $status, $e );
1501	my( $n, $idx, $c, $tot, $w, $row, $v, $deltadelta );
1502	my( $sd, $delta, $sda, $sdb, $lim );
1503
1504	errlog("Starting trending function");
1505	@info = (); # extra lines to add to graph
1506	push @info, 'Trending analysis decay factor: '.doformat($DECAY,1,0).'\l';
1507
1508	# set intervals (in seconds): 5min, 30min, 2hr, 1day
1509	foreach ( $dwmy ) {
1510		if( /w/ ) { $timeframe = "200h"; $interval = 1800; last; }
1511		if( /m/ ) { $timeframe = "800h"; $interval = 7200; last; }
1512		if( /y/ ) { $timeframe = "400d"; $interval = 3600*24; last; }
1513		$timeframe = "2000m"; $interval = 300;
1514	}
1515
1516	# Read in the RRD file
1517	$endtime = RRDs::last($interfaces{$target}{rrd});
1518	$e = RRDs::error();
1519	errlog("RRDLast: ".$interfaces{$target}{rrd}.": $e") if ($e);
1520	return if($e);
1521	( $starttime, $interval, $ds, $rrddata )
1522		= RRDs::fetch($interfaces{$target}{rrd},$consolidation,"-r",$interval,
1523			"-s",($endtime-(400*$interval)),"-e",$endtime);
1524	$e = RRDs::error();
1525	errlog("RRDFetch: ".$interfaces{$target}{rrd}.": $e") if ($e);
1526	return if($e);
1527	errlog(join(",",@$ds).": ".$#$rrddata." rows every $interval from "
1528		.localtime($starttime));
1529
1530	# Calculate trends
1531	# this results in (a) last update time, (b) avg value then,
1532	# (c) change in value per interval
1533	$trendstart = [ 0,0 ];
1534	$trenddelta = [ 0,0 ];
1535	foreach $idx ( 0, 1 ) {
1536		if( $BASE ) {
1537			# calculate weighted average (start point for line)
1538			$w = 1;   # weight of current sample
1539			$tot = 0; # total of values
1540			$c = 0;   # total weight
1541			$n = 0;   # count of samples processed
1542			foreach $row ( 0 .. $#$rrddata ) {
1543				$v = $rrddata->[$#$rrddata - $row]->[$idx];
1544				if($v) { $tot += $v * $w; $c += $w; $n++; }
1545				$w *= $DECAY; # Next value is less significant
1546			}
1547			$trendstart->[$idx] = $tot / $c if($c);
1548			errlog("Weighted average for $idx is: ".$trendstart->[$idx]." = $tot / $c, $n valid samples");
1549		} else {
1550			# use last value
1551			foreach $row ( 0 .. $#$rrddata ) {
1552				$v = $rrddata->[$#$rrddata - $row]->[$idx];
1553				last if(defined $v);
1554			}
1555			$v = 0 if(!defined $v);
1556			$trendstart->[$idx] = $v;
1557		}
1558
1559		# now calculate best angle of line.
1560		# vary delta as weighted standard deviation decreases, until
1561		# we dont get a gain by going up or down.
1562		$delta = $trenddelta->[$idx];
1563		$deltadelta = $trendstart->[$idx]/100.0;
1564		$lim = $deltadelta/32768.0;
1565		$sd = wsd($idx,$delta);
1566		$n = 0;
1567		while(($n < 100)and ($deltadelta>$lim)) { # put a cap on iterations
1568			$n++;
1569			errlog("Delta=$delta, Deviation=$sd : deltadelta=$deltadelta, limit $lim");
1570			$sda = wsd($idx,$delta+$deltadelta);
1571			$sdb = wsd($idx,$delta-$deltadelta);
1572			errlog("up->$sda, down->$sdb");
1573			if($sd<$sda and $sd<$sdb) { # we are in a trough
1574				$deltadelta /= 2;
1575			} elsif( $sda < $sdb ) {
1576				$delta+=$deltadelta;
1577				$sd = $sda;
1578			} else {
1579				$delta-=$deltadelta;
1580				# chage direction
1581				$deltadelta = -($deltadelta/2);
1582				$sd = $sdb;
1583			}
1584		}
1585		$trenddelta->[$idx] = $delta;
1586		errlog("Delta set to $delta");
1587	}
1588
1589	# create $tempfile.rrd
1590	$lastupdate = $endtime + $PREDICT * $interval;
1591	create_rrd("$tempfile.rrd");
1592}
1593#######################################################################
1594
1595sub rtr_params(@)
1596{
1597	my($ds0,$ds1,$ds2,$mds0,$mds1, $mds2,$t0,$t1)=("","","","","","","","");
1598	my($lin, $lout, $mlin, $mlout, $lextra);
1599	my($dwmy,$interface,$defrrd) = @_;
1600	my($ssin, $ssout, $sin, $sout, $ssext, $sext);
1601	my($l);
1602	my($workday) = 0;
1603	my($legendi,$legendo, $legendx);
1604	my(@clr, $escunit);
1605	my($max1, $max2);
1606	my($havepeaks) = 0;
1607
1608	# are we peak lines on this graph?
1609	#if(!defined $config{'routers.cgi-withpeak'}
1610	#	or $config{'routers.cgi-withpeak'} =~ /y/i ) {
1611	#	if( $dwmy =~ /[wmy]/ or ( $dwmy =~ /d/ and $usesixhour )) {
1612	#		$havepeaks = 1;
1613	#	}
1614	#}
1615
1616	# are we going to work out the 'working day' averages as well?
1617	#if( defined $config{'routers.cgi-daystart'}
1618	#	and defined $config{'routers.cgi-dayend'}
1619	#	and $config{'routers.cgi-daystart'}<$config{'routers.cgi-dayend'}
1620	#	and $dwmy !~ /y/ ){
1621	#	$workday = 1;
1622	#	$timezone = 0;
1623	#	# Calculate timezone.  We only need to do this if we're making a graph,
1624	#	# and we have a 'working day' defined.
1625	#	if( defined $config{'web-timezone'} ) {
1626	#		# If its been defined explicitly, then use that.
1627	#		$timezone = $config{'web-timezone'};
1628	#	} else {
1629	#		# Do we have Time::Zone?
1630	#		eval { require Time::Zone; };
1631	#		if ( $@ ) {
1632	#			my( @gm, @loc, $hourdif );
1633	#			eval { @gm = gmtime; @loc = localtime; };
1634	#			if( $@ ) {
1635	#				# Can't work out local timezone, so assume GMT
1636	#				$timezone = 0;
1637	#			} else {
1638	#				$hourdif = $loc[2] - $gm[2];
1639	#				$hourdif += 24 if($loc[3]>$gm[3] or $loc[4]>$gm[4] );
1640	#				$hourdif -= 24 if($loc[3]<$gm[3] or $loc[4]<$gm[4] );
1641	#				$timezone = $hourdif;
1642	#			}
1643	#		} else {
1644	#			# Use the Time::Zone package since we have it
1645	#			$timezone = Time::Zone::tz_local_offset() / 3600;
1646	#			# it's in seconds so /3600
1647	#		}
1648	#	}
1649	#}
1650
1651	# escaped unit string
1652	$escunit = $interfaces{$interface}{unit};
1653	$escunit =~ s/%/%%/g;
1654	$escunit =~ s/:/\\:/g;
1655	$escunit =~ s/&nbsp;/ /g;
1656
1657	# identify colours
1658	if( defined $interfaces{$interface}{colours} ) {
1659		@clr = @{$interfaces{$interface}{colours}};
1660	}
1661	if(! @clr ) {
1662		if( $gstyle =~ /b/ ) {
1663			@clr =  ("#888888", "#000000", "#cccccc","#444444", "#222222");
1664		} else {
1665			@clr = ("#00cc00", "#0000ff","#006600", "#ff00ff", "#ff0000" );
1666		}
1667	}
1668
1669	#$defrrd = $interfaces{$interface}{rrd};
1670	$defrrd =~ s/:/\\:/g;
1671
1672	$sin="In: "; $sout="Out:";
1673	$ssin = $ssout = "";
1674	$sin = $interfaces{$interface}{legendi}
1675		if( defined $interfaces{$interface}{legendi} );
1676	$sout= $interfaces{$interface}{legendo}
1677		if( defined $interfaces{$interface}{legendo} );
1678	$l = length $sin; $l = length $sout if($l < length $sout);
1679	$sin = substr($sin.'                ',0,$l);
1680	$sout= substr($sout.'                ',0,$l);
1681	$sin =~ s/:/\\:/g; $sout =~ s/:/\\:/g;
1682	$sin =~ s/%/%%/g; $sout =~ s/%/%%/g;
1683	if( $interfaces{$interface}{integer} ) {
1684		$ssin = "%5.0lf"; $ssout = "%5.0lf";
1685	} elsif( $interfaces{$interface}{fixunits} ) {
1686		$ssin = "%7.2lf "; $ssout = "%7.2lf ";
1687	} else {
1688		$ssin = "%6.2lf %s"; $ssout = "%6.2lf %s";
1689	}
1690	if( defined $config{'routers.cgi-legendunits'}
1691		and $config{'routers.cgi-legendunits'} =~ /y/i ) {
1692		$ssin .= $escunit; $ssout .= $escunit;
1693	}
1694	$sin .= $ssin; $sout .= $ssout;
1695
1696	{
1697		$lin = ":Inbound"; $lout = ":Outbound";
1698		$mlin = ":Peak Inbound"; $mlout = ":Peak Outbound";
1699		$lin = ":".$interfaces{$interface}{legend1}
1700			if( defined $interfaces{$interface}{legend1} );
1701		$lout = ":".$interfaces{$interface}{legend2}
1702			if( defined $interfaces{$interface}{legend2} );
1703		$mlin = ":".$interfaces{$interface}{legend3}
1704			if( defined $interfaces{$interface}{legend3} );
1705		$mlout = ":".$interfaces{$interface}{legend4}
1706			if( defined $interfaces{$interface}{legend4} );
1707		if($interfaces{$interface}{noo} or $havepeaks
1708			or ( defined $interfaces{$interface}{graphstyle}
1709				and $interfaces{$interface}{graphstyle} =~ /range/i )) {
1710				$lin .= "\\l";
1711		}
1712	}
1713	$lout .= "\\l" if($lout);
1714	$lin = substr( $lin."                                ",0,30 )
1715		if($lin and !$interfaces{$interface}{noo}
1716			and $lin !~ /\\l$/ );
1717	$mlout = substr( $mlout."                                ",0,30 )
1718		if ($mlout);
1719	$mlin = substr( $mlin."                                ",0,30 )
1720		if ($mlin);
1721
1722	if( $interfaces{$interface}{nolegend} ) { $lin = $lout = ""; }
1723
1724	($ds0, $ds1, $t0, $t1) = ("ds0", "ds1", "t0", "t1");
1725	push @params,
1726		"DEF:in=".$defrrd.":$ds0:$consolidation",
1727		"DEF:out=".$defrrd.":$ds1:$consolidation",
1728		"DEF:tin=".$defrrd.":$t0:$consolidation",
1729		"DEF:tout=".$defrrd.":$t1:$consolidation"
1730		;
1731	($ds0, $ds1, $t0, $t1) = ("in", "out", "tin", "tout");
1732### do this if we are using BITS
1733	if( $interfaces{$interface}{mult} ne 1 ) {
1734		push @params, "CDEF:fin=$ds0,".$interfaces{$interface}{mult}.",*",
1735			"CDEF:fout=$ds1,".$interfaces{$interface}{mult}.",*";
1736		push @params, "CDEF:ftin=$t0,".$interfaces{$interface}{mult}.",*",
1737			"CDEF:ftout=$t1,".$interfaces{$interface}{mult}.",*";
1738		($ds0, $ds1, $t0, $t1) = ("fin", "fout", "ftin", "ftout");
1739	}
1740###
1741	if( defined $interfaces{$interface}{factor} ) {
1742		push @params, "CDEF:ffin=$ds0,".$interfaces{$interface}{factor}.",*",
1743			"CDEF:ffout=$ds1,".$interfaces{$interface}{factor}.",*";
1744		push @params, "CDEF:fftin=$t0,".$interfaces{$interface}{factor}.",*",
1745			"CDEF:fftout=$t1,".$interfaces{$interface}{factor}.",*";
1746		($ds0, $ds1, $t0, $t1) = ("ffin", "ffout", "fftin", "fftout");
1747	}
1748# And the percentages
1749	$max1 = $max2 = $interfaces{$interface}{max};
1750	$max1 = $interfaces{$interface}{max1}
1751		if(defined $interfaces{$interface}{max1});
1752	$max2 = $interfaces{$interface}{max2}
1753		if(defined $interfaces{$interface}{max2});
1754	if( $max1  ) {
1755		push @params,
1756			"CDEF:pcin=$ds0,100,*,".$max1.",/";
1757	#		"CDEF:mpcin=$mds0,100,*,".$max1.",/";
1758	}
1759	if( $max2  ) {
1760		push @params,
1761			"CDEF:pcout=$ds1,100,*,".$max2.",/";
1762	#		"CDEF:mpcout=$mds1,100,*,".$max2.",/";
1763	}
1764
1765
1766	if( $interfaces{$interface}{available} ) {
1767		# availability percentage
1768		push @params, "CDEF:apc=in,UN,out,UN,+,2,EQ,0,100,IF";
1769		# Now, the average of apc is the percentage availability!
1770	}
1771
1772#	now for the actual lines : put the peaklines for d only if we have a 6 hr
1773#	dont forget to use more friendly colours if this is black and white mode
1774		push @params, "AREA:$ds0".$clr[0].$lin
1775			if(!$interfaces{$interface}{noi});
1776		push @params, "AREA:$t0#80ff80"
1777			if(!$interfaces{$interface}{noi});
1778		if(!$interfaces{$interface}{noo}) {
1779			if( defined $interfaces{$interface}{graphstyle} ) {
1780				if( $interfaces{$interface}{graphstyle} =~ /stack/i ) {
1781					push @params, "STACK:$ds1".$clr[1].$lout;
1782				} elsif( $interfaces{$interface}{graphstyle} =~ /range/i ) {
1783					push @params, "AREA:$ds1#ffffff"; # erase lower part
1784					# if workingday active, put HIGHLIGHTED lower in
1785					if( $workday and $gstyle !~ /b/) {
1786						push @params, "CDEF:lwday=wdin,UN,0,$ds1,IF",
1787							"AREA:lwday#ffffcc";
1788					}
1789					push @params, "LINE1:$ds1".$clr[0]; # replace last pixel
1790				} else {
1791					# we do it here so it isnt overwritten by the incoming area
1792					push @params, "LINE1:$ds1".$clr[1].$lout;
1793					push @params, "LINE1:$t1#8080ff";
1794				}
1795			} else {
1796				# we do it here so it isnt overwritten by the incoming area
1797				push @params, "LINE1:$ds1".$clr[1].$lout;
1798				push @params, "LINE1:$t1#8080ff";
1799			}
1800		}
1801
1802# data unavailable
1803	push @params,
1804		"CDEF:down=in,UN,out,UN,tin,UN,tout,UN,+,+,+,4,EQ,INF,0,IF","AREA:down#d0d0d0";
1805# the max line
1806	if( $interfaces{$interface}{max}
1807		and ! ( defined $config{'routers.cgi-maxima'}
1808			and  $config{'routers.cgi-maxima'} =~ /n/i )
1809		and !$interfaces{$interface}{nomax}
1810	) {
1811		my( $lmax ) = "";
1812		my( $lamax ) = "";
1813#		if( $dwmy !~ /s/ ) {
1814			if( defined $interfaces{$interface}{mblegend} ) {
1815				$lmax = $interfaces{$interface}{mblegend};
1816				$lmax =~ s/:/\\:/g; $lmax = ':'.$lmax;
1817			} elsif( $interfaces{$interface}{isif} ) {
1818				$lmax =":100% Bandwidth";
1819			} else { $lmax =":Maximum"; }
1820			if( $max1 and $max2 and ($max1 != $max2) ) {
1821			$lmax .= " (".doformat($max1,$interfaces{$interface}{fixunits},0)
1822				.$interfaces{$interface}{unit}.","
1823				.doformat($max2,$interfaces{$interface}{fixunits},0)
1824				.$interfaces{$interface}{unit}.")\\l";
1825			} else {
1826			$lmax .= " (".doformat($interfaces{$interface}{max},
1827					$interfaces{$interface}{fixunits},0)
1828				.$interfaces{$interface}{unit}.")\\l";
1829			}
1830			if( defined $interfaces{$interface}{absmax} ) {
1831				if( defined $interfaces{$interface}{amlegend} ) {
1832					$lamax = ":".$interfaces{$interface}{amlegend};
1833				} else { $lamax =":Hard Maximum"; }
1834				$lamax .= " (".doformat($interfaces{$interface}{absmax},
1835					$interfaces{$interface}{fixunits},1)
1836					.$interfaces{$interface}{unit}.")\\l";
1837			}
1838#		}
1839		if( $max1 and $max2 and ($max1 != $max2)) {
1840			if( $gstyle =~ /b/ ) {
1841				push @params, "HRULE:".$max1."#cccccc$lmax";
1842				push @params, "HRULE:".$max2."#cccccc";
1843			} else {
1844				push @params, "HRULE:".$max1."#ff0000$lmax";
1845				push @params, "HRULE:".$max2."#ff0000";
1846			}
1847		} else {
1848			if( $gstyle =~ /b/ ) {
1849			push @params, "HRULE:".$interfaces{$interface}{max}."#cccccc$lmax";
1850			} else {
1851			push @params, "HRULE:".$interfaces{$interface}{max}."#ff0000$lmax";
1852			}
1853		}
1854		if( defined $interfaces{$interface}{absmax}
1855			and !$interfaces{$interface}{noabsmax} ) {
1856			if( $gstyle =~ /b/ ) {
1857				push @params, "HRULE:".$interfaces{$interface}{absmax}
1858					."#aaaaaa$lamax";
1859			} else {
1860				push @params, "HRULE:".$interfaces{$interface}{absmax}
1861					."#ff0080$lamax";
1862			}
1863		}
1864	}
1865#	now for the labels at the bottom
1866		if( $max1 ) {
1867			if(!$interfaces{$interface}{noi}) {
1868#				push @params, "GPRINT:$mds0:MAX:Max $sin\\g" ;
1869#				push @params ,"GPRINT:mpcin:MAX: (%2.0lf%%)\\g"
1870#					if($interfaces{$interface}{percent});
1871				push @params,"GPRINT:$ds0:$consolidation:  $conspfx $sin\\g" ;
1872				push @params ,"GPRINT:pcin:$consolidation: (%2.0lf%%)\\g"
1873					if($interfaces{$interface}{percent});
1874				push @params,"GPRINT:$ds0:LAST:  Cur $sin\\g" ;
1875				push @params ,"GPRINT:pcin:LAST: (%2.0lf%%)\\g"
1876					if($interfaces{$interface}{percent});
1877				push @params, "COMMENT:\\l" ;
1878			}
1879			if(!$interfaces{$interface}{noo}) {
1880#				push @params, "GPRINT:$mds1:MAX:Max $sout\\g" ;
1881#				push @params ,"GPRINT:mpcout:MAX: (%2.0lf%%)\\g"
1882#					if($interfaces{$interface}{percent});
1883				push @params,"GPRINT:$ds1:$consolidation:  $conspfx $sout\\g" ;
1884				push @params ,"GPRINT:pcout:$consolidation: (%2.0lf%%)\\g"
1885					if($interfaces{$interface}{percent});
1886				push @params,"GPRINT:$ds1:LAST:  Cur $sout\\g" ;
1887				push @params ,"GPRINT:pcout:LAST: (%2.0lf%%)\\g"
1888					if($interfaces{$interface}{percent});
1889				push @params, "COMMENT:\\l" ;
1890			}
1891			if( defined $config{'routers.cgi-maxima'}
1892				and $config{'routers.cgi-maxima'} =~ /n/i
1893				and !$interfaces{$interface}{nomax} ) {
1894				my( $comment );
1895				if(defined $interfaces{$interface}{mblegend}) {
1896					$comment = $interfaces{$interface}{mblegend};
1897					$comment = "COMMENT:".decolon($comment);
1898				} elsif($interfaces{$interface}{isif}) {
1899					$comment = "COMMENT:100% Bandwidth";
1900				} else {
1901					$comment = "COMMENT:Maximum value";
1902				}
1903				$comment .= decolon(" ".doformat($interfaces{$interface}{max},
1904						$interfaces{$interface}{fixunits},0)
1905					.$escunit."\\l");
1906				push @params, $comment;
1907 		 	}
1908		} else {
1909			push @params,
1910#				"GPRINT:$mds0:MAX:Max $sin\\g",
1911				"GPRINT:$ds0:$consolidation:  $conspfx $sin\\g",
1912				"GPRINT:$ds0:LAST:  Cur $sin\\l"
1913					if(!$interfaces{$interface}{noi});
1914			push @params,
1915#				"GPRINT:$mds1:MAX:Max $sout\\g",
1916				"GPRINT:$ds1:$consolidation:  $conspfx $sout\\g",
1917				"GPRINT:$ds1:LAST:  Cur $sout\\l"
1918					if(!$interfaces{$interface}{noo});
1919		}
1920		if( $interfaces{$interface}{available} ) {
1921			push @params, "GPRINT:apc:AVERAGE:Data availability\\: %.2lf%%\\l";
1922		}
1923	foreach (@info) {
1924		push @params, "COMMENT:".decolon($_);
1925	}
1926}
1927
1928########################################################################
1929# Actually create the necessary graph
1930
1931sub make_graph(@)
1932{
1933	my ($e, $thisurl, $s, $autoscale);
1934	my ($tstr, $gheight, $width, $gwidth, $gtitle);
1935	my( $maxwidth ) = 30;
1936	my( $endtime );
1937	my($thisgraph,$thisrrd) = @_;
1938	my($interface) = $target;
1939	my( $rrdoutput, $rrdxsize, $rrdysize );
1940
1941
1942# Shall we scale it, etc
1943	$autoscale = 1;
1944	$s = $dwmy; $s =~ s/s//;
1945	if( $interfaces{$interface}{unscaled} ) {
1946		$autoscale = 0 if ($interfaces{$interface}{unscaled} =~ /$s/i);
1947	}
1948
1949	$tstr = "6-hour" if( $dwmy =~ /6/ ) ;
1950	$tstr = "Daily" if( $dwmy =~ /d/ ) ;
1951	$tstr = "Weekly" if( $dwmy =~ /w/ ) ;
1952	$tstr = "Monthly" if( $dwmy =~ /m/ ) ;
1953	$tstr = "Yearly" if( $dwmy =~ /y/ ) ;
1954
1955	$gtitle = $interfaces{$interface}{desc};
1956	if( ($dwmy.$gstyle)=~/s/ ) {
1957		if($gstyle=~/x/) { $maxwidth = 50; }
1958		elsif($gstyle=~/l/) { $maxwidth = 40; }
1959		else { $maxwidth = 30; }
1960	}
1961	if(!$gtitle or ((length($gtitle)>$maxwidth)and(($dwmy.$gstyle) =~ /s/))) {
1962		$gtitle = "";
1963#		$gtitle .= $routers{$router}{shdesc}.": "
1964#				if( defined $routers{$router}{shdesc});
1965		$gtitle .= $interfaces{$interface}{shdesc};
1966	}
1967	$gtitle .= ": Trend Analysis";
1968	$gtitle = $q->unescape($gtitle);
1969	$gtitle =~ s/&nbsp;/ /g; $gtitle =~ s/&amp;/&/g;
1970
1971	@params = ();
1972
1973	if ( $gstyle =~ /^s/ ) { $width = 200; $gwidth = 200; } #short
1974	elsif ( $gstyle =~ /^t/ ) { $width = 200; $gwidth = 400; } #stretch
1975	elsif ( $gstyle =~ /^l/ ) { $width = 400; $gwidth = 530; } #long
1976	elsif ( $gstyle =~ /^x/ ) { $width = 400; $gwidth = 800; } #xlong
1977	else { $width = 400; $gwidth = 400; } # default (normal)
1978	if ( $gstyle =~ /2/ ) { $gheight = 200; }
1979	else { $gheight = 100; }
1980
1981	push @params, $thisgraph;
1982	if( $graphsuffix eq "png" ) {
1983		push @params, '--imgformat',uc $graphsuffix;
1984	}
1985	push @params,"--base", $k;
1986	push @params, qw/--lazy -l 0 --interlaced/;
1987	push @params,"--units-exponent",0
1988		if($interfaces{$interface}{fixunits}
1989			and $RRDs::VERSION >= 1.00030 );
1990	# only force the minimum upper-bound of graph if we have a max,
1991	# and we dont have maxima=n, and we dont have unscaled=n
1992	if( ! $autoscale ) {
1993		if( $interfaces{$interface}{max} and (
1994			!defined $config{'routers.cgi-maxima'}
1995			or $config{'routers.cgi-maxima'} !~ /n/i
1996		) ) {
1997			push @params, "-u", $interfaces{$interface}{max} ;
1998		} else {
1999			push @params, "-u", 1;
2000		}
2001	} else {
2002		push @params, "-u", 1;
2003	}
2004# could have added a "-r" there to enforce the upper limit rigidly
2005#	push @params, "-v", $config{$statistic}{-v};
2006	push @params, "-w", $gwidth, "-h", $gheight;
2007		push @params, "-e", $lastupdate;
2008		$endtime = $lastupdate;
2009	push @params, "-s", "end".(-1 * $width)."m"  if ( $dwmy =~ /6/ );
2010	push @params, "-s", "end".(-5 * $width)."m"  if ( $dwmy =~ /d/ );
2011	push @params, "-s", "end".(-25 * $width)."m" if ( $dwmy =~ /w/ );
2012	push @params, "-s", "end".(-2 * $width)."h"  if ( $dwmy =~ /m/ );
2013	push @params, "-s", "end".(-1 * $width)."d"   if ( $dwmy =~ /y/ );
2014	push @params,"--x-grid","MINUTE:15:HOUR:1:HOUR:1:0:$dailylabel"
2015		if ( $dwmy =~ /6/ );
2016	push @params,"--x-grid","HOUR:1:HOUR:24:HOUR:2:0:$dailylabel"
2017		if ( $dwmy =~ /d/ );
2018	push @params,"--x-grid","HOUR:6:DAY:1:DAY:1:86400:%a"
2019		if ( $dwmy =~ /w/ );
2020	push @params,"--x-grid","DAY:1:WEEK:1:WEEK:1:604800:Week"
2021			." ".$monthlylabel
2022		if ( $dwmy =~ /m/ and $RRDs::VERSION >= 1.00029  );
2023	push @params,"--title", $gtitle;
2024
2025	if ( defined $interfaces{$interface}{ylegend} ) {
2026		push @params, "--vertical-label", $interfaces{$interface}{ylegend};
2027	} else {
2028		push @params, "--vertical-label", $interfaces{$interface}{unit};
2029	}
2030
2031		rtr_params($dwmy,$interface,$thisrrd);
2032
2033	if ( defined $config{'routers.cgi-withdate'}
2034		and $config{'routers.cgi-withdate'}=~/y/ ) {
2035		push @params, "COMMENT:".decolon(shortdate($endtime))."\\r";
2036	}
2037
2038	( $rrdoutput, $rrdxsize, $rrdysize ) = RRDs::graph(@params);
2039	$e = RRDs::error();
2040	if($e) {
2041		print $q->redirect($config{'routers.cgi-iconurl'}."error-lg.gif");
2042		errlog("RRDGraph: $e");
2043	} else {
2044		# output the graphic directly from disk
2045		open GRAPH, "<$thisgraph";
2046		binmode GRAPH;
2047		binmode STDOUT;
2048		print $q->header({ '-type'=>"image/$graphsuffix", '-expires'=>"now",
2049			'-Content-Disposition'=>"filename=\"image.$graphsuffix\"" });
2050		while( <GRAPH> ) { print; }
2051		close GRAPH;
2052	}
2053	return;
2054}
2055
2056#######################################################################
2057# create and output the GIF/PNG
2058sub makeimage {
2059	my( $tmpimage, $buf );
2060
2061	# Create the graphic header
2062	$headeropts{'-expires'} = "+6min";
2063	if( defined $config{'web-png'} and $config{'web-png'}=~/[1yY]/ ) {
2064		$headeropts{'-type'} = 'image/png';
2065		$tmpimage = $tempfile.".png";
2066		$graphsuffix = 'png';
2067	} else {
2068		$headeropts{'-type'} = 'image/gif';
2069		$tmpimage = $tempfile.".gif";
2070		$graphsuffix = 'gif';
2071	}
2072
2073	# call graphing on $tempfile.rrd to create $tmpimage
2074	make_graph($tmpimage,"$tempfile.rrd");
2075
2076	# remove the temporary files
2077	unlink $tmpimage;
2078	unlink $tempfile.".rrd";
2079}
2080
2081sub errorpage {
2082	if(!$q->param('img')) {
2083		print $q->header({%headeropts});
2084		start_html_ss({-title=>"Error",-class=>'error'});
2085		print $q->h1("Error in configuration")."\n";
2086		print $q->p($_[0]);
2087		print $q->hr.$q->small("S Shipway: RRD trending analysis v$VERSION")."\n";
2088		print "<!-- \nThis script should not be called from the command line!\n-->\n";
2089		print $q->end_html;
2090	} else {
2091		print $q->redirect($config{'routers.cgi-iconurl'}."error-lg.gif");
2092	}
2093}
2094#######################################################################
2095
2096# read in the conf file
2097$conffile = $q->param('conf') if(defined $q->param('conf'));
2098readconf('routers.cgi','web','trend.cgi');
2099$LOG = $config{'trend.cgi-logfile'} if( defined $config{'trend.cgi-logfile'} );
2100$debug = $config{'trend.cgi-debug'} if( defined $config{'trend.cgi-debug'} );
2101$DECAY = $config{'trend.cgi-decay'} if( defined $config{'trend.cgi-decay'} );
2102$TMPPATH = $config{'trend.cgi-workdir'}
2103	if( defined $config{'trend.cgi-workdir'}
2104	and -d $config{'trend.cgi-workdir'} );
2105
2106# For debugging only
2107if( $q->param('log') ) {
2108	print $q->header(\%headeropts);
2109	start_html_ss({-class=>'error'});
2110	if( -r $LOG ) {
2111		print "\n<PRE>\n";
2112		open RLOG, "<$LOG";
2113		while ( <RLOG> ) { print; }
2114		close RLOG;
2115		print "</PRE>\n";
2116	} else {
2117		print $q->h1("Error").$q->p("No log file is available.")."\n";
2118	}
2119	print $q->end_html."\n";
2120	exit 0;
2121}
2122# Process parameters
2123$file   = $q->param('fi') if(defined $q->param('fi'));
2124$target = $q->param('ta') if(defined $q->param('ta'));
2125$backurl = $q->param('b') if(defined $q->param('b'));
2126$targetwindow = $q->param('t') if(defined $q->param('t'));
2127$dwmy = $q->param('dwmy') if(defined $q->param('dwmy'));
2128$DECAY = $q->param('dk') if(defined $q->param('dk'));
2129$PREDICT = $q->param('pr') if(defined $q->param('pr'));
2130$BASE = $q->param('ba') if(defined $q->param('ba'));
2131$routersurl = $q->param('url') if(defined $q->param('url'));
2132
2133# HTTP headers
2134%headeropts = ( -expires=>"now" );
2135$headeropts{target} = $targetwindow if($targetwindow);
2136
2137# caching daemon
2138if( $ENV{RRDCACHED_ADDRESS} ) {
2139	errlog("Warning: RRDCACHED_ADDRESS=".$ENV{RRDCACHED_ADDRESS});
2140	delete $ENV{RRDCACHED_ADDRESS};
2141}
2142
2143# Test a few things
2144$cfile = $config{'routers.cgi-confpath'}.$pathsep.$file;
2145if( ! -r $cfile ) {
2146	errorpage("Unable to read configuration file $cfile : please check that this file exists, and is readable by the web process.  Check defaults in the trend.cgi script itself.");
2147	exit 0;
2148}
2149if( ! -w $TMPPATH ) {
2150	errorpage("Unable to write to temporary directory $TMPPATH :  Please check the definition in the routers2.conf, and the default specified in the trend.cgi script itself.");
2151	exit 0;
2152}
2153if( $debug and -f $LOG and ! -w $LOG ) {
2154	errorpage("Cannot write to log file $LOG : Either give a correct log file location (in the routers2.conf or the trend.cgi script) or disable debug logging (in the routers2.conf).");
2155	exit 0;
2156}
2157if( ! $target ) {
2158	errorpage("No Target defined: You cannot (yet) use trend.cgi as a device-level Extension, it must be defined as a Target-level one.");
2159	exit 0;
2160}
2161
2162# Set colour defaults
2163foreach ( 'routers.cgi', 'trend.cgi' ) {
2164	$fgcolour = $config{"$_-fgcolor"} if(defined $config{"$_-fgcolor"});
2165	$fgcolour = $config{"$_-fgcolour"} if(defined $config{"$_-fgcolour"});
2166	$bgcolour = $config{"$_-bgcolor"} if(defined $config{"$_-bgcolor"});
2167	$bgcolour = $config{"$_-bgcolour"} if(defined $config{"$_-bgcolour"});
2168	$linkcolour = $config{"$_-linkcolor"} if(defined $config{"$_-linkcolor"});
2169	$linkcolour = $config{"$_-linkcolour"} if(defined $config{"$_-linkcolour"});
2170}
2171
2172# read in the MRTG cfg file
2173read_cfg($cfile);
2174
2175if(!defined $interfaces{$target} ) {
2176	errorpage( "Unable to find target '$target' in file $cfile" );
2177	exit 0;
2178}
2179
2180unshift @INC, ( split /[\s,]+/,$config{"web-libadd"} )
2181	if( defined $config{"web-libadd"} );
2182
2183eval { require RRDs; };
2184if($@) {
2185	errorpage("Unable to load RRDs perl module: Have you installed it correctly?<P>$@");
2186	exit 0;
2187}
2188
2189# Background colours
2190$bgcolour = $interfaces{$target}{background}
2191	if(defined $interfaces{$target}{background});
2192
2193
2194# First, make the HTML page, calling self for the graphic
2195if(! $q->param('img') ) {
2196	print $q->header(\%headeropts);
2197	mypage;
2198	exit 0;
2199}
2200
2201# Now we read in the rrd file, and do the trending
2202$tempfile = $TMPPATH.$pathsep."trend.".time;
2203do_trending($target{rrd},"$tempfile.rrd");
2204
2205if ( -f "$tempfile.rrd" ) {
2206	# Make the page
2207	makeimage();
2208} else {
2209	print $q->redirect($config{'routers.cgi-iconurl'}."error-lg.gif");
2210	errlog("Didn't make the working rrd file");
2211}
2212
2213# End
2214errlog("Complete");
2215exit(0);
2216