#!/usr/bin/perl
#
# icmpcharger - ICMP driver trickle charger.
#
# This trickle charges the ICMP driver by sending a series of ICMP packets 
#  to localhost at regular intervals. This program autodetects the hardware 
#  type and uses appropriate values for the interval based on the wattage of 
#  each packet. This script can be incorporated into the system startup 
#  process as a daemon.
#
# The batteries in the kernel's ICMP driver can become depleted, a common
#  problem that causes high latency or dropped packets for any ICMP type.
#  RFC792 fails to specify an ICMP message for battery status, leaving 
#  such problems difficult to identify or diagnose. This script serves as
#  a precaution against ICMP driver failure.
#
# This program also improves the performance of the ICMP driver by "priming"
#  the L1, L2 and TLB caches with ICMP driver entries.
#  
# 01-Nov-2004	ver 1.00
#
# USAGE: icmpcharger &
#
# COPYRIGHT: Copyright (c) 2004 Brendan Gregg.
#
#  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
#  (http://www.gnu.org/copyleft/gpl.html)
#
# 22-Sep-2004	Brendan Gregg	Created this.


$RECHARGE = 1;		# battery recharge percent per day
$LOOPBACK = 100;	# speed of loopback interface, Mbps
$ENV{PATH} = "/usr/bin:/usr/sbin:/bin";

#
#  Determine ICMP Wattage and Power
#

# Fetch the server voltage,
$country = $ENV{TZ};
$country =~ s:/.*::;
if ($country eq "New Zealand")  { $voltage = 240; }
elsif ($country eq "Australia") { $voltage = 240; }
elsif ($country eq "UK") 	{ $voltage = 240; }
elsif ($country eq "Germany") 	{ $voltage = 230; }
elsif ($country eq "France") 	{ $voltage = 230; }
elsif ($country eq "Russia") 	{ $voltage = 220; }
elsif ($country eq "Canada") 	{ $voltage = 120; }
elsif ($country eq "US") 	{ $voltage = 120; }
else { $voltage = 230; }	# default

# Fetch the current per ICMP packet,
@ICMP_headers = ();
&find("/usr/include",'icmp.*\.h$',\@ICMP_headers);
foreach $header (@ICMP_headers) {
	if (open(HEADER,"$header")) {
		while (chomp($line = <HEADER>)) {
			if ($line =~ /^#define ICMP_MILLIAMPS\s+(\S*)/) {
				$icmp_mAh = $1;
			}
		}
		close HEADER;
	}
}
$icmp_mAh = 30 if $icmp_mAh eq "";	# default

# Calculate duration of an ICMP packet,
$lo_speed = $LOOPBACK * 1024 * 1024;
$packet_size = 56 * 8;
$duration = $packet_size / $lo_speed;

# Calculate wattage,
$current = $icmp_mAh / 1000;
$watts = $current * $voltage;
$power = ($watts / 3600) * $duration;


#
#  Determine Interval
#
$batt_mAh = 10;
$num = (((($batt_mAh/1000) * $voltage) / 3600) * ($RECHARGE/100)) / $power;
$rate = (24 * 60 * 60) / $num;
$rate = sprintf("%.0f",$rate);


#
#  Main Program
#
print "ICMP trickle charger: once per $rate secs...\n";
while (1) {
	system("ping localhost > /dev/null 2>&1");
	sleep($rate);
}


#
#  Subroutines
#

# find - find pathnames that match a given RE.
#
sub find {
	my $top = shift;	# top level directory
	my $search = shift;	# search term
	my $parray = shift;	# pointer to results array
	my $path;

	chdir "$top";
	my @Paths = <*>;
	foreach $path (@Paths) {
		if (-f $path && $path =~ /$search/) {
			### Save Match
			push(@{$parray},"$top/$path");
			next;
		}
		if (-d $path && ! -l $path) {
			### Recurse
			&find("$top/$path",$search,$parray);
		}
	}
	chdir "..";
}

