INN commit: trunk (doc/pod/news.pod scripts/innreport.in)

INN Commit rra at isc.org
Mon Jun 11 19:10:22 UTC 2012


    Date: Monday, June 11, 2012 @ 12:10:22
  Author: iulius
Revision: 9412

fix the generation of HTML pages by innreport during leap years

An illegal division by zero occurred during the generation of HTML pages
by innreport.

The problem comes from the parsing of innreport.db.  If you have in this
file a change of year for which the hours are the same between Dec 31th
and Jan 1st, then this error is generated.

For instance:

  news-notice.2010.12.31-04.15.02.html|Dec 31 04:15:02 -- Jan  1 04:15:02|28079|2791|7.9 MB|34320|1500|4.6 MB

There is an issue in how innreport translates these dates.  The ConvDate()
function assumes the dates are relative to the *current* year.  So it
thinks it has been between Dec 31th 2012 and Jan 1st 2012 (whereas it was
Dec 31th 2010 and Jan 1st 2011).  As 2012 is a leap year, and Dec 31th is
after Feb 28th, the translated date contains an unexpected "+1 day".

Thus, the main page of innreport HTML generation has not been updated
since the beginning of the year.  Note that no HTML report is lost.
They are properly generated.

Now, innreport makes use of Time::Local to convert dates to seconds since
epoch, thus modifying the behaviour of ConvDate() which was converting
dates to seconds since January, 1st.

I bet innreport now runs slower, but I think it is better to achieve
robustness (the code using Time::Local seems easier to read and maintain).

Modified:
  trunk/doc/pod/news.pod
  trunk/scripts/innreport.in

----------------------+
 doc/pod/news.pod     |    8 +++
 scripts/innreport.in |  116 +++++++++++++++++++++----------------------------
 2 files changed, 59 insertions(+), 65 deletions(-)

Modified: doc/pod/news.pod
===================================================================
--- doc/pod/news.pod	2012-05-29 17:59:04 UTC (rev 9411)
+++ doc/pod/news.pod	2012-06-11 19:10:22 UTC (rev 9412)
@@ -349,6 +349,14 @@
 
 =item *
 
+Fixed the way B<innreport> handles leap years.  It now properly generates
+HTML reports; dates were assumed to be relative to the current year, which
+may break their computation during for instance the whole 2012 leap year.
+Please note that no HTML reports have been lost, and that they will appear
+when INN is updated to this new version.
+
+=item *
+
 A new parameter has been added to F<inn.conf> to determine whether the status
 file that B<innd> can write out (depending on the value of the I<status>
 parameter) is plain text or wrapped in HTML.  It previously only was a

Modified: scripts/innreport.in
===================================================================
--- scripts/innreport.in	2012-05-29 17:59:04 UTC (rev 9411)
+++ scripts/innreport.in	2012-06-11 19:10:22 UTC (rev 9412)
@@ -95,6 +95,7 @@
 
 use strict;
 use Carp qw( cluck confess );
+use Time::Local;
 
 ## Do you want to create a Web page. Pick DO or DONT.
 my $HTML = "DONT";
@@ -338,34 +339,11 @@
 my @unrecognize;
 my ($total_line, $total_size) = (0, 0);
 my ($suffix, $HTML_output, %config, %prog_type, %prog_size);
-my ( @month_to_dayofyear, %month_to_dayofyear, $current_year );
-
+my $current_year;
 {
   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
     localtime(time);
   $current_year = $year += 1900;
-  my $isLeapYear =
-   (($year % 4 == 0) && ($year % 100 != 0)) || ($year % 400 == 0);
-
-  @month_to_dayofyear = $isLeapYear
-  ? ( -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 366 )
-  : ( -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 365 );
-
-  %month_to_dayofyear =
-  (
-    'Jan' => $month_to_dayofyear[ 0],
-    'Feb' => $month_to_dayofyear[ 1],
-    'Mar' => $month_to_dayofyear[ 2],
-    'Apr' => $month_to_dayofyear[ 3],
-    'May' => $month_to_dayofyear[ 4],
-    'Jun' => $month_to_dayofyear[ 5],
-    'Jul' => $month_to_dayofyear[ 6],
-    'Aug' => $month_to_dayofyear[ 7],
-    'Sep' => $month_to_dayofyear[ 8],
-    'Oct' => $month_to_dayofyear[ 9],
-    'Nov' => $month_to_dayofyear[10],
-    'Dec' => $month_to_dayofyear[11],
-  );
 }
 
 my $HTML_header = '';
@@ -387,14 +365,13 @@
 # an unsigned integer (either 32 or 64 bit).
 
 my $first_date = undef;    # lowest encountered date
-my $first_date_cvt = 1E30; # = &ConvDate($first_date)
+my $first_date_cvt = 1E30; # = &ConvDate($current_year . ' ' . $first_date)
 my $last_date = undef;     # highest encountered date
-my $last_date_cvt = -1;    # = &ConvDate($last_date)
+my $last_date_cvt = -1;    # = &ConvDate($current_year . ' ' . $last_date)
 
-# $date_wrap_around is a day of year.  If it is positive, then the log
-# file goes past 31st of December into January.  In that case, 365 (or
-# 366) is added to all dates less than $date_wrap_around.
-my $date_wrap_around = $month_to_dayofyear[ 0];
+# If $wrap_around is positive, then the log file goes past 31st of December
+# into January.
+my $wrap_around = 0;
 
 #########################################################################
 if (length($CLASS) == 0)
@@ -468,13 +445,17 @@
       last UNRECOGNIZED;
     } # DECODE
 
+    # Do not add $current_year to $date_str because years are not expected
+    # in $first_date and $last_date when used afterwards.
+    # Take the current year because we assume that we are not re-parsing
+    # an old log file.
     my $date_str = $day . ' ' . $hour;
-    my $cvtdate = &ConvDate ($date_str);
+    my $cvtdate = &ConvDate ($current_year . ' ' . $date_str, $wrap_around);
     last UNRECOGNIZED if (!defined($cvtdate));
 
     if ($cvtdate < $first_date_cvt)
     {
-      if ($first_date_cvt - $cvtdate > 253 * 24 * 60 * 60 && $first_date)
+      if ($first_date_cvt - $cvtdate > 253 * 24 * 60 * 60 && defined($first_date))
       { #
         # Detected excessive distance between log entries (see function
         # DateCompare for magic number 253).  This means we are crossing
@@ -490,13 +471,13 @@
         # belong to current year.  With this definition, it is not possible
         # to cross from both 31st of December to 1st of January and from
         # 28th of February to 1st of March in the same log file.
-        #
-        # Note: $month_to_dayofyear[12] - $month_to_dayofyear[4] = 245
 
-        $date_wrap_around = $month_to_dayofyear[4];
-        $cvtdate += $month_to_dayofyear[12] * 24 * 60 * 60;
+        my $first_date_cvt_tmp = $first_date_cvt;
+        $wrap_around = 1;
+        $first_date_cvt = &ConvDate ($current_year . ' ' . $first_date, $wrap_around);
 
-	confess if ($cvtdate != &ConvDate ($date_str));
+        # The numbers should have changed (one year less for $first_date_cvt).
+        confess if ($first_date_cvt == $first_date_cvt_tmp);
 	$last_date_cvt = $cvtdate;
 	$last_date = $date_str;
       }
@@ -553,9 +534,9 @@
 {
   my $default = 24 * 60 * 60; # one day
 
-  if ($DEBUG && $first_date_cvt != &ConvDate($first_date))
+  if ($DEBUG && $first_date_cvt != &ConvDate($current_year . ' ' . $first_date, $wrap_around))
   {
-    die '$last_date_cvt != &ConvDate($last_date)';
+    die '$first_date_cvt != &ConvDate($current_year . \' \' . $first_date, $wrap_around)';
   }
   if (!defined($last_date))
   {
@@ -563,9 +544,9 @@
     $last_date_cvt = $first_date_cvt;
     return $default;
   }
-  if ($DEBUG && $last_date_cvt != &ConvDate($last_date))
+  if ($DEBUG && $last_date_cvt != &ConvDate($current_year . ' ' . $last_date, $wrap_around))
   {
-    die '$last_date_cvt != &ConvDate($last_date)';
+    die '$last_date_cvt != &ConvDate($current_year . \' \' . $last_date, $wrap_around)';
   }
   my $result = $last_date_cvt - $first_date_cvt;
   return $result if ($result > 0);
@@ -588,7 +569,7 @@
 
     $month = 1 + index("JanFebMarAprMayJunJulAugSepOctNovDec", $month) / 3;
     my $year = $current_year;
-    $year-- if ($date_wrap_around >= 0);
+    $year-- if ($wrap_around > 0);
 
     $suffix = sprintf ".%02d.%02d.%02d-%02d%s%02d%s%02d",
       $year, $month, $d, $h, $SEPARATOR, $mn, $SEPARATOR, $s;
@@ -757,31 +738,37 @@
   return 1;
 }
 
-# Convert a date to number of seconds since 1st of January.
+# Convert a date to number of seconds since epoch.
 # Leap years are handled correctly, daylight saving is not.
-# Usage: $num = &ConvDate ($date);
-# Date format is "Aug 22 01:49:40"
-sub ConvDate($) {
+# Usage: $num = &ConvDate ($date, $wrap_around);
+# Date format is "2012 Aug 22 01:49:40".
+# The second argument is whether wrapping should be taken into account
+# ($wrap_around is 0 when no change of year has been detected in the logs).
+sub ConvDate($$) {
   my $T = shift;
-  if (!$T)
+  my $wrap = shift;
+  if (!defined($T))
   {
     cluck 'Parameter $T is undefined.' if ($DEBUG);
     return undef;
   }
-  my ($m, $d, $h, $mn, $s) = $T =~ /^(\S\S\S)\s+(\d+)\s+(\d+):(\d+):(\d+)$/;
-  if (!$m)
+  my ($y, $m_name, $d, $h, $mn, $s) = $T =~ /^(\d+)\s+(\S\S\S)\s+(\d+)\s+(\d+):(\d+):(\d+)$/;
+  if (!defined($m_name))
   {
     cluck "Invalid date $T" if ($DEBUG);
     return undef;
   }
-  $m = $month_to_dayofyear{ $m };
-  if (!$m)
+  my $m = index("JanFebMarAprMayJunJulAugSepOctNovDec", $m_name) / 3;
+  if (!defined($m))
   {
     cluck "Invalid month name in $T" if ($DEBUG);
     return undef;
   }
-  $m += $month_to_dayofyear[12] if ($m < $date_wrap_around);
-  return $s + 60 * $mn + 3600 * $h + 86400 * ($d + $m);
+  # Take the previous year if a change of year has been detected and
+  # the date is after May, 1st.
+  $y-- if $wrap > 0 and $m > 4;
+  # Convert the given date to the number of seconds since epoch.
+  return Time::Local::timelocal($s, $mn, $h, $d, $m, $y);
 }
 
 # Compare 2 filenames
@@ -1126,29 +1113,28 @@
 	next unless $year; # bad filename.. strange.
 	my ($start, $end) =
 	  $res[$date_idx - 1] =~ m/^(\w+\s+\d+ \S+) -- (\w+\s+\d+ \S+)$/o;
-        if (!$start)
+        if (!defined($start))
         {
           warn "Invalid line in DB file ignored: $k" if ($DEBUG);
           next;
         }
-	$start = &ConvDate ($start);
-	$end = &ConvDate ($end);
-        if ($start - $end == 0)
+	my $start_sec = &ConvDate ($year . ' ' . $start, 0);
+	my $end_sec = &ConvDate ($year . ' ' . $end, 0);
+        if ($start_sec - $end_sec == 0)
         {
           warn "Time range 0 in DB file ignored: $k" if ($DEBUG);
           next;
         }
 	# 31/12 - 1/1 ?
-	my $inc = $end < $start ? 1 : 0;
-	$start += (($year - 1970) * 365 +
-		   int (($year - 1968) / 4)) * 3600 * 24;
-	$year += $inc;
-	$end += (($year - 1970) * 365 + int (($year - 1968) / 4)) * 3600 * 24;
-	$in{$start} = $type_in ? &kb2i($res[$value_in - 1])
+        if ($end_sec < $start_sec) {
+          $end_sec = &ConvDate ($year+1 . ' ' . $end, 0);
+        }
+
+	$in{$start_sec} = $type_in ? &kb2i($res[$value_in - 1])
 	                       : $res[$value_in - 1];
-	$out{$start} = $type_out ? &kb2i($res[$value_out - 1])
+	$out{$start_sec} = $type_out ? &kb2i($res[$value_out - 1])
 	                         : $res[$value_out - 1];
-	$dates{$start} = $end;
+	$dates{$start_sec} = $end_sec;
       }
       my ($xmax, $ymax) = (500, 170);
       &Chrono ("$IMG_dir/$filename", $title, $color_bg, $xmax, $ymax,



More information about the inn-committers mailing list