#!/usr/pkg/bin/perl
use strict;
use warnings;

$ENV{PATH} .= ':/usr/pkg/sbin';

my $err   = 0;
my $disks = `sysctl -n hw.disknames`;

for my $disk ( sort devicesort split( ' ', $disks ) ) {
    my $status;
    if ( $disk =~ /^raid/ ) {
        $status = raidstatus($disk);
    }
    elsif ( $disk =~ /^[sw]d/ ) {
        $status = smartstatus($disk);
    }
    elsif ( $disk =~ /^ld/ ) {
        $status = 'ok';
    }
    else {
        $status = '?';
        next;
    }
    my $ok = $status =~ s/^ok\s*//;

    my $wedges = wedges($disk);
    $status = "$wedges $status" if $wedges;
    $status = "** $status **" if !$ok;

    print "$disk: $status\n";
    $err |= !$ok;
}
zfsstatus();
exit $err;

sub devicesort {
    my @a = $a =~ /^(\w+)(\d+)$/;
    my @b = $b =~ /^(\w+)(\d+)$/;
    return (@a && @b)
        ? $a[0] cmp $b[0] || $a[1] <=> $b[1]
        : $a cmp $b;
}

sub raidstatus {
    my $disk = shift;
    my %status;
    open( RAIDCTL, "raidctl -s $disk|" ) || die "raidctl failed: $!";
    my $issue = '';
    while (<RAIDCTL>) {
        chomp;
        if (m#^\s{4}\s*+(?:/dev/)?(\S+): (\S.*)#) {
            push( @{ $status{$2} }, $1 );
        }
        if ( m#^(Parity status: (.*))# && $2 ne 'clean' ) {
            $issue .= " ($1)";
        }
        if ( m#^((Reconstruction|Parity Re-write|Copyback)(.*))#
            && $3 ne ' is 100% complete.' )
        {
            my $msg = $1;
            $msg =~ s/\.$//;
            $issue .= " ($msg)";
        }
    }
    foreach my $status ( sort keys %status ) {
        if ( $status ne 'optimal' ) {
            $issue .= ' '
              . join( ',', sort @{ $status{$status} } ) . ' '
              . uc("$status");
        }
    }
    return $issue ? $issue : 'ok';
}

sub smartstatus {
    my $disk = shift;
    my $status;
    my $capacity;
    my $temp;
    my $maxTemp;
    my $realloc;
    for ( my $loop = 0 ; $loop < 2 ; ++$loop ) {
        open( SMARTCTL, "smartctl -x /dev/r$disk|" )
          || die "smartctl failed: $!";
        while (<SMARTCTL>) {
            if (/SMART overall-health self-assessment test result: (.*)/) {
                $status = $1;
            }
            elsif (/User Capacity: .*\[(.*)\].*/) {
                $capacity = $1;
            }
            elsif (/SMART Health Status: (.*)/) {
                $status = $1;
            }
            elsif (/Current Temperature:\s+(\S.*) Celsius/) {
                $temp = $1;
            }
            elsif (m#Min/Max recommended Temperature:\s+\d+/(\S.*) Celsius#) {
                $maxTemp = $1;
            } elsif ( /Reallocated_Sector_Ct.*\s+(\d+)/ && $1 > 0 ) {
                $realloc = $1;
            }
        }
        close(SMARTCTL);
        last if defined $status && $status ne 'UNKNOWN!';
    }
    $status =
        !defined $status                       ? 'no status'
      : $status eq 'PASSED' || $status eq 'OK' ? 'ok'
      :                                          'smart health:' . $status;
    if ($capacity) {
        $capacity =~ s/\.0{1,2} / /;
        $status .= " {$capacity}";
    }
    if ( $temp && $maxTemp ) {
        $status = 'hot' if $temp > $maxTemp;
        $status .= " ($temp/${maxTemp}C)";
    }
    elsif ($temp) {
        $status .= " (${temp}C)";
    }
    if ($realloc) {
        $status .= " [$realloc]";
    }
    return $status;
}

sub wedges {
    my $disk = shift;
    my @wedges;
    open( DKCTL, "dkctl $disk listwedges|" ) || die "dkctl failed: $!";
    while (<DKCTL>) {
        push( @wedges, $1 ) if /\w+: (\S+),/;
    }
    close(SMARTCTL);
    return join(',', @wedges);
}

sub command_output {
    open( CMD, '-|', @_ ) || die "@_ failed: $!";
    my @output = <CMD>;
    return @output;
}

sub zfsstatus {
    my @list = command_output( 'zpool', 'list' );
    if ( @list == 1 && $list[0] eq "no pools available\n" ) {
        return;
    }
    print @list;

    my @status = command_output( 'zpool', 'status', '-x' );
    if ( @status != 1 || $status[0] ne "all pools are healthy\n" ) {
        print @status;
    }
}
