#  $Header:
#
# Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      EMAgent.pm - Perl Module to provide start, stop, status functionality
#
#    DESCRIPTION
#       This script provides the stop,status,debug functionality for 
#       the emwd and the emctl. Additionally it may hold other
#       states like the start time, its PID, thrashCount etc.
#
#    MODIFIED   (MM/DD/YY)
#      bkovuri   09/10/08 - Backport bkovuri_bug-7307490 from main
#      apenmets  02/01/08 - Creating st_emagent_10.2.0.4.1db11 branch
#      apenmets  01/31/08 - Merging Changes from stpl_emdb_main_aix
#      sunagarw  01/18/08 - Backport sunagarw_bug-6434147 from main
#      svrrao    10/29/07 - Adding gencore and lsof for AIX
#      neearora  05/23/07 - changed version to 10.2.0.4.0
#      njagathe  02/23/07 - Update version to 10.2.4.0.0
#      cvaishna  08/22/06 - fixing recalculation for EMAGENT_RECYCLE_MAXMEMORY
#      smodh     07/20/06 - change version to 10.2.0.3.0
#      smodh     07/20/06 - Backport smodh_bug_4769194_agent from main
#      smodh     12/11/05 - change version to 10.2.0.2.0 
#      vnukal    10/14/05 - deleting extra corefiles on Windows 
#      kduvvuri  07/12/05 - fix version string. 
#      sksaha    06/30/05 - Kill all threads based on pid on linux
#      sksaha    06/12/05 - Fix ls output in deleteExtraAgentCores 
#      sksaha    05/29/05 - Kill with signal 14 when hang detected 
#      vnukal    04/14/05 - fixing logic of computing maxmemory_limit 
#      vnukal    03/14/05 - Patch script callout 
#      sksaha    02/26/05 - Call debugCore for second core file generated 
#      vnukal    01/14/05 - fix conversion issue 
#      vnukal    01/10/05 - adaptable MAX MEMORY 
#      vnukal    12/28/04 - adding dump of tunable env vars 
#      sksaha    11/04/04 - use sAgentUtils 
#      sksaha    10/12/04 - Fix bug 3906405 : core file generation
#      kduvvuri  11/02/04 - You should check for 'Windows_NT' aswell as 
#                           MSWin32. 
#      kduvvuri  10/27/04 - fix dump file generation on windows as well. 
#      kduvvuri  10/26/04 - fix bug 3939606. 
#      kduvvuri  10/11/04 - bug 3835050, merge from linux port. 
#      vnukal    10/05/04 - nmupm launch only when neccasary 
#      kduvvuri  09/20/04 - fix bug 3841084, check for presence of lsof before 
#                           using it. 
#      kduvvuri  07/28/04 - Add getVersion. 
#      aaitghez  05/03/04 - bug 3358285, hang fix 
#      vnukal    03/16/04 - cr comments 
#      vnukal    03/16/04 - adding checkUploadConstraints 
#      rzkrishn  03/17/04 - going back to emdctl in stop 
#      rzkrishn  03/12/04 - Recycle functionality on windows 
#      mbhoopat  03/10/04 - linux porting exception 
#      aaitghez  02/19/04 - bug 3443139 
#      rlal      01/22/04 - Fix for bug 2988737 
#      rzkrishn  12/23/03 - review changes 
#      rzkrishn  12/22/03 - compute memIncrease 
#      rzkrishn  10/08/03 - remove extra cores. 
#      gachen    10/06/03 - redirect stderr in debug 
#      gachen    09/24/03 - generate core in mainsa 
#      dmshah    09/15/03 - 
#      dmshah    09/15/03 - Integration testing changes 
#      dmshah    09/11/03 - Adding Debug flag 
#      gachen    09/17/03 - add pfile/pstack/lsof to EMAgent 
#      vnukal    08/13/03 - fixing leading whitespace of ps o/p 
#      vnukal    08/07/03 - modify ps args to hit correct emagent 
#      rzkrishn  07/23/03 - get core always when hung
#      rzkrishn  07/22/03 - agent tells watch dog to act same as for HANG in abnormal state
#      dmshah    07/08/03 - Bug fixes from 401 branch
#      vnukal    06/17/03 - adding okToRestart method
#      dmshah    04/06/03 - dmshah_bug-2849086_mainsa
#      dmshah    03/20/03 - Only way to kill is SIGKILL
#      dmshah    04/02/03 - grabtrans 'dmshah_fix_2849086_2'
#      dmshah    03/14/03 - Adding restart code
#      dmshah    03/13/03 - Bug fix 2849086 and moving PERL BIN
#      dmshah    03/11/03 - dmshah_em_watchdog
#      dmshah    03/09/03 - Making emctl start em compatible for VOBs
#      dmshah    03/06/03 - Adding debug core file logic
#      dmshah    03/03/03 - Fixing constructor syntax
#      dmshah    03/03/03 - Testing if..else block
#      dmshah    02/26/03 - Created.
#

package EMAgent;
require EMAgentPatch;
use strict;
use EmctlCommon;
use sAgentUtils;
use LWP::Simple;
use Time::Local;
use Config;

sub new
{
  my ($class) = @_;
  my $self =
  {
     PID => -1,
     name => undef,
     startTime => -1,
     thrashCount => 0,
     initialized => 0,
     printCounter => 0,
     lastMemChkTime => 0,
     lastUploadChkTime => 0,
     emHome => getEMHome("agent"),
     debug => 0,
     prevMemSize => 0,
     mamMemLimitSet => 0,
     memCheckImplemented => 1,
     patchObjRef => 0
  };

  # initialize PatchModule
  $self->{patchObjRef} = new EMAgentPatch();
  $self->{patchObjRef}->Initialize();
  
  bless $self, $class;
  return $self;
}

#
# Initialize 
# Ensure that the values are correct.
#
sub Initialize
{
  my $self = shift;
  my $inPID = shift;
  my $inStartTime = shift;
  my $debugFlag = shift;

  $self->{PID} = $inPID;
  $self->{startTime} = $inStartTime;
  $self->{thrashCount} = 0;
  $self->{name} = "EMAgent";
  $self->{initialized} = 1;
  $self->{printCounter} = 0;
  $self->{lastMemChkTime} = 0;
  $self->{lastUploadChkTime} = 0;
  $self->{prevMemSize} = 0;
  $self->{maxMemLimitSet} = 0;
  $self->{memCheckImplemented} = 1;
  $self->{debug} = $debugFlag if defined($debugFlag);

  if ($self->{debug}) {
    print("--EMAGENT_MEMCHECK_HOURS=$EMAGENT_MEMCHECK_HOURS\n");
    print("--EMAGENT_RECYCLE_DAYS=$EMAGENT_RECYCLE_DAYS\n");
    print("--EMAGENT_TIMESCALE=$EMAGENT_TIMESCALE\n");
    print("--EMAGENT_RECYCLE_MAXMEMORY=$EMAGENT_RECYCLE_MAXMEMORY\n");
    print("--EMAGENT_MAXMEM_INCREASE=$EMAGENT_MAXMEM_INCREASE\n");
  }

  return 1;
}

#
# status
# Checks the PID liveness test first and then uses the appropriate handler
# to check the about Page of the agent
#
sub status
{
  my($self) = @_;

  if($self->{initialized})
  {
      
      my $rc = 2;
      my $numStatusRetries = $NUMBER_AGENT_STATUS_RETRIES;
      my $patchRc = 0;

      $patchRc = $self->{patchObjRef}->status();

      if($patchRc != 1)
      {
	print "-- Patch status method returned error --\n";
	# Not sure what circumstances PatchModule status will return error.
	# Need to decide what actions need to occur in those circumstances.
      }

      while(($numStatusRetries > 0) &&
	    (($rc eq 4) ||
	     ($rc eq 7) ||
	     ($rc eq 2)))
      {
	  $rc = 0xffff & system("$EMDROOT/bin/emdctl status agent ".
				" $EMD_HANG_CHECK_STATUS_TIME >$devNull 2>&1");
	  $rc >>= 8;
	  $numStatusRetries = $numStatusRetries - 1;
      }
      
    if( $rc eq 3 )   #emAgent is UP and running...
    {
      $rc = $STATUS_PROCESS_OK;
    }
    elsif( $rc eq 4 )  #emAgent is UP but not ready... 
    { 
      $rc = $STATUS_AGENT_NOT_READY;
    }
    elsif( $rc eq 1 ) #emAgent process is dead...
    {
      $rc = $STATUS_NO_SUCH_PROCESS;
    }
    elsif( $rc eq 2 ) #emAgent is hanging ...
    {
       $rc = $STATUS_PROCESS_HANG;
    }
    elsif( $rc eq 7 ) #emAgent is in abnormal state ...
    {
       $rc = $STATUS_AGENT_ABNORMAL;
    }
    else
    {
       $rc = $STATUS_NO_SUCH_PROCESS; # This should not happen...
    }

    return $rc;
  }
}

#
# stop
# Stops the Agent
#
sub stop
{
  my($self, $tries) = @_;
        
  if($self->{initialized})
  {
   # Relying on the fact that emdctl status never fails...     
   system("$EMDROOT/bin/emdctl stop agent >$devNull 2>&1 &");
   
   my $rc;
   if ($tries eq undef )
   {
      $tries = 30;
   }

   while( $tries gt 0 )
   {
      $tries--;

      $rc = 0xffff & system("$EMDROOT/bin/emdctl status agent ".
            " $EMD_HANG_CHECK_STATUS_TIME >$devNull 2>&1");
      $rc >>= 8;

      if ($rc lt 2) # Agent stop succeeded...
      {
         last; 
      }
      
      if($tries gt 0)
      {
         sleep 1;
      }
   }

   if($rc ge 2) # Agent is still running
   {
     print "----- Failed to stop agent! -----\n";
     print "----- Attempting to kill $self->{name} : $self->{PID} -----\n";
     kill 9, $self->{PID}; # Force a SEGKILL ...
   }
   
   return $rc;
   
  }
}


#
# gatherProcessStatistics
# Gathers process Statistics like Memory size etc
#
sub gatherProcessStatistics
{
  my($self) = @_;
  if($self->{initialized})
  {
  }
}


#
# reInitialize
# Update after a restart, the PID and the start time are changed now
# and needs to be reflected.
# 
sub reInitialize
{
  my($self, $inPID, $inStartTime) = @_;        
  if($self->{initialized})
  {
     $self->{PID} = $inPID;
     $self->{startTime} = $inStartTime;
     $self->{printCounter} = 0;
     $self->{lastMemChkTime} = 0;
     $self->{lastUploadChkTime} = 0;
     $self->{prevMemSize} = 0;
     $self->{maxMemLimitSet} = 0;
     $self->{memCheckImplemented} = 1;
     return 0;
  }
  else
  {
     return 1;
  }
}


#
# getThrashCount
# Returns the number of restarts that has occurred
#
sub getThrashCount
{
  my($self) = @_;
  if($self->{initialized})
  {
     return $self->{thrashCount};
  }
  else
  {
     return -1;
  }
}

#
# setThrashCount
# Reset routine for the setThrashCount
#
sub setThrashCount
{
  my($self, $inThrashCount) = @_;
  if($self->{initialized})
  {
     $self->{thrashCount} = $inThrashCount;
     return 0;
  }
  else 
  {
     return 1;
  }
}

#
# incThrashCount
# Increments the thrashCount by 1
#
sub incThrashCount
{
  my($self) = @_;
  if($self->{initialized})
  {
     $self->{thrashCount}++;
     return 0;
  }
  else 
  {
     return 1;
  }
}

#
# getPID
# Returns the PID for the IASConsole
# 
sub getPID
{
  my($self) = @_;
  if($self->{initialized})
  {
     return $self->{PID};
  }
  else
  {
     return -1;
  }
}

#
# getStartTime
# Returns the start time of the IASConsole
#
sub getStartTime
{
  my($self) = @_;
  if($self->{initialized})
  {
     return $self->{startTime};
  }
  else
  {
     return -1;
  }
}

#
# getName
# Returns the Name
#
sub getName
{
  my($self) = @_;
  if($self->{initialized})
  {
    return $self->{name};
  }
  else
  {
    return undef;
  }
}

#
# debug
# Provides the Debug functionality
#
sub debug
{
  my($self) = @_;
  if($self->{initialized})
  {
    my ($tPid) = $self->{PID};
    my $EMHOME = $self->{emHome};
    my $rc = 0;
    my ($gcoreFile) = "$EMHOME"."/sysman/emd/core.hung"; #input to gcore.
    #gcore always appends 'pid' to the file name specified.
    #$coreFile contains the actual file name generated by gcore,
    my ($coreFile) = "$gcoreFile".".$tPid"; 
    my ($gcorebin)="/bin/gcore" ;
    my ($gcorecmd) = "$gcorebin"." -o "."$gcoreFile ".$tPid;;

    if($^O eq "linux")
    {
      $gcorebin="/usr/bin/gcore";
      $gcorecmd = "$gcorebin"." -o "."$gcoreFile ".$tPid;
    } #Add elsif here to create 'gcorebin' and gcorecmd' for any spl cases.

    #On windows and aix, dump file is generated with the actual file name 
    #specified. So, we use $coreFile which we generate by appending 'pid'.

    if( ($^O eq "MSWin32") or ($^O eq "Windows_NT") )
    {
      $gcorebin ="$EMDROOT/bin/userdump.exe";
      #the command is usedump.exe <pid> <filename>
      $gcorecmd = "$gcorebin"." $tPid $coreFile";
    }
    if($^O eq "AIX")
    {
      $gcorebin="/usr/bin/gencore";
      $gcorecmd = "$gcorebin ".$tPid." $coreFile";
    } #Add elsif here to create 'gcorebin' and gcorecmd' for any spl cases.

    #
    # generate gcore 
    #
    if ( -e $gcorebin )
    {
      print "----- ".localtime()."::generate first core file for diagnosis -----\n";
      $rc = 0xffff & system("$gcorecmd");
      $rc >>= 8;
      if($rc != 0)
      {
        # error running core dump command
        print "----- ".localtime()."::Error running $gcorecmd : $! \n";
      }

      my($currTime) = time();

      if( -e $coreFile )
      {
        rename $coreFile, $coreFile."_".$currTime;
        print "----- ".localtime()."::core file ".$coreFile."_".$currTime." generated -----\n";
	$self->debugCore( $coreFile."_".$currTime);
      }

      #
      # sleep 10 seconds.
      #
      sleep 10;

      #
      # generate second gcore
      #
      print "----- ".localtime()."::generate second core file for diagnosis -----\n";
      $rc = 0xffff & system("$gcorecmd");
      $rc >>= 8;
      if($rc != 0)
      {
        # error running core dump command
        print "----- ".localtime()."::Error running $gcorecmd : $! \n";
      }

      if( -e $coreFile )
      {
        rename $coreFile, $coreFile."_".$currTime . "_10s_after" ;
        print "----- ".localtime()."::core file ".$coreFile."_".$currTime."_10s_after generated -----\n";
	$self->debugCore( $coreFile."_".$currTime."_10s_after");
      } 
    }
    else
    {
      print "----- ".localtime()."::INFO Skipping core file generation for diagnosis. Binary '$gcorebin' does not exist \n";
    }
    

    #
    #  generate lsof to see the fd usage
    #
    if(($^O ne "MSWin32") && ($^O ne "Windows_NT"))
    {
      print "----- ".localtime()."::generate $coreFile.lsof.1 for diagnosis -----\n";
      if ( -r "/etc/SuSE-release" ) {
        system("/usr/bin/lsof -p $tPid > $coreFile.lsof.1 2>&1");
      }
      elsif  ( -r "/etc/redhat-release") {
        system("/usr/sbin/lsof -p $tPid > $coreFile.lsof.1 2>&1");
      }
      elsif ( -x "/usr/local/bin/lsof" )
      {
        system("/usr/local/bin/lsof -p $tPid > $coreFile.lsof.1 2>&1");
      }
      elsif ( -x "/usr/sbin/lsof" )
      {
        system("/usr/sbin/lsof -p $tPid > $coreFile.lsof.1 2>&1");
      }
    }

    print "----- Attempting to kill $self->{name} : $self->{PID} -----\n";
    if ( $^O eq "linux" )
    {
	# On Linux the threads of a process are represented as separate
	# processes on the OS, so we need to kill all the threads
	# which are obtained using ps.
        $self->killProcessAndThreads( $self->{PID} );
    }
    else
    {
       kill $EMCTL_CORE_SIGNAL, $self->{PID}; # Force a kill with SIGKILL ...
    }

    return 0;
  }
}

sub killProcessAndThreads
{
  my($self, $pid) = @_;

  # First lets recursively kill the child threads ...
  my(@procs) = `/bin/ps -emlf`;
  my $proc = "";
  foreach $proc (@procs)
  {
    if ( $proc =~ m/$pid/ )
    {
      my @cols = split ( /\s+/ , $proc );

      # Check if parent id matches this pid, then kill the child
      if ( $pid == $cols[4] )
      {
        $self->killProcessAndThreads ( $cols[3] );
      }
    }
  }

  # Now lets kill the original thread
  kill $EMCTL_CORE_SIGNAL, $pid;
}


#
# debugCore
# DebugCore is called when the monitor detects a core dump
# Parameter : CoreFile
#
sub debugCore
{
  my($self, $debugFile) = @_;
  sAgentUtils::sDebugCore($self, $debugFile);
}

#
# Delete extra cores.
#
sub deleteExtraAgentCores 
{
  my ($agentHome) = @_[0];
  my ($deletecores) = $agentHome."/sysman/emd/deletecores.tmp";
  my (@lines) ;
  my (@files) ;
  my ($count)  = 0;
  my ($LS) = "/bin/ls";
  
  if ( -e $LS ) 
  {
    # defaulting to 3 cores(core + .threads+.traceback)
    my ($maxCores) = 9; 
 
    if (defined($ENV{EMAGENT_MAX_CORES}))
    {
      $maxCores = $ENV{EMAGENT_MAX_CORES};
      $maxCores = $maxCores * 3;
    }

    @files = system ("$LS -tr $agentHome/sysman/emd/core* > $deletecores" );
 
    open (DELETECORES, $deletecores);
 
    while (<DELETECORES>)
    {
      chomp($_);
      push @lines, $_;
      $count++;
    }
    close (DELETECORES);
    unlink("$deletecores");
 
    if ( ($count + 2) <= $maxCores ) 
    {
      return;
    }
    else
    {
      my ($deleteCount) = $count + 2 - $maxCores;  
 
      while ($deleteCount > 0)
      {
        unlink(@lines[$deleteCount-1]);
        $deleteCount --;
      }
    }
  }
}

#
# recycle
# Checks wether the current process requires a recycle or not.
# 
sub recycle
{
  my($self) = @_;        
  if($self->{initialized} == 0)
  {
      return "FALSE";
  }

  my $rc = "FALSE";

  $rc = $self->checkMemConstraints();
  if ($rc eq "TRUE") {
      return $rc;
  }
  
  $rc = $self->checkUploadConstraints();
  
  return $rc;
}

sub checkMemConstraints
{

  my($self) = @_;        
  if($self->{initialized} == 0)
  {
      return "FALSE";
  }

  my($recycleInterval,$recycleSecs);
  my($memCheckInterval) = $EMAGENT_MEMCHECK_HOURS * 3600;
  my($currTime) = time();
  my($implemented) = 0;
  my($vmSize) = 0;

  $recycleSecs = 3600/$EMAGENT_TIMESCALE;
  $recycleInterval = $EMAGENT_RECYCLE_DAYS * 24 * $recycleSecs;

  # Restart the agent every $EMAGENT_RECYCLE_DAYS. Default is never
  if($recycleInterval > 0 ) {
    my($timeSinceStart) = $currTime - $self->{startTime};
    if($timeSinceStart > $recycleInterval)
      {
        my($timeSinceStart_hour) = $timeSinceStart / 3600;
        print "--- Recycling process. Up for $timeSinceStart_hour hours ---\n";
        return "TRUE";
      }
  }

  if($self->{lastMemChkTime} == 0) # this check acting as first time flag
    {
      $self->{lastMemChkTime} = $currTime;
      ($implemented, $vmSize) = $self->getVMUsage();
      $self->{prevMemSize} = $vmSize/1024; # Convert to MB
      print(localtime()." -- First VM size is $vmSize\n") if ($self->{debug});
      print(localtime()." -- MemCheck implemented is $implemented\n") if ($self->{debug});
      $self->{memCheckImplemented} = $implemented;

      print("Recycle Agent functionality disabled as VMSize checking not implemented on this platform ($^O)\n") if ($implemented == 0);

      return "FALSE";
    }

  if($self->{memCheckImplemented} == 0 || $memCheckInterval <= 0) {
    return "FALSE";
  }


  my($timeSinceStarted) = $currTime - $self->{startTime};

  if(($self->{maxMemLimitSet} == 0) && ($timeSinceStarted >= 300)) {

    $self->{maxMemLimitSet} = 1;

    if($EMAGENT_RECYCLE_SOFTLIMIT eq "TRUE") {
      ($implemented, $vmSize) = $self->getVMUsage();

      $self->{prevMemSize} = $vmSize/1024;
      print(localtime()." VMSize after 5 min is $self->{prevMemSize}\n") if ($self->{debug});
      # Set computedSize = current + 100MB
      my $computedSize = ($vmSize + 102400)/1024;
      print(localtime()." Computed size is $computedSize\n") if ($self->{debug});
      # Set the maxmemory to larger of computedSize & EMAGENT_RECYCLE_MAXMEMORY
      if( !defined($EMAGENT_RECYCLE_MAXMEMORY) )
      {
          $EMAGENT_RECYCLE_MAXMEMORY = $computedSize;
      }
      elsif ( $EMAGENT_RECYCLE_MAXMEMORY < $computedSize )
      {
          $EMAGENT_RECYCLE_MAXMEMORY = $computedSize;
      }
    }

    print(localtime()." EMAGENT_RECYCLE_MAXMEMORY set to $EMAGENT_RECYCLE_MAXMEMORY\n") if ($self->{debug});

  }

  my($timeSinceLastCheck) = $currTime - $self->{lastMemChkTime};

  if($timeSinceLastCheck < $memCheckInterval)
    {
      return "FALSE";
    }

  $self->{lastMemChkTime} = $currTime;
  
  ($implemented, $vmSize) = $self->getVMUsage();
      
  if ($implemented == 1)
    {
      $vmSize = $vmSize/1024; # Change to MB

      print(localtime()." Current Agent vmSize is $vmSize MB\n") if($self->{debug});
      if( $vmSize > $EMAGENT_RECYCLE_MAXMEMORY )
	{
	  my $memIncrease = $vmSize - $self->{prevMemSize} ;
	  print(localtime()." Memory Increase is $memIncrease\n") if($self->{debug});
	  if ($memIncrease > $EMAGENT_MAXMEM_INCREASE) 
	    {
	      print "--- Recycling process. VMSize is $vmSize MB increased by $memIncrease MB in past $EMAGENT_MEMCHECK_HOURS ---\n";
	      return "TRUE";
	    }
	}
      $self->{prevMemSize} = $vmSize;
    }
  
  return "FALSE";
}


sub getVMUsage
{
  my($self) = @_;        
  if($self->{initialized} == 0)
  {
      return "FALSE";
  }

  my($implemented) = 0;
  my($vmSize) = 0;
  my($pid) = 0;

  $pid = $self->{PID};

  $ENV{UNIX95} = "XPG4" if($^O eq "hpux");
  
  if(($^O eq "SunOS") or ($^O eq "solaris") or ($^O eq "linux") or
     ($^O eq "aix") or ($^O eq "hpux") or ($^O eq "darwin"))
    {
      my($tpid,$pvmSize) = (`ps -p $pid -o "pid,vsz"`)[1] =~ m/(\w+)\s+(\w+)/g;
      
      return "FALSE" if($pid != $tpid);
      
      chomp($pvmSize);
      
      $vmSize = $pvmSize;
      
      $implemented = 1;
    }
  elsif (($^O eq "MSWin32") or ($^O eq "Windows_NT"))
    {
      my($result) = (`$EMDROOT/bin/nmupm procInfo $pid`);
      
      my($tpid,$cpu,$pvmSize,$resmSize,$remain) = split(/\|/, $result, 5);
      
      chomp($pvmSize);
      
      $vmSize = $pvmSize;
      
      $implemented = 1;
    }
  elsif ($^O eq "dec_osf")
    {
      my $ignore = '';
      my($tpid,$pvmSize) = (`ps -p $pid -o "pid,vsz"`)[1] =~ m/(\w+)\s+(.*)/g;
      
      return "FALSE" if($pid != $tpid);
      
      chomp($pvmSize);
      
      $vmSize = $pvmSize;
      
      $implemented = 1;
      
      # The memory usage on Tru64 is given with the size suffix
      # (e.g. 2.04M). This should be parsed and converted to KB.

      if ($vmSize =~ /K/) # KB
      {
        ($vmSize, $ignore) = split('K', $vmSize);
      }
      elsif ($vmSize =~ /M/) # MB
      {
        ($vmSize, $ignore) = split('M', $vmSize);
        $vmSize *= 1024;
      }
      elsif ($vmSize =~ /G/) # GB
      {
        ($vmSize, $ignore) = split('G', $vmSize);
        $vmSize *= 1024 * 1024;
      }
    }

  return ($implemented, $vmSize);
}  

sub checkUploadConstraints
{
    my($self) = @_;        
    if($self->{initialized} == 0)
    {
	return "FALSE";
    }

    my ($rc) = "FALSE"; # donot recycle
    
    my $currTime = time();
    my($uploadCheckInterval) = $EMAGENT_MEMCHECK_HOURS * 3600;

    if($uploadCheckInterval <= 0)
    {
	return "FALSE";
    }

    if($self->{lastUploadChkTime} == 0)
    {
	$self->{lastUploadChkTime} = $currTime;
	return "FALSE";
    }
    
    my($timeSinceLastCheck) = $currTime - $self->{lastUploadChkTime};
    if($timeSinceLastCheck < $uploadCheckInterval)
    {
	return "FALSE";
    }
    $self->{lastUploadChkTime} = $currTime;

    # checking status of the upload dat files
    # errors.dat
    # severity.dat
    # rawdata.dat

    $rc = checkFileTimeStamp($currTime,$self->{emHome}."/sysman/emd/upload/errors.dat");
    if( $rc eq "TRUE") { return $rc };
    $rc = checkFileTimeStamp($currTime,$self->{emHome}."/sysman/emd/upload/rawdata.dat");
    if( $rc eq "TRUE") { return $rc };
    $rc = checkFileTimeStamp($currTime,$self->{emHome}."/sysman/emd/upload/severity.dat");
     return $rc
}

sub checkFileTimeStamp
{
    my ($currTimeSecs, $fileToCheck) = @_;

    my ($year,$month,$day,$hour,$minute,$second)=(0,0,0,0,0,0);

    if (! -e $fileToCheck) {
	return "FALSE" ;
    }
    
    unless ( open (FILETOPARSE,"<$fileToCheck")) {
	print "-- Unable to open file $fileToCheck --\n";
	return "FALSE";
    }
    
    while (<FILETOPARSE>) {
	# finding first occurence will give us the earliest timestamp
	if (/COLLECTION_TIMESTAMP/)
	{
	    ($year,$month,$day,$hour,$minute,$second) = /(\d+)\-(\d+)\-(\d+)\W+(\d+):(\d+):(\d+)/ ;
	    last;
	}
    }
    close FILETOPARSE;

    if( $year == 0) {
	# we did not hit any timestamp string
	return "FALSE";
    }
	
    # month is 0...11 so subtracting by 1
    my $timeStampSecs = timelocal($second,$minute,$hour,$day,$month-1,$year);

    # currentTime greater than 24 hours of the earliest timestamp found in
    # file
    if ($currTimeSecs > ($timeStampSecs + (24*60*60))) {
		print "-- Timestamp ($year,$month,$day,$hour,$minute,$second) of file $fileToCheck is more than 24 hours old. Current Time is ".localtime;
	return "TRUE";
    }
    else {
	return "FALSE";
    }
    return "FALSE";
}
#
# okToRestart
# Determines if sufficient resources or conditions exist for the component
# to be started
# 
sub okToRestart
{

  my($self) = @_;
  if($self->{initialized})
  {
    if(($^O eq "SunOS") or ($^O eq "solaris")) {

      my $rc;
    
      # agentok script returns 0 when it is Ok to start the agent. Non-zero
      # return code otherwise.  
      $rc = 0xffff & system("$EMDROOT/bin/agentok.sh >$devNull 2>&1");
      $rc >>= 8;

      if($rc != 0) # Insufficient resources exist.
      {
	if (($self->{printCounter} % 120) == 0) {
	  print "---- Insufficient resources exist to restart agent -----\n";
          $self->{printCounter} = 0;
	}
	$self->{printCounter} ++;
        return "FALSE";
      }
    }
  }
  else
  {
    return undef;
  }

  return "TRUE";
}

sub getVersion
{
    print "       Enterprise Manager 10g Agent Version 10.2.0.4.0\n";
}




1;
