package Monitor::procs;

require 5.002;

use strict;

@Monitor::procs::ISA = qw(Monitor::Monitor);

my $pscomm_cached;

sub new
{
    my ($this, $pscomm) = @_;
    my  $class = ref($this) || $this;
    my  $self = {};

    bless($self, $class);

    unless( $pscomm ) {
	$pscomm = "ps cax";
    }

    $self->{"pscomm"} = $pscomm;
    my( @bla)  = ();
    $self->{"htable"} = \@bla;
    $self->{"domains"} = [];

    return($self);
}


sub guess_pscomm {
    my( $self ) = @_;
    my $nextbest;
    unless( $pscomm_cached ) {
      outer:
	foreach my $comm ("/bin/ps -e", "/bin/ps cax", "/usr/bin/ps -e", "/usr/bin/ps cax", "/usr/ucb/ps cax" ) {
	    open( "_PS", "$comm|" ) || next;
	    while( <_PS> ) {
		chomp;
		$nextbest = $comm;
		if( /^[\s\t]*1[\s\t].*[\s\t]init$/ ) {
		    $pscomm_cached = $comm;
		    last outer;
		}
	    }
	}
	unless( $pscomm_cached ) {
	    $pscomm_cached = $nextbest;
	}
	unless( $pscomm_cached ) {
	    $pscomm_cached = "ps cax";
	}
    }
    $self->set_pscomm( $pscomm_cached );
}



sub set_pscomm {
    my( $self, $pscomm ) = @_;

    $self->{"pscomm"} = $pscomm;
}


sub set_alternate {
    my( $self, $f ) = @_;

    if( $f && ($f =~ /^-o/) && ($self->{"pscomm"} =~ /-e/) ) {
	$self->{"pscomm"} .= " $f";
	$self->{"alternate"} = 2;
    }
    elsif( $f && ($self->{"pscomm"} =~ /-e/) ) {
	$self->{"pscomm"} .= "f";
	$self->{"alternate"} = 1;
    }
    else {
	$self->{"alternate"} = 0;
    }
}



sub add_check {
    my( $self, $comm, $item, $min, $max, $domain, $down ) = @_;
    my( $htable ) = $self->{"htable"};
    my $domains = $self->{"domains"};

    ($domain = ":procs") unless( $domain );
    ($item = $uxmon::hostname.".procs") unless( $item );
    $down = "red" unless $down;
    push( @$htable, [ $comm, $item, $min, $max, $domain, $down ] );
    push( @$domains, $domain ) unless( grep( $domain eq $_, @$domains ) );
}





sub check
{
    my ($self) = @_;
    my( $index, @table );
    my $domains = $self->{"domains"};
    my $comm;
    my $domain;

    foreach $domain (@$domains) {
	next unless( $domain =~ /^(.*):procs$/ );
	my $host = $1;
	($host = $uxmon::hostname) unless ($host);
	$index = 1;
	if( Platforms::iswin32() ) {
	    require Win32::Service;

	    my $list = {};
	    Win32::Service::GetServices($host, $list);
	    my( $long, $short );
	    while( ($long,$short) = each %$list ) {
		print "Win32 service on $host: $short ($long)\n" if( $BigSister::common::dl>7 );
		my $status = {};
		Win32::Service::GetStatus( $host, $short, $status );
		if( $status->{"CurrentState"} & 4 ) {
		    $self->set_variable( $host, "procs.hrSWRunName.".$index, $short );
		    push( @table, "procs.hrSWRunName.$index" );
		    $index++;
		    if( $short ne $long ) {
			$self->set_variable( $host, "procs.hrSWRunName.".$index, $long );
			push( @table, "procs.hrSWRunName.$index" );
			$index++;
		    }
		}
	    }
	}
	else {
	    my $text = $self->timeout_execute( $self->execute_on( $host, $self->{"pscomm"} ) );
	    $self->set_variable( $host, "procs.pscomm", $self->{"pscomm"} );
	    foreach (split( /[\r\n]+/, $text ) ) {
		chomp;
		$comm = $_;
		if( $self->{"alternate"} == 2 ) {
		    # Tru64 has a truly pathological ps.
		    # When invoked with BSD-style options, the result is not
		    # parseable because the FLAGS column may show up as 0 to 3
		    # "columns" to split(); with USG-style options it won't
		    # show scripts the way we want them to show up.  We work
		    # around this by providing a way to use ps -eo.  -ocomm
		    # works nicely for Tru64.
		    $self->set_variable( $host, "procs.hrSWRunName.".$index, $comm ); 
		}
		elsif( $self->{"alternate"} ) {
		    print "procs: read <$comm>\n" if $BigSister::common::dl > 6;
		    # whitespace regexp + leading whitespace = null first field
		    # splitting on ' ' produces awk-like field splitting
		    my($field1,$field2,$field3,$field4,$field5,$field6,$field7,$field8,$field9) = split( ' ', $comm );
		    my($flag)=0;
		    my($process)=$field9;
		    # On Hp the process name can be in the 8 or 9 field depending if the process
		    # has been start running within the current day or not.
		    # The cause of that is that the field number 5 can be a time (ex:12:00) or a date
		    # (ex: Mar 12   - Two space separated fields 5 and 6)
		    # On linux a process with more than one day will be Mar12 in field 5 only. (Eduardo)
		    print "procs: f5 <$field5> f8 <$field8> f9 <$field9>\n" if $BigSister::common::dl > 5;
		    $process=$field8 if ($field5 =~ /(\d+)/) ;
		    print "procs: process <$process>\n" if $BigSister::common::dl > 4;
		    $process =~ s/^[\.\.\/]*|^\.+|\S*\.\S*\/|\S*\///g;
		    $process =~ s/\[|\]//g;
		    $self->set_variable( $host, "procs.hrSWRunName.".$index, $process ); 
		}
		else {
		    $comm =~ s/^.*[\s]+([^\s]+)[\s]*$/$1/;
		    $comm =~ s#.*/##;
		    $self->set_variable( $host, "procs.hrSWRunName.".$index, $comm );
		}
		push( @table, "procs.hrSWRunName.$index" );
		$index++;
	    }
	}
	$self->set_variable( $host, "procs.hrSWRunName", \@table );
	$self->set_variable( $host, "procs.processes", $index );
    }
}


sub late_check
{
    my ($self) = @_;
    my( $htable ) = $self->{"htable"};
    my( $comm, $item, $min, $max, $down );
    my( $i, $n );
    my( %procs );
    my( $table, $domain );
    my $domains = $self->{"domains"};

    foreach $domain (@$domains) {
	print "  procs:late_check(): doing domain $domain\n" if( $BigSister::common::dl>6 );
	my( $host, $sdomain ) = split( ":", $domain );
	( $host = $uxmon::hostname ) unless( $host );
	my $vars = $uxmon::monitor_variables{$host};
	next unless( $vars );
	my $table = $vars->{$sdomain.".hrSWRunName"};
	next unless( $table );
	my $domprocs = $procs{$domain} = {};
	my $i;
	foreach $i (@$table) {
	    $comm = $vars->{$i};
	    print "  procs:late_check(): adding process $comm ($i)\n" if( $BigSister::common::dl>7 );
	    $comm =~ s/ .*// if( $sdomain =~ /snmp/ );
	    if( defined $domprocs->{$comm} ) {
		$domprocs->{$comm}++;
	    }
	    else {
		$domprocs->{$comm} = 1;
	    }
	}
    }
	    

    for( $i=0; $i<=$#$htable; $i++ ) {
	($comm, $item, $min, $max, $domain, $down) = @{$htable->[$i]};
	print "checking $comm:$item:$min:$max\n" if( $BigSister::common::dl>4 );
	$n = $procs{$domain}->{$comm};
	($n = 0) unless( defined $n );
	if( $min && ($min>$n) ) {
	    if ( !$n ) {
		$self->report( $item, $BigSister::common::status_codes{$down}, "$comm <<($n)" );
	    } else {
		$self->report( $item, $BigSister::common::status_codes{"red"}, "$comm <($n)" );
	    }
	}
	elsif( length($max) && ($max<$n) ) {
	    $self->report( $item, $BigSister::common::status_codes{"red"}, "$comm >($n)" );
	}
	else {
	    $self->report( $item, $BigSister::common::status_codes{"green"}, "$comm OK($n)" );
	}
    }
}

1;
