#!/usr/local/bin/perl
# 
# $Header: emdb/sysman/admin/discover/oracledb.pl /st_emdbsa_11.2/2 2009/02/03 18:17:34 shasingh Exp $
#
# oracledb.pl
# 
# Copyright (c) 2002, 2009, Oracle and/or its affiliates.All rights reserved. 
#
#    NAME
#      oracledb.pl - <one-line expansion of the name>
#
#    DESCRIPTION
#      <short description of component this file declares/defines>
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    shasingh    12/07/08 - Choose service name as of database name
#    vgoli       12/02/08 - fix bug 7602853: conditionalize oraProcExists in
#                           isValidSid
#    tpalgudi    11/13/08 - Bug#4067458 fix
#    mkiran      11/03/08 - 7360354: Accurately parse o/p of lsnrctl services
#    shasingh    09/10/08 - 
#    nachen      06/19/08 - XbranchMerge nachen_bug-7144603 from st_emdbsa_11.1
#    nachen      06/06/08 - fix internal references security issue
#    prakgupt    05/12/08 - fix bug 6792454 - check existence of envJavaHome in
#    shasingh    02/07/08 - added discovery hint params
#    shasingh    06/18/07 - bug 6136655: unset SRVM_TRACE env variable  
#                           execute command
#    shasingh    05/13/07 - bug 5648922: add FQDN to listener host
#    mkiran      03/09/07 - 4774824: Unset LD_LIBRARY_PATH on HP-UX
#    mkiran      08/11/06 - 4774824: Unset LD_LIBRARY_PATH on HP-UX
#    xuliu       07/13/05 - CLUSTER_NAME
#    jstone      06/02/05 - add use strict 
#    xuliu       03/22/05 - check instance_number for rac 
#    jstone      03/31/05 - fixes for MAC OS X 
#    xuliu       11/10/04 - don't print rac target if not in cluster
#    dkapoor     11/11/04 - 3772840:update correct listener home 
#    xuliu       11/10/04 - OMSENV_CLUSTER_NAME
#    xuliu       10/28/04 - 10gr2 misc 
#    xuliu       10/22/04 - new rac_database metadata 
#    dkapoor     10/15/04 - fix bug 3872639 
#    dkapoor     08/12/04 - touch for 10.2SA testing 
#    dkapoor     06/16/04 - no pass for ASM 
#    xuliu       06/04/04 - fix 3667625 for rac 
#    xuliu       05/13/04 - bug 3626488 
#    xuliu       05/12/04 - bug 3468650 
#    xuliu       03/18/04 - fix 3495276 
#    mbhoopat    03/10/04 - linux port 
#    xuliu       03/01/04 - workaround 3469118 
#    xuliu       02/18/04 - fix 3450484: disable err of spfile conversion
#    dkapoor     02/17/04 - fix dynamic sid on NT
#    xuliu       02/12/04 - fix 3434862 
#    dkapoor     01/19/04 - use containsFile 
#    dkapoor     01/23/04 - don't use TNS_ADMIN env in windows 
#    xuliu       01/26/04 - fix 3383117 for NT 
#    dkapoor     12/09/03 - unset ORA_NLS vars 
#    dkapoor     12/02/03 - check for soft link tnsadmin and ohome 
#    xuliu       11/24/03 - fix 3264493 
#    dkapoor     11/15/03 - fix 10g listener 
#    xuliu       11/11/03 - fix 3178713
#    dkapoor     11/05/03 - fix 3220691 
#    dkapoor     10/31/03 - add more logging 
#    dkapoor     10/27/03 - add logging 
#    dkapoor     10/24/03 - fix bug#3202471 
#    dkapoor     10/03/03 - use db_unique_name 
#    xuliu       08/19/03 - fix 3106024 
#    xuliu       08/05/03 - fix 3079354 
#    xuliu       07/23/03 - srvctl version chg
#    xuliu       07/18/03 - use DiscoveryWarning tag
#    dkapoor     05/28/03 - use initParam subs for local listener
#    dkapoor     05/15/03 - check pmon
#    dkapoor     05/08/03 - put host for rac db
#    dkapoor     05/07/03 - use address host,filter pre 8.1 dbs
#    dkapoor     04/17/03 - move parsing code to net area
#    xuliu       03/17/03 - fix service name
#    xuliu       01/06/03 - logging for RAC
#    xshen       12/22/02 - separate osm_instance targets from db
#    xuliu       12/13/02 - rac tgt naming
#    xuliu       12/12/02 - disable cluster tgt printing & more robust host equivalence test
#    dkapoor     11/14/02 - port to NT
#    dkapoor     11/12/02 - filter out PLSEXtPROC sid
#    dkapoor     11/02/02 - add 1521 port for first OHome for Port Table
#    dkapoor     10/30/02 - fix 2649537
#    dkapoor     10/10/02 - make a union of sids found dynamically
#    xuliu       10/07/02 - comment serviceName error for non-rac case
#    xuliu       10/01/02 - redirect err to pipe in executeCommand
#    xuliu       09/25/02 - refine rac discovery logic
#    xuliu       08/30/02 - serviceName logic
#    xuliu       08/22/02 - find cluster
#    xuliu       08/20/02 - rac & service_name discovery
#    dkapoor     07/31/02 - use dbsnmp as default user
#    dkapoor     07/31/02 - remove perltrace
#    dkapoor     07/22/02 - add listener disc. and port info
#    kduvvuri    04/04/02 - remove debug statement..
#    kduvvuri    04/04/02 - consider ORATAB environment variable, if it exists..
#    kduvvuri    03/07/02 - don't process sid's that have a '*' as the value..
#    kduvvuri    02/11/02 - kduvvuri_cemd_programmatic_discovery_v2
#    kduvvuri    02/05/02 - Creation
# 
use strict;
use Config;
use Cwd;
my ($emdRoot,$hostName,$crsHome) = @ARGV;
$ENV{EMDROOT} = $emdRoot;

require "$emdRoot/sysman/admin/scripts/semd_common.pl";
require "$emdRoot/sysman/admin/scripts/db/db_common.pl";
require "$emdRoot/sysman/admin/scripts/db/net/listenerUtil.pl";
require "$emdRoot/sysman/admin/discover/utl/oracledbUtl.pl";
require "$emdRoot/sysman/admin/discover/utl/initParameterFileUtl.pl";
require "$emdRoot/sysman/admin/scripts/emd_common.pl";

my %RacSidConversionMap;

#Discovery LOG CATEGORY
my $LOG_CATEGORY = "DB_LISTENER_DISCOVERY: ";

#VIP name of the local machine if in 10g cluster
my $LOCAL_HOST_VIP_NAME = "";

#Cluster Name if it's in cluster
my $CLUSTER_NAME = "";

#CRS Home if 10g cluster
my $CRS_HOME = "";

# Hashtable contains lsnrctl versions 
my %lsnrctlVersions;
my $elapsedSec = time();
EMD_PERL_INFO("$LOG_CATEGORY ***** Start of Database and Listener Discovery***");

#Unset ORA_NLS variables if in the environment
if(defined $ENV{ORA_NLS})
{
	delete ($ENV{ORA_NLS});
}
if(defined $ENV{ORA_NLS32})
{
	delete ($ENV{ORA_NLS32});
}
if(defined $ENV{ORA_NLS33})
{
	delete ($ENV{ORA_NLS33});
}

#if in Windows, remove TNS_ADMIN from the environment variable
#because there is no global TNS_ADMIN in windows, we should get
# from registry for each Oracle Home, that is, use getDefaultTNSAdmin
if((get_osType() eq 'WIN') && defined $ENV{TNS_ADMIN})
{
	delete ($ENV{TNS_ADMIN}) if defined($ENV{TNS_ADMIN});
}

# 4774824: Unset LD_LIBRARY_PATH on HP-UX
# Reasons: 
# (1) To prevent 64bit sqlplus/tnslsnr from picking wrong version of .sl from
#     <agent-home>/lib during discovery on HP and fail with unsatisfied symbols.
# (2) 64bit sqlplus/tnslsnr should load .sl from <prod-home>/lib.
#     <prod-home>/lib is prefixed to only SHLIB_PATH on HP-UX.
#     So, 64bit execs would unsuccessfully search for .sl in LD_LIBRARY_PATH.
#     Hence unsetting LD_LIBRARY_PATH here to speed up discovery.
if(get_osType() eq 'HP')
{
	delete ($ENV{LD_LIBRARY_PATH}) if defined($ENV{LD_LIBRARY_PATH});
}

# bug 6136655: unset SRVM_TRACE env variable to disable srvctl and related
# utilities trace log 
my $srvm_trc_flg = $ENV{SRVM_TRACE};
delete $ENV{SRVM_TRACE} if defined $srvm_trc_flg ;

my %hintParams = ();
if(defined $ENV{DISCOVERY_HINTS_AVAIL})
{
  my @stdins = <STDIN>;	
  foreach my $param (@stdins)
  {
   if($param =~ m/DISCOVERY_HINTS=(.*)\s*/)
   { 	
    %hintParams = ( split /[=,]/, $1);
   }
  } 
  EMD_PERL_INFO("$LOG_CATEGORY discovery hints found");
  while ( my ($key, $value) = each(%hintParams) )
  {
    EMD_PERL_INFO("$LOG_CATEGORY env variable $key => $value\n");
  }
}

my $dieh = $SIG{__DIE__} if $SIG{__DIE__};

$SIG{__DIE__}='';
eval
{
  local $SIG{ALRM} = sub {
  alarm 0;
  my %erra = ();
  $erra{TimeOut}= "Discovery timed out";
  print "<Targets>\n";
  printDiscoveryErrors(\%erra, $hostName);
  print "</Targets>\n";
  die "discovery time out"; 
 };
  
  alarm  $hintParams{discovery_timeout} if defined $hintParams{discovery_timeout}; 
 
  print "<Targets>\n"; 
  printDatabaseAndListnerTargets();
  print "</Targets>\n";
 
  alarm 0;
 };
$SIG{__DIE__} = $dieh  if $dieh;

# bug 6136655: set SRVM_TRACE env variable to original value before
#exiting 
$ENV{SRVM_TRACE} = $srvm_trc_flg if defined $srvm_trc_flg;

$elapsedSec = time() - $elapsedSec;
#print "Total sec=$elapsedSec\n";
EMD_PERL_INFO("$LOG_CATEGORY ***** END of Database and Listener Discovery***");

##print discovered Oracle Database and Listener targets
sub printDatabaseAndListnerTargets
{
  my ($emdRoot,$hostName) = @ARGV;
  #Get Oracle Homes and Sids
  my ($ohomesRef,$sidsRef) = getOracleHomesAndSids();

  my @oHomesWithLsnrctl;
  my $lsnrctl;
  foreach my $oracleHome (@$ohomesRef)
  {
      $lsnrctl = getListenerControl($oracleHome);
      if(-e $lsnrctl)
      {
          push(@oHomesWithLsnrctl,$oracleHome);
      }
      else
      {
	EMD_PERL_DEBUG("$LOG_CATEGORY $lsnrctl does not exist in $oracleHome; this home is excluded from discovery");
      }
  }

  #Sort the Oracle Homes in the descending order of
  #its listener control version.
  @oHomesWithLsnrctl = sort { compareVersions(getLsnrctlVersion($b) , getLsnrctlVersion($a)) } @oHomesWithLsnrctl;
  $ohomesRef = \@oHomesWithLsnrctl;

  #filterOutBadOracleHomes($ohomesRef, $sidsRef);

  filterOutNotSupportedOracleHomes($ohomesRef, $sidsRef);

  #filter out sids from oratab without initialization or lk file on Solaris
  my $OSNAME = get_osType();
  my $uname = '';
  unless ( $OSNAME eq 'WIN' ) {
      chomp($uname=`/bin/uname`) || chomp($uname=`/usr/bin/uname`);
  }
  if ($uname eq "HP-UX") {
      $ENV{UNIX95} = "XPG4";
  }
  if(!($OSNAME eq 'WIN')) 
  {
    for my $sid (keys %$sidsRef) 
    {
       my $oHome = $sidsRef->{$sid}; 
       if (isValidSid($sid, $oHome))
       {
          next;
       }
       delete $sidsRef->{$sid};
       my $initDir = getDefaultInitFileLocation($oHome);
       EMD_PERL_DEBUG("$LOG_CATEGORY SID=$sid excluded from discovery as it does not have a running pmon process, \"$initDir/lk". uc ($sid) . " file or initialization file.");
    }
  }

      
  my ($clusterRef, $racInstsRefs, $errRefs) = discoverRacTargets($ohomesRef, $hostName);
  
  my %racDBHome;
  my %racDBPort;
  my %racDBLsnrHost;
  my %racDBSID;
  my %racDBServiceName;
  my %racDBGlbName;
  
  # delete rac database entries 
  foreach my $racDB (@{$clusterRef->{RACDATABASES}})
  {
      delete $sidsRef->{$racDB};
      EMD_PERL_DEBUG("$LOG_CATEGORY deleting RAC db $racDB"); 
  }
  
  
  # $racSidDB{$racSid} is the rac database name of the $racSid 
  my %racSidDB;
  
  # add rac instance entries to the $sidRef
  foreach my $goodRacDB (keys %$racInstsRefs)
  {
      my $insts = $racInstsRefs->{$goodRacDB};
      for(my $i=0; $i<@$insts; $i++) 
      {
         my ($node, $sid, $ohome) = @{$insts->[$i]};
         
         if (&equalHosts($node, $hostName))
         {
            if (isValidSid($sid, $ohome))
            {
                $sidsRef->{$sid} = $ohome;
                addToArrayIfNotExisting($ohomesRef, $ohome);
                $racSidDB{$sid} = $goodRacDB;
            }    
            else
            {
                my $initDir = getDefaultInitFileLocation($ohome);
                EMD_PERL_DEBUG("$LOG_CATEGORY SID=$sid excluded from discovery as it does not have a running pmon process, \"$initDir/lk". uc ($sid) . " file or initialization file.");
            }    
         }
      }
   }   
  
  #Discover listeners information and dynamic SIDS configured with 
  #listeners
  my ($dynamicSidsRef,$listenersRef) = discoverListenersSids($ohomesRef);

# $dynamicSidsRef is of the followig form:
# (
#   orcl92 =>    
#   {
#      SERVICE_NAME => orcl92.example.com,
#      ORACLE_HOME => d:\oracle92\ora92,
#      PORT => 1234 (obtained from the first address description)
#    },
#   orcloid =>    
#    {
#      SERVICE_NAME => orclServiceName,
#      ORACLE_HOME => d:\oracle92\test,
#      PORT => 1234
#    },
# )

  
  #Add/(update for) dynamic sid in the sid list obtained 
  #from the standard location
  foreach my $dynmamicSid(keys (%$dynamicSidsRef)) 
  { 
    my $href = $dynamicSidsRef->{$dynmamicSid};
    my $oHome =  $href->{ORACLE_HOME};
    if(!isSupportedHome($oHome))
    {
       #dynamic db home is pre 8i, remove it from discovered db.
       delete $sidsRef->{$dynmamicSid};
       delete $dynamicSidsRef->{$dynmamicSid};
       EMD_PERL_DEBUG("$LOG_CATEGORY deleting dynamic \"$dynmamicSid\" as its Oracle Home \"$oHome\" is not supported."); 
       next;
    }
    #if home is defined and not empty string
    #update the sids list
    if(defined $oHome && trim($oHome) ne "")
    {
    	$sidsRef->{$dynmamicSid} = $oHome;
	EMD_PERL_DEBUG("$LOG_CATEGORY adding dynamic sid \"$dynmamicSid\" with Oracle Home \"$oHome\"."); 
    }
    else
    {
	#Oracle home not found for the dynamic sid, may be on Windows
	if(defined $sidsRef->{$dynmamicSid})
	{
		# sid discovered statically, update the home in the dynamic list
		$href->{ORACLE_HOME} = $sidsRef->{$dynmamicSid};
		EMD_PERL_DEBUG("$LOG_CATEGORY sid discovered statically, update the home in the dynamic list");
		EMD_PERL_DEBUG("$LOG_CATEGORY \"$dynmamicSid\" with Oracle Home \"".$href->{ORACLE_HOME}."\"."); 
	}
	else
	{
		EMD_PERL_WARN("$LOG_CATEGORY discard dynamic \"$dynmamicSid\" : cannot determine its home"); 
	}
    }
  }

  my @dbTargetNames;
  ##print database targets registered dynamically with listeners
  foreach my $discoveredSid (keys (%$sidsRef)) 
  { 
    EMD_PERL_INFO("$LOG_CATEGORY processing sid=\"$discoveredSid\"");
    #check if this sid id dynamically registered with any listener
    my $href = $dynamicSidsRef->{$discoveredSid};
    if(defined $href)
    {
      EMD_PERL_DEBUG("$LOG_CATEGORY sid=\"$discoveredSid\" is found dynamically registered with a listener.");
      my $serviceNamesRef = $href->{SERVICE_NAME};
      my $serviceName = "";
      $serviceName = @$serviceNamesRef[0] if defined $serviceNamesRef; #Default to first element (mostly there would only one element) 	
#      my $addr = $href->{ADDRESSES};

      #$discoveredSid is dynamically registered with a listener
      #Get the Best TCP port of the listener and use it for this sid.
#      my $port = getBestTCPPort($addr)->{PORT};
      my $port = $href->{PORT};
      my $lsnrHost = $href->{HOST};
      ##print this sid target
       my ($err, $targetName);
   
       my $dbn = checkNAddToRac($hostName, $sidsRef->{$discoveredSid}, 
                    $discoveredSid, $racInstsRefs);
         
       if ($dbn ne "")
       {
          # RAC: cluster_database = true
          if ($dbn eq "_INVALID_SID_DETECTED_")
          {
             #let's skip this instance since we don't know its real sid
             #otherwise, it can produce duplicate targets
             EMD_PERL_DEBUG("$LOG_CATEGORY invalid RAC target: $discoveredSid:$sidsRef->{$discoveredSid}, SKIPPED");

             # record the error
             $errRefs->{"$discoveredSid"} = "Invalid cluster database instance - couldn't find the instance_name '$discoveredSid' in oracle home '$sidsRef->{$discoveredSid}'\n";

             delete $sidsRef->{$discoveredSid};

             next;
         }    
         
         $racSidDB{$discoveredSid} = $dbn;
      }
       
      if (defined $racSidDB{$discoveredSid})
      {
           ($err, $targetName) = getTargetName($sidsRef->{$discoveredSid}, $discoveredSid, 1);
           $racDBPort{$racSidDB{$discoveredSid}} = $port;
           $racDBLsnrHost{$racSidDB{$discoveredSid}} = $lsnrHost;
           $racDBSID{$racSidDB{$discoveredSid}} = $discoveredSid;
           $racDBHome{$racSidDB{$discoveredSid}} = $sidsRef->{$discoveredSid};
           $racDBGlbName{$racSidDB{$discoveredSid}} = &getRacDBGlbName($targetName, $racSidDB{$discoveredSid});
                       
	   if($serviceName eq "")
	   {
           	my ($err, $serviceName) = discoveryServiceName($sidsRef->{$discoveredSid},
                    $discoveredSid);
           
           	# record the error for now
           	if ($err ne "")
           	{
              		$errRefs->{"$discoveredSid.ServiceName"} = $err;
           	}
        }
        else # Retrive db name matching service name in case of multiple one
        {
        	my $racServiceName;
        	foreach my $servName (@$serviceNamesRef)
        	{
        		if($servName eq $racSidDB{$discoveredSid})
        		{
                           $racServiceName = $servName;	
                           last;
        		}
        	}
           $serviceName = $racServiceName if defined($racServiceName) and $racServiceName ne "";	 
        }
                    
           $racDBServiceName{$racSidDB{$discoveredSid}} = $serviceName;
           
           if ($CLUSTER_NAME ne "")
           {
               printRACInstanceEntry($discoveredSid,$lsnrHost,$hostName,$sidsRef->{$discoveredSid},
                    $port, $racDBGlbName{$racSidDB{$discoveredSid}}, $serviceName);
           }
           elsif (defined($ENV{OMSENV_WARN_CLUSTER_FOUND}) && ! defined($errRefs->{"WARN_CLUSTER_FOUND"}))
           {
                $errRefs->{"WARN_CLUSTER_FOUND"} = "Unknown";
           }
      }
      else
      {
	   ($err, $targetName) = getTargetName($sidsRef->{$discoveredSid}, $discoveredSid);
	   if($serviceName eq "")
	   {
           	my ($err, $serviceName) = discoveryServiceName($sidsRef->{$discoveredSid}, $discoveredSid);

           	# record the error for now
           	if ($err ne "")
           	{
              		#$errRefs->{"$discoveredSid.ServiceName"} = $err;
           	}
	   }
                    
           printOracleDatabaseEntry($targetName,$discoveredSid,$lsnrHost,$hostName,$sidsRef->{$discoveredSid},
                    $port, $serviceName,\@dbTargetNames);
      }      
      #Delete the printed database target
      delete $sidsRef->{$discoveredSid};
    }
  }

  #Now we have non-dynamic SIDs to associate with a listener
  
  ##print Listener targets and database targets statically register with a listener
  my @listenerTargetNames;
  foreach my $listener (@$listenersRef) 
  {
    EMD_PERL_INFO("$LOG_CATEGORY processing listener \"$listener->{NAME}\" from \"$listener->{FILE}\"");
    #Get the best TCP address of the listener
    my $addr = getBestTCPPort($listener->{ADDRESSES});
    if(!defined $addr)
    {
    	EMD_PERL_INFO("$LOG_CATEGORY listener does not have any TCP port, don't process further.");
	next;
    }
    #get the listener target name given its name
    my $targetName = getListenerTargetName($listener->{NAME},\@listenerTargetNames);
    
    ##print the listener target
    printListenerEntry($targetName, $listener->{FILE},$listener->{NAME},
     $addr->{HOST}, $hostName, $listener->{ORACLE_HOME},$addr->{PORT});
          
    #Get static SID list of the listener
    my $sidsDescs = $listener->{SID_DESC};
    if(defined $sidsDescs)
    {
      #There is a static SID register with this listener
      foreach my $sidDesc (@$sidsDescs)
      {
        my $registerSID = $sidDesc->{SID_NAME};
        #check if a static SID matches any remaining discovered
        #sids, so that we can match this listener with the SID
        foreach my $discoveredSid (keys (%$sidsRef)) 
        { 
          if($registerSID eq $discoveredSid)
          {
    	      EMD_PERL_INFO("$LOG_CATEGORY SID=$discoveredSid  is statically registered with listener \"$listener->{NAME}\" from \"$listener->{FILE}\"");
              if(defined $addr)
              {
    	        EMD_PERL_DEBUG("$LOG_CATEGORY it has some address");
                #$discoveredSid matches $registerSID, associate the 
                #the best TCP address $addr with $discoveredSid.
                ##print the $discoveredSid database target
                 my ($err_t, $targetName);
   
                 my $dbn = checkNAddToRac($hostName, $sidsRef->{$discoveredSid}, 
                            $discoveredSid, $racInstsRefs);
                 
                 if ($dbn ne "")
                 {
                    # RAC: cluster_database = true
                    if ($dbn eq "_INVALID_SID_DETECTED_")
                    {
                       #let's skip this instance since we don't know its real sid
                       #otherwise, it can produce duplicate targets
                       EMD_PERL_DEBUG("$LOG_CATEGORY invalid RAC target: $discoveredSid:$sidsRef->{$discoveredSid}, SKIPPED");
            
                       # record the error
                       $errRefs->{"$discoveredSid"} = "Invalid cluster database instance - couldn't find the instance_name '$discoveredSid' in oracle home '$sidsRef->{$discoveredSid}'\n";
           
                       delete $sidsRef->{$discoveredSid};
            
                       next;
                     }    
                     $racSidDB{$discoveredSid} = $dbn;
                  }

                if (defined $racSidDB{$discoveredSid})
                {
                     ($err_t, $targetName) = getTargetName($sidsRef->{$discoveredSid}, $discoveredSid, 1);
                     $racDBPort{$racSidDB{$discoveredSid}} = $addr->{PORT};
           	     $racDBLsnrHost{$racSidDB{$discoveredSid}} = $addr->{HOST};
                     $racDBSID{$racSidDB{$discoveredSid}} = $discoveredSid;
                     $racDBHome{$racSidDB{$discoveredSid}} = $sidsRef->{$discoveredSid};
                     $racDBGlbName{$racSidDB{$discoveredSid}} = &getRacDBGlbName($targetName, $racSidDB{$discoveredSid});
                     
                     my ($err, $serviceName) = discoveryServiceName($sidsRef->{$discoveredSid},
                               $discoveredSid);
                     # record the error for now
                     if ($err ne "")
                     {
                         $errRefs->{"$discoveredSid.ServiceName"} = $err;
                     }
                               
                     $racDBServiceName{$racSidDB{$discoveredSid}} = $serviceName;

                     if ($CLUSTER_NAME ne "")
                     {
                         printRACInstanceEntry($discoveredSid,$addr->{HOST},
    			                $hostName,$sidsRef->{$discoveredSid},
                                $addr->{PORT}, $racDBGlbName{$racSidDB{$discoveredSid}}, $serviceName);
                     }
                     elsif (defined($ENV{OMSENV_WARN_CLUSTER_FOUND}) && ! defined($errRefs->{"WARN_CLUSTER_FOUND"}))
                     {
                          $errRefs->{"WARN_CLUSTER_FOUND"} = "Unknown";
                     }
                }
                else
                {
                      ($err_t, $targetName) = getTargetName($sidsRef->{$discoveredSid}, $discoveredSid);
                      my ($err, $serviceName) = discoveryServiceName($sidsRef->{$discoveredSid},
                            $discoveredSid, $errRefs);

                      # record the error for now
                      if ($err ne "")
                      {
                         #$errRefs->{"$discoveredSid.ServiceName"} = $err;
                      }

                      printOracleDatabaseEntry($targetName,
                        $discoveredSid,
			$addr->{HOST},
                        $hostName,
                        $sidsRef->{$discoveredSid},$addr->{PORT},$serviceName,\@dbTargetNames);
                }      
                
                #Delete the printed database target
                delete $sidsRef->{$discoveredSid};
              }
          }
        }#End of foreach $sid (keys (%sidOhomes))
      }#End of foreach $sidDesc (@$sidsDescs)
    }#End of if(defined $sidsDescs)
  }#End of foreach $listener (@$listenersRef)

  #Now we have SIDs which are neither dynamically registered 
  #nor statically registered with a discovered listener
  
  EMD_PERL_DEBUG("$LOG_CATEGORY Now we have SIDs which are neither dynamically registered nor statically registered with a discovered listener");

  foreach my $discoveredSid (keys (%$sidsRef)) 
  { 
      EMD_PERL_DEBUG("$LOG_CATEGORY get local listener for $discoveredSid");
      my $addListHref = getLocalListenerAddresses(
                $discoveredSid,$sidsRef->{$discoveredSid});
      #$discoveredSid is dynamically registered with a listener
      #Get the Best TCP port of the listener and use it for this sid.
      if(defined $addListHref)
      {
        my $addr = getBestTCPPort($addListHref);
        if(defined $addr)
        {
            my ($err_t, $targetName);
   
            my $dbn = checkNAddToRac($hostName, $sidsRef->{$discoveredSid}, 
                        $discoveredSid, $racInstsRefs);
             
             if ($dbn ne "")
             {
                # RAC: cluster_database = true
                if ($dbn eq "_INVALID_SID_DETECTED_")
                {
                   #let's skip this instance since we don't know its real sid
                   #otherwise, it can produce duplicate targets
                   EMD_PERL_DEBUG("$LOG_CATEGORY invalid RAC target: $discoveredSid:$sidsRef->{$discoveredSid}, SKIPPED");
        
                   # record the error
                   $errRefs->{"$discoveredSid"} = "Invalid cluster database instance - couldn't find the instance_name '$discoveredSid' in oracle home '$sidsRef->{$discoveredSid}'\n";
        
                   delete $sidsRef->{$discoveredSid};
        
                   next;
                 }    
                 $racSidDB{$discoveredSid} = $dbn;
              }

            if (defined $racSidDB{$discoveredSid})
            {
                 ($err_t, $targetName) = getTargetName($sidsRef->{$discoveredSid}, $discoveredSid, 1);
                 $racDBPort{$racSidDB{$discoveredSid}} = $addr->{PORT};
           	 $racDBLsnrHost{$racSidDB{$discoveredSid}} = $addr->{HOST};
                 $racDBSID{$racSidDB{$discoveredSid}} = $discoveredSid;
                 $racDBHome{$racSidDB{$discoveredSid}} = $sidsRef->{$discoveredSid};
                 $racDBGlbName{$racSidDB{$discoveredSid}} = &getRacDBGlbName($targetName, $racSidDB{$discoveredSid});
                 
                 # use rac database name as the default service name
                 my ($err, $serviceName) = discoveryServiceName($sidsRef->{$discoveredSid},
                           $discoveredSid);

                 # record the error for now
                 if ($err ne "")
                 {
                     $errRefs->{"$discoveredSid.ServiceName"} = $err;
                 }

                 $racDBServiceName{$racSidDB{$discoveredSid}} = $serviceName;

                 if ($CLUSTER_NAME ne "")
                 {
                     printRACInstanceEntry($discoveredSid,$addr->{HOST},$hostName,$sidsRef->{$discoveredSid},
                        $addr->{PORT}, $racDBGlbName{$racSidDB{$discoveredSid}}, $serviceName);
                 }
                 elsif (defined($ENV{OMSENV_WARN_CLUSTER_FOUND}) && ! defined($errRefs->{"WARN_CLUSTER_FOUND"}))
                 {
                      $errRefs->{"WARN_CLUSTER_FOUND"} = "Unknown";
                 }
            }
            else
            {
                ($err_t, $targetName) = getTargetName($sidsRef->{$discoveredSid}, $discoveredSid);
                my ($err, $serviceName) = discoveryServiceName($sidsRef->{$discoveredSid},$discoveredSid);

                # record the error for now
                if ($err ne "")
                {
                   #$errRefs->{"$discoveredSid.ServiceName"} = $err;
                }
                
                printOracleDatabaseEntry($targetName,
                  $discoveredSid,$addr->{HOST},$hostName,
                    $sidsRef->{$discoveredSid},$addr->{PORT},$serviceName,\@dbTargetNames);
            }      

            #Delete the printed database target
            delete $sidsRef->{$discoveredSid};
        }
      }
  }

  # print rac databases

  if ($CLUSTER_NAME ne "")
  {
      printRACDatabaseEntries($racInstsRefs, \%racDBHome, \%racDBLsnrHost, \%racDBPort, 
            \%racDBSID, $hostName, \%racDBServiceName, \%racDBGlbName, $clusterRef);
  }

  # print the error during the discovery  
  printDiscoveryErrors($errRefs, $hostName);
  
}

#Gets a unique listener target name
sub getListenerTargetName
{
      my ($lsnrName,$listenerTargetNames) = @_;
      my $counter = 0;
      my $retTargetName = $lsnrName;
      my $uniqueNotFound = 1;
      while($uniqueNotFound)
      {
        $uniqueNotFound = 0;
        if(contains($retTargetName,@$listenerTargetNames))
        {
           $retTargetName = $lsnrName . $counter;			
           $counter++;
           $uniqueNotFound = 1;
        }
      }
      push(@$listenerTargetNames,$retTargetName);
      return $retTargetName;
}

# Get the TCP address
# prefer the one with 1521 port
sub getBestTCPPort
{
	my ($addrRef) = @_;
	my $tcpAddDetailRef;
	foreach my $addDetailRef (@$addrRef)
	{	
	     if(uc ($addDetailRef->{PROTOCOL}) eq "TCP" )
	     {
      		$tcpAddDetailRef = $addDetailRef if (!defined $tcpAddDetailRef || trim($tcpAddDetailRef->{PORT}) eq "1521" 
      		        || equalHosts($addDetailRef->{HOST}, $LOCAL_HOST_VIP_NAME));
	     }	
  	   if((defined $tcpAddDetailRef) && trim($tcpAddDetailRef->{PORT}) eq "1521")
	     {	
      		 last;	
	     }
	}	
	return $tcpAddDetailRef;	
}

#Gets the listener info
#Also gets a hashtable of port,protocol,ohome, passwords to discover dynamic sids
#with port as the key
sub discoverListenersSids
{
  my ($ohomesRef) = @_;	
  my @listeners;	
  my %dynamicSids;	
  my %portTable;
  my $parseGlobalFile = 0;	
  my $tnsAdminDir;
  my $globalTnsAdminDir;
  my %alreadyParsed = ();

  if ( defined $ENV{TNS_ADMIN} )
  {
    $globalTnsAdminDir = trim ( $ENV{TNS_ADMIN} );
    if (! -d $globalTnsAdminDir )
    {
      $globalTnsAdminDir = "";
    } 
    if($globalTnsAdminDir ne "")
    {
      $parseGlobalFile = 1;
    }
  }
  foreach my $oHome (@$ohomesRef) 
  {
      if($parseGlobalFile )
      {
      	  EMD_PERL_DEBUG("$LOG_CATEGORY Parsing listeners from TNS_ADMIN=$globalTnsAdminDir");
          parseListener($ohomesRef,$globalTnsAdminDir,$oHome,\@listeners,\%portTable,\%alreadyParsed);
          $parseGlobalFile = 0; 
      }
      $tnsAdminDir = getDefaultTNSAdmin($oHome);
      parseListener($ohomesRef,$tnsAdminDir,$oHome,\@listeners,\%portTable,\%alreadyParsed);
  }

  if(!defined $portTable{1521} && defined $ohomesRef->[0])
  {
        #Update Port Table with default TCP port 1521 
	#and for first Oracle home
        my @pswd = ();
        my $address = { PORT=> 1521 , HOST=>$hostName, PROTOCOL=>"TCP" };
        my @addresses;
        push(@addresses,$address);
        updateListenerData($ohomesRef->[0],\%portTable,\@addresses,@pswd);
  }
 
  updateDynamicSids(\%portTable,\%dynamicSids);
  return (\%dynamicSids,\@listeners);
}

sub parseListener
{
    my ($ohomesRef,$tnsAdminDir,$oHome,$listeners,$portTable,$alreadyParsed) = @_;
    my $listenerFile = $tnsAdminDir . "/listener.ora";
    if(-e  $listenerFile)
    {
      my $OSNAME = get_osType();
      #if we are not in Windows, check for file stat
      #else match the file names ignore case.
      if(!($OSNAME eq 'WIN')) 
      {
      	my ($dev, $ino) = stat $listenerFile;
      	if(defined $alreadyParsed->{$dev,$ino})
      	{
      	    EMD_PERL_DEBUG("$LOG_CATEGORY $listenerFile already parsed, curr oh = $oHome");
	    return;
      	}
      	$alreadyParsed->{$dev,$ino}  = $listenerFile;
      }
      else
      {
        if(containsFile($listenerFile,keys %$alreadyParsed))
      	{
      	    EMD_PERL_DEBUG("$LOG_CATEGORY $listenerFile already parsed, curr oh = $oHome");
	    return;
      	}
      	$alreadyParsed->{$listenerFile} = $listenerFile;
      }
      my $listenersInfo = parseOracleConfigFile($listenerFile);
      if(!(defined $listenersInfo))
      {
        next;
      }
      EMD_PERL_DEBUG("$LOG_CATEGORY Listeners from $listenerFile for OH=$oHome");
      my @listenerNames = getListenerNames($listenersInfo);
      foreach my $lsnr (@listenerNames)
      {
        EMD_PERL_DEBUG("$LOG_CATEGORY Name $lsnr");
        my $addresses = getListenerAddresses($lsnr,$listenersInfo);

        # Xun: fix for bug 3178713
        # We'll get rid of all remote addresses (defined as the address whose 
        # host ip as returned by gethostbyname() doesn't match that of the local host
        #
        getRidOfRemoteAddresses($addresses, $hostName);
        if (0 == @$addresses)
        {
            EMD_PERL_DEBUG("$LOG_CATEGORY Skip $lsnr: it has no local address on host $hostName");
            next;
        }

        my $sids = getStaticSIDs($lsnr,$listenersInfo);

      	my @passwords = getPasswords($lsnr,$listenersInfo);
        updateListenerData($oHome,$portTable,$addresses,@passwords);
        #Get listener home
        my $lsnrOHome = getListenerHome($ohomesRef,$lsnr,$oHome,$tnsAdminDir,@passwords);
        EMD_PERL_DEBUG("$LOG_CATEGORY Listener [$lsnr] Ohome=$lsnrOHome");
        my $listener = 
          {
            NAME => $lsnr,
            ADDRESSES => $addresses,
            # where $addresses is an array reference and each entry is a hashtable 
            # containing address parameters like PROTOCOL, HOST , PORT etc.
            # For example:
            # (
            #   {
            #      PROTOCOL => TCP,
            #      HOST => hsunnaa25
            #      PORT => 1234
            #    },
            #      PROTOCOL => IPC,
            #      KEY => EXTPROC
            #    },
            SID_DESC => $sids,
            # where $sids is an array reference and each entry is a hashtable 
            # containing sid parameters like SID_NAME, GLOBAL_DBNAME , ORACLE_HOME 
            # For example:
            # (
            #   {
            #      SID_NAME => emdw1,
            #      ORACLE_HOME => home2
            #    },
            #      GLOBAL_DBNAME => gdbname,
            #      SID_NAME => orcl
            #      ORACLE_HOME => home1
            #    },
            FILE => $tnsAdminDir,
            ORACLE_HOME => $lsnrOHome,
            # where oracle may be empty if this listener is not under an Oracle Home
          };
          push (@$listeners,$listener);
      }
    }
}

#run lsnrctl for each port in the $portTable
#and collect sid information in the dynamic sids
sub updateDynamicSids
{
    my ($portTable,$dynamicSids) = @_;
    #First update dynamic sids for 1521 if present
    #and do the same for rest later
    updateDynamicSidsForPort($portTable,$dynamicSids,1521);
    foreach my $port (keys %$portTable)
    {
      if($port eq 1521)
      {
        next;
      }
      updateDynamicSidsForPort($portTable,$dynamicSids,$port);
    }
}

sub updateDynamicSidsForPort
{
    my ($portTable,$dynamicSids,$port) = @_;
    my $address;
    my $oracleHome;
    my $executable;
    my @discoveredSids;

    my $portInfo = $portTable->{$port};
    if(!defined $portInfo)
    {
      return;
    }
    $oracleHome = $portInfo->{ORACLE_HOME};
    $address = "(ADDRESS=";
    $address .= "(PROTOCOL=".$portInfo->{PROTOCOL}.")"; 
    $address .= "(HOST=".$portInfo->{HOST}.")"; 
    $address .= "(PORT=".$port.")"; 
    $address .=")"; 
    my $passwordList = $portInfo->{PASSWORDS};
    my @commands = ();
    push(@commands,"set displaymode raw");
    push(@commands,"services $address");
    push(@commands,"exit");

    my $result = getLsnrctlOutput($oracleHome,$passwordList,@commands);
    @discoveredSids = (keys %$dynamicSids);
    my $dbDetails = getDBDetailsDiscovery($result,$port,$portInfo->{HOST},@discoveredSids);
    my $sid ;
    EMD_PERL_DEBUG("$LOG_CATEGORY dynamically discovered sids for address \"$address\"");
    foreach $sid (keys %$dbDetails)
    {
        EMD_PERL_DEBUG("$LOG_CATEGORY dynamic sid =\"$sid\"");
        
	if(! defined $dynamicSids->{$sid} || equalHosts($portInfo->{HOST}, $LOCAL_HOST_VIP_NAME))
	{
        	$dynamicSids->{$sid} = $dbDetails->{$sid};
	} 	
    }
}


#Get lsnrctl output for a command
sub getLsnrctlOutput
{
    my ($oracleHome,$passwordList,@cmds) = @_;
    my $executable;

    $executable = getListenerControl($oracleHome);
    my $result = "" ;
    if(-e $executable)
    {
      my @commands ;
      my $totalPswd = @$passwordList;
      my $currPswdInex = 0;
      do
      {
        @commands = ();
        my $pswd;
        #Add password if present
        if($currPswdInex < $totalPswd)
        {
          push(@commands,"set password ".$passwordList->[$currPswdInex++]);
        }
	foreach my $cmdLine (@cmds)
	{
          push(@commands,$cmdLine);
	}
        eval
        {
          $result = getRunCommandOutput($oracleHome,$executable,@commands);
        };
        #if there is a password error try the next password, if there is 
        #password error
        #TNS-01169: The listener has not recognized the password
      }while($result =~ /TNS-01169/i && $currPswdInex < $totalPswd);
    }
    return $result;
}

#Gets the Oracle Home of the listener
#from log or trace directory.
#If the listener is not running from the passed tnsAdminDir, associate the
#passed Oracle Home to the listener.
#By default, associate the passed Oracle Home to the listener.
sub getListenerHome
{
  my ($ohomesRef,$name,$oHome,$tnsAdminDir,@passwords) = @_;

  EMD_PERL_DEBUG("$LOG_CATEGORY In getListenerHome for $name,$oHome,$tnsAdminDir"); 
  my @commands = ();
  push(@commands,"set current_listener $name");
  push(@commands,"status");
  push(@commands,"show log_directory");
  push(@commands,"show trc_directory");
  push(@commands,"exit");

  #Set the TNS_ADMIN to the passed tnsAdminDir, backup the old one
  my $oldTNS_ADMIN = $ENV{TNS_ADMIN};
  $ENV{TNS_ADMIN} = $tnsAdminDir;

  my $result = getLsnrctlOutput($oHome,\@passwords,@commands);
  #Restore the old TNS_ADMIN 
  if(defined $oldTNS_ADMIN)
  {
      $ENV{TNS_ADMIN} = $oldTNS_ADMIN;
  }
  else
  {
      delete ($ENV{TNS_ADMIN});
  }
 
  #If there are no TNS errors and listener is running
  #using the passed tnsAdminDir, check the log and trace directory for OH
  if ($result !~ /^.*TNS-[0-9]*/i && isThisListenerRunning($result,$tnsAdminDir,$name))
  {
      EMD_PERL_DEBUG("$LOG_CATEGORY $name is running from $tnsAdminDir"); 
      my @resultArray = split /\n/, $result;

      foreach my $ln (@resultArray)
      {
          if ( $ln !~ /log_directory/i && $ln !~ /trc_directory/i)
          {
	    next;
          }
          if($ln =~ /^(.*)\"log_directory\"\s+\w+\s+\w+\s+(.*)/i)
          {
  	    my $lsnrHome ;
	    return $lsnrHome if (defined ($lsnrHome= getHomeFromDir($ohomesRef,"log",$2,$oHome)));
          }
          if($ln =~ /^(.*)\"trc_directory\"\s+\w+\s+\w+\s+(.*)/i)
          {
  	    my $lsnrHome ;
	    return $lsnrHome if (defined ($lsnrHome= getHomeFromDir($ohomesRef,"trace",$2,$oHome)));
          }
      }
  }
  else
  {
      EMD_PERL_DEBUG("$LOG_CATEGORY $name is NOT running from $tnsAdminDir"); 
  }

  return $oHome;
}

#Checks the log/trace directory for Oracle Home of the listener
#If the log directory matches OH/network/log for any valid OH, then that's
#the Oracle Home of the listener.
#If the trace directory matches OH/network/trace for any valid OH, then that's
#the Oracle Home of the listener.
#Otherwise, return undefined home.
sub getHomeFromDir
{
  my ($ohomesRef,$dirName,$dir,$oHome) = @_; 
  my $foundHome;
  my $endingSlash = "/" if ($dir =~ /\/$/ || $dir =~ /\\$/);
  EMD_PERL_DEBUG("$dirName directory = [$dir] compare against [$oHome/network/$dirName$endingSlash]");
  #Sometimes there is an ending slash in the directory
  #need to add that to the comparing directory.
  #Otherwise, comparison fails on Windows
  if(isSameFileSystemEntity(
    $dir,"$oHome/network/$dirName$endingSlash"))
  {
     $foundHome = $oHome;
     EMD_PERL_DEBUG("$LOG_CATEGORY Passed home is the home"); 
  }
  else
  {
   foreach my $home (@$ohomesRef) 
   {
     if($home ne $oHome &&
 	 isSameFileSystemEntity(
 	$dir,"$home/network/$dirName$endingSlash"))
      {
         $foundHome = $home;
         EMD_PERL_DEBUG("$LOG_CATEGORY Non-Passed home is the home"); 
         last;
      }
    }
  }
  return $foundHome;
}

#Update $portTable with port, protocol,ohome, passwords to discover dynamic sids
#with port as the key
sub updateListenerData
{
    my ($oracleHome,$portTable,$addresses,@passwords) = @_;
    my ($port,$protocol,$host) = ("","","");
    foreach my $addr (@$addresses)
    {
        $port = $addr->{PORT} if defined($addr->{PORT});          
        $protocol = $addr->{PROTOCOL} if defined($addr->{PROTOCOL});          
        $host = $addr->{HOST} if defined($addr->{HOST});          
        EMD_PERL_DEBUG("$LOG_CATEGORY Address = $host,$port,$protocol");
        if(
          defined $port && $port ne "" 
          && defined $protocol && $protocol ne "" 
          )
        {
          my $portInfo = $portTable->{$port};
          if(defined $portInfo)
          {
            #info already present, need to update info
            #password only
              
            #update passwords
            my $passwordList = $portInfo->{PASSWORDS};
            my $pswd;
            foreach $pswd (@passwords)
            {
               push(@$passwordList,$pswd);
            }
          }
          else
          {
                $portInfo->{ORACLE_HOME} = $oracleHome;
                $portInfo->{PROTOCOL} = $protocol;
                $portInfo->{HOST} = $host;
                my @passwordList = ();	
                foreach my $pswd (@passwords)
                {
                   push(@passwordList,$pswd);
                }
                $portInfo->{PASSWORDS} = \@passwordList;
                $portTable->{$port} = $portInfo;
          }
        }
    }
}

##print listener target entry
sub printListenerEntry
{
      my ($targetName,$oraDir,$name,$lsnrHost,$hostName,$oHome,$port) = @_;
      $lsnrHost = resolveLsnrHostName($lsnrHost,$hostName);

     print "<Target TYPE=\"oracle_listener\" NAME=\"${targetName}_$hostName\" >\n";
     print "<Property NAME=\"ListenerOraDir\" VALUE=\"$oraDir\" />\n";
     print "<Property NAME=\"LsnrName\" VALUE=\"$name\" />\n";
     print "<Property NAME=\"Machine\" VALUE=\"$lsnrHost\" />\n";
     print "<Property NAME=\"OracleHome\" VALUE=\"$oHome\" />\n";
     print "<Property NAME=\"Port\" VALUE=\"$port\" />\n";
     print "</Target>\n";

     EMD_PERL_INFO("$LOG_CATEGORY listener target \"${targetName}_$hostName\" discovered. Details: [$oraDir,$name,$lsnrHost,$hostName,$oHome,$port]");
}

#Gets a unique Database target name
sub getDatabaseTargetName
{
      my ($targetName,$sid,$hostName,$dbTargetNames) = @_;
      my $useSidHostName = 0;
      if(trim($targetName) eq "")
      {
          $targetName = "${sid}_$hostName";
          $useSidHostName = 1;
      }
      my $counter = 0;
      my $retTargetName = $targetName;
      my $uniqueNotFound = 1;
      my $dbTargetName;
      while($uniqueNotFound)
      {
        $uniqueNotFound = 0;
        if(contains($retTargetName,@$dbTargetNames))
        {
           if($useSidHostName)
           {
             $retTargetName = $sid."_".$counter."_".$hostName;			
           }
           else
           {
             $retTargetName = $targetName . $counter;			
           }
           $counter++;
           $uniqueNotFound = 1;
        }
      }
      push(@$dbTargetNames,$retTargetName);
      return $retTargetName;
}

##print Oracle Database target entry
# if encounters '+' in SID, print to osm targets instead
sub printOracleDatabaseEntry
{
      my ($targetName,$sid,$lsnrHost,$hostName,$oHome,$port,$servName,$dbTargetNames) = @_;
     $lsnrHost = resolveLsnrHostName($lsnrHost,$hostName);

      $targetName = getDatabaseTargetName($targetName,$sid,$hostName,$dbTargetNames);
 
      # if discovered SID is preceded with '+', it belongs to osm target list
      if ( $sid =~ /^\+/ )
      {
        printOracleStorageManagerEntry( $targetName, $sid, $hostName, $oHome,
                                        $port, $servName );
        return;
      }
 
     print "<Target TYPE=\"oracle_database\" NAME=\"$targetName\" >\n";
      print "<Property NAME=\"OracleHome\" VALUE=\"$oHome\" />\n";
      print "<Property NAME=\"UserName\" VALUE=\"dbsnmp\" ENCRYPTED=\"FALSE\"/>\n";
      if(isPasswordValid("dbsnmp","dbsnmp",$oHome, $sid))
      {
      	print "<Property NAME=\"password\" VALUE=\"dbsnmp\" ENCRYPTED=\"FALSE\"/>\n";
      }
      print "<Property NAME=\"MachineName\" VALUE=\"$lsnrHost\" />\n";
      print "<Property NAME=\"Port\" VALUE=\"$port\" />\n";
      print "<Property NAME=\"SID\" VALUE=\"$sid\"/> \n";
      print "<Property NAME=\"ServiceName\" VALUE=\"$servName\"/> \n";      
     # print "<Property NAME=\"emdPerlTraceLevel\" VALUE=\"\"/> \n";
      print "</Target>\n";
     EMD_PERL_INFO("$LOG_CATEGORY database target \"$targetName\" discovered. Details: [$targetName,$sid,$lsnrHost,$hostName,$oHome,$port,$servName]");
}

# print Oracle Storage Manager target entries
# service name and emd perl trace currently not in use
sub printOracleStorageManagerEntry
{
    my ($targetName,$sid,$hostName,$oHome,$port,$servName) = @_;
    
    # target name already processed
    print "<Target TYPE=\"osm_instance\" NAME=\"$targetName\" >\n",
    "<Property NAME=\"OracleHome\" VALUE=\"$oHome\" />\n",
    "<Property NAME=\"UserName\" VALUE=\"sys\" ENCRYPTED=\"FALSE\"/>\n",
    "<Property NAME=\"Role\" VALUE=\"sysdba\" ENCRYPTED=\"FALSE\"/>\n",
    "<Property NAME=\"MachineName\" VALUE=\"$hostName\" />\n",
    "<Property NAME=\"Port\" VALUE=\"$port\" />\n",
    "<Property NAME=\"SID\" VALUE=\"$sid\"/> \n",
    #      "<Property NAME=\"ServiceName\" VALUE=\"$servName\"/> \n",      
    #      "<Property NAME=\"emdPerlTraceLevel\" VALUE=\"\"/> \n",
    "</Target>\n";
     EMD_PERL_INFO("$LOG_CATEGORY osm_instance target \"$targetName\" discovered. Details: [$targetName,$sid,$hostName,$oHome,$port,$servName]");
}


#Get the initialization file for the sid given SID and Ohome
sub getInitFile
{
  my ($SID,$OracleHome) = @_;

  my $initDir = getDefaultInitFileLocation($OracleHome);
  my $retInitFile;
  if(-e ($initDir . "/spfile$SID.ora"))
  {
    $retInitFile =  ($initDir . "/spfile$SID.ora");
  }
  elsif (-e ($initDir . "/spfile.ora"))
  {
    $retInitFile =  ($initDir . "/spfile.ora");
  }
  elsif (-e ($initDir . "/init$SID.ora"))
  {
    $retInitFile = ($initDir . "/init$SID.ora");
  }
  
  return $retInitFile;
}

#Get the local listener address for the sid given SID and Ohome
#Return a reference for an array of addresses
# (
#    {
#         PROTOCOL = "TCP",  
#         HOST = "machine",  
#         PORT = "1234",  
#     },
#    {
#         PROTOCOL = "TCP",  
#         HOST = "machine",  
#         PORT = "1222",  
#     }
# )
#
sub getLocalListenerAddresses
{
  my ($SID,$OracleHome) = @_;

  my $initFile = getInitFile($SID,$OracleHome);

  #The default listener
  my @defaultAddresses = ( 
          {
               PROTOCOL => "TCP",  
               HOST => $hostName,  
               PORT => "1521",  
           }
       );
  my $retAddresses = \@defaultAddresses;
  
  my @addresses;
  if (defined $initFile && (-e $initFile))
  {
    my ($localListenerValue ) =  discoverParameterValue($OracleHome, 
            $SID, "LOCAL_LISTENER", $SID);
    if(defined $localListenerValue)
    {
      my $addresses = getAddresses($localListenerValue);
      if(@$addresses == 0)
      {
        my @namedAddresses = getCharsSeparatedWords($localListenerValue,",");
        #resolve named address to an address or address list from the 
        #tnsnames.ora
        my $addresses = getAddressesFromNames($OracleHome,@namedAddresses,);
        if(defined $addresses && @$addresses != 0)
        {
           $retAddresses = $addresses;
        }
      }
      else
      {
        $retAddresses = $addresses;
      }
    }
  }

  return $retAddresses;    
}

sub getAddressesFromNames
{
    my ($OracleHome,@namedAddresses) = @_;
    # Check if ORATAB env is specified
    my $tnsnamesFile ;
    my $addresses;
    if ( defined $ENV{TNS_ADMIN} )
    {
      my $tnsAdminDir = trim ( $ENV{TNS_ADMIN} );
      if (! -d $tnsAdminDir )
      {
        $tnsAdminDir = "";
      } 
      if($tnsAdminDir ne "")
      {
        $addresses = resolveAddressesFromNames($tnsAdminDir,\@namedAddresses)   
      }
    }
    if(! (defined $addresses))
    {
      $addresses = resolveAddressesFromNames(getDefaultTNSAdmin($OracleHome),\@namedAddresses) ;
    }
    return $addresses;
}


sub resolveAddressesFromNames
{
    my ($tnsAdminDir,$namedAddresses) = @_;
    my $addresses;
    my $tnsnamesFile;
    my $tnsnamesInfoRef ;

    my $sqlnetFile ;
    my $sqlnetInfo ;
    my $addrValue;
    my $defaultDomain = "";
    
    #print "TNSADMIN = [$tnsAdminDir]\n";
    if ( -d $tnsAdminDir )
    {
      $tnsnamesFile = $tnsAdminDir ."/tnsnames.ora";
      #print "tnsnames.ora file = [$tnsnamesFile]\n";
      if(-e $tnsnamesFile )
      {
        $sqlnetFile = $tnsAdminDir ."/sqlnet.ora";
        $sqlnetInfo = parseOracleConfigFile($sqlnetFile);
        if(defined $sqlnetInfo)
        {
          $defaultDomain = trim($sqlnetInfo->{"NAMES.DEFAULT_DOMAIN"});
          #print "setting default domain = [$defaultDomain]\n";
        }
      }
          
      $tnsnamesInfoRef = parseOracleConfigFile($tnsnamesFile);
      if(defined $tnsnamesInfoRef)
      {
        foreach my $namedAddr (@$namedAddresses)
        {
            if($namedAddr !~ /\./)
            { 
              #Does not contain domain, add domain 
              if($defaultDomain ne "")
              {
                $namedAddr .=".".$defaultDomain;
                #print "Domain added [$defaultDomain]\n";
              }
            }
            #print "Check named address [$namedAddr]\n";
            $addrValue = $tnsnamesInfoRef->{uc ($namedAddr)};
#            #print "Value = [$addrValue]\n";
            if(defined $addrValue)
            {
              $addresses = getAddresses($addrValue);
            }#End of if(defined $addrValue)
        }# End of foreach $namedAddr (@$namedAddresses)
      }# End of if(defined $tnsnamesInfo)
    }# End of if ( -d $tnsAdminDir )
    return $addresses;
}

# Return: an array of words separated by zero or more given chars
sub getCharsSeparatedWords
{
      my ($paramValue,$charList) = @_;
      my $wd;
      my @words;
      #print "paramValue =[$paramValue],  charList =[$charList] \n";
      while ($paramValue =~ /(\s*\w+\s*)[$charList]*/g) 
      {
            $wd = $1;
            #print "wd =[$wd]\n";
            $wd =~ s/^\s*|\s*$//;
            push @words, $wd if ($wd ne "");
      }
      return @words;
}      



#strips leading and trailing spaces and returns the string
sub trim 
{
  my $origStr = $_[0];
  #Strip trailing and leading
  $origStr =~ s/^\s*|\s*$//g;
  return $origStr;
}

##############################################################################
##                 Start of RAC Discovery Functions                          #
##############################################################################

# Usage: printRACDatabaseEntries($racInstsRefs, \%racDBHome, 
#              \%racDBLsnrHost, \%racDBPort, \%racDBSID, $hostName, \%racDBServiceName, \%racDBGlbName, $clusterRef);
# $racInstsRefs is the one returned by discoverRacTargets()
# $hostName is the second command line argument    
# %racDBHome, %racDBPort, %racDBSID, %racDBServiceName, %cluster are as defined 
# in printDatabaseAndListnerTargets
sub printRACDatabaseEntries
{
    my ($racInstsRefs, $racDBHome, $racDBLsnrHost, $racDBPort, $racDBSID, $hostName, $servName, $racDBGlbName, $cluster) = @_;
   
    # good rac databases
    foreach my $racDB (keys %$racInstsRefs)
    {
      if (defined $racDBSID->{$racDB})
      {
        my $assocTargetName = &getInstName($racDBGlbName->{$racDB}, $racDBSID->{$racDB});
        my $racTarget = $racDBGlbName->{$racDB};
        $racTarget = $racTarget . $hintParams{"db_target_suffix"} if defined $hintParams{"db_target_suffix"};
        $racTarget = $hintParams{"db_target_prefix"}. $racTarget  if defined $hintParams{"db_target_prefix"};
         
print <<__END;
<Target TYPE="rac_database" NAME="$racTarget">
  <Property NAME="ServiceName" VALUE="$servName->{$racDB}"/>
  <Property NAME="ClusterName" VALUE="$cluster->{NAME}"/>
  <AssocTargetInstance ASSOCIATION_NAME="rac_instance" ASSOC_TARGET_TYPE="oracle_database" ASSOC_TARGET_NAME="$assocTargetName"/>
</Target>
__END
       EMD_PERL_INFO("$LOG_CATEGORY rac_database target \"$racTarget\" discovered. Details: [$racTarget,$assocTargetName ( $racDBSID->{$racDB} ),$servName->{$racDB},$cluster->{NAME}]");
     } 
     else
     {
       EMD_PERL_DEBUG("$LOG_CATEGORY SKIPPED Rac database '$racDB': no valid sid found on the host");
     }        
   }
}

# Usage: printDiscoveryErrors($errRefs, $hostName)
# $errRefs is the one returned by discoverRacTargets() and further filled by discoveryServiceName()
sub printDiscoveryErrors
{
    my ($errRefs, $hostName) = @_;
    
    foreach my $err (keys %$errRefs)
    {
        my $escapedErrMsg = escapeXMLChar($errRefs->{$err});
        $escapedErrMsg = convertToUTF8($escapedErrMsg);
print <<__END;
<DiscoveryWarning DISCOVERY_SCRIPT="$hostName : $err">
$escapedErrMsg
</DiscoveryWarning>
__END
    }    

#<Target TYPE="rac_discovery_error" NAME="$err">
#  <Property NAME="MachineName" VALUE="$hostName"/>
#  <Property NAME="Message" VALUE="$escapedErrMsg"/>
#</Target>

}    

##print Oracle Database target entry
sub printRACInstanceEntry
{
      my ($sid,$lsnrHost, $hostName,$oHome,$port,$racDBName, $servName) = @_;
      
      $lsnrHost = resolveLsnrHostName($lsnrHost,$hostName);
      my $instName = &getInstName($racDBName, $sid);
      
      my $dbsmpPasswd ="dbsnmp";
      
      $racDBName = $racDBName . $hintParams{"db_target_suffix"} if defined $hintParams{"db_target_suffix"};
      $racDBName = $hintParams{"db_target_prefix"}. $racDBName  if defined $hintParams{"db_target_prefix"};
      print "<Target TYPE=\"oracle_database\" NAME=\"$instName\" >\n";
      print "<Property NAME=\"OracleHome\" VALUE=\"$oHome\" />\n";
      print "<Property NAME=\"UserName\" VALUE=\"dbsnmp\" ENCRYPTED=\"FALSE\"/>\n";
      if(isPasswordValid("dbsnmp",$dbsmpPasswd,$oHome, $sid))
      {
      	print "<Property NAME=\"password\" VALUE=\"$dbsmpPasswd\" ENCRYPTED=\"FALSE\"/>\n";
      }
      print "<Property NAME=\"MachineName\" VALUE=\"$lsnrHost\" />\n";
      print "<Property NAME=\"Port\" VALUE=\"$port\" />\n";
      
      my $osid = (defined $RacSidConversionMap{$sid})? $RacSidConversionMap{$sid} : $sid;
      print "<Property NAME=\"SID\" VALUE=\"$osid\"/> \n";
      print "<Property NAME=\"ServiceName\" VALUE=\"$servName\"/> \n";
      print "<CompositeMembership> \n";
      print "    <MemberOf TYPE=\"rac_database\" NAME=\"$racDBName\" ASSOCIATION=\"cluster_member\"/>\n";
      print "</CompositeMembership> \n";
      print "</Target>\n";
}

# Usage: ($clusterRef, $racInstsRefs, $errRefs) = discoverRacTargets($ohomesRef, $hostName);
# $ohomesRef is the reference of an array of Oracle Homes
# return reference of three hashtables. See findRacTargetsFromOracleHome() for 
# more details
#     
sub discoverRacTargets
{
    my (@oracleHomes) = @{$_[0]};
    my $hostName = $_[1];
    addToArrayIfNotExisting(\@oracleHomes, $ENV{ORACLE_HOME}) if (defined $ENV{ORACLE_HOME});
    
    my ($clusterRef, $racInstsRefs, $errRefs);
    
    my $oracleHome;
    my ($racOracleHome, $srvctlVersion) = ("", "0.0.0.0.0");
    my %errs;

    my ($clusterName, $crsOh) = ("","");
    
    my $warn_cluster_found = 0;

    # discover clusterName and crsOh 
    ($clusterName, $crsOh) = getClusterName($emdRoot, $crsHome);
    
    if (defined $ENV{CLUSTER_NAME})
    {
       # UI can mandidate a clusterwide discovery
       $clusterName = $ENV{CLUSTER_NAME};
       EMD_PERL_DEBUG("$LOG_CATEGORY ENV{CLUSTER_NAME}=$clusterName");
    }
    
    # if the UI wants to be warned when cluster is found, do so
    if ($clusterName ne "" && defined $ENV{OMSENV_WARN_CLUSTER_FOUND})
    {
      $warn_cluster_found = 1;
    }
    
    if ($clusterName eq "")
    {
        # if clusterName is empty, then we are not in a cluster. Stop here
        EMD_PERL_DEBUG("$LOG_CATEGORY discoverRacTargets: not in a cluster");    
        return ((), (), ());
    }    

    # remember it's in a cluster
    $CLUSTER_NAME = $clusterName;
    $CRS_HOME = $crsOh if (defined($crsOh));
    
    EMD_PERL_DEBUG("discoverRacTargets: cluster is '$clusterName'");    
    
    # get newest srvctl
    foreach $oracleHome (@oracleHomes)
    {
        EMD_PERL_DEBUG("$LOG_CATEGORY discoverRacTargets: looking in $oracleHome...");
        if (-e "$oracleHome/bin/srvctl" || -e "$oracleHome/bin/srvctl.bat")
        {
            my ($err, $version) =  getSrvCtlVersion($oracleHome);

            if ($err eq "")
            {
                EMD_PERL_DEBUG("$LOG_CATEGORY discoverRacTargets: srvctl found, version $version");

                $err = srvctl_sanity_check($oracleHome, $version);
                if ($err eq "")
                {
                    if (compareVersions($version, $srvctlVersion) == 1)
                    {
                        $srvctlVersion = $version;
                        $racOracleHome = $oracleHome;
                    }    
                }
                else
                {
                    $errs{$oracleHome} = $err;
                }    
            }
            else
            {
                $errs{$oracleHome} = $err;
            }
        }    
    }
    
    if ($racOracleHome ne "")
    {
        ($clusterRef, $racInstsRefs, $errRefs) = 
            findRacTargetsFromOracleHome($racOracleHome, $hostName, $srvctlVersion);
    }    
    
    $clusterRef->{NAME} = $clusterName;
    
    # we need to put the errors returned by getSrvCtlVersion() in the $errRefs
    foreach my $k (keys %errs)
    {
        $errRefs->{$k} = $errs{$k};
    }
    
    if ($warn_cluster_found)
    {
        $errRefs->{"WARN_CLUSTER_FOUND"} = $clusterName;
    }    
       
   ($clusterRef, $racInstsRefs, $errRefs);
}        

# Usage: ($clusterRef, $racInstsRefs, $errRefs) = 
#           findRacTargetsFromOracleHome($oracleHome, $hostName, $srvctlVersion)
# $srvctlVersion is optional. If not specified, the program will try to find out
#
# Logic:
#   if ("$oracleHome/bin/lsnodes" exists)
#   {
#        1. run "$OracleHome/bin/lsnodes" to get the cluster nodes
#        2. run "$OracleHome/bin/srvctl config" to get all rac databases in the cluster
#        3. foreach $racDatabase discovered in 2
#           {
#               run "$OracleHome/bin/srvctl config database -d $racDatabase" (for version >= 9.2)
#                  or "$OracleHome/bin/srvctl config database -p $racDatabase -n $hostName" (9.0.1)
#                  to get the ($racInstanceName, $racInstanceOracleHome) on the host being discovered.
#           }     
#  }
#
# return references of three hashtables ($clusterRef, $racInstsRefs, $errRefs), in the following format
#
# $clusterRef = {
#        #NAME => "cluster1",
#        #NODES => [ "sun1.example.com", "sun2.example.com", "sun3.example.com" ],
#        RACDATABASES => [ "racDB1", "racDB2", "racDB3", "racDB4" ]
# }
# 
# For now, $racInstsRefs only contains rac instances on $hostName. But the data structure is extensible
# for instances not on the $hostName
# $racInstsRefs = {
#           
#     "racDB1" => [ 
#                     [ "sun1.example.com", "racDB11", "/oracle/9.2.0" ],
#                     #[ "sun2.example.com", "racDB12", "/oracle/9.2.0" ]
#                 ],
#
#     
#     "racDB3" => [ 
#                     [ "sun1.example.com", "racDB31", "/oracle/9.3.0" ],
#                     #[ "sun2.example.com", "racDB32", "/oracle/9.3.0" ],
#                     #[ "sun3.example.com", "racDB32", "/oracle/9.3.0" ]  
#                 ]
# }   
# 
# $errRefs = {
#     "racDB3" => "cannot execute "srvctl ...",
#     "racDB4" => "PRKR-1007 : ...",
#     "/oracle/home" =>  "bad '/oracle/home/bin/srvctl config database'..",
# }
#   
#
# 
sub findRacTargetsFromOracleHome
{
    my ($oracleHome, $hostName, $srvctlVersion) = @_;
    my ($clusterRef, $racInstsRefs, $errRefs) = ((), (), ());
    
    my ($cmd, $err, $output);
    
    # bug 3469118 workaround
    my $orgDIR = changeCWDForSrvctlIfNecessary($oracleHome, $srvctlVersion);

    EMD_PERL_DEBUG("$LOG_CATEGORY findRacTargetsFromOracleHome: discovering RAC targets in $oracleHome");    
    
    if (compareVersions($srvctlVersion, "10.0.0.0.0") >= 0)
    {
        #Get the VIP of the machine
        $LOCAL_HOST_VIP_NAME = getVIPName($oracleHome, $hostName);
        EMD_PERL_DEBUG("VIP Name on $hostName is $LOCAL_HOST_VIP_NAME");
    }    
    
    #get the rac databases in the cluster
    if (-e "$oracleHome/bin/srvctl" || -e "$oracleHome/bin/srvctl.bat")
    {
        $cmd = "$oracleHome/bin/srvctl config"; 
        EMD_PERL_DEBUG("$LOG_CATEGORY findRacTargetsFromOracleHome: executing 'srvctl config'");    

        my $lib_path = unset_lib_path_env();
        ($err, $output) = executeCommand($cmd, $oracleHome);
        set_lib_path_env($lib_path);
                
        if ($err ne "")
        {
            $errRefs->{$oracleHome} = $err;
        }
        else
        {
            #rac database found
            EMD_PERL_DEBUG("$LOG_CATEGORY findRacTargetsFromOracleHome: 'srvctl config' return $output");
            
            my @racDBList = split /\n/, $output;
            $clusterRef->{RACDATABASES} = \@racDBList;
            
            if ($srvctlVersion eq "")
            {
                ($err, $srvctlVersion) = getSrvCtlVersion($oracleHome);
                if ($err ne "")
                {
                    $errRefs->{$oracleHome} = $err;
                }
            }
            
            if ($srvctlVersion ne "")
            {
                my $before92 = (compareVersions($srvctlVersion, "9.2.0.0.0") == -1);
                
                foreach my $racDB (@racDBList)
                {
                    if ($before92)
                    {
                        # 9.0.1 database
                        $cmd = "$oracleHome/bin/srvctl config database -p $racDB -n $hostName"
                    }
                    else
                    {    
                        $cmd = "$oracleHome/bin/srvctl config database -d $racDB"; 
                    }
                    
                    EMD_PERL_DEBUG("$LOG_CATEGORY findRacTargetsFromOracleHome: executing $cmd");
                        
                    my $lib_path = unset_lib_path_env();    
                    ($err, $output) = executeCommand($cmd, $oracleHome);
                    set_lib_path_env($lib_path);
                    
                    
                    # on NT, CAPITAL case is used for simplicity
                    $racDB = convertSIDForOS($racDB);
                    
                    if ($err ne "")
                    {
                        $errRefs->{$racDB} = $err;
                    }
                    else
                    {    
                        EMD_PERL_DEBUG("$LOG_CATEGORY findRacTargetsFromOracleHome: returned $output");
                        
                        my @instRefAry = ();
                        my @instInfs = split /\n/, $output;
                     
                        foreach my $instInf (@instInfs)
                        {
                            if ($instInf =~ /^([^\s]+)\s([^\s]+)\s(.+)$/)
                            {
                                if (&equalHosts($1, $hostName))
                                {
                                    # on NT, CAPITAL case is used for simplicity
                                    my $racsid = convertSIDForOS($2);
                                    
                                    # 3667625 workaround for rac sid mismatch problem 
                                    $RacSidConversionMap{$racsid} = $2;

                                    push @instRefAry, [$1, $racsid, $3];
                                    EMD_PERL_DEBUG("$LOG_CATEGORY findRacTargetsFromOracleHome: Instance [$1, $2, $3] is on the host");
                                    
                                    # Bug 3468650 - contine to find all other instances on the host
                                    # last;
                                }    
                            }
                            else
                            {
                                $errRefs->{$racDB} = "Unknown format from output of "
                                        . "\"$cmd\": " . $output;
                                
                                EMD_PERL_ERROR("findRacTargetsFromOracleHome: $errRefs->{$racDB}");
                                last;
                            }  
                        }
                        
                        if (!defined $errRefs->{$racDB} && @instRefAry)
                        {
                            $racInstsRefs->{$racDB} = \@instRefAry;
                        }    
                    }        
                }    
            }    
        }    
    }
    else
    {
        $errRefs->{$oracleHome} = "$oracleHome/bin/srvctl doesn't exist";
        EMD_PERL_ERROR("findRacTargetsFromOracleHome: $errRefs->{$oracleHome}");
    }    

    # bug 3469118 workaround: we'll revert the cwd 
    if($orgDIR ne "__CURRENT_DIR_UNCHANGED__")
    {
        chdir($orgDIR);
    }    
    
    ($clusterRef, $racInstsRefs, $errRefs);     
}    

# getSrvCtlVersion($OracleHome)
# return ($err, $version)
# when $err is empty, the $version contains the version $OracleHome/bin/srvctl
sub getSrvCtlVersion
{
    my $oracleHome = $_[0];
    my ($err, $output, $version);
    
    if (-e "$oracleHome/bin/srvctl" || -e "$oracleHome/bin/srvctl.bat")
    {
        my $lib_path = unset_lib_path_env();    
        ($err, $output) = executeCommand("$oracleHome/bin/srvctl -V", $oracleHome);
        set_lib_path_env($lib_path);
        
        if ($err eq "")
        {
            if ($output =~ /\d+\.\d+\.\d+\.\d+/)
            {
                $version = $&;
            }    
            else
            {
                $err = "Wrong version format: $output";
                EMD_PERL_ERROR("getSrvCtlVersion: $err");
            }    
        }
    }
    else
    {
        $err = "$oracleHome/bin/srvctl doesn't exist";
        EMD_PERL_ERROR("getSrvCtlVersion: $err");
    }
    
    ($err, $version);
}

# Usage: ($err, $output) = executeCommand($cmd, $envOracleHome, $envJavaHome)
#  
# Run the $cmd and 
# if success, return the empty to $err and output string to $output
# else, return the error msg to $err and empty to $output

sub executeCommand
{
    my ($cmd, $envOracleHome, $envJavaHome) = @_;
    my ($oldOH, $oldJH);
    
    $oldOH = $ENV{ORACLE_HOME};
    if ($envOracleHome ne "")
    {
        $ENV{ORACLE_HOME} = $envOracleHome;
    }
    else
    {
        delete $ENV{ORACLE_HOME};
    }

    $oldJH = $ENV{JAVA_HOME};
    if ($envJavaHome ne "")
    {
        $ENV{JAVA_HOME} = $envJavaHome;
    }
    else
    {
        delete $ENV{JAVA_HOME};
    }
    
    
    my $err = "";
    my $output = "";

    if (open(CMDOUTPUT, "$cmd 2>&1 |"))
    {
        my @outputs = <CMDOUTPUT>;
        $output = join "", @outputs;

        if (!close(CMDOUTPUT))
        {
            # close error
            if ($output ne "")
            {
               $err = "\"${cmd}\" returned: \"" . $output . "\"";
               $output = "";
            }
            else
            {
               $err = "bad \"$cmd\": $! $?";
            }   
        }   
    }
    else
    {
        # open error
        $err = "cannot execute \"$cmd\": $!";
    }    

    if ($oldOH eq "")
    {
        delete $ENV{ORACLE_HOME};
    }
    else
    {
        $ENV{ORACLE_HOME} = $oldOH;
    }
    
    if ($oldJH eq "")
    {
        delete $ENV{JAVA_HOME};
    }
    else
    {
        $ENV{JAVA_HOME} = $oldJH;
    }
    
    EMD_PERL_ERROR("executeCommand: $cmd: $err") if ($err ne "");
    
    ($err, $output);
}    

sub escapeXMLChar
{
    my $newStr = $_[0];
    $newStr =~ s/&/&amp;/g;
    $newStr =~ s/</&lt;/g;
    $newStr =~ s/>/&gt;/g;
    $newStr =~ s/\"/&quot;/g;
    $newStr =~ s/'/&apos;/g;
    $newStr;
}

# compareVersions($v1, $v2)
# return 1 if $v1 > $v2
#        0 if $v1 = $v2
#        -1 if $v1 < $v2
sub compareVersions
{
    my ($v1, $v2) = @_;
    my @subv1;
    my @subv2;
    
    while ($v1 =~ /\d+/g)
    {
        push @subv1, $&;
    }    
    
    while ($v2 =~ /\d+/g)
    {
        push @subv2, $&;
    }    
    
    my $size = (@subv1 > @subv2)? @subv1 : @subv2;
    
    my $i;
    for($i=0; $i<$size; $i++)
    {
        if ($subv1[$i] > $subv2[$i])
        {
            return 1;
        }
        
        if ($subv1[$i] < $subv2[$i])
        {
            return -1;
        }
    }
    
    return 0;
}

# addToArrayIfNotExisting ($aryRef, $string)
# if there is no element, $s, in @$aryRef such that ($s eq $string), then
# push @$aryRef, $string
sub addToArrayIfNotExisting 
{
    my ($aryRef, $string) = @_;
    
    foreach my $s (@$aryRef)
    {
        if ($s eq $string)
        {
            return 0;
        }
    }
    
    push @$aryRef, $string;
    1;
}

# Usage: ($err, $targetName) = getTargetName($oracleHome, $sid, $isRac)
# return the database target name if found. If any error occured, it'll be returned in $err
# 
# Logic:
# find value of db_unique_name
# if not found and db_domain exists return db_name.db_domain
# else return db_name
#
sub getTargetName 
{
   my ($oracleHome, $sid, $isRac) = @_;
   
   my $err = "";
   
   my $isStandby = 0;
   
   #get param with quotes preserved
   my ($dbUniqueName) = discoverParameterValue($oracleHome, 
            $sid, "db_unique_name", $sid,1);
   if ($dbUniqueName eq "" || $dbUniqueName eq "''" || $dbUniqueName eq '""')
   {
       # bug fix 2755537
       #$err = checkErrorMessageInParameterDiscovery();
      # 
      # if ($err ne "")
      # {
      #     # error 
      #     $err = "Failed to retrieve value for parameter \"$sid.db_unique_name\": " . $err; 
      #     
      #     return ($err, ""); 
      # }
      # 
      #Check if the database is a standby
      if (isStandby($oracleHome, $sid))
      {
          EMD_PERL_INFO("$LOG_CATEGORY $sid is a standby db");
	  #the target name would be sid_hostname
          $isStandby = 1;
          if (!$isRac)
          {
              $dbUniqueName = "${sid}_$hostName";
          }    
      }
      
      if (!$isStandby || $isRac)
      {
       # db_unique_name not specified, try db_name.db_domain 
       #get param with quotes preserved
       my ($dbName) = discoverParameterValue($oracleHome, 
            $sid, "db_name", $sid,1);
        
        $err = checkErrorMessageInParameterDiscovery();
        if ($err ne "")
        {
           #error
           $err = "Failed to retrieve value for parameter \"$sid.db_name\": " . $err; 
           return ($err, "");
        }     
        
        # db_name retrieve succeeded, try db_domain     
        #get param with quotes preserved
        my ($db_domain) = ("");
        if(!(defined $hintParams{"no_db_domain"} && "true" eq $hintParams{"no_db_domain"} ))
        {
        ($db_domain) = discoverParameterValue($oracleHome, 
                $sid, "db_domain", $sid,1);
           
        $err = checkErrorMessageInParameterDiscovery();
        if ($err ne "")
        {
           #error
           $err = "Failed to retrieve value for parameter \"$sid.db_domain\": " . $err; 
           return ($err, "");
        }
        }     
	$dbName = convertToUcIfNotQuoted($dbName);
	$dbName = trimQuote($dbName);
	$db_domain = convertToUcIfNotQuoted($db_domain);
	$db_domain = trimQuote($db_domain);
        # db_name and db_domain are retrieved
        if($db_domain ne "")
      	{
           $dbUniqueName = $dbName . "." . $db_domain;
      	}
        else
        {
           $dbUniqueName = $dbName;
        }
      }
   }         
   else
   {
        # db_name retrieve succeeded, try db_domain     
        #get param with quotes preserved
        my ($db_domain) = ("");
        if(!(defined $hintParams{"no_db_domain"} && "true" eq $hintParams{"no_db_domain"} ))
        {
        ($db_domain) = discoverParameterValue($oracleHome, 
                $sid, "db_domain", $sid,1);
           
        $err = checkErrorMessageInParameterDiscovery();
        if ($err ne "")
        {
           #error
           $err = "Failed to retrieve value for parameter \"$sid.db_domain\": " . $err; 
           return ($err, "");
        }     
        }
        # db_name and db_domain are retrieved
	$dbUniqueName = convertToUcIfNotQuoted($dbUniqueName);
	$dbUniqueName = trimQuote($dbUniqueName);
	$db_domain = convertToUcIfNotQuoted($db_domain);
	$db_domain = trimQuote($db_domain);
        if($db_domain ne "")
      	{
           $dbUniqueName = $dbUniqueName . "." . $db_domain;
      	}
   }
   
   if ($isRac && $isStandby)
   {
        $dbUniqueName .= "_$CLUSTER_NAME"; 
   }     
   
   ("", $dbUniqueName);
}        

#Check if the database is a standby
#The database is standby if one of the following is true:
# - ora_mrp<0-9>*_sid process is running
# - fal_client is present in the init.ora
# - fal_server is present in the init.ora
#
sub isStandby
{
   my ($oracleHome, $sid) = @_;
   if((get_osType() ne 'WIN') )
   {
        if ( oraProcExists('mrp[0-9]*', $sid) ) {
            #MRP standby process process exists
            #This is standby database
        	EMD_PERL_DEBUG("$LOG_CATEGORY mrp process found for, its a standby db");
            return 1;
	    }
   }
   my $standbyParam;
   ($standbyParam) = discoverParameterValue($oracleHome, 
                $sid, "fal_client", $sid,1);
           
   my $err;
   $err = checkErrorMessageInParameterDiscovery();
   if ($err ne "")
   {
       	#error
        EMD_PERL_DEBUG("$LOG_CATEGORY Failed to retrieve value for parameter \"$sid.fal_client\": ");
   }     
   if($standbyParam ne "")
   {
	return 1;
   }
   ($standbyParam) = discoverParameterValue($oracleHome, 
                $sid, "fal_server", $sid,1);
           
   $err = checkErrorMessageInParameterDiscovery();
   if ($err ne "")
   {
       	#error
        EMD_PERL_DEBUG("$LOG_CATEGORY Failed to retrieve value for parameter \"$sid.fal_server\": ");
   }     
   if($standbyParam ne "")
   {
	return 1;
   }
   return 0; 
}

# Usage: ($err, $serviceName) = discoveryServiceName($oracleHome, $sid)
# return the service name if found. If any error occured, it'll be returned in $err
# 
# Logic:
# find value of service_names
# if not found and db_domain exists return db_name.db_domain
# else return db_name
#
sub discoveryServiceName 
{
   my ($oracleHome, $sid) = @_;
   
   my $err = "";
   
   EMD_PERL_DEBUG("$LOG_CATEGORY discoveryServiceName: discovering value of parameter service_names");
   my ($serviceName) = discoverParameterValue($oracleHome, 
            $sid, "service_names", $sid);
   
   $err = checkErrorMessageInParameterDiscovery();
   
   if ($err ne "")
   {
       # error 
       $err = "Failed to retrieve value for parameter \"$sid.service_names\": " . $err; 
       return ($err, ""); 
   }
   
   EMD_PERL_DEBUG("$LOG_CATEGORY discoveryServiceName: service_names = $serviceName");
   if ($serviceName eq "")
   {
      EMD_PERL_DEBUG("$LOG_CATEGORY discoveryServiceName: discovering value of parameter db_unique_name");
      ($serviceName) = discoverParameterValue($oracleHome, 
            $sid, "db_unique_name", $sid);
      
      EMD_PERL_DEBUG("$LOG_CATEGORY discoveryServiceName: db_unique_name = $serviceName");
      
      if ($serviceName eq "")
      {      
          # service_names not specified, try db_name
          EMD_PERL_DEBUG("$LOG_CATEGORY discoveryServiceName: discovering value of parameter db_name");
          ($serviceName) = discoverParameterValue($oracleHome, 
                $sid, "db_name", $sid);
        
      
          $err = checkErrorMessageInParameterDiscovery();
          if ($err ne "")
          {
             #error
             $err = "Failed to retrieve value for parameter \"$sid.db_name\": " . $err; 
             return ($err, "");
          }
      }    
      
      EMD_PERL_DEBUG("$LOG_CATEGORY discoveryServiceName: db_name = $serviceName");
   }
   
   my ($db_domain) = ("");
   if(!(defined $hintParams{"no_db_domain"} && "true" eq $hintParams{"no_db_domain"} ))
   {
   # get db_domain     
   EMD_PERL_DEBUG("$LOG_CATEGORY discoveryServiceName: discovering value of parameter db_domain");
   ($db_domain) = discoverParameterValue($oracleHome, 
            $sid, "db_domain", $sid);
       
   $err = checkErrorMessageInParameterDiscovery();
   if ($err ne "")
   {
       #error
       $err = "Failed to retrieve value for parameter \"$sid.db_domain\": " . $err; 
       return ($err, "");
   }     
   
   EMD_PERL_DEBUG("$LOG_CATEGORY discoveryServiceName: db_domain = $db_domain");
   }
   # put them together
   if ($db_domain ne "" && $serviceName !~ /$db_domain$/)
   {
        $serviceName = $serviceName . "." . $db_domain;
   }     
   
   EMD_PERL_DEBUG("$LOG_CATEGORY discoveryServiceName: serviceName = $serviceName");
   
   ("", $serviceName);
}        

# Usage: equalHosts($host1, $host2)
# return 1 if the one of $host1's ip addresses equals one of $host2's
# otherwise return 0
sub equalHosts
{
    
    if ($_[0] eq "" || $_[1] eq "")
    {
        return 0;
    }
    
    my ($name1, $aliases1, $addrtype1, $length1, @addrs1) = gethostbyname($_[0]);
    my ($name2, $aliases2, $addrtype2, $length2, @addrs2) = gethostbyname($_[1]);
    

    foreach my $a1 (@addrs1)
    {
        foreach my $a2 (@addrs2)
        {
            return 1 if ($a1 eq $a2);
        }
    }

    0;
}

   
# Usage: filterOutNotSupportedOracleHomes($ohomesRef,$sidsRef)
# ($ohomesRef,$sidsRef) are those returned by getOracleHomesAndSids(); 
# The method will delete the entries with not supported oracle home. 
sub filterOutNotSupportedOracleHomes
{
    my ($ohomesRef, $sidsRef) = @_;
    my $sid;
    for $sid (keys %$sidsRef) 
    {
        if (!isSupportedHome($sidsRef->{$sid}))
        {
	    EMD_PERL("$LOG_CATEGORY Oracle Home $sidsRef->{$sid} for sid=$sid is not supported. It is excluded from discovery.");
            delete $sidsRef->{$sid};
        }
    }
    
    my $i;
    my @ohs = @{$ohomesRef};
    
    splice @$ohomesRef, 0;
    
    for($i=0; $i<@ohs; $i++)
    {
        if (isSupportedHome($ohs[$i]))
        {
            push @{$ohomesRef}, $ohs[$i];
        }
        else
	{
	    EMD_PERL_INFO("$LOG_CATEGORY Oracle Home $ohs[$i] is not supported. It is excluded from discovery.");
	}
    }
}

# Usage: filterOutBadOracleHomes($ohomesRef,$sidsRef)
# ($ohomesRef,$sidsRef) are those returned by getOracleHomesAndSids(); 
# The method will delete the entries with an oracle home that doesn't exist
sub filterOutBadOracleHomes
{
    my ($ohomesRef, $sidsRef) = @_;
    my $sid;
    for $sid (keys %$sidsRef) 
    {
        if (! -e $sidsRef->{$sid})
        {
            delete $sidsRef->{$sid};
        }
    }
    
    my $i;
    my @ohs = @{$ohomesRef};
    
    splice @$ohomesRef, 0;
    
    for($i=0; $i<@ohs; $i++)
    {
        if (-e $ohs[$i])
        {
            push @{$ohomesRef}, $ohs[$i];
        }
    }
}

# Usage: removeElementFromArray($arrayRef, $element)
# if (existing $e in @$arrayRef such that $e eq $element)
#   remove it from the array
sub removeElementFromArray
{
    my ($arrayRef, $element) = @_;
    my $i;
    for($i=0; $i<@$arrayRef; $i++)
    {
        if ($arrayRef->[$i] eq $element)
        {
            last;
        }
    }
    
    if ($i < @$arrayRef)
    {
        splice @$arrayRef, $i, 1;
    }
}

#Usage: getRacDBGlbName($targetName, $dbName)
sub getRacDBGlbName
{
    my $targetName = $_[0];
    
    if ($targetName eq "")
    {
        $targetName = $_[1];
    }
    $targetName;
}

#Usage: getInstName($racDBName, $sid)    
sub getInstName
{
    my $nameinst = $_[0] . "_" . $_[1];
    $nameinst = $nameinst . $hintParams{"db_target_suffix"} if defined $hintParams{"db_target_suffix"};
    $nameinst = $hintParams{"db_target_prefix"}. $nameinst  if defined $hintParams{"db_target_prefix"}; 
    return $nameinst;
}    

#Check if Oracle Home is supported like pre 8.1
sub isSupportedHome
{
  my ($home) = @_;
#  if (! -e ($home . "/javavm"))
#  {
#    return 1;    
#  }
#  return 0;
#return all the homes supported till we get a good logic to filter out
#an Old Oracle home
    return 1;    
}

# Usage: getRidOfRemoteAddresses($addresses, $localHost)
# $addresses is the one returned by getListenerAddresses()
#
# Remove from $addresses any entry whose ip address doesn't match that of $localHost
#
sub getRidOfRemoteAddresses
{
    my ($addresses, $localHost) = @_;
    
    my @addrList = @{$addresses};
    
    splice @$addresses, 0;

    for(my $i=0; $i<@addrList; $i++)
    {
        my $host = "";
        $host = $addrList[$i]->{HOST} if (defined($addrList[$i]->{HOST}));
        if($host ne "" && !&equalHosts($host, $localHost) 
            && !&equalHosts($host, $LOCAL_HOST_VIP_NAME) )
        {
           next; 
        }
        
        push @{$addresses}, $addrList[$i];
    }
}

#
# The agent's lib_path doesn't work with 92 srvctl even after calling set_lib_path()
# Unsetting the lib_path works fine with srvctl
#  
# Return the old lib_path before unset
#
# This method is adapted from set_lib_path_env() in semd_common.pl
sub unset_lib_path_env
{
    my ($oh) = @_;
    my $rst;

    if ( get_osType() eq "SOL" || get_osType() eq "LNX" || get_osType() eq "OSF1" ) {
        $rst = $ENV{LD_LIBRARY_PATH};
        delete $ENV{LD_LIBRARY_PATH};
    } elsif ( get_osType() eq "HP" ) {
        $rst = $ENV{SHLIB_PATH};
        delete $ENV{SHLIB_PATH};
    } elsif ( get_osType() eq "AIX" ) {
        $rst = $ENV{LIBPATH};
        delete $ENV{LIBPATH};
    } elsif ( get_osType() eq "MAC OS X" ) {
        $rst = $ENV{DYLD_LIBRARY_PATH};
        delete $ENV{DYLD_LIBRARY_PATH};
    } else {
        # Unsupported Operating System
        # Do nothing
    }
    
    $rst;
}

#
# Take the lib_path and set it to the env variable 
# Return the old lib_path
#
# This method is adapted from set_lib_path_env() in semd_common.pl
sub set_lib_path_env
{
    my $libpath = $_[0];

    my $rst;

    if ( get_osType() eq "SOL" || get_osType() eq "LNX" || get_osType() eq "OSF1" ) {
        $rst = $ENV{LD_LIBRARY_PATH};
        $ENV{LD_LIBRARY_PATH} = $libpath;
    } elsif ( get_osType() eq "HP" ) {
        $rst = $ENV{SHLIB_PATH};
        $ENV{SHLIB_PATH} = $libpath;
    } elsif ( get_osType() eq "AIX" ) {
        $rst = $ENV{LIBPATH};
        $ENV{LIBPATH} = $libpath;
    } elsif ( get_osType() eq "MAC OS X" ) {
        $rst = $ENV{DYLD_LIBRARY_PATH};
        $ENV{DYLD_LIBRARY_PATH} = $libpath;
    } else {
        # Unsupported Operating System
        # Do nothing
    }
    
    $rst;
}    

#
# Run "$oracleHome/bin/srvctl config", if failed return the error
# else return empty
#
sub srvctl_sanity_check
{
   my ($oracleHome, $srvctlVersion) = @_;
   
   # bug 3469118 workaround:
   my $orgDIR = changeCWDForSrvctlIfNecessary($oracleHome, $srvctlVersion);

   my $cmd = "$oracleHome/bin/srvctl config"; 
   EMD_PERL_DEBUG("srvctl_sanity_check: $cmd");    
        
   my $lib_path = unset_lib_path_env();
   my ($err, $output) = executeCommand($cmd, $oracleHome);
   set_lib_path_env($lib_path);
        
   if ($err ne "")
   {
      EMD_PERL_DEBUG("srvctl_sanity_check failed: $err");
   }
   
   EMD_PERL_DEBUG("srvctl_sanity_check passed");

   # bug 3469118 workaround: we'll revert the cwd
   if($orgDIR ne "__CURRENT_DIR_UNCHANGED__")
   {
      chdir($orgDIR);
   }
   
   $err; 
}

sub convertToUTF8
{
   my $value = $_[0];
   my $num = 0;
   my $utf8 = '';

   for (my $i = 0; $i < length($value); $i++)
   {
      $num = ord(substr($value,$i,1));
      if ($num < 0x80)
      {
          $utf8 .= chr($num);
      }
      else
      {
          $utf8 .= chr(0xC0 | (($num >> 6) & 0x03));
          $utf8 .= chr(0x80 | ($num & 0x3F));
      }
   }

   return $utf8;
}

# Given the output from the services command in raw mode
# from the lsnrctl, this subroutine 
# returns a hashtable for sids found and itss correponding details
# For lsnrctl services output in raw mode like:
#LSNRCTL> services (ADDRESS=(PROTOCOL=TCP)(HOST=dkapoor-pc3)(PORT=1234))
#Connecting to (ADDRESS=(PROTOCOL=TCP)(HOST=dkapoor-pc3)(PORT=1234))
#Services Summary...
#(SERVICE=(SERVICE_NAME=orclServiceName)(INSTANCE=(INSTANCE_NAME=orcloid)(NUM=1)(IN
#STANCE_STATUS=UNKNOWN)(HANDLER=(HANDLER_DISPLAY=DEDICATED SERVER)(HANDLER_INFO=L
#OCAL SERVER)(HANDLER_MAXLOAD=0)(HANDLER_LOAD=0)(ESTABLISHED=0)(REFUSED=0)(HANDLE
#R_ID=B94B489DEB84-11D6-B6FD-0002A517EED1)(PRE=any)(HANDLER_NAME=DEDICATED)(SESSI
#ON=NS)(ADDRESS=(PROTOCOL=beq)(PROGRAM=extproc)(ENVS='ORACLE_HOME=d:\oracle92\test
#,ORACLE_SID=orcloid')(ARGV0=extprocPLSExtProc)(ARGS='(LOCAL=NO)')))(NUMREL=
#1)))\n"
#(SERVICE=(SERVICE_NAME=orcl92.example.com)(INSTANCE=(INSTANCE_NAME=orcl92)(NUM
#=1)(INSTANCE_STATUS=UNKNOWN)(HANDLER=(HANDLER_DISPLAY=DEDICATED SERVER)(HANDLER_
#INFO=LOCAL SERVER)(HANDLER_MAXLOAD=0)(HANDLER_LOAD=0)(ESTABLISHED=0)(REFUSED=0)(
#HANDLER_ID=B94B489EEB84-11D6-B6FD-0002A517EED1)(PRE=any)(HANDLER_NAME=DEDICATED)
#(SESSION=NS)(ADDRESS=(PROTOCOL=beq)(PROGRAM=oracle)(ENVS='ORACLE_HOME=d:\oracle9
#2\ora92,ORACLE_SID=orcl92')(ARGV0='oracleorcl92')(ARGS='(LOCAL=NO)')))(NUMREL=1)))
#
# Following hashtable is returned:
# (
#   orcl92 =>    
#   {
#      SERVICE_NAME => orcl92.example.com,
#      ORACLE_HOME => d:\oracle92\ora92,
#      PORT => 1234 (obtained from the first address description)
#    },
#   orcloid =>    
#    {
#      SERVICE_NAME => orclServiceName,
#      ORACLE_HOME => d:\oracle92\test,
#      PORT => 1234
#    },
# )
#
#Assuptions:
# 0. The entry for INSTANCE_NAME = *extproc* (ignoring case) is not returned.
# 1. If no ORACLE_SID, INSTANCE_NAME or ORACLE_HOME found , the entry is not returned
# 2. If the instance is not local to the host, the entry is not returned
#
sub getDBDetailsDiscovery
{
  my ($result,$port,$lsnrHost,@dynamicDiscoveredSids) = @_; 
  my %dbDetails;

  if(!defined $port || $port eq "")
  {
     return \%dbDetails;
  }
  my @resultArray = split /\n/, $result;

  foreach my $ln (@resultArray)
  {
      if($ln !~ /^\s*\(SERVICE/i)
      {
        next;
      }
      if($ln !~ /(ORACLE_HOME)|(ORACLE_SID)|(INSTANCE_NAME)/i)
      {
        next;
      }
      
      my ($srv, @lns) = split /\(INSTANCE=/, $ln;
      
      foreach my $line (@lns)
      {
          $line = $srv . "(INSTANCE=" . $line;
         
          if (!isLocalInstance($line, $lsnrHost))
          {
             EMD_PERL_DEBUG("Ignored: NOT Local instance -- $line");
             next;
          }
            
          my ($serviceName, $oracleHome,$oracleSid, $instanceName) = ("","","","");
 
          if($line =~ /\(ENVS='.*ORACLE_SID=(.*?)(,|')/i)
          {
            $oracleSid = convertSIDForOS($1);
          }
          # is sid not defined get it from INSTANCE_NAME
          if(!defined $oracleSid || $oracleSid eq "")
          {
            if($line =~ /\(INSTANCE_NAME=(.*?)\)/i)  
    	    {
    	      $oracleSid = convertSIDForOS($1);
    	    }
          }
          if ($oracleSid =~ /extproc/i)
          {
              #filter out sids containing with extproc
              next;
          }
          if(contains($oracleSid,@dynamicDiscoveredSids) && !equalHosts($lsnrHost, $LOCAL_HOST_VIP_NAME))
          {
            next;
          }
          if($line =~ /\(ENVS='.*ORACLE_HOME=(.*?)(,|')/i)
          {
            $oracleHome = $1;
          }
          if($line =~ /\(SERVICE_NAME=(.*?)\)/i)
          {
            $serviceName = $1;
          }
          EMD_PERL_INFO("$LOG_CATEGORY found dynamic sid=\"$oracleSid\" service =$serviceName port = $port host =$lsnrHost home=$oracleHome");
  
          if( defined $oracleSid && $oracleSid ne "")
          {
          	if(defined $dbDetails{$oracleSid})
          	{
          		my $sidDetails = $dbDetails{$oracleSid};
          		my $serviceNamesRef = $sidDetails->{SERVICE_NAME};
          		push(@$serviceNamesRef,$serviceName);
          		$sidDetails->{SERVICE_NAME} = $serviceNamesRef;
            }
          	else
          	{
                 my @servNamesArr = $serviceName;    
                 my $sidDetails = { ORACLE_HOME=> $oracleHome, 
                       SERVICE_NAME => \@servNamesArr, PORT => $port,HOST => $lsnrHost} ;
                $dbDetails{$oracleSid} = $sidDetails;
          	}
          	
          }
      }    
  }
  return \%dbDetails;
}


## 
## The following method is written to workaround bug 3469118.
## For pre-10g NT rac, if PATH env doesn't include $oracleHome/bin, 
## the srvctl will return error when invoked not within $oracleHome/bin. 
## The workaround for this bug is to change the current dir to $oracleHome/bin 
## before calling srvctl and set it back when finished
##

# Take in $oracleHome and $srvctlVersion
#
# Return the original current dir if it's changed
# Otherwise return "__CURRENT_DIR_UNCHANGED__"
sub changeCWDForSrvctlIfNecessary
{
    my ($oracleHome, $srvctlVersion) = @_;
    my $orgDIR = "__CURRENT_DIR_UNCHANGED__";
    
    if (get_osType() eq 'WIN' && compareVersions($srvctlVersion, "10.0.0.0.0") == -1)
    {    
        # This is a NT pre-10g srvctl
        $orgDIR = getcwd;
        chdir "$oracleHome/bin";
    }
    
    $orgDIR;
}

#
# isLocalInstance($instanceBlock, $hostname)
# Given an instance block from lsnrctl raw service output, something like 
#
#  ..(INSTANCE=(INSTANCE_NAME=orcloid)(NUM=1)(INSTANCE_STATUS=UNKNOWN)
# (HANDLER=(HANDLER_DISPLAY=DEDICATED SERVER)(HANDLER_INFO=LOCAL SERVER)
# (HANDLER_MAXLOAD=0)(HANDLER_LOAD=0)(ESTABLISHED=0)(REF....
#
# return 1 if this instance is on the local host (specified by $hostname) as in
# the following situations
# 1. For dedicated server, one of its HANDLER_INFO equals "LOCAL SERVER"
# 2. For shared server, one of its HANDLER_INFO with machine of $hostname
#
sub isLocalInstance
{
    my ($instance, $hostname) = @_;
    my $handler_info;
    
    while ($instance =~ /HANDLER_INFO=(.*?)\)/ig)
    {
        $handler_info = $1; 

        if ($handler_info eq "LOCAL SERVER")
        {
            return 1;
        }
        if ($handler_info =~ /DISPATCHER\s*<machine:\s*(.*?),/i)
        {
            if (equalHosts($1, $hostname))
            {
                return 1;
            }
        }    
    }
    
    return 0;
}

#
# Use the srvctl to retrieve the vip name of the machine specified
#
sub getVIPName
{
    my ($oh, $host) = @_;
    
    my $vipn = "";
    
    my $localnode = "";
    my $cmd;
    my $lib_path = unset_lib_path_env();  

    my ($err, $output) = ("", "");

    if ($CRS_HOME ne "")
    {
       $cmd = "$CRS_HOME/bin/olsnodes";
       ($err, $output) = executeCommand($cmd, $oh);
    
        EMD_PERL_DEBUG("$LOG_CATEGORY findRacTargetsFromOracleHome: getVIPName: $cmd returned (err=$err, output=$output)");
    
        if ($err eq "")
        {
            my @nodes = split /\n/, $output;
            foreach my $node (@nodes)
            {
               if (&equalHosts($node, $host))
               {
                  $localnode = $node;
                  last;
               }
            }   
        }    
    } #END of if ($CRS_HOME ne "")
    
    if ($localnode eq "")
    {
         ($localnode) = split /\./, $host;
    }        
    
    if ($localnode ne "")
    {
        $cmd = "$oh/bin/srvctl config nodeapps -n $localnode -a";
        ($err, $output) = executeCommand($cmd, $oh);
        
        EMD_PERL_DEBUG("$LOG_CATEGORY findRacTargetsFromOracleHome: getVIPName: $cmd returned (err=$err, output=$output)");
        
        if ($err eq "")
        {
            my @lns = split /\n/, $output;
            for my $ln (@lns)
            {
                if ($ln =~ /VIP exists.*?\/(.*?)\/(.*?)\//)
                {
                    $vipn = ($1 eq "")? $2 : $1;
                    last;
                }
            }
        }    
    }   
    
    set_lib_path_env($lib_path);
    
    $vipn;
}    


# Check whether the sid is a rac sid
#
# If it's a rac AND $addToRac is 1, make it a rac target and add it to $racInstsRefs
#
# Return dbname if it's RAC. 
# Return "_INVALID_SID_DETECTED_" if it's rac, but instance_name is not same as sid
# Return "" otherwise
sub checkNAddToRac
{
    my ($hostName, $oracleHome, $sid, $racInstsRefs) = @_;
    my ($israc) = discoverParameterValue($oracleHome, 
                $sid, "cluster_database", $sid);
    
    my $err = checkErrorMessageInParameterDiscovery();
    if ($err ne "")
    {
       return "";
    }     
    
    if (uc($israc) eq "TRUE")
    {
        #rac instance
        
        my ($dbName) = discoverParameterValue($oracleHome, 
                $sid, "db_unique_name", $sid);
        
        if ($dbName eq "")
        {
            ($dbName) = discoverParameterValue($oracleHome, 
                $sid, "db_name", $sid);
        }
        
        if ($dbName eq "")
        {
            return "";
        }

        my ($insno) = discoverParameterValue($oracleHome,
                $sid, "instance_number", $sid);

        if ($insno eq "")
        {
           # BAD!! It means $sid is not really a valid instance name
           EMD_PERL_DEBUG("$LOG_CATEGORY checkNAddToRac: RAC, but sid($sid) does't have instance_number specified");
           return "_INVALID_SID_DETECTED_";
        }
        if (!defined $racInstsRefs->{$dbName})
        {
            my @instRefAry = ();
            push @instRefAry, [$hostName, $sid, $oracleHome];
            $racInstsRefs->{$dbName} = \@instRefAry;
        }
        else
        {
            # already defined. do nothing for now
        }

        # we need to the retrieve the sid with original case
        my ($osid) = discoverParameterValue($oracleHome, 
                $sid, "instance_name", $sid);
        
        if ($osid ne "")
        {
            $RacSidConversionMap{$sid} = $osid;
        }    
        
        return $dbName;
    }    
    
    return "";
}            

# return if the given $sid, $oh is valid or not
sub isValidSid
{
   my ($sid, $oHome) = @_;

   if ( oraProcExists('pmon', $sid) )
   {
      #PMON process exists
      return 1;
   }
   
   my $initDir = getDefaultInitFileLocation($oHome);
   if (-e ($initDir . "/lk". uc ($sid)))
   {
      #lk file exists
      return 1;
   }
   my $initFile = getInitFile($sid,$oHome);
   if (defined $initFile && (-e $initFile))
   {
      #initalization file exists
      return 1;
   }
   
   return 0;
}

#Gets listener control version.
#if not found returns 0.
sub getLsnrctlVersion
{
    my ($oracleHome) = @_;
    my $lsnrctl = getListenerControl($oracleHome);
    # if version already present, return it
    return $lsnrctlVersions{$lsnrctl} if (defined $lsnrctlVersions{$lsnrctl});

    if(-e $lsnrctl)
    {
      my @commands =("version","exit");
      my $result = getRunCommandOutput($oracleHome,$lsnrctl,@commands);
      my @resultArray = split /\n/, $result;

      foreach my $ln (@resultArray)
      {
     	if($ln =~ /\s*LSNRCTL (.+) Version ([0-9.a-zA-Z]+?) (.+)/i)
        {
		EMD_PERL_INFO("$LOG_CATEGORY The version of the $lsnrctl=[$2]");
    		$lsnrctlVersions{$lsnrctl} = $2;
       		return $2;
      	}
      }
    }
    EMD_PERL_ERROR("$LOG_CATEGORY Could not get version of $lsnrctl");
    $lsnrctlVersions{$lsnrctl} = 0;
    return 0;
}

# Test for the presence of an ora_ process
sub oraProcExists {

   my $retval = 0;
   my ($proc, $sid) = @_;
   if ( !defined($proc) || !defined($sid) ) {
       return($retval);
   }

   my $uname;
   chomp($uname=`/bin/uname`) || chomp($uname=`/usr/bin/uname`);
   my $regexp = "[o]ra_".$proc."_".$sid."\$" ;

   my $ps_opts = "-ef";
   if ( $uname eq "Darwin" ) { 
       # MAC OS
       $ps_opts = "-x";
   }

   system("/bin/ps $ps_opts | /bin/grep $regexp > /dev/null");
   if ($? == 0) {
      $retval = 1;
   }

   return( $retval );
}

# ------------------------------  End of File  ---------------------------------
