The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
# See copyright, etc in below POD section.
######################################################################

package Schedule::Load::Reporter::Network;
use Schedule::Load qw (:_utils);
use Time::HiRes qw (gettimeofday);
use IO::File;
use strict;
use Carp;

our $Debug;

######################################################################
#### Configuration Section

our $_Proc_Filename = "/proc/net/dev";

######################################################################
#### Methods

sub new {
    my $class = shift;
    my $self = {_stats => {},
		skip_device_regexp => qr/^lo$/,	# Skip loopback device
		enabled => (-e $_Proc_Filename),
		@_};

    return bless $self, $class;
}

sub stats { return $_[0]->{_stats}; }

sub poll {
    my $self = shift;
    my $now_sec = shift; my $now_usec = shift;
    if (!$now_sec) { ($now_sec, $now_usec) = gettimeofday(); }
    return if !$self->{enabled};

    my @stats = $self->_net_raw_stats();

    if (my $last = $self->{_net_last_stats}) {
	my $delt = ($now_sec - $self->{_net_last_sec})
	    + ($now_usec - $self->{_net_last_usec})*1e-6;
	$self->{_stats}{network_rx_bytes}   = _diff($stats[0], $last->[0],$delt);
	$self->{_stats}{network_rx_packets} = _diff($stats[1], $last->[1],$delt);
	$self->{_stats}{network_tx_bytes}   = _diff($stats[2], $last->[2],$delt);
	$self->{_stats}{network_tx_packets} = _diff($stats[3], $last->[3],$delt);
    }
    $self->{_net_last_stats} = \@stats;
    $self->{_net_last_sec} = $now_sec;
    $self->{_net_last_usec} = $now_usec;
}

sub _net_raw_stats {
    my $self = shift;
    my $fh = IO::File->new("<$_Proc_Filename");
    if (!$fh) {
	#warn "%Warning: $! $_Proc_Filename," if $Debug;
	return undef;
    }

    my @data;
    while (defined(my $line = $fh->getline)) {
	if ($line =~ /:/) {
	    next if $line =~ /^\s+lo:/;  # Ignore loopback
	    $line =~ s/^ +//;
	    my @linedata = split(/[ \t:]+/,$line);
	    next if $linedata[0] =~ /$self->{skip_device_regexp}/;

	    $data[0] += $linedata[1];  # bytes rx
	    $data[1] += $linedata[2];  # packets rx
	    $data[2] += $linedata[9];  # bytes tx
	    $data[3] += $linedata[10];  # packets tx
	}
    }
    $fh->close();
    #print "_net_raw_stats ",join('  ',@data),"\n" if $Debug;
    return @data;
}

#######################################################################

sub _diff {
    my $new = shift;
    my $old = shift;
    my $delt = shift;
    # Note statistics CAN WRAP!
    return undef if !defined $new;
    if ($old > $new) { $new += 4*1024*1024*1024; }
    return ($new - $old)/$delt;
}

######################################################################
#### Package return
1;
__END__

=pod

=head1 NAME

Schedule::Load::Reporter::Network - slreportd network data collector

=head1 SYNOPSIS

  use Schedule::Load::Reporter::Network;

  my $n = new Schedule::Load::Reporter::Network;
  $n->poll;
  print Dumper($n->stats);

=head1 DESCRIPTION

L<Schedule::Load::Reporter::Network> is a plugin for slreportd that collects
network statistics from Linux 2.16 machines.

=over 4

=item new

Creates a new report object.

=item poll ($now_secs, $now_usecs)

Collects statistics, and scales by the time since the last poll.  Pass in
the current time (this avoids multiple syscalls when there's many plugins).

=item stats

Return an array reference with the statistics.

=back

=head1 DISTRIBUTION

The latest version is available from CPAN and from L<http://www.veripool.org/>.

Copyright 1998-2011 by Wilson Snyder.  This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License Version 2.0.

=head1 AUTHORS

Wilson Snyder <wsnyder@wsnyder.org>

=head1 SEE ALSO

L<Schedule::Load>, L<slreportd>

=cut