#! /usr/bin/perl -w
#
# (c) 2004 by Björn Teipel <me@visit-bjoern.de>
# 
# This script checks existing software RAID for failures
# collecting information from /proc/mdstat
#
#

use strict;
use Getopt::Long;
Getopt::Long::Configure('bundling', 'no_ignore_case');

#
# Variables
###
my $PROGNAME = "UNKNOWN";
my $PROGPATH = "UNKNOWN";
my $REVISION = "1.2";
my %ERRORS=('DEPENDENT'=>4,'UNKNOWN'=>3,'OK'=>0,'WARNING'=>1,'CRITICAL'=>2);

my @modes;
my @mdstat;
my $state="OK";

if ($0 =~ m/^(.*?)[\/\\]([^\/\\]+)$/) {
        $PROGPATH = $1;
        $PROGNAME = $2;
};


# Get options from shell first
GetOptions
        ("V|version"    => \&version,
        "h|help"       => \&print_help);




#
# Here we go
###

# make array mdstat first
if (open(FH,"/proc/mdstat")) {
	while (<FH>) {
		push @mdstat,$_;
	};
	close (FH);

	# now evaluate data
	for (my $i=0; $i<=$#mdstat; $i++) {

		# get compilied in modes for software RAID
		if ($mdstat[$i] =~ m/^Personalities :/) {
			my @tmp = split(/\s+/,$mdstat[$i]);
			foreach my $l (@tmp) {
				if ($l =~ m/^\[(.[^]]*)\]$/i) {
					push @modes,$1;
				};
			};
			if (scalar @modes > 0) {
				print "Loaded RAID drivers [@modes]";
			} else {
				print "RAID support activated, but no personalities found !";
				exit $ERRORS{'WARNING'};
			};
		};



		# now get status of all installed raids and set global status
		if ($mdstat[$i] =~ m/^md[0-99]/i) {
			my $string;
			my %dsk; my $c = 0;
			my @md = split(/\s+/,$mdstat[$i]);
			push @md, split(/\s+/,$mdstat[$i+1]);

			$string = "<br>$md[0] [$md[2],$md[3]";
			if ($md[2] ne "active") { $state = "CRITICAL";};

			# get drives bound to md device
			for (my $i=4;$i<=$#md;$i++) {
				if ($md[$i] =~ m/^(.[^[ ]*)\[(\d)\](.*)/) {
					$dsk{'name'}[$c]=$1;
					$dsk{'num'}[$c]=$2;
					$c++;
				};
			};

			# sort drives bound to md device
			# with bubble-sort algorithms
			for (my $i=$c-1;$i>=0;$i--) {
				for (my $j=1;$j<=$i;$j++) {
					if ($dsk{'num'}[$j-1] gt $dsk{'num'}[$j]) {
						my $tmpnum = $dsk{'num'}[$j-1];
						my $tmpname = $dsk{'name'}[$j-1];

						$dsk{'num'}[$j-1] = $dsk{'num'}[$j];
						$dsk{'name'}[$j-1] = $dsk{'name'}[$j];
						$dsk{'num'}[$j] = $tmpnum;
						$dsk{'name'}[$j] = $tmpname;
					};
				};
			};

			# get status of drives
			for (my $i=0;$i<$c;$i++) {
				my $str = substr $md[-1],$i+1,1;
				if ($str eq "U") {
					$dsk{'status'}[$i]="UP";
				} else  {
					$dsk{'status'}[$i]="<b>DOWN</b>";
					$state = "CRITICAL";
				};
			};

			for (my $i=0; $i<$c;$i++) {
				$string .= ",$dsk{'name'}[$i]($dsk{'status'}[$i])";
			};
			$string .= "]";
			print $string;
		};


		# get unused devices outside RAID
		if ($mdstat[$i] =~ m/^unused devices:/) {
			my @tmp = split(/\s+/,$mdstat[$i]);
			my @tmp1;
			foreach my $l (@tmp) {
				if ($l !~ m/unused|devices:/i) {
					push @tmp1,$l;
				};
			};
			#print "<br>Unused devices [@tmp1]";
		};
	};

} else {
	print "could not open /proc/mdstat, RAID support not activated ?\n";
	exit $ERRORS{'WARNING'};
};



exit $ERRORS{$state};


#
# Program END, sub functions follows
###

sub print_help() {
        print "$PROGNAME Revision: $REVISION\n";
        print "\n";
        print "Check health of all running software RAIDs\n ";
	print "--- Note : This is only available for Linux ---";
        print "\n";
        print_usage();
        print "\n";
        print "-h, --help\n";
        print "   Print detailed help\n";
        print "-V, --version\n";
        print "   Print version numbers and license information\n";
        exit $ERRORS{'OK'};
};

sub print_usage () {
        print "$PROGNAME\n";
        print "$PROGNAME [-h | --help]\n";
        print "$PROGNAME [-V | --version]\n";
        exit $ERRORS{'OK'};
};

sub version () {
        print "$PROGNAME Revision: $REVISION\n";
        exit $ERRORS{'OK'};
};
