#!perl

use strict;
use warnings;
our $VERSION = '0.005'; # VERSION

use Env '@PATH';
push @PATH, qw(/sbin /usr/sbin /usr/local/sbin);
use Getopt::Std;
getopts 'ev', \our %opts;

sub run { print "\$ $_[0]\n" if $opts{e}; `$_[0]` }

if ($opts{v}) { print "App::lsnic v$VERSION\n"; exit 0 }
die "ERROR: lsnic must be run as super user.\n" if $<;

my ($i, @hw) = (-1);
my @moreinfo = run 'lshw -c network -numeric';
my ($indent) = $moreinfo[0] =~ /^(\s*)/;
for (@moreinfo) {
    /^$indent\S*\s(\w+)$/ and $hw[++$i]{STATE} = "[$1]" or
    /^$indent\S*$/        and ++$i or
    /^\s*(.*?):\s*(.*)$/  and $hw[$i]{$1} = $2;
}

sub lspci_serial {
    my $addr = shift or return;
    $addr =~ s/^pci@//;
    my $lspci = run "lspci -v -s $addr";
    if (my ($serial) = ($lspci =~ /Device Serial Number (.*)/)) {
        $serial =~ s/(.{8})-ff-ff-(.{8})/$1-$2/;
        return $serial;
    }
    return;
}

sub max { my $x = shift // 0; my $y = shift // 0; $x > $y ? $x : $y }
my @nics = ([qw(Bus Interface Serial Driver Description)]);
my @offsets = map { length } @{$nics[0]};
sub add {
    my ($nic, $i, $val) = @_;
    $nic->[$i] = $val;
    $offsets[$i] = max($offsets[$i], length $nic->[-1]);
}
for (@hw) {
    $_->{STATE} = $_->{STATE} // '';

    my ($i, @nic) = (0, ('' x 5));
    add(\@nic, $i++, $_->{'bus info'});
    add(\@nic, $i++, $_->{'logical name'});
    add(\@nic, $i++, $_->{serial} // lspci_serial($nic[0]));

    if (not defined $_->{configuration}) {
        add(\@nic, $i++, $_->{STATE})
    } else {
        my ($driver) = $_->{configuration} =~ /driver=(\S+)/;
        add(\@nic, $i++, $driver // '')
    }

    my @descr = $_->{product};
    push @descr, $_->{STATE} if $_->{STATE} ne '[UNCLAIMED]';
    @descr = grep defined, @descr;
    add(\@nic, $i++, "@descr");

    push @nics, \@nic;
}
for my $nic (@nics) {
    my $i = 0;
    for (@offsets) {
        printf "%-${_}s ", ($nic->[$i++] // '')
    }
    print "\n";
}

=pod

=encoding utf8

=head1 NAME

lsnic - Display table of network controllers

=head1 VERSION

version 0.005

=head1 SYNOPSIS

    # lsnic
    Bus              Interface Serial            Driver   Description
    pci@0000:26:00.0 enp38s0   10:7b:44:90:00:00 igb      I211 Gigabit Network Connection [8086:1539]
    pci@0000:29:00.0           68-05-ca-61-00-00 vfio-pci I210 Gigabit Network Connection [8086:1533]
                     br0       42:6c:1c:88:00:00 bridge
                     br1       10:7b:44:90:00:00 bridge

=head1 INSTALLATION

    # cpan App::lsnic

=head1 DESCRIPTION

L<This script|http://github.com/a3f/.dotfiles/blob/master/bin/lsnic> packaged as CPAN module.
This script reformats C<lshw -c network -numeric> output in the tabular format above.
If the driver doesn't report a MAC address, it's extracted from the serial number read by C<lspci(1)>
This script requires super user priviliges to work.

=head1 GIT REPOSITORY

L<http://github.com/athreef/App-lsnic>

=head1 SEE ALSO

L<http://github.com/a3f/.dotfiles>

=head1 AUTHOR

Ahmad Fatoum C<< <athreef@cpan.org> >>, L<http://a3f.at>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2017 Ahmad Fatoum

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
