#!/usr/bin/perl # # Summarize all IP accounting files from start to end time # # Copyright (C) 1997 - 2000 Moritz Both # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # The author can be reached via email: moritz@daneben.de, or by # snail mail: Moritz Both, Im Moore 26, 30167 Hannover, # Germany. Phone: +49-511-1610129 # use 5.000; use Getopt::Long; use Sys::Hostname; use POSIX; BEGIN { eval {require GD; import GD;}; $have_GD = $@ ? 0 : 1; } @moff = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ); @mofg = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); @mons = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); @wday_short = ("Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"); @wday = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"); # =()<$datdir="@@";>()= $datdir="/var/log/ip-acct"; # =()<$datdelim="@@";>()= $datdelim="#-#-#-#-#"; # =()<$version="@@";>()= $version="1.10"; # =()<$chain_name[0]="@@";>()= $chain_name[0]="ipac_in"; # =()<$chain_name[1]="@@";>()= $chain_name[1]="ipac_out"; # =()<$chain_name[2]="@@";>()= $chain_name[2]="ipac_bth"; $rcs_id = q|$Id: ipacsum,v 1.76 2000/08/07 10:24:22 kruemelmo Exp $|; $copyright="(C) 1997 - 2000 Moritz Both"; $me=$0; $me =~ s|^.*/([^/]+)$|$1|; $now=time; $replace=0; $exact = 0; $progression = 0; # if 1, collect progressions (needs memory!) $graph = 0; # make ascii graphs $png = undef; # make png graphs $png_filename_prefix = undef; # prefix png files with this prefix $rule_regex = ".*"; # match rules with this regex only $show_run_progression = 0; # show % finished while running $fixed_quantity = undef; # undef==off; can be one of [tgmk ] $png_header_in_index = "ip accounting graph page"; # default header text for index file $png_start = 1; # print start time by default $png_end = 0; # if 1, print end time too $png_charset = "iso-8859-1"; # default charset for png index file $c_day = 24*60*60; $c_year = $c_day * 365.24; @C_QU = (1, 1024, 1048576, 1073741824, 1099511627776 ); @C_QUC= ('','K', 'M', 'G', 'T'); # a list of possible x axis separator intervals and -alignments. @possible_interval = ( 1, 2, 5, 10, 15, 20, 30, # sec 60, 120, 300, 600, 900, 1200, 1800, # 1,2,5,10,15,20,30 min 3600, 7200, 10800, 14400, # 1,2,3,4 hours 21600, 43200, # 6,12 hours $c_day, $c_day*2, $c_day*5, $c_day*7, # if you change the following, you must change them in sub # optimal_label_interval_time, too! $c_day*15, $c_day*30, $c_day*60, $c_year/4, $c_year/3, $c_year/2, $c_year, $c_year*2, $c_year*4, $c_year*5, $c_year*10, $c_year*20, $c_year*25, $c_year*50, $c_year*100); # hope this is enough # a list of possible time labels on x axis of png image with reasonable # time limits. # if duration is under... display time as... @png_time_labels = ( 4*60, 'sprintf("%02d\'%02d\'\'", $m, $s)', 60*60, 'sprintf("%02d", $m)', $c_day*2, 'sprintf("%02d:%02d", $h, $m)', 5*$c_day, 'sprintf("%s %02d", $WD, $h)', 50*$c_day, 'sprintf("%02d.", $D)', 2*$c_year, 'sprintf("%02d/%02d", $M, $D)', 1E37, 'sprintf("%04d", $Y)', ); $graph_width = 55; $graph_interval = 60*60; # seconds $graph_interval_explicit = 0; # 1 if the user set it # png defaults. # =()<$png_width=@@;>()= $png_width=500; # =()<$png_height=@@;>()= $png_height=150; # =()<$png_index_default_name = "@@";>()= $png_index_default_name = "index.html"; # png defaults. $png_x_sp = 50; $png_x_spr = 3; # space on right side of png picture $png_y_sp = 15; $png_xaxis_sep_per_pix = 0.015; # seperators on x axis per pixel $png_yaxis_sep_per_pix = 0.04; # separators on y axis per pixel $png_yaxix_sep_width = 2; $png_xaxix_sep_height = 2; $png_font = GD::gdSmallFont; $png_font_offset_xax_x = -3; $png_font_offset_xax_y = 1; $png_font_offset_yax_x = -7; $png_font_offset_yax_y = -8; $png_average_character_width = 6; $png_average_character_width_vert = 4; $png_top_label_height = 15; $png_index = ""; # name of html index file. "" means default $png_normalized = 1; # normalize png: show bps (not b per # graph_interval) $png_caption_in_index = 0; # make max: and Average: in html index file $png_no_average = 0; # dont draw dotted horizontal line with average $png_asis = 0; # create 'asis' files (apache) $png_average_curve = 0; # create 'average on n dots around' line $png_total = 0; # show total in image # png defaults for other fonts. %png_defaults_fonts = ( TINY => [ \$png_x_sp, 30, \$png_y_sp, 10, \$png_xaxis_sep_per_pix, 0.03, \$png_yaxis_sep_per_pix, 0.04, \$png_font_offset_yax_y, -3, \$png_font_offset_xax_y, 2, \$png_average_character_width, 3, \$png_top_label_height, 10, ] ); # calculate time zone offset in seconds - use difference of output of date # command and time function, round it $tzoffset = 0; # ! makeunixtime needs this! $tzoffset = int( ($now-makeunixtime(`date +"%Y%m%d%H%M%S"`)) / 60) *60; # get time zone name $tzname = `date +%Z`; chop $tzname; # get host name $hostname = &hostname; $starttime=0 + $tzoffset; $endtime=$now; $starttime_explicit=0; $endtime_explicit=0; # configure option parser if the Getopt::Long package is new enough to # support this. eval {&Getopt::Long::config("bundling_override")}; $getopt_supports_bundling = $@ ? 0 : 1; # find out if we use gif or png images. GD versions <=1.19 support GIF, # >=1.20 make png images. $image_type = "png"; $image_TYPE = "PNG"; if ($have_GD) { $png_not_gif = ($GD::VERSION ge "1.20"); if ($png_not_gif) { } else { $image_type = "gif"; $image_TYPE = "GIF"; } } # parse command line. Option values are placed in $opt_X @GetOptionsControl= ( "d|dir=s", #@ DEBUG CODE BEGIN "debug=s" =>\$debug, "debug-current-time=s", #@ DEBUG CODE END "e|endtime=s", "f|filter=s", "fixed-quantity=s" =>\$fixed_quantity, # for backward compatibelity, allow options to be named 'gif-*'. "gif:s", "gif-filename-prefix=s"=>\$png_filename_prefix, "gif-asis", \$png_asis, "gif-average-curve=i" =>\$png_average_curve, "gif-charset=s", => \$png_charset, "gif-no-average" => \$png_no_average, "gif-caption-in-index"=>\$png_caption_in_index, "gif-height=i" => \$png_height, "gif-index:s" => \$png_index, "gif-header-in-index=s"=>\$png_header_in_index, "gif-normalize=i"=> \$png_normalized, "gif-start!" => \$png_start, "gif-end!" => \$png_end, "gif-total" => \$png_total, "gif-use-smallfont", "gif-width=i" => \$png_width, "g|graph", "h|help", "i|interval|intervall=s", "png:s", "png-filename-prefix=s"=>\$png_filename_prefix, "png-asis", \$png_asis, "png-average-curve=i" =>\$png_average_curve, "png-no-average" => \$png_no_average, "png-caption-in-index"=>\$png_caption_in_index, "png-charset=s", => \$png_charset, "png-height=i" => \$png_height, "png-index:s" => \$png_index, "png-header-in-index=s"=>\$png_header_in_index, "png-normalize=i"=> \$png_normalized, "png-start!" => \$png_start, "png-end!" => \$png_end, "png-total" => \$png_total, "png-use-smallfont", "png-width=i" => \$png_width, "r|replace", "s|starttime=s", "show-run-progression" => \$show_run_progression, "t|timeframe=s", "version", "x|exact", ); if (! &GetOptions(@GetOptionsControl)) { unless ($getopt_supports_bundling) { warn "(Use a space character between option letters and ". "their values. Or update to a\nnewer version of the ". "Getopt::Long perl module.)\n"; } die "$me: illegal option specified. \"$me --help\" for help.\n"; } if ($opt_version || $opt_version) { print < $#C_QUC) { die "$me: option --fixed-quantity requires an argument '', K, M, G or T\n"; } } # sort out $png_index hassle. "" means default; /dev/null means none. $png_index = $png_index_default_name if ($png_index eq ""); $png_index = undef if ($png_index eq "/dev/null"); $endtime = $now if ($endtime > $now); $mystarttime = makemytime($starttime); $myendtime = makemytime($endtime); %rule_firstfile = %rule_lastfile = ( ); if ($png_average_curve) { # initialize array of multipliers (weights) @av_curv_weight = ( ); $av_curv_weight_sum = 0; my($half) = $png_average_curve / 2; for ($i=0; $i<=$png_average_curve; $i++) { $av_curv_weight[$i] = (1-abs($i-$half)/$half)*2; $av_curv_weight_sum += $av_curv_weight[$i]; } } # Loop through dir, identify files # remember newest file before starttime so we know when data for # the first file starts opendir(DIR, $datdir) || die "$me: can't open datdir $datdir\n"; $newest_file_before_starttime = ""; $oldest_file_after_endtime = ""; while(defined($file = readdir DIR)) { next if (! -f "$datdir/$file" || $file !~ /^\d\d\d\d\d\d\d\d-\d\d\d\d\d\d$/); # a file belongs into our time frame if it was created # *after* starttime and *before or exactly at* endtime # (this is true!) if ($file le $mystarttime && ($newest_file_before_starttime eq "" || $file gt $newest_file_before_starttime)) { $newest_file_before_starttime = $file; } if ($file gt $myendtime && ($oldest_file_after_endtime eq "" || $file lt $oldest_file_after_endtime)) { $oldest_file_after_endtime = $file; } push(@files_before, $file) if($file le $mystarttime && $file =~ /\d{8}-\d{6}/); next if ($file le $mystarttime || $file gt $myendtime); push(@files, $file) if($file =~ /\d{8}-\d{6}/); } closedir DIR; push(@files, $oldest_file_after_endtime) if ($oldest_file_after_endtime); unshift(@files, $newest_file_before_starttime) if ($newest_file_before_starttime); $rulenumber=0; @files = sort @files; @files_before = sort @files_before; if(defined(@files_before) && $starttime_explicit && $endtime_explicit){ my $r_opt=$replace?'-r':''; if($opt_t=~/hour/i){ my($hr,$done)=('0','0'); foreach(@files_before){ $_=~/(\d{8})-(\d\d)/; if($hr eq ($1.$2) && $done ne ($1.$2)){ $done=$1.$2; makemytime(makeunixtime($hr)+3600)=~/(\d{4})(\d\d)(\d\d)-(\d\d)/; system(sprintf('%s %s -s %s0000 -e %s0000',$0,$r_opt,$hr,$1.$2.$3.$4)); } $hr=length($1.$2)==10?($1.$2):$done; } }elsif($opt_t=~/day/i){ my($dy,$done)=('0','0'); foreach(@files_before){ $_=~/(\d{8})/; if($dy eq $1 && $done ne $1){ $done=$1; makemytime(makeunixtime($dy)+86400)=~/(\d{4})(\d\d)(\d\d)/; system(sprintf('%s %s -s %s000000 -e %s000000',$0,$r_opt,$dy,$1.$2.$3)); } $dy=length($1)==8?$1:$done; } }elsif($opt_t=~/week/i){ my($wk,$done)=('0','0'); my $tmp; foreach(@files_before){ $_=~/(\d{8})/; $tmp=makeunixtime($1.'000000'); makemytime($tmp-(((localtime($tmp))[6]-1)%7)*86400)=~/(\d{8})/; if($wk eq $1 && $done ne $1){ $done=$1; makemytime(makeunixtime($wk)+604800)=~/(\d{4})(\d\d)(\d\d)/; system(sprintf('%s %s -s %s000000 -e %s000000',$0,$r_opt,$wk,$1.$2.$3)); } $wk=length($1)==8?$1:$done; } }elsif($opt_t=~/month/i){ my($mon,$done)=('0','0'); my($year,$month); foreach(@files_before){ $_=~/(\d{6})/; if($mon eq $1 && $done ne $1){ $done=$1; $year=int(substr($mon,0,4)); $month=int(substr($mon,4,2))+1; $year+=int($month/12); $month%=12; system(sprintf('%s %s -s %s01000000 -e %d%02d01000000',$0,$r_opt,$mon,$year,$month)); } $mon=$1; } }elsif($opt_t=~/year/i){ my($yr,$done)=('0','0'); foreach(@files_before){ $_=~/(\d{4})/; if($yr eq $1 && $done ne $1){ $done=$1; system(sprintf('%s %s -s %s0101000000 -e %d0101000000',$0,$r_opt,$yr,int($yr)+1)); } $yr=$1; } } } #@ DEBUG CODE BEGIN if ($debug =~ /files_to_read/) { print "DEBUG: files to read="; foreach (@files) { print "$_, "; } print "\n"; } #@ DEBUG CODE END # read all data files we need and put the data into memory. &read_files; @rules_sorted = sort { $rulenames{$a} <=> $rulenames{$b} } keys %rulenames; &make_one_file_from_many() if $replace; printf "IP accounting summary\nHost: $hostname / Time created: %s $tzname\n", nice_date(makemytime($now)); printf "Data from %s $tzname to %s $tzname\n", nice_date($starttime_explicit || !@files ? $mystarttime : $files[$[]), nice_date($myendtime); $incomplete_data=0; foreach (@rules_sorted) { if (/$rule_regex/) { &print_sum_line($_); } } if ($incomplete_data) { print "* = data incomplete, rule was not there all the time\n"; } if ($graph || $png) { &out_graph($png); } ########################## # END OF MAIN PROGRAM ########################## sub print_sum_line { my($f) = shift; my($s) = " "; if ($glb_number_of_records > $rule_count{$f}) { $incomplete_data++; $s="*"; } printf("%s %s: %15s\n", $s, $filter{$f}, &customized_number($bytes{$f}) ); } # read all data files (@files contains the names, must be sorted!) # and put the data into our global memory data # structures. special care must be taken with data of the first and # the last file we read, since we only want data which is from our # time frame. Furthermore, data from before and after this time frame # must be preserved in special data structures because we might replace # them (option --replace) and have to write extra files for these times # then. sub read_files { my $run_s; my $s; if ($show_run_progression) { $| = 1; $run_s = ""; printf " (%d files total)\r", $#files+1; } my $lastfile = undef; my $file_in_time = 0; my $file_after_time = 0; # global: $glb_number_of_records = 0; for ($ifile=0; $ifile<=$#files; $ifile++) { if ($show_run_progression) { my($s) = sprintf "%3d %%\r", int($ifile * 100 / ($#files + 1)); if ($s != $run_s) { print($s); $run_s = $s; } } my $file = $files[$ifile]; my $filedata; my $do_collect = 1; if ($file le $mystarttime) { # this file is too old, we dont need the data. # However, the file name gives us a clue on the # time period the next file covers. $do_collect = 0; } # read the actual file if we want the data so far. $filedata = read_file($file) if ($do_collect); if ($do_collect && $file_in_time == 0) { # the file is from after starttime. if it is the # first one, split the data (if we know for how # long this data is valid, and if $lastfile is not # equal to $mystarttime in which case the split is # redundant). If we don't have a clue about the # last file time before our first file was created, # we do not know how much of the file data is in our # time frame. we assume everything belongs to us. $file_in_time = 1; if ($lastfile && $lastfile ne $mystarttime) { my $newdata = &split_filedata($filedata, $lastfile, $file, $mystarttime); $glb_filedata_before = $filedata; $filedata = $newdata; $lastfile = $mystarttime; } } if ($file gt $myendtime) { # this file is too new, but the data in it may have # begun within our time frame. if ($file_after_time == 0) { $file_after_time = 1; if ($lastfile) { $glb_filedata_after = &split_filedata($filedata, $lastfile, $file, $myendtime); } else { $do_collect = 0; } } else { $do_collect = 0; # just too new. } } if ($do_collect) { &collect_data_from_file($filedata, $ifile); $glb_number_of_records++; } $lastfile = $file; } print " \r" if ($show_run_progression); } # split the data in $1 (format as from read_file) into a pair of two # such data sets. The set referenced to as $1 will afterwards contain # the first part of the data, another set which is returned contains # the second part of the data. # interpret the data as having start time=$2 and end time=$3 and split # time=$4 sub split_filedata { my $data = shift; my $mstart = shift; my $mend = shift; my $msplit = shift; # calculate factors for multiplications my $ust = makeunixtime($mstart); my $uperiod = makeunixtime($mend) - $ust; my $usplit = makeunixtime($msplit) - $ust; if ($uperiod < 0) { # hmmm? die Daten sind rueckwaerts??? $uperiod = -$uperiod; } my $fac1; if ($usplit < 0) { $fac1 = 0; } elsif ($usplit > $uperiod) { $fac1 = 1; } else { $fac1 = $usplit / $uperiod; } # $fac1 now says us how much weight the first result has. # initialize the set we will return. my @ret = ( ); foreach $set (@$data) { my ($rule, $bytes, $pkts) = @$set; $$set[1] = int($bytes * $fac1 + 0.5); $$set[2] = int($pkts * $fac1 + 0.5); push(@ret, [ $rule, $bytes - $$set[1], $pkts - $$set[2] ]); } return \@ret; } # put data from one file into global data structures # must be called in correct sorted file name order to set rules_lastfile # and rules_firstfile (which are currently useless) # arguments: # $1=index number of file; $2 = reference to array with data from file sub collect_data_from_file { my($filedata, $ifile, $i); $filedata = shift; $ifile=shift; for ($i=0; $i<=$#$filedata; $i++) { my $set = $$filedata[$i]; my $rule = $$set[0]; my $bytes = $$set[1]; my $pkts = $$set[2]; # if rule first appeared in this file, initialize its # life. if (!defined($rulenames{$rule})) { $rulenames{$rule}=$rulenumber++; &init_filter_id($rule); } $bytes{$rule} += $bytes; $pkts{$rule} += $pkts; $rule_count{$rule}++; $rule_lastfile{$rule} = $file; if ($progression) { $$prog_bytes{$rule}[$ifile] += $bytes; $$prog_pkts{$rule}[$ifile] += $pkts; $prog_bytes_max{$rule} = $bytes if ($prog_bytes_max{$rule} < $bytes); $prog_pkts_max{$rule} = $pkts if ($prog_pkts_max{$rule} < $pkts); } } } # initialize data variables for a new rule - if it is new sub init_filter_id { my($s, $ifile) = @_; if (!defined $bytes{$s}) { $bytes{$s}=0; $pkts{$s}=0; $filter{$s} = sprintf("%-48s", $s); $rule_firstfile{$s} = $files[$ifile]; $rule_lastfile{$s} = ""; $rule_count{$s} = 0; if ($progression) { $prog_pkts{$s} = [ ]; $prog_pkts_max{$s} = 0; $prog_bytes{$s} = [ ]; $prog_bytes_max{$s} = 0; } } } # read file $1 # Return value: reference to array a of length n; # n is the number of rules in the file # each field in a is an array aa with 3 fields # the fields in arrays aa are: [0]=name of rule; [1]=byte count; # [2]=packet count # function does not use global variables sub read_file { my($file, $beforedata, $indata, $i); my($pkts, $bytes); my(@result); $file=shift; $indata=0; $beforedata=1; open(FILE, "$datdir/$file") || die "$me: cant open file $file\n"; $i=-1; while() { if ($beforedata) { if (/^$datdelim$/) { $beforedata=0; $indata=1; next; } next if (/^#/); chop; push(@result, [ $_, 0, 0 ]); next; } if (/^IP accounting rules/) { $indata = 1; next; } if ($indata && /^\s*(\d+)\s+(\d+)/) { $indata=1; $beforedata=0; $i++; # found accouting data ($pkts, $bytes) = ($1, $2); if (!defined($result[$i])) { print STDERR "$me: more data than rules in $file - extra ignored\n"; last; } $result[$i][1] = $bytes; $result[$i][2] = $pkts; } } if (defined($result[$i+1])) { print STDERR "$me: more rules declared than data in file $file?!\n"; } close FILE; \@result; } # given a string in format YYYYMMDD[hh[mm[ss]]], make unix time # use time zone offset $tzoffset (input=wall clock time, output=UTC) sub makeunixtime { my($y, $m, $d, $h, $i, $e); $s = shift; $h=0; $i=0; $e=0; if ($s =~ /^(\d\d\d\d)(\d\d)(\d\d)/) { ($y, $m, $d) = ($1, $2, $3); if ($s =~ /^\d\d\d\d\d\d\d\d-?(\d\d)/) { $h=$1; if ($s =~ /^\d\d\d\d\d\d\d\d-?\d\d(\d\d)/) { $i=$1; if ($s =~ /^\d\d\d\d\d\d\d\d-?\d\d\d\d(\d\d)/) { $e=$1; } } } } else { return 0; } $y-=1970; $s = (($y)*365) + int(($y+2)/4) + $moff[$m-1] + $d-1; $s-- if (($y+2)%4 == 0 && $m < 3); $s*86400 + $h*3600 + $i*60 + $e + $tzoffset; } # return the given unix time in localtime in "my" time format sub makemytime { my($s)=shift; my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($s); # ugly side effect of this function: set a global scalar # containing the day of week number. $mmt_wday = $wday; return sprintf("%04d%02d%02d-%02d%02d%02d", 1900+$year, $mon+1, $mday, $hour, $min, $sec); } # parse time as a duration # syntax is # cmd_time: seconds | cmd_time_with_size # cmd_time_with_size: cmd_time_atom | cmd_time_with_size cmd_time_atom # cmd_time_atom: number size # size: "s"|"m"|"h"|"D"|"W"|"M"|"Y" # (sec, min, hours, Days, Weeks, Months, Years) # seconds: number # return number of seconds sub parse_cmd_time { my($sec) =0; $_=shift; return $_ if (/^\d+$/); while($_) { if (! /^(\d+)\s?([smhDWMY])(.*)$/) { die "$me: syntax error in time (duration)\n"; } $_=$3; if ($2 eq "s") { $sec += $1; } elsif ($2 eq "m") { $sec += $1*60; } elsif ($2 eq "h") { $sec += $1*60*60; } elsif ($2 eq "D") { $sec += $1*60*60*24; } elsif ($2 eq "W") { $sec += $1*60*60*24*7; } elsif ($2 eq "M") { $sec += $1*60*60*24*30; } elsif ($2 eq "Y") { $sec += $1*60*60*24*365; } } $sec; } sub set_time_frame { my($opt_t) = shift; my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($now); if ($opt_t =~ /^this hour/i) { $opt_t = "the hour 0 hours ago"; } elsif ($opt_t =~ /^last hour/i) { $opt_t = "the hour 1 hour ago"; } elsif ($opt_t =~ /^today/i) { $opt_t = "the day 0 days ago"; } elsif ($opt_t =~ /^yesterday/i) { $opt_t = "the day 1 day ago"; } elsif ($opt_t =~ /^the day before yesterday/i) { $opt_t = "the day 2 days ago"; } elsif ($opt_t =~ /^this week/i) { $opt_t = "the week 0 weeks ago"; } elsif ($opt_t =~ /^last week/i) { $opt_t = "the week 1 week ago"; } elsif ($opt_t =~ /^the week before last week/i) { $opt_t = "the week 2 weeks ago"; } elsif ($opt_t =~ /^this month/i) { $opt_t = "the month 0 months ago"; } elsif ($opt_t =~ /^last month/i) { $opt_t = "the month 1 month ago"; } elsif ($opt_t =~ /^this year/i) { $opt_t = "the year 0 years ago"; } elsif ($opt_t =~ /^last year/i) { $opt_t = "the year 1 year ago"; } if ($opt_t =~ /^the hour (\d+) hours? ago/i) { $i=$1; my($thishour)=makeunixtime(sprintf("%04d%02d%02d%02d0000", 1900+$year, $mon+1, $mday, $hour)); $starttime=$thishour - 60*60 * $i; $endtime = $thishour - 60*60 * ($i-1); } elsif ($opt_t =~ /^the day (\d+) days? ago/i) { $i=$1; my($thismorning)=makeunixtime(sprintf("%04d%02d%02d000000", 1900+$year, $mon+1, $mday)); $starttime=$thismorning - 60*60*24 * $i; $endtime=$thismorning - 60*60*24 * ($i-1); $starttime += (localtime($starttime))[2]?3600:0; $endtime += (localtime($endtime))[2]?3600:0; } elsif ($opt_t =~ /^the week (\d+) weeks? ago/i) { $i=$1; $mday = $mday-($wday >0 ? $wday-1 : 6); if ($mday < 1) { $mon--; if ($mon < 0) { $mon += 12; $year--; } $mday += $mofg[$mon]; } my($monday)=makeunixtime(sprintf("%04d%02d%02d000000", 1900+$year, $mon+1, $mday)); $starttime=$monday - 60*60*24*7 * $i; $endtime=$monday- 60*60*24*7 * ($i-1); $starttime += (localtime($starttime))[2]?3600:0; $endtime += (localtime($endtime))[2]?3600:0; } elsif ($opt_t =~ /^the month (\d+) months? ago/i) { $mon = $mon - $1; while ($mon < 0) { $year--; $mon += 12; } $starttime=makeunixtime(sprintf("%04d%02d01000000", 1900+$year, $mon+1)); $endtime=$starttime + 60*60*24*$mofg[$mon]; $endtime += 60*60*24 if ((1900+$year)%4 ==0 && $mon==1); $starttime += (localtime($starttime))[2]?3600:0; $endtime += (localtime($endtime))[2]?3600:0; } elsif ($opt_t =~ /^the year (\d+) years? ago/i) { $i=$1; $starttime=makeunixtime(sprintf("%04d0101000000", 1900+$year-$i)); $endtime=makeunixtime(sprintf("%04d0101000000", 1900+$year-$i+1)); $starttime += (localtime($starttime))[2]?3600:0; $endtime += (localtime($endtime))[2]?3600:0; } else { die "$me: Unknown time frame: \"$opt_t\"\n"; } } sub nice_number { my($n)=shift; if ($n > 10484711424) { # 9999 MByte $n = sprintf("%dG", $n/1073741824); } elsif ($n > 9999 * 1024) { $n = sprintf("%dM", $n/1048576); } elsif ($n > 9999) { $n = sprintf("%dK", $n/1024); } $n; } sub customized_number { my $n = shift; if ($exact) { } elsif (defined($fixed_quantity)) { $n = int($n/$C_QU[$fixed_quantity]+.5); $n .= $C_QUC[$fixed_quantity] if ($C_QUC[$fixed_quantity]); } else { $n = &nice_number($n); } $n; } # format date in format YYYYMMDD-HHMMSS nicely. sub nice_date { $s = shift; my($wday); $s =~ s@^(\d\d\d\d)(\d\d)(\d\d)-(\d\d)(\d\d)(\d\d)@$1/$2/$3 $4:$5:$6@; $s; } # Format a number representing seconds into a nice time (duration). sub nice_time { my($s) = shift; my($t, $i, $mz); $t = ""; $i = int($s / 31557600); $s = $s % 31557600; $mz = "s" if ($i > 1); $t = "$i year$mz " if ($i); $i = int($s / 86400); $s = $s % 86400; $mz = "s" if ($i > 1); $t = "$t$i day$mz " if ($i); $i = int($s / 3600); $s = $s % 3600; $mz = "s" if ($i > 1); $t = "$t$i hour$mz " if ($i); $i = int($s / 60); $s = $s % 60; $t = "$t$i min " if ($i); $t = "$t$s sec" if ($s); $t; } # replace: summarize all data from within time frame into one file # generate extra files for times before and after the time frame # if we have such data # try to be atomic and clean up in case of failures. sub make_one_file_from_many { return if (! @files); # rename the data files we read. dont rename # $newest_file_before_starttime, though. my $ok = 1; my($file1, $file2, $file3); my @unlink_names = (); foreach(@files) { next if ($_ eq $newest_file_before_starttime); my $n = "$datdir/$_.rep"; unlink($n); $ok = rename("$datdir/$_", $n); last if (!$ok); push(@unlink_names, $n); } if ($ok && $#unlink_names < $[) { # no files to summarize. return; } # the file name is end time. if ($ok) { $file1 = $myendtime; my $data = &sum_file; $ok = &store_data_file($file1, $data, "(core summary)"); } # write two more files: if ($ok) { # 1) before starttime, if $glb_filedata_before if (defined($glb_filedata_before)) { $file2 = $mystarttime; $ok = &store_data_file($file2, $glb_filedata_before, "(pre-timeframe split data part)"); remove($file1) if (!$ok); } } if ($ok) { # 2) after endtime, if $glb_filedata_after if (defined($glb_filedata_after)) { $file3 = $oldest_file_after_endtime; $ok = &store_data_file($file3, $glb_filedata_after, "(post-timeframe split data part)"); unlink($file1, $file2) if (!$ok); } } if ($ok) { # we wrote all files well. unlink the renamed ones. $ok = unlink(@unlink_names); } else { # rename back. warn "$me: Error while --replace. Moving back to old state.\n"; foreach my $file (@unlink_files) { if ($file =~ /^(.*)\.rep$/) { if (!rename($file, $1)) { warn "$me: could not rename $file to $1\n"; } } } } } # from the global data, generate one record of data in the format of # read_file sub sum_file { my $rule; my @result = ( ); # sort rules by rule number (sequence of appearance) foreach $rule (sort { $rulenames{$a} <=> $rulenames{$b} } keys %rulenames) { push(@result, [ $rule, $bytes{$rule}, $pkts{$rule} ]); } return \@result; } # store the data $2 in file $1. If file $1 exists, read it and store the # sum of both. Return 1 for success, 0 otherwise. sub store_data_file { my $file = shift; my $data = shift; my $comment = shift; if ( -f "$datdir/$file") { my $olddata = read_file($file); $data = [ @$data, @$olddata ]; &compress_data_record($data); } # we "print" the file in memory my(@text); push(@text, sprintf("# ipac $version summary file generated %s\n", makemytime(time))); push(@text, sprintf("# source files: %s to %s\n", $files[0], $files[$#files])); push(@text, "# $comment\n") if (defined($comment)); push(@text, "#\n"); my $set; foreach $set (@$data) { push(@text, "$$set[0]\n"); } push(@text, "$datdelim\n"); foreach $set (@$data) { push(@text, "$$set[2] $$set[1]\n"); } # write updated file if (! open(FILE, ">$datdir/$file")) { warn "$me: could not open $datdir/$file, this is a fatal error\n"; return 0; } print FILE @text; close FILE; return 1; } # find identical rules in $1 and make them one by adding the counters. sub compress_data_record { my $data = shift; my $rule; my $set; my @newdata; my $set2; foreach $set (@$data) { $rule = $$set[0]; next if (!defined($rule)); my $bytes = 0; my $pkts = 0; foreach $set2 (@$data) { if ($rule eq $$set2[0]) { $bytes += $$set2[1]; $pkts += $$set2[2]; $$set2[0] = undef; } } push(@newdata, [ $rule, $bytes, $pkts ]); } @$data = @newdata; } sub out_graph { my($png) = shift; my(@rules_filtered, @rules_filtered_data, $i, $lst); # Do preparations for all graphs. $startt_graph = $starttime; $startt_graph = makeunixtime($files[$[]) if (!$starttime_explicit && @files); # IF we make a png and we show throughput instead of absolute values # (which is the normal case), AND # the user has not set --interval, OR # $graph_inverval is smaller than one pixel would be # THEN # set $graph_interval to one pixel. $i = $png_width-$png_x_sp-$png_x_spr; # actual x pixel number if ($png && $png_normalized && ( ($endtime - $startt_graph)/$graph_interval > $i || ! $graph_interval_explicit) ) { $graph_interval = ($endtime-$startt_graph) / $i; } # Calculate HTTP Expires: Header. # if $endtime is $now, set it to $now + $graph_interval. # if it is not $now, omit it. undef($expires); if ($endtime == $now) { $expires = &http_time($now + $graph_interval); } # HTTP Last-Modified (asis) $last_modified = &http_time($now) if ($png_asis); $lst = ""; foreach $rule (@rules_sorted) { next if ($rule eq $lst); $lst = $rule; if ($rule =~ /$rule_regex/) { push(@rules_filtered, $rule); my($graph_data) = &single_graph($rule, $png); push(@rules_filtered_data, $graph_data); } } &out_index_html(\@rules_filtered, \@rules_filtered_data) if ($png && defined($png_index)); } sub single_graph { my($rule) = shift; my($png) = shift; my($max, $i, $ifile, $inter_st, $inter_end, $value, $iut, $oldiut); my($sum, $avg, @values, @valtime, $bytes, %dat); print "Graph for rule \"$rule\"\n" unless $png; $max=0; $sum = 0; $inter_st = $startt_graph; $inter_end = $inter_st + $graph_interval; $value=0; $iut=$inter_st; for ($ifile=0; $ifile<=$#files; $ifile++) { #@ DEBUG CODE BEGIN DBXXX("graph_calc",sprintf("-- \$inter_st=%s, \$inter_end=%s\n", makemytime($inter_st), makemytime($inter_end))); DBXXX("graph_calc",sprintf("-- \$iut=%s, \$oldiut=%s\n", makemytime($iut), makemytime($oldiut))); DBXXX("graph_calc","new file: nr=$ifile, name=$files[$ifile]\n"); #@ DEBUG CODE END if (defined($$prog_bytes{$rule}[$ifile])) { $bytes = $$prog_bytes{$rule}[$ifile]; } else { $bytes = 0; } # while the timestamp is before start time, skip my $newut = makeunixtime($files[$ifile]); next if ($newut <= $startt_graph); $oldiut = $iut; $iut = $newut; #@ DEBUG CODE BEGIN DBXXX("graph_calc",sprintf("-- \$iut=%s, \$oldiut=%s\n", makemytime($iut), makemytime($oldiut))); #@ DEBUG CODE END # if the file is younger than the end of the time frame # we are examining, split the count. while ($iut > $inter_end) { # if the timestamp is from after our overall end time, # pretend it is from end time. (Data has been adjusted # in read_data() anyway.) if ($iut > $endtime) { $iut = $endtime; #@ DEBUG CODE BEGIN DBXXX("graph_calc", sprintf("adjust \$iut to \$endtime: %s\n", makemytime($iut))); #@ DEBUG CODE END last unless ($iut > $inter_end); } # number of bytes still in current time frame $i = int($bytes * ($inter_end-$oldiut)/($iut-$oldiut)+.5); #@ DEBUG CODE BEGIN DBXXX("graph_calc",sprintf("- add rest: \$bytes=$bytes, \$value=$value, \$inter_st=%s, \$inter_end=%s,\n", makemytime($inter_st), makemytime($inter_end))); DBXXX("graph_calc",sprintf("\t\$iut=%s, \$oldiut=%s; so \$i=$i\n", makemytime($iut), makemytime($oldiut))); #@ DEBUG CODE END $value += $i; push(@values, $value); #@ DEBUG CODE BEGIN DBXXX("graph_calc", "pushed value=$value\n"); #@ DEBUG CODE END $max = $value if ($value > $max); $sum += $value; $value=0; $oldiut=$inter_end; $inter_st = $inter_end; $inter_end += $graph_interval; $bytes -= $i; } #@ DEBUG CODE BEGIN DBXXX("graph_calc"," add \$value += \$bytes: $value + $bytes ="); #@ DEBUG CODE END $value += $bytes; #@ DEBUG CODE BEGIN DBXXX("graph_calc"," $value\n"); #@ DEBUG CODE END } # no: the value will be inaccurate push(@values, $value); $max = $value if ($value > $max); $sum += $value; $avg = @values ? ($sum / ($#values + 1)) : 0; %dat = ( 'max' => $max, 'min' => 0, 'sum' => $sum, 'avg' => $avg ); if ($png) { my($i) = &draw_png($png_width, $png_height, \%dat, \@values, $startt_graph, $rule); my($filename)= $png_asis ? "$rule.asis" : "$rule.$image_type"; $filename = &good_filename($filename); if (-d $png) { if ($png_filename_prefix ne "") { $filename="$png/$png_filename_prefix$filename"; } else { $filename="$png/$filename"; } } open(OUT, ">$filename") || die "$me: can't open \"$filename\": $!\n"; if ($png_asis) { print OUT "Status: 200 OK\n"; print OUT "Expires: $expires\n" if ($expires); print OUT "Last-Modified: $last_modified\n"; printf OUT "Content-Length: %d\n", length($i); print OUT "Content-Type: image/$image_type\n"; print OUT "\n"; } binmode OUT; print OUT $i; close OUT; } else { &draw_ascii_graph(\@values, $max, $startt_graph); } # return some interesting data about the graph. return \%dat; } sub good_filename { my($s) = shift; $s =~ s@[\s\\/\|\>\<]@_@g; $s; } sub draw_ascii_graph { my($values, $max, $startt_graph) = @_; my($s, $i, $inter_st); printf "time bytes 0%s%5s\n", string(" ", $graph_width-6), nice_number($max); $inter_st=$startt_graph; for ($i=0; $i<=$#{$values}; $i++) { $s= $max ? string("*", int(($$values[$i]/$max)*$graph_width+.5) ) : ""; printf "%s %s\n", nice_date(makemytime($inter_st)), $s; $inter_st += $graph_interval; } } # repeat string n times sub string { my($s, $i); $s=""; for ($i=1; $i<$_[1]; $i++) { $s .= $_[0]; } $s; } sub draw_png { return 0 unless $have_GD; my($xsiz, $ysiz, $dat, $values, $startt_graph, $rule) = @_; my($maxval) = $$dat{'max'}; my($minval) = $$dat{'min'}; my($im) = new GD::Image($xsiz, $ysiz); # hack to leave pixels blank at the right border. $xsiz -= $png_x_spr; my($backg) = $im->colorAllocate(224,224,224); my($white) = $im->colorAllocate(255,255,255); my($black) = $im->colorAllocate(0,0,0); my($blue) = $im->colorAllocate(0,0,255); my($green) = $im->colorAllocate(0,128,0); my($red) = $im->colorAllocate(255,0,0); my($png_arc_size)=1; my($x, $y, $i, $s, $xstep, $ymulti, $arc_color, $line_color, $font_color, $title_color, $axle_color, $x1, $y1, $duration, $normalize_factor, $avg_color); $im -> interlaced(1); $maxval = 1 unless $maxval; $png_arc_size = 5 unless $png_normalized; # normalize_factor is a multiplier for Y values to get user # caption values. $normalize_factor = $png_normalized?$png_normalized/$graph_interval : 1; $$dat{'max_caption'} = $maxval * $normalize_factor; $$dat{'avg_caption'} = $$dat{'avg'} * $normalize_factor; # division by 0 in the following line? FIXME $xstep = ($xsiz-$png_x_sp) / ($#{$values} + 1); $ymulti = ($ysiz-$png_y_sp-$png_top_label_height) / ($maxval-$minval); $arc_color = $black; $line_color = $black; $font_color = $blue; $axle_color = $blue; $title_color = $green; $avg_color = $green; $avg_line_color = $red; # draw axles. $im->line($png_x_sp, $ysiz-$png_y_sp, $xsiz, $ysiz-$png_y_sp, $axle_color); $im->line($png_x_sp, $png_top_label_height, $png_x_sp, $ysiz-$png_y_sp, $axle_color); $s = $png_normalized ? $png_normalized : $graph_interval; if ($s == 1) { $s = "bytes / sec"; } elsif ($s == 8) { $s = "bit / sec"; } else { $s = "bytes / $s sec"; } ¢erStringUp($im, $s, $png_font, 1, $png_top_label_height, $ysiz, $font_color); # draw total average line if requested. unless ($png_no_average) { $y = ($ysiz - $png_y_sp) - $$dat{'avg'} * $ymulti; $im -> GD::Image::dashedLine($png_x_sp, $y, $xsiz, $y, $avg_color); } # compute optimal interval of labels. unless (defined($x_labels)) { $x_labels = &optimal_label_interval_time( int($png_xaxis_sep_per_pix * ($xsiz-$png_x_sp) + 0.5), $#{$values}+1, $startt_graph); } my($y_lab_int) = &optimal_label_interval_2( int($png_yaxis_sep_per_pix * ($ysiz-$png_y_sp-$png_top_label_height) + 0.5), $maxval*$normalize_factor, 1); # draw labels. # X AXIS $y = $ysiz - $png_y_sp; # time we cover in seconds $duration = ($#{$values}+1) * $graph_interval; # $%x_labels: keys are times, values are label strings foreach $i (keys %$x_labels) { $x = $png_x_sp + (($i - $startt_graph)/$duration * ($xsiz - $png_x_sp)); $im->line($x, $y - $png_xaxix_sep_height, $x, $y + $png_xaxix_sep_height, $axle_color); $im->string($png_font, $x + $png_font_offset_xax_x - length($$x_labels{$i}) / 2 * $png_average_character_width, $y + $png_font_offset_xax_y, $$x_labels{$i}, $font_color); } # Y AXIS. $x = $png_x_sp; for ($i = 0; $i <= $maxval * $normalize_factor; $i += $y_lab_int) { $y = ($ysiz-$png_y_sp) - int(($i / $normalize_factor * $ymulti + 0.5)); $im->line($x - $png_yaxix_sep_width, $y, $x + $png_yaxix_sep_width, $y, $axle_color); $s = &nice_number($i); $im->string($png_font, $png_x_sp - $png_average_character_width* length($s)+$png_font_offset_yax_x, $y + $png_font_offset_yax_y, $s, $font_color); } # put the first dot $i = $[; $x = $png_x_sp; $y = ($ysiz - $png_y_sp) - $$values[$i++] * $ymulti; $im -> GD::Image::arc($x, $y, $png_arc_size, $png_arc_size, 0, 360, $black) if ($png_arc_size >1); # put all lines and dots. $yavg = undef; my(@av_line); my($gal_offset) = int($png_average_curve/2+.5); while($i <= $#{$values}) { $x1 = $x + $xstep; if ($png_average_curve) { push(@av_line, $$values[$i]); shift(@av_line) if ($#av_line > $png_average_curve); if ($#av_line == $png_average_curve) { # draw it. my($ia); my($asum)=0; for ($ia=0; $ia<$png_average_curve+1; $ia++) { $asum += $av_line[$ia] * $av_curv_weight[$ia]; } $yavg1 = ($ysiz-$png_y_sp) -($asum/$av_curv_weight_sum) *$ymulti; if ($yavg) { $im-> GD::Image::line($x-$gal_offset, $yavg, $x1-$gal_offset, $yavg1, $avg_line_color); } $yavg = $yavg1; } } $y1 = ($ysiz - $png_y_sp) - $$values[$i++] * $ymulti; $im -> GD::Image::arc($x1, $y1, $png_arc_size, $png_arc_size, 0, 360, $arc_color) if ($png_arc_size >1); $im -> GD::Image::line($x, $y, $x1, $y1, $line_color); $x = $x1; $y = $y1; } # put the title string. $s = $rule; if ($png_start) { # sets &mmt_wday my ($ss) = &nice_date(&makemytime($startt_graph)); $s .= sprintf(" Start: %s %s", $wday[$mmt_wday], $ss); } if ($png_end) { # sets &mmt_wday my ($ss) = &nice_date(&makemytime($endtime)); $s .= sprintf(" End: %s %s", $wday[$mmt_wday], $ss); } $s .= sprintf(" Max: %s Avg: %s", &nice_number(int($$dat{'max_caption'}+0.5)), &nice_number(int($$dat{'avg_caption'}+0.5))); $s .= sprintf(" Total: %s", &nice_number($bytes{$rule})) if ($png_total); $x1 = length($s)*$png_average_character_width; $x = int($xsiz/2 - $x1/2+0.5); $im->string($png_font, $x, 0, $s, $title_color); return ($png_not_gif) ? $im->png : $im->gif; } sub optimal_label_interval { my($want_separators, $max, $useKM) = @_; my($mat, $exp); return 1 if (! $max || ! $want_separators); $mat = $max / $want_separators; $exp=0; # stupid way, i am not a mathematican. while ($mat >= 10) { $mat = $mat / 10; $exp++; } while ($mat < 1) { $mat = $mat * 10; $exp--; } # $mat is now between 1 and 9.99... if ($mat < 1.7) { $mat = 1; } elsif ($mat < 3.7) { $mat = 2.5; } elsif ($mat < 7.5) { $mat = 5; } else { $mat = 10; } $mat * (10 ** $exp); } # compute a list of time labels for the x axix. Make about $want_separators # labels. First column is of time $startt_graph, there are $n values. sub optimal_label_interval_time { my($want_separators, $n, $startt_graph) = @_; my($duration, $sps, $div, $interval, $firsttime, $i, %result); my($Y, $M, $D, $h, $m, $s, $pfunc); # We need to find out what the best label notation and the best label # interval is. The magic measure is "seconds per separator", sps. $duration = $n * $graph_interval; $sps = $duration / $want_separators; # find the closest value from @possible_interval. Linear search. for ($i=$[; $i<=$#possible_interval && $sps > $possible_interval[$i]; $i++) { } $i-- if ($i>$[ && $possible_interval[$i]-$sps > $sps-$possible_interval[$i-1]); $interval = $possible_interval[$i]; for ($i=$[; $i<=$#png_time_labels; $i+=2) { last if ($png_time_labels[$i] > $duration); } $pfunc = $png_time_labels[$i+1]; # this is the first time. $firsttime = int(($startt_graph + $interval - $tzoffset) / $interval - 0.001) * $interval + $tzoffset; # if the interval found is more than a week, we want to display # dates on the x axis on half month, month, and year # beginnings. # set $increment_func to a sub which does the increment. The sub # gets three arguments year, month and day by reference. The month # may grow above 12. my $increment_func = undef; if ($interval > $c_day*7) { if ($interval == $c_day * 15) { # step by half months. $increment_func = sub { if ($_[2] >14) { $_[2] = 1; $_[1]++; } else { $_[2] = 15; } }; } elsif ($interval >= $c_day * 30 && $interval <= $c_year/2) { # step by months, months*2, months*3, months*4 or # months * 6. if ($interval == $c_day * 30) { $inc_func_div = 1; } elsif ($interval == $c_day * 60) { $inc_func_div = 2; } elsif ($interval == $c_year/4) { $inc_func_div = 3; } elsif ($interval == $c_year/3) { $inc_func_div = 4; } elsif ($interval == $c_year/2) { $inc_func_div = 6; } $increment_func = sub { $_[2] = 1; $_[1] = int(($_[1]-1) / $inc_func_div + 1) * $inc_func_div + 1; }; } elsif ($interval >= $c_year) { $inc_func_div = int($interval / $c_year + 0.5); $increment_func = sub { $_[2] = 1; $_[1] = 1; $_[0] = int($_[0] / $inc_func_div + 1) * $inc_func_div; }; } } # move forward to first "even" label point if we have # increment_func now. if (defined($increment_func)) { ($s,$m,$h,$D,$M,$Y,$WD) = localtime($firsttime); $M++; $Y+=1900; &$increment_func($Y, $M, $D); $firsttime = POSIX::mktime(0, 0, 0, $D, $M-1, $Y-1900); } # generate a list of labels with linear distance. $n=""; for ($i = $firsttime; $i < $startt_graph+$duration; ) { ($s,$m,$h,$D,$M,$Y,$WD) = localtime($i); $WD = $wday_short[$WD]; $M++; $Y+=1900; $result{$i} = eval($pfunc); if ($result{$i} eq $n) { # do not make two labels with the same value. $result{$i} = ""; } else { $n = $result{$i}; } # increment if (defined($increment_func)) { &$increment_func($Y, $M, $D); $i = POSIX::mktime(0, 0, 0, $D, $M-1, $Y-1900); } else { $i += $interval; } } \%result; } sub optimal_label_interval_2 { my($want_separators, $max) = @_; my($mat, $x); return 1 if (! $max || ! $want_separators); $mat = int($max / $want_separators + 0.5); # find the power of 2 which is most close to $mat. for ($x = 1; $x < $mat; $x <<= 1) {}; if ($x-$mat < $mat-($x>>1)) { $mat = $x; } else { $mat = $x >> 1; } $mat ? $mat : 1; } sub out_index_html { my($rules) = shift; my($rules_data) = shift; my($filename); my($n_now, $n_st, $n_end, $s, $s1, $i); my($max, $avg, $tot, $resolution, $text); $n_st = nice_date($starttime_explicit || !@files ? $mystarttime : $files[$[]); $n_end = nice_date($myendtime); $n_now = nice_date(makemytime($now)); $resolution = &nice_time($graph_interval); $filename = $png_index ? $png_index : $png_index_default_name; if (-d $png && $filename !~ m|^/|) { $filename="$png/$filename"; } $text = qq| |; $text .= qq|\n| if ($expires); $text .= qq|$png_header_in_index

$png_header_in_index

Host:$hostname
Time created:$n_now $tzname
Data Start time:$n_st $tzname
Data End time:$n_end $tzname
Resolution (time/pixel):$resolution
|; $text .= ""; foreach(@{$rules}) { $s = &good_filename($_); $text .= "[ $_ ]  "; } $text .= "\n"; for ($i=0; $i<=$#{$rules}; $i++) { $_ = ${$rules}[$i]; $dat = ${$rules_data}[$i]; $s = &good_filename($_); $max = &nice_number(int($$dat{'max_caption'} + 0.5)); $avg = &nice_number(int($$dat{'avg_caption'} + 0.5)); $tot = &nice_number($bytes{$_}); $s1=""; $s1="
Max: $max Average: $avg Total: $tot" if ($png_caption_in_index); $text .= qq|

$_

graph for $_ $s1
|; } $text .= qq|
Generated by ipacsum which is part of IPAC version $version. IPAC home page: http://www.comlink.apc.org/~moritz/ipac.html
|; open(OUT, ">$filename") || die "$me: cant open \"$filename\": $!\n"; print OUT $text; close OUT; } # Print a string upwards centered. sub centerStringUp { my($im, $text, $font, $x, $y1, $y2, $color) = @_; $y1 = $y1 + int(($y2-$y1)/2 + length($text)*$png_average_character_width_vert/2 + 0.5); $im->stringUp($font, $x, $y1, $text, $color); } sub http_time { my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(shift); # Fri, 27 Nov 1998 13:50:41 GMT sprintf "%s, %02d %s %04d %02d:%02d:%02d GMT", $wday[$wday], $mday, $mons[$mon], $year+1900, $hour, $min, $sec; } sub usage { print <