pax_global_header00006660000000000000000000000064126402254240014513gustar00rootroot0000000000000052 comment=a59d0ec3fd0c2caec70b8eb20dab9fb6e1406abb ethstats-1.1.1/000077500000000000000000000000001264022542400133525ustar00rootroot00000000000000ethstats-1.1.1/CHANGES000066400000000000000000000031771264022542400143550ustar00rootroot00000000000000Change log for ethstats, the network traffic statistics utility 1.1.1 2015/12/28 - 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 - do not make all the warnings fatal, this is bad for forwards-compatibility - use autovivification instead of an unwiedly defined-or - drop a needless sorting while fetching the interface stats - reduce the scope of some variables - update the "test" target in the Makefile: the Getopt::Tiny module used by ethstats.p6 does not recognize clustered single-letter options, so pass -V and -h separately - parametrize the "test" target and add the "test-p6" one 1.1.0 2015/12/11 - use Getopt::Std for command-line option processing - add some command-line options besides the existing -t: -C (color) or -M (monochrome) -c (count) -h (help) -i interface to only poll a single interface -n interval to specify the polling interval -V (version) - add a simple Makefile - declare a 5.010 minimum Perl version dependency - update the Perl source style a bit: - use strict mode and the warnings pragma - reflow to a tab indent - remove the unused tquad() and toquad() functions - break the output formatting out into format_line() - use the version module - use fewer parentheses and quotes - use a local variable for the input file stream - use data structures instead of passing data in a herd of global variables - add a mandoc manual page 1.0 (unknown) - released into the public domain by Drew Strieb Contact: Peter Pentchev ethstats-1.1.1/Makefile000066400000000000000000000027201264022542400150130ustar00rootroot00000000000000#!/usr/bin/make -f SCRIPT= ethstats SRC= ethstats.pl MAN1= ${SCRIPT}.1 MAN1GZ= ${MAN1}.gz TEST_SCRIPT?= ${SCRIPT} GZIP?= gzip -cn INSTALL?= install COPY?= -c MKDIR?= mkdir -p PERL?= perl RM?= rm -f LOCALBASE?= /usr/local PREFIX?= ${LOCALBASE} BINDIR?= ${PREFIX}/bin MANDIR?= ${PREFIX}/share/man/man BINOWN?= root BINGRP?= root BINMODE?= 755 SHAREOWN?= root SHAREGRP?= root SHAREMODE?= 644 INSTALL_DATA?= ${INSTALL} ${COPY} -o ${SHAREOWN} -g ${SHAREGRP} -m ${SHAREMODE} INSTALL_EXEC?= ${INSTALL} ${COPY} -m ${BINMODE} INSTALL_SCRIPT?= ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} all: ${SCRIPT} ${MAN1GZ} clean: ${RM} ${SCRIPT} ${MAN1GZ} install: all ${MKDIR} ${DESTDIR}${BINDIR} ${INSTALL_SCRIPT} ${SCRIPT} ${DESTDIR}${BINDIR}/ ${MKDIR} ${DESTDIR}${MANDIR}1 ${INSTALL_SCRIPT} ${MAN1GZ} ${DESTDIR}${MANDIR}1/ test: all @echo "=== Perl syntax check" ${PERL} -c ${TEST_SCRIPT} @echo "=== Basic version/usage functionality" ${PERL} ${TEST_SCRIPT} -V -h @echo @echo "=== Error out on invalid options" ! ${PERL} ${TEST_SCRIPT} -X @echo @if [ -r "/proc/net/dev" ]; then \ echo "=== A single run"; \ ${PERL} ${TEST_SCRIPT} -n3 -c2; \ else \ echo "=== Skipping the single run"; \ fi @echo test-p6: ${MAKE} TEST_SCRIPT=ethstats.p6 PERL=perl6 test ${SCRIPT}: ${SRC} ${INSTALL_EXEC} ${SRC} ${SCRIPT} ${MAN1GZ}: ${MAN1} ${GZIP} ${MAN1} > ${MAN1GZ} || (${RM} ${MAN1GZ}; false) .PHONY: all clean install test ethstats-1.1.1/ethstats.1000066400000000000000000000044441264022542400153010ustar00rootroot00000000000000.\" Copyright (c) 2015 Peter Pentchev .\" This manual page is hereby placed into the public domain. .\" .Dd December 11, 2015 .Dt ETHSTATS 1 .Os .Sh NAME .Nm ethstats .Nd quickly display statistics of network interfaces .Sh SYNOPSIS .Nm .Op Fl t .Op Fl C | Fl M .Op Fl c Ar count .Op Fl i Ar iface .Op Fl n Ar period .Nm .Op Fl V | Fl h .Sh DESCRIPTION The .Nm utility is a quick way to display network interface statistics. It periodically samples the byte and packet counters exported by the Linux kernel into the .Pa /proc/net/dev pseudo-file and outputs a line for each network interface on the system (except for the loopback) and a totals line. The data shown is the input and output rate in megabits per second and packets per second. .Pp The .Nm utility accepts the following command-line options: .Bl -tag -width indent .It Fl C Highlight (color) the totals line in the output. .It Fl c Exit after the specified number of samples. .It Fl h Display program usage information and exit. .It Fl i Specify a single network interface to monitor. .It Fl M Do not highlight (monochrome) the totals line in the output. .It Fl n Specify the polling interval in seconds (default: 10). .It Fl t Prefix the totals line with the Unix timestamp. .It Fl V Display program version information and exit. .El .Sh ENVIRONMENT The operation of the .Nm utility is not directly influenced by any environment variables. .Sh FILES The operation of the .Nm utility is not directly influenced by the contents of any files. .Sh EXAMPLES Continuously poll the system network interfaces on five-second intervals: .Pp .Dl ethstats -n 5 .Pp Take four samples of the transfer rate of the .Pa wlan0 interface, ten seconds apart: .Pp .Dl ethstats -c 4 -n 10 -i wlan0 .Pp .Sh DIAGNOSTICS .Ex -std .Sh SEE ALSO .Xr netstat 8 .Sh STANDARDS No standards were harmed during the production of the .Nm utility. .Sh HISTORY This application was released from an unknown source via Drew Strieb. It is now maintained by Peter Pentchev. .Pp A manual page was originally written by Scott Dier for the Debian project and then reworked by Peter Pentchev. .Sh AUTHORS .An Peter Pentchev .Aq roam@ringlet.net .An M. Drew Streib .Aq dtype@dtype.org .An Scott Dier .Aq sdier@debian.org .Sh BUGS No, thank you :) But if you should actually find any, please report them to the author. ethstats-1.1.1/ethstats.p6000077500000000000000000000136131264022542400154670ustar00rootroot00000000000000#!/usr/bin/env perl6 # # Similarly to the Perl 5 version ethstats.pl, I am hereby # releasing ethstats.p6 into the public domain. # - Peter Pentchev use v6; use strict; use Getopt::Tiny; use Terminal::ANSIColor; sub version() { say 'ethstats 1.1.1'; } sub usage(Bool:D $err = True) { 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; } } class Stats { has Real $.in = 0; has Real $.out = 0; method !set(Real:D $in, Real:D $out) { $!in = $in; $!out = $out; } method reset() { self!set(0, 0); } method update_traffic(Stats:D $old, UInt:D $period) { my $diff-in = $!in - $old.in; $diff-in += 4294967296 if $diff-in < 0; my $diff-out = $!out - $old.out; $diff-out += 4294967296 if $diff-out < 0; $old!set($!in, $!out); self!set($diff-in / $period, $diff-out / $period); } method add(Stats:D $more) { $!in += $more.in; $!out += $more.out; } method kb_from(Stats:D $bytes) { $!in = $bytes.in / 1000000 * 8; $!out = $bytes.out / 1000000 * 8; } } class InterfaceStats { has Str $.name is required; has Stats $.bytes = Stats.new; has Stats $.packets = Stats.new; has Stats $.kb = Stats.new; method reset() { $!bytes.reset(); $!packets.reset(); $!kb.reset(); } method update_traffic(InterfaceStats:D $odev, UInt:D $period) { $!bytes.update_traffic($odev.bytes, $period); $!packets.update_traffic($odev.packets, $period); } method add(InterfaceStats:D $more) { $!bytes.add($more.bytes); $!packets.add($more.packets); $!kb.add($more.kb); } method format() { 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', $!kb.in, $!kb.out, $!packets.in, $!packets.out); } } my InterfaceStats %prevstat; grammar Interfaces { token TOP { * } rule headerline { \n } token nonnl { <- [\n] >* } rule ifaceline { ':' \n } rule in { } rule out { } token iface { \w+ } token bytes { } token packets { } token errs { } token drop { } token fifo { } token frame { } token compressed { } token multicast { } token colls { } token carrier { } token uint { \d+ } } class InterfaceActions { method TOP($/) { $/.make: $.map(*.made) } method ifaceline($/) { $/.make: InterfaceStats.new( name => $.made, bytes => Stats.new( in => $.made, out => $.made, ), packets => Stats.new( in => $.made, out => $.made, ), ) } method in($/) { $/.make: { :bytes($.made), :packets($.made) } } method out($/) { $/.make: { :bytes($.made), :packets($.made) } } method iface($/) { $/.make: ~$/; } method bytes($/) { $/.make: $.made } method packets($/) { $/.make: $.made } method uint($/) { $/.make: $/.UInt } } sub convert(Str $iface, UInt:D $period, InterfaceStats:D $total) { try my $f = open :r, '/proc/net/dev', :chomp(False), :enc('latin1'); die "Could not open the interface info pseudo-file: $!" if $!; my Str $contents = ''; for $f.lines -> $line { # Ah, the joys of reading from pseudo-files... last if $line eq ''; $contents ~= $line; } $f.close; my InterfaceStats %devs = Interfaces.parse($contents, :actions(InterfaceActions)) .made.map: { $_.name => $_ }; if defined $iface { %devs = %devs{$iface}:kv; } $total.reset; %devs:delete; for %devs.values -> $dev { my $name = $dev.name; %prevstat{$name} //= InterfaceStats.new(:name($name)); $dev.update_traffic(%prevstat{$name}, $period); $dev.kb.kb_from($dev.bytes); $total.add($dev); } return %devs; } { my Bool %flags; my Getopt::Tiny $opts .= new; $opts.bool('h', Nil, -> $v { %flags = $v }); $opts.bool('V', Nil, -> $v { %flags = $v }); my Bool $addtime = False; $opts.bool('t', Nil, -> $v { $addtime = $v; }); my UInt $period = 10; $opts.int('n', Nil, -> $v { if $v < 1 { note 'The period must be a positive integer'; usage; } $period = $v; }); my UInt $count = 0; $opts.int('c', Nil, -> $v { if $v < 1 { note 'The count must be a positive integer'; usage; } $count = $v; }); my Str $iface = Nil; $opts.str('i', Nil, -> $v { $iface = $v; }); my Bool $use_color = $*OUT.t; $opts.bool('C', Nil, -> $v { if %flags { note 'The -C and -M options are mutually exclusive'; usage; } %flags = True; $use_color = True; }); $opts.bool('M', Nil, -> $v { if %flags { note 'The -C and -M options are mutually exclusive'; usage; } %flags = True; $use_color = False; }); $opts.parse([@*ARGS]); if %flags { version; } if %flags { usage False; } if %flags || %flags { exit 0; } my Str %c = .map: { $_ => $use_color?? color($_)!! '' }; my InterfaceStats $total .= new(:name('total')); convert $iface, 1, $total; sleep 1; loop { my InterfaceStats %devs = convert $iface, $period, $total; print time ~ ' ' if $addtime; if %devs.elems > 1 { say %c ~ $total.format ~ %c; } .format.say for %devs.values.sort: *.name; if $count > 0 { $count--; exit 0 if $count < 1; } sleep $period; } } ethstats-1.1.1/ethstats.pl000077500000000000000000000116051264022542400155540ustar00rootroot00000000000000#!/usr/bin/perl # I received this without copyright, but have since been told by the owner # that it is released into the public domain. # # Any changes I make are released into the public domain. # - M. Drew Streib # # Similarly to the above, any changes I make are also released into # the public domain. # - Peter Pentchev use v5.010; use strict; use warnings; use version; our $VERSION = version->declare("1.1.1"); use Getopt::Std; my $have_ansi_color; BEGIN { eval { require Term::ANSIColor; $have_ansi_color = 1; }; } my %prevstat; sub usage($) { my ($err) = @_; my $s = <{kb}{in}, $dev->{kb}{out}, $dev->{packets}{in}, $dev->{packets}{out}); } sub update_traffic($ $ $) { my ($dev, $odev, $period) = @_; for my $what (keys %{$dev}) { for my $dir (keys %{$dev->{$what}}) { 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($ $) { my ($total, $dev) = @_; for my $what (keys %{$total}) { for my $dir (qw(in out)) { $total->{$what}{$dir} += $dev->{$what}{$dir}; } } } #Inter-| Receive | Transmit # face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed # lo: 2356 32 0 0 0 0 0 0 2356 32 0 0 0 0 0 0 # eth0: 1217210 9400 0 0 0 8 0 11 1207648 8019 0 0 0 0 0 0 # eth1: 2039952 21982 6 0 0 6 0 0 47000710 34813 0 0 0 821 0 0 sub convert($ $ $) { my ($iface, $period, $total) = @_; open my $in, '/proc/net/dev' or die "Can't open /proc/net/dev: $!\n"; <$in>; <$in>; my %devs; while (my $l = <$in>) { chop $l; my ($dev, $rest) = split /:/, $l; $dev =~ s/\s//g; next if defined $iface && $dev ne $iface; $rest =~ s/^\s+//; my @devarr = split /\s+/, $rest; $devs{$dev} = { name => $dev, data => { bytes => { in => $devarr[0], out => $devarr[8], }, packets => { in => $devarr[1], out => $devarr[9], }, }, }; } close($in); %{$total} = (kb => {}, packets => {}); delete $devs{lo}; foreach my $dev (values %devs) { $prevstat{$dev->{name}} //= { name => $dev->{name} }; update_traffic $dev->{data}, $prevstat{$dev->{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; } MAIN: { $| = 1; my $period = 10; my %opts; getopts 'Cc:hi:Mn:tV', \%opts or usage(1); version if $opts{V}; usage 0 if $opts{h}; exit 0 if $opts{V} || $opts{h}; my $addtime = $opts{t}; if (defined $opts{n}) { if ($opts{n} !~ /^([1-9]\d*)$/) { warn "The period must be a positive integer\n"; usage 1; } $period = $1; } my $count; if (defined $opts{c}) { if ($opts{c} !~ /^([1-9]\d*)$/) { warn "The count must be a positive integer\n"; usage 1; } $count = $1; } my $iface = $opts{i}; # also works if it isn't defined my $use_color; if ($opts{C}) { if ($opts{M}) { die "The -C and -M options are mutually exclusive\n"; } elsif (!$have_ansi_color) { die "The Term::ANSIColor Perl module is not available\n"; } $use_color = 1; } elsif ($opts{M}) { $use_color = 0; } else { $use_color = $have_ansi_color && -t \*STDOUT; } my %c = map { $_ => $use_color? Term::ANSIColor::color($_): '' } qw(yellow reset); my $total = {}; convert $iface, 1, $total; sleep 1; while(1) { my $devs = convert $iface, $period, $total; print time.' ' if $addtime; if (scalar keys %{$devs} > 1) { say $c{yellow}.format_line('total', $total).$c{reset}; } foreach my $dev (sort { $a->{name} cmp $b->{name} } values %{$devs}) { say format_line($dev->{name}, $dev->{data}); } if (defined $count) { $count--; exit 0 if $count < 1; } sleep $period; } }