Commit 21d384ec authored by Peter Pentchev's avatar Peter Pentchev

Add a trivial translation of ethstats to Perl 6.

To do: actually use a Perl 6 grammar for parsing /proc/net/dev.
parent 80787035
......@@ -3,6 +3,7 @@ Change log for ethstats, the network traffic statistics utility
1.1.1 not yet ;)
- fix a grave bug I introduced in 1.1.0: get the correct value
for the number of packets sent out on an interface
- add a trivial translation of ethstats to Perl 6
1.1.0 2015/12/11
- use Getopt::Std for command-line option processing
......
#!/usr/bin/env perl6
use v6;
use strict;
use Getopt::Tiny;
use Terminal::ANSIColor;
my $use_color;
my %prevstat;
my %total;
sub version()
{
say 'ethstats 1.1.0';
}
sub usage(Bool $err)
{
my $s = q:to/EOUSAGE/;
Usage: ethstats [-t] [-C | -M] [-c count] [-i iface] [-n period]
ethstats -V | -h
-C color the "total" line in the output
-c exit after the specified number of samples
-h display program usage information and exit
-i specify a single network interface to monitor
-M do not color the "total" line in the output
-n specify the polling period in seconds (default: 10)
-t prefix each line with the Unix timestamp
-V display program version information and exit
EOUSAGE
if $err {
$*ERR.print($s);
exit 1;
} else {
print $s;
}
}
sub reset_totals()
{
%total = ( :kb({}), :packets({}) );
}
sub update_traffic(%dev, %odev, UInt $period)
{
for %dev.keys -> $what {
for %dev{$what}.keys -> $dir {
my $diff = %dev{$what}{$dir} -
(%odev{$what}{$dir} // 0);
$diff += 4294967296 if $diff < 0;
%odev{$what}{$dir} = %dev{$what}{$dir};
%dev{$what}{$dir} = $diff / $period;
}
}
}
sub add_up_totals(%total, %dev)
{
for %total.keys -> $what {
for <in out> -> $dir {
%total{$what}{$dir} += %dev{$what}{$dir};
}
}
}
sub convert(Str $iface, UInt $period)
{
try my $f = open :r, '/proc/net/dev';
die "Could not open the interface info pseudo-file: $!" if $!;
my %devs;
my $count = 0;
for $f.lines -> $line {
# Ah, the joys of reading from pseudo-files...
last if $line eq '';
$count++;
next if $count < 3;
my ($dev, $rest) = $line.split(':', 2).map(*.trim);
next if defined($iface) && $dev ne $iface;
my @data = $rest.split(/\s+/).map(*.Int);
%devs{$dev} = {
:name($dev),
:data({
:bytes({
:in(@data[0]),
:out(@data[8]),
}),
:packets({
:in(@data[1]),
:out(@data[9]),
}),
}),
};
}
$f.close;
reset_totals;
%devs<lo>:delete;
for %devs.values -> %dev {
my $name = %dev<name>;
%prevstat{$name} //= { :name($name) };
update_traffic %dev<data>, %prevstat{$name}, $period;
%dev<data><kb> = {
:in(%dev<data><bytes><in> / 1000000 * 8),
:out(%dev<data><bytes><out> / 1000000 * 8),
};
add_up_totals %total, %dev<data>;
}
return %devs;
}
sub acolor(Str $name)
{
return $use_color?? color($name)!! '';
}
sub format_line(Str $name, %dev)
{
return ($name eq 'total'?? 'total: '!! sprintf(' %6s: ', $name)) ~
sprintf('%7.2f Mb/s In %7.2f Mb/s Out - ' ~
'%8.1f p/s In %8.1f p/s Out',
%dev<kb><in>, %dev<kb><out>,
%dev<packets><in>, %dev<packets><out>);
}
{
my %flags;
my $opts = Getopt::Tiny.new;
$opts.bool('h', Nil, -> $v { %flags<h> = $v });
$opts.bool('V', Nil, -> $v { %flags<V> = $v });
my Bool $addtime = False;
$opts.bool('t', Nil, -> $v { $addtime = $v; });
my UInt $period = 10;
$opts.int('n', Nil, -> $v {
if $v < 1 {
$*ERR.say('The period must be a positive integer');
usage True;
}
$period = $v;
});
my UInt $count = 0;
$opts.int('c', Nil, -> $v {
if $v < 1 {
$*ERR.say('The count must be a positive integer');
usage True;
}
$count = $v;
});
my Str $iface = Nil;
$opts.str('i', Nil, -> $v { $iface = $v; });
$use_color = $*OUT.t;
$opts.bool('C', Nil, -> $v {
if %flags<M> {
$*ERR.say('The -C and -M options are mutually exclusive');
usage True;
}
%flags<C> = True;
$use_color = True;
});
$opts.bool('M', Nil, -> $v {
if %flags<C> {
$*ERR.say('The -C and -M options are mutually exclusive');
usage True;
}
%flags<M> = True;
$use_color = False;
});
$opts.parse([@*ARGS]);
if %flags<V> {
version;
}
if %flags<h> {
usage False;
}
if %flags<V> || %flags<h> {
exit 0;
}
convert $iface, 1;
sleep 1;
loop {
my %devs = convert $iface, $period;
print time ~ ' ' if $addtime;
if %devs.elems > 1 {
say acolor('yellow') ~
format_line('total', %total) ~
acolor('reset');
}
for %devs.values.sort(*<name> cmp *<name>) -> $dev {
say format_line($dev<name>, $dev<data>);
}
if $count > 0 {
$count--;
exit 0 if $count < 1;
}
sleep $period;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment