#!FOOPERL

#
# Copyright (c) Xerox Corporation 1997, 1998. All rights reserved.
#
# License is granted to copy, to use, and to make and to use derivative
# works, provided that Xerox is acknowledged in all documentation
# pertaining to any such copy or derivative work. Xerox grants no other
# licenses expressed or implied. The Xerox trade name should not be used
# in any advertising without its written permission.
#
# XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
# MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
# FOR ANY PARTICULAR PURPOSE.  The software is provided "as is" without
# express or implied warranty of any kind.
#
# These notices must be retained in any copies of any part of this software.
#



#----- default directory names and executable paths ----------
# users can override these values using the command line options listed
# the output directory for the route montitor program, command line opt -d
$rtemondir = "FOORTEMONDIR";
# the executable for the route montitor program, command line opt -E
$rtemon = "FOORTEEXEC";
# the output directory for the as lookup program, command line opt -l
$aslookupdir = "FOOHMKRDIR";
# the executable for the as lookup program, command line opt -L
$aslookup = "FOOASLEXEC";
# the output directory for this program, command line opt -h
$hmakerdir =  "FOOHMKRDIR";
# the directory to put new web pages, command line opt -w
$webdir = "FOOWEBDIR";
# the directory to archive any route table or route stats, command line opt -a
$archivedir = "FOOARDIR";
# the complete href to the associated cgi search script, command line opt -s
$search_cgi = "FOOCGISEARCH";
# no modifications should occur below this point

#-----------------------------------------------
# command line options
# option: -r rtr
#   action: instruct route monitor to only accept route updates from rtr
#   default: route monitor accepts updates from all neighboring routers
# option: -x
#   action:  instrutct route monitor to always dump the route table on exit
#   default: route monitor only dumps the route table only when signaled
# option: -d directory
#   action: instruct route monitor to use the output directory for all output
#   default: route monitor uses its default directory for all output
# option: -e executable
#   action: instruct route monitor to run the routewatch program executable
#   default: route monitor runs its default routewatch program
# option: -E executable
#   action:  run route monitor program executable
#   default: run the route monitor program $rtemon defined above
# option: -l directory
#   action: instruct aslookup to use the output directory for all output
#   default: aslookup uses its default directory for all output
# option: -L executable
#   action:  run as lookup program executable
#   default: run the as lookup program $aslookup defined above
# option: -h directory
#   action: write pid file and error log to directory
#   default: write pid file and error log to $hmakerdir defined above
# option: -w directory
#   action: write the html files generated to directory
#   default: write the html files generated to $webdir defined above
# option: -s href
#   action: use href as the hyper link to the cgi search script
#   default: use $search_cgi above as the href (used in week/month html files)
# option: -a directory
#   action: write any route tables or route stats archived to directory
#   default: write any route tables or stats archived $archivedir defined above
# option: -W
#   action: archive the route statistics each week
#   default: do not archive route statistics weekly
# option: -M
#   action: archive the route statistics each month
#   default: do not archive route statistics monthly
# option: -T
#   action: archive each route table generated by route monitor
#   default: do not archive any route tables generated by route monitor
# option: -R script
#   action: run script to copy the html files generated to a remote web server
#   default: web server directories are locally accessible
#  NOTE: this option should be used by sites where this program can not
#  directly write to a web server area.  The script specified with -R
#  should use rcp or ftp or any other method to copy all the files from
#  $webdir onto the web server.  The script will be run by this program
#  each time new files are generated in $webdir


#------ perl standard modules and libraries -----------
use Getopt::Std;

#----- main program --------------------
&initialize;
&init_routemonitor;
&read_current_stats;
$SIG{ALRM} = 'get_table';
alarm ($rpt_interval * 60);
while(1) {
    # wait for an alarm
    sleep;
}

sub get_table {
    # signal routemonitor for a new route table 
    alarm ($rpt_interval * 60);
    if (&signal_routemonitor == -1) {
	return(-1);
    }
    # read the new table, update stats and generate new html files
    if (&update_stats ==  -1) {
	return(-1);
    }
    # archive the old statistics if requested by command line opts
    &archive_data;
    # write the updates stats to file for use by cgi search scripts
    &write_current_stats;
    # if the web server is not local, run the locally defined $web_copy 
    # script to copy the html and stats file to the web server 
    # to the server
    if ($web_copy ne "no") {
	$ret = system("$web_copy > /dev/null");
	if ($ret !=0) {
	    &log_error(webcopy);
	}
    }
    return(1);
}

#------------ initialization operations -----------------------
sub initialize {
    # check the command line args are correct
    if (!getopts('xr:x:e:l:L:h:w:a:TWME:R:s:')  || (@ARGV > 0)) {
	print "usage: htmlmaker [-xTWM] [-e rtewatch] [-E rtemon] [-r rtr]\n";
	print "                  [-d rtemondir] [-h hmakerdir] [-w webdir]\n";
	print "                  [-a archivedir] [-R remote_cp_script] [-s href]\n";
	print "                  [-l aslookupdir] [-L aslookup]\n";
	exit(-1);
    }
    # determine the route monitor output directory
    if (defined($opt_d)) {
	$rtemondir = $opt_d;
    }
    $rtemonpid = "$rtemondir/route_monitor.pid";
    $rtemontable = "$rtemondir/route_table";
    # determine the route monitor executable name and options
    if (defined($opt_E)) {
	$rtemon = $opt_E;
    }
    if (defined($opt_e)) {
	$rtemon = "$rtemon -e $opt_e";
    }
    if (defined($opt_x)) {
	$rtemon = "$rtemon -e $opt_e -x";
    }
    $rtemon = "$rtemon -d $rtemondir";
    # determine the html maker output directory
    if (defined($opt_h)) {
	$hmakerdir = $opt_h;
    }
    $errorfile = "$hmakerdir/html_maker.err";
    $pidfile = "$hmakerdir/html_maker.pid";
    # initialize the as lookup variables
    if (defined($opt_l)) {
	$aslookupdir = $opt_l;
    }
    if (defined($opt_L)) {
	$asscript = "$opt_L -h $aslookupdir";
    } else {
	$asscript = "$aslookup -h $aslookupdir";
    }
    $asinfile = "$aslookupdir/find_as_for";
    $asoutfile = "$aslookupdir/found_as_for";
    $aspidfile = "$aslookupdir/as_lookup.pid";
    $lookup_up_as = "no";
    # indicate start in the error log
    log_error(start);
    # write my pid file
    open(PIDFILE, ">$pidfile") || &log_quit(pid);
    print PIDFILE "$$\n";
    close(PIDFILE);
    # determine the web output directory
    if (defined($opt_w)) {
	$webdir = $opt_w;
    }
    $statsfile = "$webdir/route_stats";
    $sumhtml = "$webdir/summary.html";
    $printweek = 50;
    $weekhtml = "$webdir/week.html";
    $printmonth = 50;
    $monthhtml = "$webdir/month.html";
    $backhtml = "./stone.gif";
    $linehtml = "./hline.gif";
    # determine the href to the cgi search script, used in creating $weekhtml
    # and $monthhtml
    if (defined $opt_s) {
	$search_cgi = $opt_s;
    }
    # determine if a script is need to copy the files to the web server
    if (defined($opt_R)) {
	$web_copy = $opt_R;
    } else {
	$web_copy = "no";
    }
    # determine the archive directory and archive rules
    if (defined($opt_a)) {
	$archivedir = $opt_a;
    }
    if (defined($opt_W)) {
	$weekarchive = "$archivedir/weekly_stats";
    } else {
	$weekarchive = "none";
    }
    if (defined($opt_M)) {
	$montharchive = "$archivedir/monthly_stats";
    } else {
	$montharchive = "none";
    }
    if (defined($opt_T)) {
	$tablearchive = "$archivedir/route_table";
    } else {
	$tablearchive = "none";
    }
    #how often route monitor data is updated in minutes
    #really, really likes this to be 60 minutes
    $rpt_interval = 60;
    #the interval reported by route monitor must be within this many minutes
    $interval_diff = 5;
    #route table must have been written within this many seconds of update
    $time_diff = 300;
    #the number of seconds a down route should be left in the statistics file
    # recommend 60 days
    $expire = 5184000;
    $asdata{-3} = "-3 0 0 0 0.0.0.0 0 0 0.0.0.0 0";
    &declare_fields;
}

#------------ route monitor control functions------------------
# initialize the route monitor
sub init_routemonitor {
    $rtepid = &check_routemonitor;
    if ($rtepid == -1) {
	# launch new route_monitor
	&start_routemonitor;
    } else {
	# reset the running routemontior
	kill USR1, $rtepid;
    }
}

# launch a new route monitor program
sub start_routemonitor {
    # run a new route monitor, exit html_maker on failure
    system("nohup $rtemon > /dev/null &");
    # give it a few seconds to initialize (probably not needed...)
    sleep 3;
    # check it started and get the new pid
    $rtepid = &check_routemonitor;
    if ($rtepid == -1 ) {
	&log_quit("start");
    }
}

sub check_routemonitor {
    # returns the route monitor pid if its alive else returns -1
    # get the routemonitor pid from its pid file
    open(RTEPID, "$rtemonpid") || return (-1);
    local($newpid) = <RTEPID>;
    chop($newpid);
    close(RTEPID);
    # check pid is really running
    if (kill(0,$newpid) != 1) { return(-1); }
    # the pid is running, check the pid has not changed
    if ($newpid != $rtepid) {
	&log_error(newpid);
	$rtepid = $newpid;
    }
    return($rtepid);
}

sub signal_routemonitor{
    # tell the route monitor to dump its route table and reset its stats
    $rtepid = &check_routemonitor;
    if ($rtepid == -1) {
	# the route monitor has failed...
	&log_error(nortemon);
	# lets try starting over with a new route monitor
	&start_routemonitor;
	return(-1);
    } else {
	kill(USR1,$rtepid);
	# pause to make sure route table dump completes
	sleep(5);
	return(1);
    }
    
}

#--------- statistics file operations-------------
sub read_current_stats {
    # read in the current stats database
    local($src, $a);
    # check if a datafile exists
    if (!(-f $statsfile)) {
	&log_error(nofile);
	# initialize the number of dumps for this week and month
	$totalw = 0; $totalm = 0; $stime = time;
	return(-1);
    }
    open(STATSFILE, "$statsfile") || &log_quit(statsopen);
    # verify the correct first line of the file
    $line = <STATSFILE>;
    ($Route,$Data,$From, $stime, $Time, $totalw, $totalm) = split(" ",$line);
    if (($Route ne "Route") || (split(" ",$line) != 7)) {
	&log_error(startline);
	system("mv $statsfile $statsfile.bak");
	# initialize the number of dumps for this week and month
	$totalw = 0; $totalm = 0; $stime = time;
	return(-1);
    }
    # read in the stats for each known source and store it in an array
    while($line = <STATSFILE>) {
       chop($line);
       # check that the line looks valid
       if (split(" ",$line) != $total) {
	   &log_error(statsline); 
       } else {
	   @array = split(" ",$line);
	   $a = &field_num("addr");
    	   if ($a == -1) {
	       &log_error(field);
	   } else {
	       # store the stats line for this address
	       $stats{$array[$a]} = $line;
	       $active{$array[$a]} = "no";
	   }
       }
   }
   close(STATSFILE);
}

sub write_current_stats {
    open(STATSFILE, "> $statsfile") || &log_quit(statswrite);
    $stime = $dtime;
    local($sc,$mn,$hr,$dy,$mo,$yr,$wd,$yd,$isd) = gmtime($stime);
    $mo = $mo + 1;
    local($Time) = "$mo/$dy/$yr,$hr:$mn:$sc-GMT";
    print STATSFILE "Route Data From $stime ($Time) $totalw $totalm\n";
    foreach $src (keys %stats) {
	print STATSFILE "$stats{$src}\n";
    }
    close(STATSFILE);
}

#---- reading a new route table dump -----------
sub update_stats {
    open(NEWTABLE, "$rtemontable") || &log_quit(newtable);
    # check that we have a valid table, get time and period
    if (&valid_table == -1) {
	return(-1);
    }
    &init_hourly_stats;
    &init_weekly_stats;
    &init_monthly_stats;
    &init_as_stats;
    while($line = <NEWTABLE>) {
	($src,$first,$metric,$flaps,$updates) = split(" ",$line);
	if (!defined($stats{$src})) {
	    &new_src;
	    $newup++;
	}
	$active{$src} = "yes";
	@array = split(" ",$stats{$src});
	&update_hourly_stats;
	&update_weekly_stats;
	&update_monthly_stats;
	&update_as_stats;
	&save_src;
    }
    close(NEWTABLE);
    foreach $src (keys %stats) {
	if ($active{$src} eq "no") {
	    @array = split(" ",$stats{$src});
	    if (&expire_src == -1) {
		&inactive_hourly_stats;
		&inactive_weekly_stats;
		&inactive_monthly_stats;
		&inactive_as_stats;
		$active{$src} = "no";
		&save_src;
	    }
	} else {
	    $active{$src} = "no";
	}
    }
    &compute_worst;
    &write_hourly_html;
    &write_weekly_html;
    &write_monthly_html;
    &as_lookup;
}

sub valid_table {
    local($difftime,$diffint);
    # return 1 if the new route table is valid, -1 otherwise
    $line = <NEWTABLE>;
    # check the first header line is correct
    ($Route,$Table,$Dumped,$At,$dtime,$Time,$Total,$Routes,$In,$Table) = 
	split(" ",$line);
    if (($Route ne "Route") || (split(" ",$line) != 10)) {
	&log_error(tablestart);
	$dtime = time;
	return(-1);
    }
    # check the second header line is correct, learn time and interval
    $line = <NEWTABLE>;
    ($Report,$Interval,$Is,$rptint,$Minutes, $Table,$Last,$Reset,$At,$Ltime, 
     $Time) = split(" ",$line);
    if (($Report ne "Report") || (split(" ",$line) != 11)) {
	&log_error(tablestart);
	$dtime = time;
	return(-1);
    }
    # check the route table was written within a reasonable time
    $difftime = abs($dtime - time);
    if ($difftime > $time_diff) {
	&log_error(oldtable);
	return(-1);
    }
    # check the update period is the correct number of minutes
    $diffint = abs($rptint - $rpt_interval);
    if ($diffint > $interval_diff) {
	&log_error(interval);
	return(-1);
    }
    return(1);
}

sub new_src {
    # set the new source name and as number as "need to lookup"
    $active{$src} = "yes";
    $a = &field_num("addr"); $n = &field_num("asnum");
    if (($a == -1) || ($n == -1)) {
	&log_error(field);
	$stats{$src} = "error";
	return(-1);
    }
    $array[$a] = $src;
    $array[$n] = "-2";
    # intialize the hourly stats for the source
    $s = &field_num("state"); $c = &field_num("change"); 
    $m = &field_num("metric"); $f = &field_num("flaps");
    $u = &field_num("updates");
    if (($s == -1) || ($c == -1) || ($m == -1) || ($f == -1) || ($u == -1)) {
	&log_error(field);
	$stats{$src} = "error";
	return(-1);
    }
    $array[$s] = "down";
    $array[$c] = time;
    $array[$m] = 32;
    $array[$f] = 0;
    $array[$u] = 0;
    # initialize the weekly stats
    $f = &field_num("wflaps");  $p = &field_num("whour"); 
    $n = &field_num("wcount");  $s = &field_num("wstates");
    if (($f == -1) || ($p == -1) || ($n == -1) || ($s == -1)) {
	&log_error(field);
	$stats{$src} = "error";
	return(-1);
    }
    $array[$f] = 0;
    $array[$p] = 0;
    $array[$n] = 0;
    $array[$s] = 0; 
    # initialize the monthly stats
    $f = &field_num("mflaps");  $p = &field_num("mhour"); 
    $n = &field_num("mcount");  $s = &field_num("mstates");
    if (($f == -1) || ($p == -1) || ($n == -1) || ($s == -1)) {
	&log_error(field);
	$stats{$src} = "error";
	return(-1);
    }
    $array[$f] = 0;
    $array[$p] = 0;
    $array[$n] = 0;
    $array[$s] = 0;
    # save the initial source data
    &save_src;
}


sub save_src {
# the updates have modified the array fields, save them to a stats line
    local($save) = $array[0];
    for($i = 1; $i < $total; $i++) {
	$save = "$save $array[$i]";
    }
    $stats{$src} = $save;
}

sub expire_src {
    # if the source has been inactive for a long period delete the source
    # and return 1, else return -1
    $s = &field_num("state"); $c = &field_num("change"); 
    if (($s == -1) || ($c == -1)) {
	&log_error(field);
	return(-1);
    }
    $ctime = $array[$c];
    local($time_diff) = $dtime - $ctime;
    if ($time_diff > $expire) {
	delete $stats{$src};
	delete $active{$src};
	return(1);
    }
    return(-1);
}

sub compute_worst {
    local($i,$myasnum);
    # the as number, total sources in the as, as sources which are down
    local($asnum,$astotal,$asdown);
    # the sources flapping this week, the worst source and number of flaps
    local($aswflap,$aswsrc,$aswmax);
    # the sources flapping this month, the worst source and number of flaps
    local($asmflap,$asmsrc,$asmmax); 
    for($i =0; $i < $printweek; $i++) {
	$weekworst[$i] = -2;
	$mywworst[$i] = 0;
    }
    for($i =0; $i < $printmonth; $i++) {
	$monthworst[$i] = -2;
	$mymworst[$i] = 0;
    }
    foreach $myasnum (keys %asdata) {
	($asnum,$astotal,$asdown,$aswflap,$aswsrc,$aswmax,
	 $asmflap,$asmsrc,$asmmax) = split(" ",$asdata{$myasnum});
	if ($aswmax >= $mywworst[$printweek]) {
	    $myspot = $printweek -1;
	    while(($myspot >=0) && ($aswmax >= $mywworst[$myspot])) {
		$myspot = $myspot  -1;
	    }
	    $myspot++;
	    for($i=$printweek; $i > $myspot; $i--) {
		$prev = $i -1;
		$weekworst[$i] = $weekworst[$prev];
		$mywworst[$i] = $mywworst[$prev];
	    }
	    $weekworst[$myspot] = $asnum;
	    $mywworst[$myspot] = $aswmax;
	}
	if ($mymworst[$printmonth] < $asmmax) {
	    $myspot = $printmonth -1;
	    while(($myspot >= 0) && ($asmmax >= $mymworst[$myspot])) {
		$myspot = $myspot  -1;
	    }
	    $myspot++;
	    for($i=$printmonth; $i > $myspot; $i--) {
		$prev = $i -1;
		$monthworst[$i] = $monthworst[$prev];
		$mymworst[$i] = $mymworst[$prev];
	    }
	    $monthworst[$myspot] = $asnum;
	    $mymworst[$myspot] = $asmmax;
	}
    }
}
	
	    
	

#--------- hourly statistic operations ---------
sub init_hourly_stats {
    # initialize all the hourly counters
    # $dtime and $rptint were initialized from the table header lines
    # routes total routes, stable routes, and unreachable (metric 32) routes
    $routes = 0; $stable = 0; $noreach = 0;
    # the hours maximum number of flaps and source causing max flaps
    $max = -1; $maxsrc = "0.0.0.0";
    # the number of routes the changed state from down to up and vice versa
    $cameup = 0; $wentdown = 0; $newup =0;
}

sub update_hourly_stats {
    $routes++;
    if ($metric == 32) {
	$noreach++;
    }
    if ($flaps == 0) {
	$stable++;
    }
    if ($flaps > $max) {
	$max = $flaps;
	$maxsrc = $src;
    }
    $s = &field_num("state"); $c = &field_num("change"); 
    $m = &field_num("metric"); $f = &field_num("flaps");
    $u = &field_num("updates");
    if (($s == -1) || ($c == -1) || ($m == -1) || ($f == -1) || ($u == -1)) {
	&log_error(field);
	return(-1);
    }
    if ($array[$s] eq "down") {
	$cameup++;
	$array[$s] = "up";
	$array[$c] = $dtime;
    }
    $arrary[$m] = $metric;
    $array[$f] = $flaps;
    $array[$u] = $updates;
}

sub inactive_hourly_stats {
    # the source did not appear this hour so declare it as down
    $s = &field_num("state"); $c = &field_num("change"); 
    $m = &field_num("metric"); $f = &field_num("flaps");
    $u = &field_num("updates");
    if (($s == -1) || ($c == -1) || ($m == -1) || ($f == -1) || ($u == -1)) {
	&log_error(field);
	return(-1);
    }
    if ($array[$s] eq "up") {
	$array[$s] = "down";
	$array[$c] = $dtime;
	$wentdown++;
    }
    $array[$m] = 32;
    $array[$f] = 0;
    $array[$u] = 0;
}

sub write_hourly_html {
    local($prct);
    local($sc,$mn,$hr,$dy,$mo,$yr,$wd,$yd,$isd) = gmtime($dtime);
    $mo = $mo + 1;
    local($day) = "$mo/$dy/$yr";
    local($hour) = "$hr:$mn:$sc GMT";
    open(SUMHTML, ">$sumhtml") || &log_error(sumhtml);
    print SUMHTML "<html> <head> <title>Route Report Summary</title> \n";
    print SUMHTML '<meta http-equiv="Refresh" content="3600"> </head>';
    print SUMHTML '<body background="';
    print SUMHTML "$backhtml";
    print SUMHTML '" text="#000000">';
    print SUMHTML "<center><h1>Multicast Routing Summary Report</h1></center>\n";
    print SUMHTML "<font size=4> \n";
    print SUMHTML "<ul> \n";
    if ($routes == 0) {
	$prct = 0;
    } else {
	$prct = int($stable/$routes * 100);
    }
    print SUMHTML "<li>Report for a $rptint minute period, dated $hour $day\n";
    print SUMHTML "<li> $stable routes did not change during report time\n";
    print SUMHTML "<font size=4 color=red>($prct%)</font> \n";
    print SUMHTML "<li> Max number of flaps was \n";
    print SUMHTML "<font size=4 color=red> $max </font> for route $maxsrc\n";
    print SUMHTML "<li> $routes Routes Advertised in Total \n";
    print SUMHTML "<li> $noreach routes listed unreachable at end of report period \n";
    print SUMHTML "<li> $cameup routes changed state to up this period \n";
    print SUMHTML "<li> $newup of routes which came up are new sources\n";
    print SUMHTML "<li> $wentdown routes changed state to down this period\n";
    print SUMHTML "</ul> </body> </html> \n";
    close(SUMHTML);
}

#-------------- weekly statistics operations ------------------
sub init_weekly_stats {
    # the time of the statistics file
    local($ssc,$smn,$shr,$sdy,$smo,$syr,$swd,$syd,$sisd) = gmtime($stime);
    $smo = $smo + 1;
    # the time of the current route table dump
    local($dsc,$dmn,$dhr,$ddy,$dmo,$dyr,$dwd,$dyd,$disd) = gmtime($dtime);
    $dmo = $dmo + 1;
    if (($dwd < $swd) || (($dyd - $syd) > 7 )) {
	# this is the first hour of a new week
	$totalw = 1;
	$new_week = 1;
    } else {
	# this is another hour in the current week
	$totalw++;
	$new_week = 0;
    }
}

sub update_weekly_stats {
    $f = &field_num("wflaps"); $c = &field_num("change"); 
    $p = &field_num("whour"); $n = &field_num("wcount");
    $s = &field_num("wstates");
    if (($f == -1) || ($c == -1) || ($p == -1) || ($n == -1) || ($s == -1)) {
	&log_error(field);
	return(-1);
    }
    if ($new_week == 1) {
	# reset average flaps, hours this week, and state changes
	$array[$f] = $flaps;
       	$array[$n] = 1;
	$array[$s] = 0;
    } else {
	# adjust average flaps and active hours this week
	$array[$f] = (($array[$n] * $array[$f]) + $flaps) / ($array[$n] + 1);
	$array[$n] = $array[$n] + 1;
    }
    if ($array[$c] == $dtime) {
	# if there was a state change this time, increment state changes
	$array[$s] = $array[$s] + 1;
    }
    # percentage of hours active is hours active/total hours this week
    $array[$p] = int(100*($array[$n]/$totalw));
}

sub inactive_weekly_stats {
    $f = &field_num("wflaps"); $c = &field_num("change"); 
    $p = &field_num("whour"); $n = &field_num("wcount");
    $s = &field_num("wstates");
    if (($f == -1) || ($c == -1) || ($p == -1) || ($n == -1) || ($s == -1)) {
	&log_error(field);
	return(-1);
    }
    if ($new_week == 1) {
	# reset average flaps, hours this week, and state changes
	$array[$f] = 0;
       	$array[$n] = 0;
	$array[$s] = 0;
    } 
    if ($array[$c] == $dtime) {
	# if there was a state change this time, increment state changes
	$array[$s] = $array[$s] + 1;
    }
    # percentage of hours active is hours active/total hours this week
    $array[$p] = int(100*($array[$n]/$totalw));
}

sub write_weekly_html {
    local($sc,$mn,$hr,$dy,$mo,$yr,$wd,$yd,$isd) = gmtime($dtime);
    $mo = $mo + 1;
    local($day) = "$mo/$dy/$yr";
    local($hour) = "$hr:$mn:$sc GMT";
    open(HTML, ">$weekhtml") || &log_error(weekhtml);
    # set to expire/refresh in one hour
    print HTML "<html>\n\n";
    print HTML "<head>\n";
    print HTML "   <title> This Week's Worst Routes </title>\n";
    print HTML '   <meta http-equiv="Refresh" content="3600">';
    print HTML "\n";
    print HTML "</head>\n\n";
    # start html body of the page
    print HTML "<body>\n";
    print HTML "   <center> <h1> This Week's Worst Routes </h1> </center>\n\n";
    print HTML '   <br> <img src="';
    print HTML "$linehtml";
    print HTML '" width="100%"'; 
    print HTML "\n    alt=";
    print HTML '"----------------------------------------------------------">';
    print HTML "   <br>\n\n";
    print HTML "   <p> The table lists this week's most unstable routes.\n";
    print HTML "   Our monitoring tool tracks the distance\n";
    print HTML "   to each source in the MBone.  A change in distance\n";
    print HTML "   indicates a change in the route to the source, ie a flap\n";
    print HTML "   Using this data, we calculate the <b>average number of\n";
    print HTML "   flaps per hour</b>.  This page reports the most unstable\n";
    print HTML "   routes observed during the current week.  This page is\n";
    print HTML "   updated hourly.  <b>Only one route per Autonomous System\n";
    print HTML "   is listed</b>.\n\n";
    print HTML "  <p>The cause of this instability is often one of the bugs\n";
    print HTML "   listed in the \n";
    print HTML ' <a href="http://ganef.cs.ucla.edu/~masseyd/Route/bugs.html">';
    print HTML "  \n";
    print HTML "   Current Bugs</a> page.  Note Autonomous Systems listed \n";
    print HTML "   here typically advertise many unstable routes, but only \n";
    print HTML "   the worst route for the AS is reported.  For a full \n";
    print HTML "   report on the AS, click on the AS number. \n\n"; 
    print HTML '   <br> <img src="';
    print HTML "$linehtml";
    print HTML '" width="100%"'; 
    print HTML "   \n    alt=";
    print HTML '"----------------------------------------------------------">';
    print HTML "   <br>\n\n";
    print HTML "   <font size=4>";
    print HTML "   <p> Data last updated at $day $hour <br> \n";
    #start the table for the worst routes
    print HTML "   <table border=2 cellpadding=2 cellspacing=4> \n";
    print HTML "      <tr>\n";
    print HTML "         <th align=center> Source</th>\n";
    print HTML "         <th align=center> Avg. Flaps/Hour </th> \n";
    print HTML "         <th align=center>AS Number</th> \n";
    print HTML "         <th align=center> Sources in AS </th> \n";
    print HTML "         <th align=center> Sources Flapping </th> \n";
    print HTML "         <th align=center> Sources Down </th> \n";
    print HTML "      </tr> \n";
    # print out the worst AS entires
    local($printas);
    # the as number, total sources in the as, as sources which are down
    local($asnum,$astotal,$asdown);
    # the sources flapping this week, the worst source and number of flaps
    local($aswflap,$aswsrc,$aswmax);
    # the sources flapping this month, the worst source and number of flaps
    local($asmflap,$asmsrc,$asmmax); 
    for($i = 0; $i < $printweek; $i++) {
	$printas = $asdata{$weekworst[$i]};
	($asnum,$astotal,$asdown,$aswflap,$aswsrc,$aswmax,
	 $asmflap,$asmsrc,$asmmax) = split(" ",$printas);
	$aswmax = int($aswmax);
	print HTML "      <tr>\n";
	print HTML "         <td align=left> $aswsrc </td>\n";
	print HTML "         <td align=right>$aswmax</td>\n";
	print HTML "         <td align=right>\n";
	print HTML '<a href="';
	print HTML "$search_cgi";
	print HTML "?use=state&use=change&use=asnum&use=wflaps&use=whour";
	print HTML "&care=state&use=wstates&asnum=$asnum&sortnum=wflaps";
	print HTML '">';
	print HTML "$asnum </a> </td>\n";
	print HTML "         <td align=right>$astotal</td>\n";
	print HTML "         <td align=right>$aswflap</td>\n";
	print HTML "         <td align=right>$asdown</td>\n";
	print HTML "      </tr>\n";
    }
    # complete and close the html file
    print HTML "   </table>\n";
    print HTML "   <br>\n";
    print HTML "</body>\n\n";
    print HTML "</html>\n";
    close(HTML);
}


#-------------- monthly statistics operations ------------------
sub init_monthly_stats {
    # the time of the statistics file
    local($ssc,$smn,$shr,$sdy,$smo,$syr,$swd,$syd,$sisd) = gmtime($stime);
    $smo = $smo + 1;
    # the time of the current route table dump
    local($dsc,$dmn,$dhr,$ddy,$dmo,$dyr,$dwd,$dyd,$disd) = gmtime($dtime);
    $dmo = $dmo +1;
    if ($smo != $dmo ) {
	# this is the first hour of a new month
	$totalm = 1;
	$new_month = 1;
    } else {
	# this is another hour in the current month
	$totalm++;
	$new_month = 0;
    }  
}

sub update_monthly_stats {
    $f = &field_num("mflaps"); $c = &field_num("change"); 
    $p = &field_num("mhour"); $n = &field_num("mcount");
    $s = &field_num("mstates");
    if (($f == -1) || ($c == -1) || ($p == -1) || ($n == -1) || ($s == -1)) {
	&log_error(field);
	return(-1);
    }
    if ($new_month == 1) {
	# reset average flaps, hours this month, and state changes
	$array[$f] = $flaps;
       	$array[$n] = 1;
	$array[$s] = 0;
    } else {
	# adjust average flaps and active hours this month
	$array[$f] = (($array[$n] * $array[$f]) + $flaps) / ($array[$n] + 1);
	$array[$n] = $array[$n] + 1;
    }
    if ($array[$c] == $dtime) {
	# if there was a state change this time, increment state changes
	$array[$s] = $array[$s] + 1;
    }
    # percentage of hours active is hours active/total hours this month
    $array[$p] = int(100*($array[$n]/$totalm));

}
sub inactive_monthly_stats {
    $f = &field_num("mflaps"); $c = &field_num("change"); 
    $p = &field_num("mhour"); $n = &field_num("mcount");
    $s = &field_num("mstates");
    if (($f == -1) || ($c == -1) || ($p == -1) || ($n == -1) || ($s == -1)) {
	&log_error(field);
	return(-1);
    }
    if ($new_month == 1) {
	# reset average flaps, hours this month, and state changes
	$array[$f] = 0;
       	$array[$n] = 0;
	$array[$s] = 0;
    } 
    if ($array[$c] == $dtime) {
	# if there was a state change this time, increment state changes
	$array[$s] = $array[$s] + 1;
    }
    # percentage of hours active is hours active/total hours this week
    $array[$p] = int(100*($array[$n]/$totalm));
}

sub write_monthly_html {
    local($sc,$mn,$hr,$dy,$mo,$yr,$wd,$yd,$isd) = gmtime($dtime);
    $mo = $mo + 1;
    local($day) = "$mo/$dy/$yr";
    local($hour) = "$hr:$mn:$sc GMT";
    open(HTML, ">$monthhtml") || &log_error(monthhtml);
    # set to expire/refresh in one hour
    print HTML "<html>\n\n";
    print HTML "<head>\n";
    print HTML "   <title> This Month's Worst Routes </title>\n";
    print HTML '   <meta http-equiv="Refresh" content="3600">';
    print HTML "\n";
    print HTML "</head>\n\n";
    # start html body of the page
    print HTML "<body>\n";
    print HTML "   <center><h1> This Month's Worst Routes </h1> </center>\n\n";
    print HTML '   <br> <img src="';
    print HTML "./$linehtml";
    print HTML '" width="100%"'; 
    print HTML "\n    alt=";
    print HTML '"----------------------------------------------------------">';
    print HTML "   <br>\n\n";
    print HTML "   <p> The table lists this months's most unstable routes.\n";
    print HTML "   Our monitoring tool tracks the distance\n";
    print HTML "   to each source in the MBone.  A change in distance\n";
    print HTML "   indicates a change in the route to the source,ie a flap.\n";
    print HTML "   Using this data, we calculate the <b>average number of\n";
    print HTML "   flaps per hour</b>.  This page reports the most unstable\n";
    print HTML "   routes observed during the current month.  This page is\n";
    print HTML "   updated hourly.  <b>Only one route per Autonomous System\n";
    print HTML "   is listed</b>.\n\n";
    print HTML "  <p>The cause of this instability is often one of the bugs\n";
    print HTML "    listed in the \n";
    print HTML ' <a href="http://ganef.cs.ucla.edu/~masseyd/Route/bugs.html">';
    print HTML "   \n";
    print HTML "   Current Bugs</a> page.  Note Autonomous Systems listed \n";
    print HTML "   here typically advertise many unstable routes, but only \n";
    print HTML "   the worst route for the AS is reported.  For a full \n";
    print HTML "   report on the AS, click on the AS number. \n\n";
    print HTML '   <br> <img src="';
    print HTML "./$linehtml";
    print HTML '" width="100%"'; 
    print HTML "   \n    alt=";
    print HTML '"----------------------------------------------------------">';
    print HTML "   <br>\n\n";
    print HTML "   <font size=4>";
    print HTML "   <p> Data last updated at $day $hour <br> \n";
    # start the month worst table
    print HTML "   <table border=2 cellpadding=2 cellspacing=4> \n";
    print HTML "      <tr>\n";
    print HTML "         <th align=center> Source</th>\n";
    print HTML "         <th align=center> Avg. Flaps/Hour </th> \n";
    print HTML "         <th align=center>AS Number</th> \n";
    print HTML "         <th align=center> Sources in AS </th> \n";
    print HTML "         <th align=center> Sources Flapping </th> \n";
    print HTML "         <th align=center> Sources Down </th> \n";
    print HTML "      </tr> \n";
    # print out the worst AS entires
    local($i) = 0;
    # the as number, total sources in the as, as sources which are down
    local($asnum,$astotal,$asdown);
    # the sources flapping this week, the worst source and number of flaps
    local($aswflap,$aswsrc,$aswmax);
    # the sources flapping this month, the worst source and number of flaps
    local($asmflap,$asmsrc,$asmmax); 
    local($printas);
    for($i=0; $i < $printmonth; $i++) {
	$printas = $asdata{$monthworst[$i]};
	($asnum,$astotal,$asdown,$aswflap,$aswsrc,$aswmax,
	 $asmflap,$asmsrc,$asmmax) = split(" ",$printas);
	$asmmax = int($asmmax);
	print HTML "      <tr>\n";
	print HTML "         <td align=left> $asmsrc </td>\n";
	print HTML "         <td align=right>$asmmax</td>\n";
	print HTML "         <td align=right>\n";
	print HTML '<a href="';
	print HTML "$search_cgi";
	print HTML "?use=state&use=change&use=asnum&use=mflaps&use=mhour";
	print HTML "&care=state&use=mstates&asnum=$asnum&sortnum=mflaps";
	print HTML '">';
	print HTML "$asnum </a> </td>\n";
	print HTML "         <td align=right>$astotal</td>\n";
	print HTML "         <td align=right>$asmflap</td>\n";
	print HTML "         <td align=right>$asdown</td>\n";
	print HTML "      </tr>\n";
    }
    # complete and close the html file
    print HTML "   </table>\n";
    print HTML "   <br>\n";
    print HTML "</body>\n\n";
    print HTML "</html>\n";
    close(HTML);
}


#--------- AS statistic operations ---------
sub init_as_stats {
    #reset the totals for each AS
    # the as number, total sources in the as, as sources which are down
    local($asnum,$astotal,$asdown);
    $astotal = 0; $asdown = 0;
    # the sources flapping this week, the worst source and number of flaps
    local($aswflap,$aswsrc,$aswmax);
    $aswflap = 0; $aswsrc = "0.0.0.0"; $aswmax = 0;
    # the sources flapping this month, the worst source and number of flaps
    local($asmflap,$asmsrc,$asmmax);
    $asmflap = 0; $asmsrc = "0.0.0.0"; $asmmax = 0;
    foreach $asnum (keys %asdata) {
	$asdata{$asnum} = 
   "$asnum $astotal $asdown $aswflap $aswsrc $aswmax $asmflap $asmsrc $asmmax";
    }
    if ($looking_up_as ne "yes") {
	# we have not asked the as script to lookup any source as numbers
	return(1);
    }
    # set the looking up as flag to no unless we discover it's still running
    $looking_up_as = "no";
    # we have asked for as numbers, see if the script is still working
    # get the routemonitor pid from its pid file
    open(ASPID, "$aspidfile") || &log_error(aspidfile);
    local($newpid) = <ASPID>;
    chop($newpid);
    close(ASPID);
    # check if pid is really running
    if (kill(0,$newpid) == 1) {
	# the as lookup script is still working
	$looking_up_as = "yes";
	return(1); 
    }
    $a = &field_num("asnum");
    if ($a == -1) {
	&log_error(field);
	return(-1);
    }
    # the as lookup script exited, read in the new as data
    open(ASOUT, "$asoutfile") || &log_error(asout);
    while($aline = <ASOUT>) {
	if (split(" ",$aline) != 2) {
	    # something bad happened to as lookup output
	    &log_error(asline);
	} else {
	    # get the source and number from the output file
	    ($src,$anum) = split(" ",$aline);
	    if (!defined($stats{$src})) {
		# why are we looking the as for an unknown source
		&log_error(assrc);
	    } else {
		# update the asnum for the source
		@array = split(" ",$stats{$src});
		$array[$a] = $anum;
		&save_src;
	    }
	    # delete the record indicating we an as for this source
	    delete $need_as{$src}; 
	}
    }
    close(ASOUT);
}

sub update_as_stats {
    # add an active source to the current as data
    # the as number, total sources in the as, as sources which are down
    local($asnum,$astotal,$asdown);
    # the sources flapping this week, the worst source and number of flaps
    local($aswflap,$aswsrc,$aswmax);
    # the sources flapping this month, the worst source and number of flaps
    local($asmflap,$asmsrc,$asmmax); 
    # the asnum, weekly flaps and monthly flaps for the current source
    local($a,$w,$m,$asnum,$wflaps,$mflaps);
    # get the asnum, weekly flaps and monthly flaps for the current source
    $a = &field_num("asnum"); 
    $w = &field_num("wflaps"); $m = &field_num("mflaps"); 
    if (($a == -1) || ($w == -1) || ($m == -1)) {
	&log_error(field);
	return(-1);
    }
    $asnum = $array[$a]; $wflaps = $array[$w]; $mflaps = $array[$m];
    if (!defined($asdata{$asnum})) {
	# define a new as number structure if needed
	$astotal = 0; $asdown = 0; 
	$aswflap = 0; $aswsrc = "0.0.0.0"; $aswmax = 0;
	$asmflap = 0; $asmsrc = "0.0.0.0"; $asmmax = 0;
    } else {
	($asnum,$astotal,$asdown,$aswflap,$aswsrc,$aswmax,
	 $asmflap,$asmsrc,$asmmax) = split(" ",$asdata{$asnum});
    }
    if ($asnum == -2) {
	# we need to lookup an asnumber for this source
	if (!defined($need_as{$src}) || ($need_lookup ne "yes")) {
	    # we have not already request a lookup for this source
	    $need_as{$src} = "waiting";
	    $need_lookup = "yes";
	}
    }
    if ($wflaps >= $aswmax) {
	# if the new source is the worst weekly flapper for this as
	$aswmax = $wflaps;
	$aswsrc = $src;
    }
    if ($mflaps >= $asmmax) {
	# if the new source is the worst weekly flapper for this as
	$asmmax = $mflaps;
	$asmsrc = $src;
    }
    if (int($wflaps) > 0) {
	# add the weekly flapping count for the as
	$aswflap++;
    }
    if (int($mflaps) > 0) {
	# add the monthly flapping count for the as
	$asmflap++;
    }
    $astotal++;
    $asdata{$asnum} =  
  "$asnum $astotal $asdown $aswflap $aswsrc $aswmax $asmflap $asmsrc $asmmax";
}

sub inactive_as_stats {
    # the source did not appear this hour so add it to the as down count    
    # the as number, total sources in the as, as sources which are down
    local($asnum,$astotal,$asdown);
    # the sources flapping this week, the worst source and number of flaps
    local($aswflap,$aswsrc,$aswmax);
    # the sources flapping this month, the worst source and number of flaps
    local($asmflap,$asmsrc,$asmmax); 
    # the asnum, weekly flaps and monthly flaps for the current source
    local($a,$w,$m);
    # get the asnum, weekly flaps and monthly flaps for the current source
    $a = &field_num("asnum");
    $w = &field_num("wflaps"); $m = &field_num("mflaps"); 
    if (($a == -1) || ($w == -1) || ($m == -1)) {
	&log_error(field);
	return(-1);
    }
    $asnum = $array[$a]; $wflaps = $array[$w]; $mflaps = $array[$m];
    if (!defined($asdata{$asnum})) {
	# define a new as number structure if needed
	$astotal = 0; $asdown = 0; 
	$aswflap = 0; $aswsrc = "0.0.0.0"; $aswmax = 0;
	$asmflap = 0; $asmsrc = "0.0.0.0"; $asmmax = 0;
    } else {
	($asnum,$astotal,$asdown,$aswflap,$aswsrc,$aswmax,
	 $asmflap,$asmsrc,$asmmax) = split(" ",$asdata{$asnum});
    }
    if ($asnum == -2) {
	# we need to lookup an asnumber for this source
	if (!defined($need_as{$src}) || ($need_lookup ne "yes")) {
	    # we have not already request a lookup for this source
	    $need_as{$src} = "waiting";
	    $need_lookup = "yes";
	}
    }
    if ($wflaps >= $aswmax) {
	# if the new source is the worst weekly flapper for this as
	$aswmax = $wflaps;
	$aswsrc = $src;
    }
    if ($mflaps >= $asmmax) {
	# if the new source is the worst weekly flapper for this as
	$asmmax = $mflaps;
	$asmsrc = $src;
    }
    if (int($wflaps) > 0) {
	# add the weekly flapping count for the as
	$aswflap++;
    }
    if (int($mflaps) > 0) {
	# add the monthly flapping count for the as
	$asmflap++;
    }
    $astotal++;
    $asdown++;
    $asdata{$asnum} =  
  "$asnum $astotal $asdown $aswflap $aswsrc $aswmax $asmflap $asmsrc $asmmax";
}

sub as_lookup {
    # lookup as numbers for sources if needed
    if (($looking_up_as eq "yes") || ($need_lookup eq "no")) {
	# we are already looking up stuff or don't need to lookup anything
	return(1);
    }
    # write the sources we want to look up into an input file
    open(ASIN,">$asinfile") || &log_error(asin);
    foreach $src (keys %need_as) {
	# indicate was have starting searching for this as number
	$need_as{$src} = "searching";
	print ASIN "$src\n";
    }
    close(ASIN);
    # launch the as lookup program
    $ret = system("$asscript -i $asinfile -o $asoutfile > /dev/null &");
    if ($ret != 0) {
        &log_error(asstart);
    }
    $need_lookup = "no";
    $looking_up_as = "yes";
}

#-------------- archive data --------------------------------
sub archive_data {
    local($sc,$mn,$hr,$dy,$mo,$yr,$wd,$yd,$isd) = gmtime($stime);
    $mo = $mo + 1;
    if ($new_month == 1) {
	if ($montharchive ne "none") {
	    $ret = system("cp $statsfile $montharchive.$mo.$yr");
	    if ($ret != 0) {
		&log_error(copy);
	    }
	}
    }
    if ($new_week == 1) {
	if ($weekarchive ne "none") {
	    # rough estimate of the week number
	    local($myweek) = int($yd/7) + 1;
	    $ret = system("cp $statsfile $weekarchive.$myweek.$yr");
	    if ($ret != 0) {
		&log_error(copy);
	    }
	}
    }
    if ($tablearchive ne "none") {
	($sc,$mn,$hr,$dy,$mo,$yr,$wd,$yd,$isd) = gmtime($dtime);
	$ret = system("cp $rtemontable $tablearchive.$hr.$yd.$yr"); 
	if ($ret != 0) {
	    &log_error(copy);
	}
    }
    return(1);
}

#-------------- error handling --------------------------------
sub log_error {
    # write any error messages into the errorfile
    local($errname) = $_[0];
    local($sc,$mn,$hr,$dy,$mo,$yr,$wd,$yd,$isd) = gmtime();
    $mo = $mo +1;
    local($errdate) = "$mo/$dy/$yr $hr:$mn:$sc GMT";
    open(ERRORFILE, ">>$errorfile") ||
        die "Html Maker unable to open error log $errorfile\n";
    if ($errname eq "newpid") {
        print ERRORFILE "$errdate ERROR: the routemonitor pid changed\n";
	print ERRORFILE "RESUMING WITH NEW ROUTEMONITOR PID...\n";
    } elsif ($errname eq "nortemon") {
	print ERRORFILE "$errdate ERROR: route monitor has died\n";
	print ERRORFILE "LAUNCHING A NEW ROUTE MONITOR...\n";
    } elsif ($errname eq "start") {
	print ERRORFILE "$errdate START: html maker started\n";
    } elsif ($errname eq "nofile") {
	print ERRORFILE "$errdate ERROR: no route stats file exists\n";
	print ERRORFILE "CREATING NEW ROUTE STATS FILE...\n";
    } elsif ($errname eq "startline") {
	print ERRORFILE "$errdate ERROR: wrong first route stats line:\n";
	print ERRORFILE "$line";
	print ERRORFILE "invalid route stats file renamed as $statsfile.bak";
	print ERRORFILE "CREATING NEW ROUTE STATS FILE...\n";
    } elsif ($errname eq "statsline") {
	print ERRORFILE "$errdate ERROR: invalid route stats line:\n";
	print ERRORFILE "$line";
	print ERRORFILE "LINE IGNORED, RESUMING...\n";
    } elsif ($errname eq "field") {
	print ERRORFILE "$errdate ERROR: unable to look up stats field:\n";
	print ERRORFILE "@array \n";
	print ERRORFILE "SOURCE STATS IGNORED, RESUMING...\n";
    } elsif ($errname eq "tablestart") {
	print ERRORFILE "$errdate ERROR: wrong initial route table lines:\n";
	print ERRORFILE "$line";
	print ERRORFILE "NEW ROUTE TABLE IGNORED, RESUMING...\n";
    } elsif ($errname eq "oldtable") {
	print ERRORFILE "$errdate ERROR: route table age is too old\n";
	print ERRORFILE "NEW ROUTE TABLE IGNORED, RESUMING...\n";
    } elsif ($errname eq "interval") {
	print ERRORFILE "$errdate ERROR: route table interval is incorrect\n";
	print ERRORFILE "expected $rpt_interval minutes, but got $rptint\n";
	print ERRORFILE "NEW ROUTE TABLE IGNORED, RESUMING...\n";
    } elsif ($errname eq "sumhtml") {
	print ERRORFILE "$errdate ERROR: unable to generate html summary\n";
	print ERRORFILE "html summary file is: $sumhtml \n";
	print ERRORFILE "HTML PAGE NOT UPDATED, RESUMING...\n";
    } elsif ($errname eq "weekhtml") {
	print ERRORFILE "$errdate ERROR: unable to generate week html file\n";
	print ERRORFILE "html summary file is: $weekhtml \n";
	print ERRORFILE "HTML PAGE NOT UPDATED, RESUMING...\n";
    } elsif ($errname eq "monthhtml") {
	print ERRORFILE "$errdate ERROR: unable to generate month html file\n";
	print ERRORFILE "html summary file is: $monthhtml \n";
	print ERRORFILE "HTML PAGE NOT UPDATED, RESUMING...\n";
    } elsif ($errname eq "aspidfile") {
	print ERRORFILE "$errdate ERROR: unable to open as lookup pid file\n";
	print ERRORFILE "as lookup pid file is: $aspidfile \n";
	print ERRORFILE "UNABLE TO UPDATE AS NUMBERS, RESUMING...\n";
    } elsif ($errname eq "asout") {
	print ERRORFILE "$errdate ERROR: unable to open as lookup results\n";
	print ERRORFILE "as lookup output file is: $asoutfile \n";
	print ERRORFILE "UNABLE TO UPDATE AS NUMBERS, RESUMING...\n";
    } elsif ($errname eq "asin") {
	print ERRORFILE "$errdate ERROR: unable to write as lookup requests\n";
	print ERRORFILE "as lookup input file is: $asinfile \n";
	print ERRORFILE "UNABLE TO UPDATE AS NUMBERS, RESUMING...\n";
    } elsif ($errname eq "asline") {
	print ERRORFILE "$errdate ERROR: invalid line in as lookup results\n";
	print ERRORFILE "$aline";
	print ERRORFILE "AS RESULTS LINE IGNORED, RESUMING...\n";
    } elsif ($errname eq "assrc") {
	print ERRORFILE "$errdate ERROR: as lookup result for unknown src\n";
	print ERRORFILE "source is $src \n";
	print ERRORFILE "AS RESULTS FOR SOURCE IGNORED, RESUMING...\n";
    } elsif ($errname eq "asstart") {
	print ERRORFILE "$errdate ERROR: unable to launch aslookup\n";
	print ERRORFILE "AS LOOKUP WILL RETRY NEXT HOUR, RESUMING...\n";
    } elsif ($errname eq "copy") {
	print ERRORFILE "$errdate ERROR: unable to copy file to archives\n";
	print ERRORFILE "archive directory is: $archivedir\n";
	print ERRORFILE "DATA NOT ARCHIVED, RESUMING...\n";
    } elsif ($errname eq "webcopy") {
	print ERRORFILE "$errdate ERROR: copy to web server failed\n";
	print ERRORFILE "web copy script is: $web_copy\n";
	print ERRORFILE "DATA NOT COPIED TO WEB SERVER, RESUMING...\n";
    } else {
        print ERRORFILE "$errdate ERROR: unidentified error code $errname:\n";
        print ERRORFILE "ERROR CODE IGNORED, RESUMING...\n";
    }
    close(ERRORFILE);
}

sub log_quit {
    # write any error messages into the errorfile and exit
    local($errname) = $_[0];
    local($sc,$mn,$hr,$dy,$mo,$yr,$wd,$yd,$isd) = gmtime();
    $mo = $mo + 1;
    local($errdate) = "$mo/$dy/$yr $hr:$mn:$sc GMT";
    open(ERRORFILE, ">>$errorfile") ||
        die "Html Maker unable to open error log $errorfile\n";
    if ($errname eq "newtable") {
	print ERRORFILE "$errdate ERROR: unable to open new route table\n";
	print ERRORFILE "route monitor table in $rtemontable \n"; 
    } elsif ($errname eq "start") {
	print ERRORFILE "$errdate ERROR: unable to start route monitor\n";
	print ERRORFILE "route monitor executable is $rtemon\n";
    } elsif ($errname eq "pid") {
	print ERRORFILE "$errdate ERROR: unable to write pid file $pidfile\n";
    } elsif ($errname eq "statsfile") {
	print ERRORFILE "$errdate ERROR: unable to open existing stats file\n";
	print ERRORFILE "statsfile in $statsfile\n";
    } else {
        print ERRORFILE "$errdate ERROR: unidentified error code $errname:\n";
    }
    print ERRORFILE "FATAL ERROR, EXITING.\n";
    close(ERRORFILE);
    exit(-1);
}

#------------ from Fields.pm -----------------------------------------

# expects these fields as datafile entries/search parameters
sub declare_fields{
   #name used in CGI    #name displayed         # none/hour/week/month
   $data[0]="addr";     $header[0]="CIDR addr"; $time[0]="none";
   $data[1]="asnum";    $header[1]="AS Number"; $time[1]="none";
   $data[2]="state";    $header[2]="State";     $time[2]="none";
   $data[3]="change";   $header[3]="Change";    $time[3]="none";
   $data[4]="metric";   $header[4]="Metric";    $time[4]="hour";
   $data[5]="flaps";    $header[5]="Flaps";     $time[5]="hour";
   $data[6]="updates";  $header[6]="Updates";   $time[6]="hour";
   $data[7]="wflaps";   $header[7]="Flaps";     $time[7]="week";
   $data[8]="whour";    $header[8]="Uptime(%)"; $time[8]="week";
   $data[9]="wcount";   $header[9]="Hours #";   $time[9]="week";
   $data[10]="wstates"; $header[10]="Changes";  $time[10]="week";
   $data[11]="mflaps";  $header[11]="Flaps";    $time[11]="month";
   $data[12]="mhour";   $header[12]="Uptime(%)"; $time[12]="month";
   $data[13]="mcount";  $header[13]="Hours #";  $time[13]="month";
   $data[14]="mstates"; $header[14]="Changes";  $time[14]="month";
   $total = 15;         # total number of fields
   $special = 4;        # fields < this number are not max/min type fields
}

# find a number to match the field name
sub field_num{
   my($name) = @_[0];
   my($j);
   for($j=0; $j < $total; $j++) {
      if ($data[$j] eq $name) {
         return ($j);
      }
   }
   return (-1);
}




