#!@@BASH@@
# -*- sh -*-

set -e

: << =cut

=head1 NAME

if_ - Wildcard-plugin to monitor traffic and errors on network
interfaces.

=head1 CONFIGURATION

This plugin does not normally require configuration.

The plugin may need to run as root to determine the maximum speed of
the network interface.  This is configured like this:

  [if_*]
      user root

If the interface speed cannot be determined automatically by this
plugin, you may manually configure the speed using the "speed"
environment variable, in megabits per second, like this:

  [if_*]
      env.speed 1000

To set warning and critical levels do like this:

  [if_*]
      env.warning 10000000

or

  [if_*]
      env.if_0_warning 10000000

This is a wildcard plugin. To monitor an interface, link
if_<interface> to this file. For example,

  ln -s /usr/share/munin/plugins/if_ \
        /etc/munin/plugins/if_eth0

will monitor eth0.

Most devices found in /proc/net/dev can be monitored. Examples include
ipsec*, eth*, irda*, and lo. Please note that aliases cannot be
monitored with this plugin.

=head1 AUTHOR

Original author unknown

Copyright (C) 2011 Diego Elio Pettenò <flameeyes@flameeyes.eu>

=head1 LICENSE

GPLv2

=head1 MAGIC MARKERS

 #%# family=auto
 #%# capabilities=autoconf suggest

=cut

. "$MUNIN_LIBDIR/plugins/plugin.sh"

INTERFACE=${0##*if_}
speed=${speed:-}

# Who whould have thought it's so much work to determine the
# maximum speed of a network interface.  Buckle up!
findspeed_mbps() {
    # Do not use interface name to guess technology.  Many many
    # wifi drivers use "eth*" names.
    IWLIST=$(type -p iwlist)
    if [[ -x "$IWLIST" ]]; then
        SPEED=$("$IWLIST" "$INTERFACE" rate 2>&1 |
            awk 'BEGIN { RATE="U" }
                       { if ($2 == "Mb/s") RATE=$1; }
                 END   { print RATE; }')

        if [[ "$SPEED" != "U" ]]; then
            echo "$SPEED"
            return
        fi
    fi

    # sysfs can report the speed if the driver supports it (but it
    # doesn't work as well for wireless cards, thus why we check for
    # iwlist first)
    if [[ -r "/sys/class/net/$INTERFACE/speed" ]]; then
            SPEED=$(cat "/sys/class/net/$INTERFACE/speed" 2>/dev/null)
            if [[ "$SPEED" -gt 0 ]]; then
                echo "$SPEED"
                return
            fi
    fi

    ETHTOOL=$(type -p ethtool)
    if [[ -x "$ETHTOOL" ]]; then
        SPEED=$("$ETHTOOL" "$INTERFACE" 2>&1 |
                 awk '/Speed:/ { gsub(/[^0-9]*/,"",$2); print $2; }')

        if [[ $SPEED == [0-9]* ]]; then
            echo "$SPEED"
            return
        fi
    fi

    MIITOOL=$(type -p mii-tool)
    if [[ -x "$MIITOOL" ]]; then
        case $("$MIITOOL" "$INTERFACE" 2>&1) in
            *1000base*) echo 1000; return ;;
            *100base*)  echo 100; return ;;
            *10base*)   echo 10; return ;;
        esac
    fi

    echo U
}

findspeed() {
    if [[ "$speed" ]]; then
        SPEED=$speed
    else
        SPEED=$(findspeed_mbps)
    fi

    if [[ -z "$SPEED" ]] || [[ "$SPEED" == "U" ]]; then
        printf "up.info Traffic of the %s interface. Unable to determine interface speed." "$INTERFACE"
        if [[ $EUID -ne 0 ]]; then
                echo " Please run the plugin as root."
        else
                echo " Please install ethtool, wireless-tools, mii-tool or whatever is appropriate for the interface."
        fi

        return
    fi

    BPS=$(( SPEED * 1000 * 1000 ))

    cat <<EOF
up.max $BPS
up.info Traffic of the $INTERFACE interface. Maximum speed is $SPEED Mb/s.
down.max $BPS
EOF

    return
}


case $1 in
    autoconf)
        if [[ -r /proc/net/dev ]]; then
            echo yes
            exit 0
        else
            echo "no (/proc/net/dev not found)"
            exit 0
        fi
        ;;
    suggest)
        if [[ -r /proc/net/dev ]]; then
            sed -rne '/^[[:space:]]*(lo|gre[[:digit:]]|sit[[:digit:]]+|[a-z0-9]+\.[0-9]+):/d;s,^[[:space:]]*([^:]+):.*,\1,p' /proc/net/dev
        fi
        exit 0
        ;;
    config)

        echo "graph_order down up"
        echo "graph_title $INTERFACE traffic"
        echo 'graph_args --base 1000'
        # shellcheck disable=SC2016
        echo 'graph_vlabel bits in (-) / out (+) per ${graph_period}'
        echo 'graph_category network'
        echo "graph_info This graph shows the traffic of the $INTERFACE network interface. Please note that the traffic is shown in bits per second, not bytes. IMPORTANT: On 32-bit systems the data source for this plugin uses 32-bit counters, which makes the plugin unreliable and unsuitable for most 100-Mb/s (or faster) interfaces, where traffic is expected to exceed 50 Mb/s over a 5 minute period.  This means that this plugin is unsuitable for most 32-bit production environments. To avoid this problem, use the ip_ plugin instead.  There should be no problems on 64-bit systems running 64-bit kernels."
        echo 'down.label received'
        echo 'down.type DERIVE'
        echo 'down.graph no'
        echo 'down.cdef down,8,*'
        echo 'down.min 0'
        echo 'up.label bps'
        echo 'up.type DERIVE'
        echo 'up.negative down'
        echo 'up.cdef up,8,*'
        echo 'up.min 0'
        print_warning down
        print_warning up
        print_critical down
        print_critical up

        findspeed

        exit 0
        ;;
esac

# Escape dots in the interface name (eg. vlans) before using it as a regex
if [[ -r /sys/class/net/$INTERFACE/statistics/rx_bytes ]]; then
    echo "down.value $(cat "/sys/class/net/$INTERFACE/statistics/rx_bytes")"
    echo "up.value $(cat "/sys/class/net/$INTERFACE/statistics/tx_bytes")"
else
    awk -v interface="$INTERFACE" \
        'BEGIN { gsub(/\./, "\\.", interface) }
        $1 ~ "^" interface ":" {
            split($0, a, /: */); $0 = a[2];
            print "down.value " $1 "\nup.value " $9}' \
        /proc/net/dev
fi
