#!/usr/bin/perl -w

# 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
# 

use POSIX;
use RRDs;
use strict;

#
# Basic setup
#

my @ifaces	= qw(wlan0 eth0 eth1);   # wireless and other interface
my $step	= 10;	# sec
my $next1_factor = 60;	# $step * $next1_factor = 10*60  = 600 sec  = 10 min
my $next2_factor = 360;	# $step * $next2_factor = 10*360 = 3600 sec = 1 hour
my $update_graph_period = 10;	# How often will be graph updated (1..1000)

#
# Setup files locations
# 

my $wifi_proc_file='/proc/net/wireless';
my $dev_proc_file='/proc/net/dev';
my ($pid_file,$base_dir);
if ($0 =~ m#^(.*?)/(.+)\.pl$#i) {
	$base_dir = $1;
	$pid_file = "$base_dir/$2.pid";
} else {
	die "can't decode base dir and script name from '$0'";
}

#
# End setup
#
############################################################

my $start=time;
my $syncrorun;
my $switcher=$ARGV[0];
if (!($switcher)) { $switcher='undef'; }

if ($switcher eq 'start') {
	&start();
} elsif ($switcher eq 'stop') {
	&stop();
} elsif ($switcher eq 'graph') {
	foreach (@ifaces) {
		&update_graph($_);
	}
} elsif ($switcher eq 'install') {
	foreach (@ifaces) {
		&install($_);
	}
} else {
	print qq(Usage: ./istatd.pl option
	option: start   - Starting daemon
		stop    - Stopping daemon
		graph   - Get the latest graph
		install - Init database
	);
}

###############################################################
sub install {
	my $iface = shift || die "need interface name!";
	my $rrd = "$base_dir/$iface.rrd";
	my $sec_in_the_day=86400;
	my $sec_in_the_week=$sec_in_the_day*7;
	my $sec_in_the_month=$sec_in_the_day*31;
	my $minvalue=0;
	my $maxvalue=255;
	my $xfiles_factor=0.5;
	my $max_interval=$step/$xfiles_factor;
	my $number_steps_day=floor($sec_in_the_day/$step);
	my $number_steps_week=floor($sec_in_the_week/($step*$next1_factor));
	my $number_steps_month=floor($sec_in_the_month/($step*$next2_factor));
	RRDs::create ($rrd, "--start",$start-1, "--step",$step,
		"DS:link:GAUGE:$max_interval:$minvalue:$maxvalue",
		"DS:level:GAUGE:$max_interval:$minvalue:$maxvalue",
		"DS:noise:GAUGE:$max_interval:$minvalue:$maxvalue",
		"DS:in:COUNTER:$max_interval:0:U",
		"DS:out:COUNTER:$max_interval:0:U",
		"RRA:AVERAGE:$xfiles_factor:1:$number_steps_day",
		"RRA:AVERAGE:$xfiles_factor:$next1_factor:$number_steps_week",
		"RRA:AVERAGE:$xfiles_factor:$next2_factor:$number_steps_month");
	my $ERROR = RRDs::error;
	die "$0: unable to create `$rrd': $ERROR\n" if $ERROR;
	print "$iface done.\n";
}

###############################################################
sub start {
	my $pid=fork;
	exit if $pid;
	die "Could't fork: $!" unless defined($pid);
	POSIX::setsid() or die "Can't start a new session: $!";
	$SIG{INT}=$SIG{TERM}=$SIG{HUP}=\&syncro_handler;
	$syncrorun=1;
	my $pid_id=$$;
	open(FILE, ">>$pid_file") || die "Cant write to $pid_file file: $!";
	print FILE $pid_id;
	close(FILE);
	my $counter=0;
	while ($syncrorun) {
		$counter++;
		if (!( -e $pid_file)) { $syncrorun=0; }
		foreach (@ifaces) {
			&get_data($_);
		}
		if ($counter == $update_graph_period) {
			foreach (@ifaces) {
				&update_graph($_);
			}
			$counter=0;
		}
		sleep($step);
	}
}

##############################################################
sub stop {
	my $pid=`cat $pid_file`;
	`kill $pid`;
}

##############################################################
sub update_graph {
	my $iface = shift || die "need interface name!";
	my $rrd = "$base_dir/$iface.rrd";

	RRDs::graph "$iface-signal.png",
		"--title", " $iface signal statistics",
		"--start", "now-12h",
		"--end", "now",
		"--lower-limit=0",
		"--interlace",
		"--imgformat","PNG",
		"--width=450",
		"DEF:link=$rrd:link:AVERAGE",
#		"DEF:level=$rrd:level:AVERAGE",
#		"DEF:noise=$rrd:noise:AVERAGE",
		"AREA:link#00b6e4:link",
#		"LINE1:level#0022e9:level",
#		"LINE1:noise#cc0000:noise"
	;
	print "ERROR ".RRDs::error."\n" if (RRDs::error);

	RRDs::graph "$iface-transfer.png",
		"--title", " $iface transfer statistics",
		"--start", "now-12h",
		"--end", "now",
		"--lower-limit=0",
		"--interlace",
		"--imgformat","PNG",
		"--width=450",
		"DEF:in=$rrd:in:AVERAGE",
		"DEF:out=$rrd:out:AVERAGE",
		"AREA:in#00b6e4:in",
		"LINE1:out#0022e9:out",
	;
	print "ERROR ".RRDs::error."\n" if (RRDs::error);
}

###############################################################
sub get_data {
	my $iface = shift || die "need interface name!";
	my $rrd = "$base_dir/$iface.rrd";

	my ($link,$level,$noise,$in,$out) = ("U","U","U","U","U");

	open(FILE, "$wifi_proc_file") || die "Cant open $wifi_proc_file file: $!";
	while (my $line=<FILE>) {
		($link,$level,$noise) = ($1,$2,$3) if ($line=~/\s*?$iface\:\s+?\d+?\s+?(\d+)\.*?\d*?\s+?(\d+)\.*?\d*?\s+?(\d+)\.*?\d*?.*/);
	}
	close(FILE);

	open(FILE, "$dev_proc_file") || die "Cant open $dev_proc_file file: $!";
	while (my $line=<FILE>) {
		if ($line=~/\s*?$iface\:\s*?(\d+.*)$/) {
			my @v = split(/\s+/,$1,16);
			($in,$out) = ($v[0],$v[8]);
		}
	}
	close(FILE);
	RRDs::update ("$rrd", "--template", "link:level:noise:in:out", "N:$link:$level:$noise:$in:$out");
	print "ERROR ".RRDs::error."\n" if (RRDs::error);
}

###############################################################
sub syncro_handler {
    unlink($pid_file) || die "Cant unlink $pid_file file: $!";
    $syncrorun=0;
}
