#!/usr/bin/perl
#
# <Controller#> [Key]
#

use POSIX;
use Fcntl qw(:flock);
use File::stat;

if ($#ARGV lt 0 || $#ARGV gt 1 || $ARGV[0] !~ m/^\d+$/) {
	print STDERR "Usage: $0 <Controller#> [Key]\n";
	exit(2);
}

$ARGV[0] =~ m/^(\d+)$/;
my $CONTROLLER = $1;

# Untaint the path
$ENV{'PATH'} = '/bin:/usr/bin:/sbin:/usr/sbin:/usr/RaidMan';


my $FRESHEN=180;
my $EXPIRE=240;
my $CACHE="/dev/shm/arcconf.GETCONFIG.$CONTROLLER.AL";
my $ARCCONF="/usr/RaidMan/arcconf";

# If we don't have arcconf it's not going to work...
# TODO: Check $PATH?
if (! -x $ARCCONF) {
	print "ZBX_NOTSUPPORTED\n";
	exit(0);
}


# arcconf drops crap all the place in $PWD, so move to /tmp
chdir('/tmp');

sub age_of {
	$fh_or_path = shift;
	if ($st = stat($fh_or_path)) {
		if ($st->size > 16) {
			return time()-$st->mtime;
		}
	}
	return POSIX::INT_MAX;
}

sub regenerate {
	if (sysopen(NEWFH, "$CACHE.new", O_CREAT | O_RDWR, 0600)) {
		if (flock(NEWFH, LOCK_EX | LOCK_NB)) {
			# We hold the lock, but did we win the race?
			if (age_of($CACHE) > $FRESHEN) {
				# Fraid so....
				seek(NEWFH, 0, SEEK_SET);
				truncate(NEWFH, 0);
				select NEWFH; $| = 1;
				open(CPOUT, ">&STDOUT");
				open(STDOUT, ">&NEWFH");
				select STDOUT; $| =1;
				system("$ARCCONF GETCONFIG $CONTROLLER AL");
				open(STDOUT, ">&CPOUT");
				unlink('UcliEvt.log');
				unlink('arcerror.txt');
			}
			unlink($CACHE);
			rename("$CACHE.new", $CACHE);
			flock(NEWFH, LOCK_UN);
		} else {
			# Couldn't get the lock, wait on it, someone else is doing our work....
			if (flock(NEWFH, LOCK_SH)) {
				flock(NEWFH, LOCK_UN);
			}
		}
		close(NEWFH);
	}
}

open(FH, '<', $CACHE);
if (FH) {
	$age = age_of(FH);
	if ($age > $EXPIRE) {
		# Foreground update
		close(FH);
		regenerate();
		open(FH, '<', $CACHE);
	} elsif ($age > $FRESHEN) {	
		# Background update
		if (fork() == 0) {
			# TODO : need to detach?
			regenerate();
			exit();
		}
	}
} else {
	regenerate();
	open(FH, '<', $CACHE);
}


#  Recheck age here, is the data ok?
if (age_of(FH) > $EXPIRE) {
	print STDERR "Data is stale!\n";
	exit(1);
}

my %DATA = ();

while (<FH>) {
  if (/^(Controller|Logical drive|Physical (?:Drive|Device)) information$/i) {
    $seckey = $section = $1;
    $seckey =~ s/^([^\s]+)\s.*/\1/;
    $seckey = "$seckey";
    $sub = "";
  }
  if ($seckey eq "") {
    next;
  }
  if (/^\s*$seckey ([^\s]+) Information$/) {
    $sub = "$1 ";
    <FH>;
    next;
  }
  if (/^\s*-+\s*$/) {
    $sub = "";
  }
  if (/^\s*(.*?)\s*:\s*(.*?)\s*$/) {
    $key = $1; $val = $2;
	# Remove the section name from the key if present
    $key =~ s/^$section\s*//;
	# Remove any trailing '#' from the key
    $key =~ s/#$//;
	# Remove any '(....)' subscripts from the key
    $key =~ s/^(.+)\(.*\)$/\1/;
	# In this case the $section strip doesn't do it
    $key =~ s/ of logical drive$//;
	# Prepend the subkey (eg "Battery " for controller information
    $key = "$sub$key";
	# Reverse the sub/key ordering for "Version" information
    $key =~ s/^Version (.+)$/\1 Version/;
	# Trim the key of any whitespace
    $key =~ s/(^\s+|\s+$)//g;
	# For some vague consistency
    $key = ucfirst($key);
	# Normalise all sizes to bytes
    $val =~ s/^(\d+) MB$/$1*1024*1024/e;
    $val =~ s/^(\d+) KB$/$1*1024/e;
	# Strip the units from percentages
    $val =~ s/^(\d+) (percent|%)$/$1/;
	# Normalise runtimes to seconds
    $val =~ s/^(\d+)\s+days,\s+(\d+)\s+hours,\s+(\d+)\s+minutes$/($1*86400)+($2*3600)+($3*60)/e;
    $DATA{"$seckey.$idx$key"} = $val;
  } elsif (/(#|\s)(\d+)$/) {
    $idx="$2.";
  }
}

if ($ARGV[1]) {
	my $reqkey = $ARGV[1];
	# Case insensitive lookup
	for my $key ( grep { uc($_) eq uc($ARGV[1]) } keys %DATA ) {
		print $DATA{$key} . "\n";
		exit(0);
	}
	# Synthesized keys
	if ($reqkey =~ m/^(Physical|Logical)\.Count$/i) {
		my $type = $1;
		my %indexes;
		foreach $key ( grep { m/^$type\.\d+\./ } keys %DATA ) {
			$key =~ s/^($type\.\d+)\..*/\1/;
			$indexes{$key}++
		}
		my $count = keys %indexes;
		print  $count .  "\n";
		exit(0);
	#} elsif ($reqkey =~ m/^Logical.(\d+)\.mdstat$/) {
	#	my $idx = $1;
	#	if (exists $DATA{"Logical.$idx.Status"}) {
	#		printf("raid%s %s [%s]\n", $DATA{"Logical.$idx.RAID level"}, $DATA{"Logical.$idx.Status"}, '???');
	#		exit(0);
	#	}
	}
	# Keep zabbix-agent happy
	print "ZBX_NOTSUPPORTED\n";
} else {
	# No key requested show the user what they can have
	# TODO: Add synthesized keys
	for my $key (sort keys %DATA) {
		printf("%-40s\t%s\n", $key, $DATA{$key});
	}
}


