#!/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;
}