#!/usr/bin/perl -w
use strict;
use DBI;
use Symbol;
use IPC::Open3;
use threads stack_size => 16384;

my $args = parseArgs();

my $root_my_cnf = "/root/.my.cnf";
if (-e $root_my_cnf && -r $root_my_cnf) {
    $$args{cmd_extra} = "--defaults-extra-file=$root_my_cnf";
}

my $msg = ""; my $state = "OK";

if ($$args{ping}) {
    my @cmd = ();
    push @cmd, "mysqladmin";
    push @cmd, $$args{cmd_extra} if defined $$args{cmd_extra};
    push @cmd, $$args{mysqlargs} if defined $$args{mysqlargs};
    push @cmd, "ping";
    my ($rval, $output) = safeExec(@cmd);

    if ($output =~ m#mysqld is alive#i) {
        $msg .= " - mysqld is running";
    } else {
        $msg .= " - mysqld is NOT running";
        $state = "CRITICAL";
    }
}

if ($$args{uptime}) {
    my @cmd = ();
    push @cmd, "mysql";
    push @cmd, $$args{cmd_extra} if defined $$args{cmd_extra};
    push @cmd, $$args{mysqlargs} if defined $$args{mysqlargs};
    push @cmd, ("-e", "SHOW STATUS WHERE Variable_name='Uptime';");
    my ($rval, $output) = safeExec(@cmd);
    if ($output =~ m#Uptime\s+(\d+)#ms) {
        my $uptime = $1;
        if ($uptime > 600) {
            $msg .= " - uptime $uptime"
        } else {
            $msg .= " - mysql restarted with uptime $uptime";
            $state = "WARNING";
        }
    }
}


if ($$args{slavesql} or $$args{slaveio} or $$args{slavebehind}) {
    my @cmd = ();
    push @cmd, "mysql";
    push @cmd, $$args{cmd_extra} if defined $$args{cmd_extra};
    push @cmd, $$args{mysqlargs} if defined $$args{mysqlargs};
    push @cmd, ("-e", "SHOW SLAVE STATUS\\G");
    my ($rval, $output) = safeExec(@cmd);

    my ($io_running)   = $output =~ m#Slave_IO_Running:\s+(Yes|No)#ms;   $io_running ||= 'No'; 
    my ($sql_running)  = $output =~ m#Slave_SQL_Running:\s+(Yes|No)#ms;  $sql_running ||= 'No';
    my ($slave_behind) = $output =~ m#Seconds_Behind_Master:\s+(\d+)#ms; 

    if (defined $$args{slavesql} and $$args{slavesql} == 1) {
        if ($sql_running ne 'Yes') {
            $msg .= " - slave SQL not running";
            $state = "CRITICAL";
        } else {
            $msg .= " - slave SQL running";
        }
    }
    
    if (defined $$args{slaveio} and $$args{slaveio} == 1) {
        if ($io_running ne 'Yes') {
            $msg .= " - slave IO not running";
            $state = "CRITICAL";
        } else {
            $msg .= " - slave IO running";
        }
    }

    if (defined $$args{slavebehind} and $$args{slavebehind} == 1) {
        if (defined $slave_behind and $slave_behind > $$args{slavebehindmax}) {
            $msg .= " - slave lags behind outside margin ($slave_behind > $$args{slavebehindmax})";
            $state = "CRITICAL";
        } elsif (not defined $slave_behind) {
            $msg .= " - slave seconds behind did not parse";
            $state = "WARNING";
        } else {
            $msg .= " - slave lag is within margins ($slave_behind)";
        }
    }
}

print "${state}${msg}\n";
exit 0 if $state eq "OK";
exit 1 if $state eq "WARNING";
exit 2;

#
##
###
####
#
#
#

sub safeExec {
    my (@command) = @_;
    my $timeout = 10;

    my ($err, $in, $out) = do{ local *FH; \*FH };
    my $pid = open3($in, $out, $err, @command) or die "@command : $!/$@/$^E";

    my ($Tout) = threads->create(
        sub {
            my ($Terr) = threads->create(
                sub { 
                    my @in = <$err>; return join("\n", @in);
                }
            );
            my @in = <$out>;
            return join("\n", @in), $Terr->join;
        }
    );

    sleep 1 while checkPid($pid) and --$timeout;
    kill 3, $pid unless $timeout;
    my @out = $Tout->join;
    foreach my $elem (@out) {
             $out .= $elem;
    }
    return (0, $out);
}


sub checkPid{
    scalar(`tasklist /nh /fi "pid eq $_[0]"` !~ m[INFO]sm);
}


sub parseArgs {
    my $ret = {};

    while (@ARGV) {
        my $arg = shift @ARGV;

        if ($arg eq "-a") {
            $$ret{mysqlargs} = shift @ARGV;

        } elsif ($arg eq "-u") {
            $$ret{uptime} = 1;

        } elsif ($arg eq "-p") {
            $$ret{ping} = 1;

        } elsif ($arg eq "-r") {
            $$ret{slavesql} = 1;

        } elsif ($arg eq "-i") {
            $$ret{slaveio} = 1;

        } elsif ($arg eq "-b") {
            $$ret{slavebehind} = 1;
            $$ret{slavebehindmax} = shift @ARGV;
            die "Argument to -b is not numeric.\n" if (not defined $$ret{slavebehindmax} or $$ret{slavebehindmax} !~ m#^\d+$#);

        } elsif ($arg eq "-h") {
            print "Usage: $0 [-u] [-p] [-r] [-i] [-b seconds] [-h]\n";
            print "          -u checks uptime of mysql to be higher then 10 minutes\n";
            print "          -p ping the SQL Server process to see if its alive\n";
            print "          -r checks for the SLAVE_RUNNING option to be YES\n";
            print "          -i checks for the SLAVE_IO option to be YES\n";
            print "          -b checks for the SECONDS_BEHIND_MASTER not to be higher\n";
            print "             then given number of seconds\n";
            print "          -a mysql arguments (like -u root -p secret)\n";
            print "          -h shows this help\n";
            exit 0;

        } else {
            die "Unknown argument '$arg' - try -h\n";
        }
    }

    return $ret;
}


__END__

