#!/usr/bin/env perl
# https://gitlab.com/alasdairkeyes/nagios-plugin-check_wireguard/-/tree/master

use strict;
use warnings;
use Monitoring::Plugin::Getopt;
use Monitoring::Plugin::Functions;

my $VERSION = '0.3.0';

my $default_binary = '/usr/bin/wg';
my $default_interfaces_expected = `ls -l /etc/wireguard/wg*.conf | wc -l`;
my $default_seconds_connected = 300;

# Get Opts
my $go = Monitoring::Plugin::Getopt->new(
    usage       => "Usage: %s [ -b $default_binary ] [ -i $default_interfaces_expected ] [ -s $default_seconds_connected ]",
    version     => $VERSION,
    url         => 'https://gitlab.com/alasdairkeyes/nagios-plugin-check_wireguard',
    blurb       => "Check wireguard server.",
);

$go->arg(
    spec        => 'binary|b=s',
    help        => "The full path to the wg executable. (default: $default_binary)",
    required    => 0,
    default     => $default_binary,
);

$go->arg(
    spec        => 'interfaces|i=i',
    help        => "The number of interfaces expected to be found. (default: $default_interfaces_expected)",
    required    => 1,
    default     => $default_interfaces_expected,
);

$go->arg(
    spec        => 'seconds_connected|s=i',
    help        => "A peer is considered connected if a handshake has been set within this many seconds. If set to 0 it will always be considered connected if at least one handshake has been sent. (default: $default_seconds_connected)",
    required    => 1,
    default     => $default_seconds_connected,
);

$go->getopts;
my $binary = $go->binary;
my $interfaces_expected = $go->interfaces;
my $seconds_connected = $go->seconds_connected;

# Check binary is usable
plugin_exit ( CRITICAL, "Path $binary is not executable")
unless (-x $binary);

# run pre-script
my $prescript = "/etc/wireguard/ping.sh";
my $ping = `$prescript`
  if (-x $prescript);

# Execute
my $output = `$binary show`;

# Parse and process wg output
my $data = parse_output($output);
process_output($data);

sub parse_output {
    my $output = shift;
    my @lines = split(/\n/, $output);
    my $data = {};
    my $interface;
    foreach my $line (@lines) {
        if ($line =~ /^interface:\s+(.*)$/) {
            $interface = $1;
            $data->{ $interface }{ peers } = 0;
            $data->{ $interface }{ connected } = 0;
            next;
        }
            # Increment peer and connected counters
        if ($line =~ /^peer:\s+(.*)$/) {
            $data->{ $interface }{ peers }++;
        } elsif ($line =~ /^\s+latest handshake:\s+(.*)$/) {
            $data->{ $interface }{ connected }++;
        }
    }
    return $data;
}

sub seconds_since_handshake {
    my $handshake_time_value = shift;

    my $seconds = 0;
    foreach my $time_section (split (/,\s?/, $handshake_time_value)) {
        if ($time_section =~ /^(\d+)\s+(\w+)\b/) {
            my $time_value = $1;
            my $time_denomination = $2;

                # Convert times into a single second value
            if ($time_denomination =~ /^years?/) {
                $seconds += $time_value * 60 * 60 * 24 * 365;
            } elsif ($time_denomination =~ /^days?/) {
                $seconds += $time_value * 60 * 60 * 24;
            } elsif ($time_denomination =~ /^hours?/) {
                $seconds += $time_value * 60 * 60;
            } elsif ($time_denomination =~ /^minutes?/) {
                $seconds += $time_value * 60;
            } elsif ($time_denomination =~ /^seconds?/) {
                $seconds += $time_value;
            }
        } else {
            die "Unknown handshake value";
        }
    }
    return $seconds;
}

sub process_output {
    my $data = shift;

    my $number_of_interfaces = scalar(keys(%$data));

    my $result_string = "Interfaces: Online:$number_of_interfaces Expected:".int($interfaces_expected)." - ";

    my $number_of_clients = 0;
    my $clients_expected = 0;

    foreach my $interface (sort(keys(%$data))) {
        $result_string .= " $interface:$data->{ $interface }{ connected }/$data->{ $interface }{ peers }";
        if ($interface =~ /wg0/) {
            $number_of_clients += $data->{ $interface }{ connected };
            $clients_expected += $data->{ $interface }{ peers };
        }
    }

    my $status = OK;
    $status = WARNING  
        if ($number_of_clients != $clients_expected);
    $status = CRITICAL
        if ($number_of_interfaces != $interfaces_expected);

    plugin_exit ( $status, $result_string);
}
