# $Header: emdb/sysman/admin/scripts/db/dbclone/db_clone.pl /st_emdbsa_11.2/5 2009/06/11 14:41:05 sjconnol Exp $
#
# db_clone.pl
#
# Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. 
#
#    NAME
#      db_clone.pl - scripts for database cloning
#
#    DESCRIPTION
#      <short description of component this file declares/defines>
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    sjconnol    06/10/09 - Bug 8575467
#    ngade       02/11/09 - Code slap 10.2.0.5.0 -> 11.2 SA round 2
#    ngade       02/02/09 - Code slap 10.2.0.5.0 -> 11.2 round 2
#    rimmidi     02/05/09 - LRG fix 3775465
#    ngade       11/12/08 - Fix bug 7492475
#    rimmidi     09/12/08 - Bug fix 7385965
#    rimmidi     05/05/08 - Code slap from 11GC to 10.2.0.5
#    rimmidi     09/18/08 - Backport rimmidi_bug-7385965 from
#                           st_emdbgc_10.2.0.1.0
#    rimmidi     07/30/08 - Code slap from 10205 to 11.2DBControl
#    nachen      06/19/08 - XbranchMerge nachen_bug-7144603 from st_emdbsa_11.1
#    nachen      06/06/08 - fix internal references security issue
#    sxzhu       03/26/07 - Config listener service to auto start
#    sxzhu       03/12/07 - Fix bug 5913084: handle spaces in ftp passwd
#    sxzhu       09/29/06 - Add quotes for alter user
#    ngade       10/18/06 - Backport ngade_bug-5141850 from main
#    sxzhu       08/14/06 - Backport fix for bug 5352507
#    sxzhu       06/21/05 - Add Linux as an OS type
#    sxzhu       06/07/05 - Remove version check
#    sxzhu       06/28/06 - Increase buffer for bigvalue 
#    mappusam    04/20/06 - Fix for the bug-5023992 
#    ngade       12/22/05 - fix bug 4771808 
#    sxzhu       06/21/05 - Add Linux as an OS type 
#    sxzhu       06/07/05 - Remove version check 
#    sxzhu       05/19/05 - Add compareVer
#    sxzhu       04/07/05 - Handle large plsql output
#    sxzhu       02/07/05 - Remove dir creation for external files
#    sxzhu       08/06/04 - Change to DEBUG for ORA messages
#    sxzhu       07/29/04 - Move compress and external obj to db_clone_10_2
#    sxzhu       07/23/04 - Set linesize for sqlplus
#    sjconnol    07/15/04 - Bug 3633774
#    sjconnol    06/29/04 - Don't use connect desc in db connections
#    szhu        05/24/04 - Support compressing backup files
#    szhu        04/14/04 - Handle glob for NFS mount case
#    mbhoopat    03/10/04 - linux port
#    szhu        02/11/04 - Create dirs for external files
#    szhu        02/05/04 - Re-create directory objects
#    szhu        12/09/03 - Set dbsnmp password
#    szhu        11/19/03 - Use source pfile for cold clone
#    szhu        11/12/03 - Cleanup cloned DB
#    szhu        11/04/03 - Fix startLsnr on NT
#    szhu        10/31/03 - Handle startLsnr on NT
#    sjconnol    10/10/03 - Don't do pfile remove if DG
#    szhu        10/06/03 - Fix spfile used flag for 10g DB
#    szhu        09/16/03 - Handle nls_ init parameters
#    szhu        09/11/03 - Convert back to single quotes from %%%
#    szhu        09/03/03 - Fix version comparison
#    szhu        08/05/03 - NT support
#    sjconnol    06/17/03 - Don't do pfile rename if DG
#    szhu        06/06/03 - Rename pfile if using spfile
#    szhu        06/05/03 - Create admin dirs
#    szhu        06/04/03 - Handle obsolete ORA message
#    gallison    06/03/03 - Handle resetlogs
#    szhu        05/29/03 - Fix bug 2982763, support multiple members in one log group
#    szhu        04/22/03 - Set destination DB to noarchivelog for cold cloning
#    szhu        04/17/03 - Allow deprecated init parameters
#    szhu        04/08/03 - set TNS_ADMIN env variable
#    szhu        03/31/03 - Handle no source password file
#    szhu        01/27/03 - Handle filenames delimeters for NT
#    szhu        01/24/03 - addDatafilesForTesting
#    szhu        01/17/03 - Add hiding FTP output
#    sjconnol    01/18/03 - Change to modifyInitFile
#    szhu        01/08/03 -
#    sjconnol    01/11/03 - Change createNewDirs to error if mkDir fails
#    szhu        12/06/02 -
#    szhu        12/06/02 - Add ftp support
#    szhu        11/18/02 - Correct control file backup
#    sjconnol    12/05/02 - Change to modifyInitFile
#    sjconnol    11/12/02 - Logical standby 10i support
#    sjconnol    10/28/02 - Fix parameter filtering
#    szhu        10/18/02 - Switch log mode with specified parameters
#    szhu        10/11/02 - Support cold cloning
#    szhu        10/10/02 - Redirect output of startLsnr to a temp file
#    sjconnol    10/08/02 - Support for physical/logical standby
#    sjconnol    09/24/02 - Add startLsnr
#    szhu        09/22/02 - Backup control file
#    szhu        09/19/02 - Handle offline datafiles
#    szhu        09/18/02 - Set dest DBName in control file
#    szhu        09/13/02 - Fix archive log sql
#    szhu        09/13/02 -
#    szhu        09/12/02 - Create new directories for database files and temp dir
#    szhu        09/11/02 - Get maxInfo for control file
#    sjconnol    09/07/02 - Don't run addTempfiles if there aren't any
#    szhu        09/03/02 - Customize database file locations
#    szhu        08/29/02 - Handle ORA messages specially
#    sjconnol    08/30/02 - Don't run clonePrepare if standby; fix SQL
#    szhu        08/22/02 - Set clone purpose
#    sjconnol    08/23/02 - Add optional TNS descriptor to connect str
#    szhu        08/15/02 - Restore log on same host
#    szhu        08/12/02 - More init.ora creation
#    szhu        08/09/02 - Create init.ora for restrarting source DB
#    szhu        08/02/02 - Add tempfile support
#    szhu        08/01/02 - Add init.ora location for destination DB startup
#    szhu        07/31/02 - Solve 817 DB issues
#    szhu        07/25/02 - Hide standard output when get datafile numbers
#    szhu        07/24/02 - More debug info
#    szhu        07/23/02 - Add debug info
#    szhu        07/23/02 - Move script location
#    szhu        07/16/02 - Add flag for control file backup
#    szhu        06/26/02 - Hide source DB credential
#    xxu         06/25/02 - remove /usr/local/bin/perl
#    szhu        06/19/02 - Get source spfile info
#    szhu        06/17/02 - Create standby control file
#    szhu        06/14/02 - Init parameters
#    szhu        06/02/02 - szhu_clone_db
#    szhu        06/02/02 - More switch archivelog mode
#    szhu        05/31/02 - Add checking and switching archivelog mode
#    szhu        05/29/02 - More script work.
#    szhu        05/28/02 - More database recovery
#    szhu        05/24/02 - Recover database
#    szhu        05/23/02 - Restore archivedLogs and recover database
#    szhu        05/22/02 - backup datafiles
#    szhu        05/21/02 - Creation
#

require "emd_common.pl";
require "$ENV{EMDROOT}/sysman/admin/scripts/db/db_common.pl";

use strict;
use File::Temp qw/ tempfile tempdir /;
use vars qw/ $userID $clonePurpose $clodClone $OS $NT $S $TEMP $CP $MV $PS $DF $DELIMITER/;

# Global variables
$userID = "";
$clonePurpose = "NORMAL";
$clodClone = "N";

#A temporary solution and will be removed after EMD supports this setting
#in logging.xml.
#$ENV{EMAGENT_PERL_TRACE_LEVEL} = 0;  #DEBUG level.

# Set environment variables
# To set TNS_ADMIN to non-default value, pass listener.ora full file name
# as the third argument.
# Call sub set_env_var() in emd_common.pl
# set_env(oracleHome, oracleSid)
sub set_env
{
  EMD_PERL_DEBUG("db_clone.set_env(): *** START ***");

  my($listenerFile) = @_[2];
  if(!$listenerFile)
  {
    &set_env_var(@_[0], @_[1]);
  }
  else
  {
    my($idx) = rindex($listenerFile,"$S");
    if($idx == -1){
      EMD_PERL_DEBUG("db_clone.set_env(): Cannot determine TNS_ADMIN");
      exit(1);
    }
    my($tns) = substr($listenerFile,0,$idx);
    &set_env_var(@_[0], @_[1], undef(), $tns);
  }

  EMD_PERL_DEBUG("db_clone.set_env(): *** END ***");
}

# Set the source DB credential
# setSrcDBCredential(userName, password)
sub setSrcDBCredential
{
  EMD_PERL_DEBUG("db_clone.setSrcDBCredential(): *** START ***");
  my ($userName, $password, $tns) = @_;
#	if(!$tns){
			$userID = $userName."/".$password;
#	}
#	else{
#			$userID = $userName."/".$password."\@${tns}";
#	}
  EMD_PERL_DEBUG("db_clone.setSrcDBCredential(): User Name: $userName");
  EMD_PERL_DEBUG("db_clone.setSrcDBCredential(): *** END ***");
}

# Set the source DB credential (added for DG; need TNS string to
#  connect in dev environment). Reuses source credentials
sub setDestDBCredential
{
  EMD_PERL_DEBUG("db_clone.setDestDBCredential(): *** START ***");
  setSrcDBCredential(@_);
  EMD_PERL_DEBUG("db_clone.setDestDBCredential(): *** END ***");
}

# Set the clone purpose
# setClonePurpose(purpose)
sub setClonePurpose
{
  EMD_PERL_DEBUG("db_clone.setClonePurpose(): *** START ***");
  my ($purpose) = @_;
  s/$_/\U$purpose/gi;
  $clonePurpose = $_;
  EMD_PERL_DEBUG("db_clone.setClonePurpose(): Clone purpose: $clonePurpose");
  EMD_PERL_DEBUG("db_clone.setClonePurpose(): *** END ***");
}

# Set cold clone flag: Y or N
# setColdCloneFlag(cold)
sub setColdCloneFlag
{
  EMD_PERL_DEBUG("db_clone.setColdCloneFlag(): *** START ***");
  my ($cold) = @_;
  s/$_/\U$cold/gi;
  $clodClone = $_;
  EMD_PERL_DEBUG("db_clone.setColdCloneFlag(): Cold clone flag: $clodClone");
  EMD_PERL_DEBUG("db_clone.setColdCloneFlag(): *** END ***");
}

# Filter out DB credential from sql script, which is written to emd_perl.trc
# filterDBCredential(sqlScript)
sub filterDBCredential
{
  my ($sqlScript) = @_;

  my $position1 = index($sqlScript, "CONNECT");
  my $position2 = index($sqlScript, "AS");
  my $replacedLength = $position2 - $position1 - 9;
  substr($sqlScript, $position1 + 8, $replacedLength) = "username/password(Hiden intentionally)";

  return $sqlScript;
}

# Parse the output string to detect ORA- errors
# parseOutput(output)
sub parseOutput
{
  EMD_PERL_DEBUG("db_clone.parseOutput(): *** START ***");

  my ($output) = @_;

  if ($output eq "")
  {
    EMD_PERL_ERROR("db_clone.parseOutput(): Output file is empty!");
  }

  if($output !~ /ORA-[0-9]/)
  {
    EMD_PERL_DEBUG("db_clone.parseOutput(): No Error found.");
  }
  elsif (($output =~ /ORA-00278/) || ($output =~ /ORA-00279/) ||
        ($output =~ /ORA-00280/) || ($output =~ /ORA-00289/) ||
        ($output =~ /ORA-32006/) || ($output =~ /ORA-32004/))
  {
    EMD_PERL_DEBUG("db_clone.parseOutput(): ORA- messages!");
  }
  else
  {
    EMD_PERL_ERROR("db_clone.parseOutput(): ORA- ERROR! Exit! See output log.");
    exit(1);
  }

  EMD_PERL_DEBUG("db_clone.parseOutput(): *** END ***");
}

# Run given sql script on source DB
# The caller is responsible to close the returned fileHandle
# runSqlOnSource(sqlScript, hideOutput) will hide standard output for any
# defined parameter "hideOutput"
# runSqlOnSource(sqlScript) will print standard output
sub runSqlOnSource
{
  EMD_PERL_DEBUG("db_clone.runSqlOnSource(): *** START ***");

  my ($sql_string) = @_[0];
  my $sql_string_debug = &filterDBCredential($sql_string);
  EMD_PERL_DEBUG("db_clone.runSqlOnSource(): SQL:\n$sql_string_debug");

  (my $fh, my $filename) = &create_temp_file();

  if($NT)
  {
    $filename = "$TEMP\\"."dbclone.$$";
    #$filename = "$TEMP\\".getBasename($filename);
    EMD_PERL_DEBUG("db_clone.runSqlOnSource(): temp file: $filename");
    open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename")
      || die "Cannot open pipe for SQL_SCRIPT";
    #print SQL_SCRIPT "spool $filename; \n";
    print SQL_SCRIPT $sql_string;
    close SQL_SCRIPT || die "Bad SQL_SCRIPT";
  }
  else
  {
    open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename")
      || die "Cannot open pipe for SQL_SCRIPT";
    print SQL_SCRIPT $sql_string;
    close SQL_SCRIPT || die "Bad SQL_SCRIPT";
  }

  #Open the temp file to print output to standard output and debug trace file
  open (OUT_PUT, "$filename") || die "Unable to open tempfile for OUT_PUT\n";
  my @output_content = <OUT_PUT>;
  my $output_string = "@output_content";
  close OUT_PUT;

  if(!defined(@_[1]))
  {
    print STDOUT $output_string;
  }

  EMD_PERL_DEBUG("db_clone.runSqlOnSource(): OUT_PUT:\n$output_string");
  &parseOutput($output_string);

  EMD_PERL_DEBUG("db_clone.runSqlOnSource(): *** END ***");

  return ($fh, $filename);
}

# Run given sql script on destination oracleHome
# The caller is responsible to close the returned fileHandle
# runSqlOnDestination(sqlScript, hideOutput) will hide standard output for any
# defined parameter "hideOutput"
# runSqlOnDestination(sqlScript, hideOutput, hideSQL) will hide standard
# output for any defined parameter "hideOutput" and SQL script for any defined
# parameter "hideSQL"
# runSqlOnDestination(sqlScript) will print standard output
sub runSqlOnDestination
{
  EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): *** START ***");

  my ($sql_string) = @_[0];

  if(!defined(@_[2]))
  {
    EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): SQL:\n$sql_string");
  }

  (my $fh, my $filename) = &create_temp_file();
  if($NT)
  {
    $filename = "$TEMP\\"."dbclone.$$";
  }

  # If a username/password had been specified, use it. Otherwise,
  # default to OS authentication
  if($userID){
    EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): Connecting with connect descriptor");
    open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename")
      || die "Cannot open pipe for SQL_SCRIPT";
    print SQL_SCRIPT "CONNECT $userID AS SYSDBA;\n";
  }
  else{
    EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): Connecting with OS auth");
    open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus '/ AS SYSDBA' >$filename")
        || die "Cannot open pipe for SQL_SCRIPT";
  }
  print SQL_SCRIPT $sql_string;
  close SQL_SCRIPT;
  if($?) { 
    die "ERROR: SQLPlus existed with status $?";
  }

  #Open the temp file to print output to standard output and debug trace file
  open (OUT_PUT, "$filename") || die "Unable to open tempfile for OUT_PUT\n";
  my @output_content = <OUT_PUT>;
  my $output_string = "@output_content";
  close OUT_PUT;

  if(!defined(@_[1]))
  {
    print STDOUT $output_string;
  }

  EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): OUT_PUT:\n$output_string");
  &parseOutput($output_string);

  EMD_PERL_DEBUG("db_clone.runSqlOnDestination(): *** END ***");

  return ($fh, $filename);
}

# Check the archivelog mode of source DB.  If not in archivelog mode, switch it
# to archivelog mode.
# Call &set_env(oracleHome, oracleSid) before calling this method.
# checkAndSwitchLogMode(pfileFullName, modifiedNames, modifiedValues)
sub checkAndSwitchLogMode
{
  EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): *** START ***");

  #Check if the source database is in archivelog mode
  my $sql_string = "";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "select log_mode from v\$database;\n";
  $sql_string .= "EXIT;\n";

  (my $fh, my $filename) = &runSqlOnSource($sql_string,"");

  #Open the temp file to process the output
  open (LOG_MODE, "$filename") || die "Unable to open tempfile for LOG_MODE\n";
  while (<LOG_MODE>)
  {
    if ((/ARCHIVELOG/) && !(/NOARCHIVELOG/))
    {
      close LOG_MODE;
      close $fh;
      if($NT)
      {
	&removeFile($filename);
      }
      EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): LOG_MODE: ARCHIVELOG");
      EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): *** END ***");
      return "ARCHIVELOG";
    }
  }
  close LOG_MODE;
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): LOG_MODE: NOARCHIVELOG");

  my ($pfileFullName, $modifiedNames, $modifiedValues) = @_;

  #Check if the source DB is using spfile
  my $spfile = &spfileUsed();

  if($spfile eq "") #not using spfile
  {
    if($pfileFullName eq "$DELIMITER") #no pfile specified, create a temp one
    {
      my $pfile_fh; #not defined
      my $pfile_filename; #not defined
      ($pfile_fh, $pfile_filename) = &create_temp_file();
      if($NT)
      {
	$pfile_filename = "$TEMP\\"."dbclone.$$";
      }
      EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): Temp pfile: $pfile_filename");
      &shutdownDB("immediate", $pfile_filename, $modifiedNames, $modifiedNames, $modifiedValues);
      &startupDB("mount", $pfile_filename);
      close $pfile_fh;
      if($NT)
      {
        &removeFile($pfile_filename);
      }
    }
    else  #modify the original pfile
    {
      &modifyInitFile($modifiedNames, $modifiedValues, $pfileFullName);
      &shutdownDB("immediate");
      &startupDB("mount", $pfileFullName);
    }
  }
  else
  {
    &modifySpfile($modifiedNames, $modifiedValues);
    &shutdownDB("immediate");
    &startupDB("mount");
  }

  #The source database is not in archivelog mode, switch it to archivelog mode.
  $sql_string = "";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "alter database ARCHIVELOG;\n";
  $sql_string .= "alter database open;\n";
  $sql_string .= "EXIT;\n";

  ($fh, $filename) = &runSqlOnSource($sql_string,"");
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.checkAndSwitchLogMode(): *** END ***");
}

# Shutdown and mount DB for cold clone
# Call &set_env(oracleHome, oracleSid) before calling this method.
# closeDBForColdClone(pfile)
sub closeDBForColdClone
{
  EMD_PERL_DEBUG("db_clone.closeDBForColdClone(): *** START ***");

  my ($pfile) = @_;

  #Check if the source DB is using spfile
  my $pfile_filename; #not defined
  my $spfile = &spfileUsed();
  if($spfile eq "") #not using spfile
  {
    $pfile_filename = $pfile;
    EMD_PERL_DEBUG("db_clone.closeDBForColdClone(): pfile: $pfile_filename");
  }

  #shutdown the source DB
  &shutdownDB("immediate");

  #startup the source DB
  &startupDB("mount", $pfile_filename);

  EMD_PERL_DEBUG("db_clone.closeDBForColdClone(): *** END ***");
}

# Open the mounted source DB
# Call &set_env(oracleHome, oracleSid) before calling this method.
# openDBAfterColdClone()
sub openDBAfterColdClone
{
  EMD_PERL_DEBUG("db_clone.openDBAfterColdClone(): *** START ***");

  my $sql_string = "";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "alter database open;\n";
  $sql_string .= "EXIT;\n";

  (my $fh, my $filename) = &runSqlOnSource($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.openDBAfterColdClone(): *** END ***");
}

# Get datafile numbers
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# getDatafileNum()
sub getDatafileNum
{
  EMD_PERL_DEBUG("db_clone.getDatafileNum(): *** START ***");

  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "set serveroutput on;\n";
  $sql_string .= "declare\n";
  $sql_string .= "  i    binary_integer := 1;\n";
  $sql_string .= "BEGIN\n";
  $sql_string .= "  dbms_output.enable(2000000);\n";
  $sql_string .= "  FOR dfrec IN (SELECT file# FROM v\$datafile WHERE status <> 'OFFLINE'\n";
  $sql_string .= "                union \n";
  $sql_string .= "                SELECT file# FROM v\$datafile WHERE status = 'OFFLINE' and file# in (SELECT file# FROM v\$datafile_header))\n";
  $sql_string .= "  LOOP\n";
  $sql_string .= "    dbms_output.put_line('Checked file ' || i || ':');\n";
  $sql_string .= "    dbms_output.put_line('file# '||dfrec.file#);\n";
  $sql_string .= "    i := i + 1;\n";
  $sql_string .= "  END LOOP;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n";

  my $hideOutput = "";
  (my $fh, my $filename) = &runSqlOnSource($sql_string, $hideOutput);

  open (DATAFILE_NUM, "$filename") || die "Unable to open tempfile for DATAFILE_NUM\n";
  while (<DATAFILE_NUM>)
  {
    if (($_=~/\d/) && ($_=~/\bfile#/))
    {
      chomp($_);
      print "$_ ";
      EMD_PERL_DEBUG("db_clone.getDatafileNum(): Datafile: $_");
    }
  }

  close DATAFILE_NUM;
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.getDatafileNum(): *** END ***");
}

# Get max sequence number for archived log
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# getMaxSeqNum()
sub getMaxSeqNum
{
  EMD_PERL_DEBUG("db_clone.getMaxSeqNum(): *** START ***");

  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "set serveroutput on;\n";
  $sql_string .= "set linesize 512;\n";
  ## SJC: Ensure archiving is on prior to switching log
  $sql_string .= "variable ver varchar2(256);\n";
  $sql_string .= "BEGIN\n";
  $sql_string .= "  dbms_output.enable(2000000);\n";
  $sql_string .= "  select version INTO :ver from v\$instance;\n";
  $sql_string .= "  dbms_output.put_line('version# '||:ver);\n";
  $sql_string .= "  IF (SUBSTR(:ver, 1 ,INSTR(:ver, '.', 1, 1)+1) < 10.2) then\n";
  $sql_string .= "   EXECUTE IMMEDIATE 'ALTER SYSTEM ARCHIVE LOG START';\n";
  $sql_string .= "  end if;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "ALTER SYSTEM ARCHIVE LOG CURRENT;\n";
  $sql_string .= "variable max_seq number;\n";
  $sql_string .= "variable rowcount number;\n";
  $sql_string .= "variable spfile_val varchar2(256);\n";
  $sql_string .= "BEGIN\n";
  $sql_string .= "  dbms_output.enable(2000000);\n";
  #$sql_string .= "  select max(sequence#) INTO :max_seq from v\$archived_log;\n";
  $sql_string .= "  select checkpoint_change# INTO :max_seq from v\$database;\n";
  $sql_string .= "  dbms_output.put_line('Start printing max_seq number: ');\n";
  $sql_string .= "  dbms_output.put_line('max_seq# '||:max_seq);\n";
  $sql_string .= "  select count(*) INTO :rowcount from v\$parameter where name = 'spfile';\n";
  $sql_string .= "  dbms_output.put_line('Row count: '||:rowcount);\n";
  $sql_string .= "  IF (:rowcount > 0) then\n";
  $sql_string .= "    select value INTO :spfile_val from v\$parameter where name = 'spfile';\n";
  $sql_string .= "    IF NOT (:spfile_val IS NULL) then\n";
  $sql_string .= "      dbms_output.put_line('Start printing spfile value: ');\n";
  $sql_string .= "      dbms_output.put_line('spfile# '||:spfile_val);\n";
  $sql_string .= "    end if;\n";
  $sql_string .= "  end if;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n";

  my $hideOutput = "";
  (my $fh, my $filename) = &runSqlOnSource($sql_string, $hideOutput);

  open (MAX_SEQUENCE_NUM, "$filename") || die "Unable to open tempfile for MAX_SEQUENCE_NUM\n";
  while (<MAX_SEQUENCE_NUM>)
  {
    if (($_=~/\d/) && ($_=~/\bmax_seq#/))
    {
      chomp($_);
      print "$_\n";
      EMD_PERL_DEBUG("db_clone.getMaxSeqNum(): Max sequence number: $_");
    }
    if ($_=~/\bspfile#/)
    {
      chomp($_);
      print "$_\n";
      EMD_PERL_DEBUG("db_clone.getMaxSeqNum(): spfile name: $_");
    }
  }

  close MAX_SEQUENCE_NUM;
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.getMaxSeqNum(): *** END ***");
}

# Get max info for creating control file
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# getMaxInfo()
sub getMaxInfo
{
  EMD_PERL_DEBUG("db_clone.getMaxInfo(): *** START ***");

  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "set serveroutput on;\n";

  $sql_string .= "declare\n";
  $sql_string .= "  mlogf  binary_integer;\n";
  $sql_string .= "  mlogm  binary_integer;\n";
  $sql_string .= "  mdatf  binary_integer;\n";
  $sql_string .= "  minst  binary_integer;\n";
  $sql_string .= "  mlogh  binary_integer;\n";
  $sql_string .= "  chset  varchar2(256);\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  dbms_output.enable(2000000);\n";
  $sql_string .= "  dbms_backup_restore.getMaxInfo(mlogf,\n";
  $sql_string .= "                                 mlogm,\n";
  $sql_string .= "                                 mdatf,\n";
  $sql_string .= "                                 minst,\n";
  $sql_string .= "                                 mlogh,\n";
  $sql_string .= "                                 chset);\n";
  $sql_string .= "  dbms_output.put_line('Start printing max info: ');\n";
  $sql_string .= "  dbms_output.put_line('mlogf# '||mlogf);\n";
  $sql_string .= "  dbms_output.put_line('mlogm# '||mlogm);\n";
  $sql_string .= "  dbms_output.put_line('mdatf# '||mdatf);\n";
  $sql_string .= "  dbms_output.put_line('minst# '||minst);\n";
  $sql_string .= "  dbms_output.put_line('mlogh# '||mlogh);\n";
  $sql_string .= "  dbms_output.put_line('chset# '||chset);\n";

  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n";

  my $hideOutput = "";
  (my $fh, my $filename) = &runSqlOnSource($sql_string, $hideOutput);

  open (MAX_INFO, "$filename") || die "Unable to open tempfile for MAX_INFO\n";
  while (<MAX_INFO>)
  {
    if (($_=~/\d/) && ($_=~/\mlogf#/))
    {
      chomp($_);
      print "$_\n";
      EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max log files: $_");
    }
    elsif (($_=~/\d/) && ($_=~/\mlogm#/))
    {
      chomp($_);
      print "$_\n";
      EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max log members: $_");
    }
    elsif (($_=~/\d/) && ($_=~/\mdatf#/))
    {
      chomp($_);
      print "$_\n";
      EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max datafiles: $_");
    }
    elsif (($_=~/\d/) && ($_=~/\minst#/))
    {
      chomp($_);
      print "$_\n";
      EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max instances: $_");
    }
    elsif (($_=~/\d/) && ($_=~/\mlogh#/))
    {
      chomp($_);
      print "$_\n";
      EMD_PERL_DEBUG("db_clone.getMaxInfo(): Max archived logs: $_");
    }
    elsif ($_=~/chset#/)
    {
      chomp($_);
      print "$_\n";
      EMD_PERL_DEBUG("db_clone.getMaxInfo(): Character set: $_");
    }
  }

  close MAX_INFO;
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.getMaxInfo(): *** END ***");
}

# Create password file for cloned DB
# This is specific to Unix, will be fixed later
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# createPasswordFile()
sub createPasswordFile
{
  EMD_PERL_DEBUG("db_clone.createPasswordFile(): *** START ***");

  my $oracleHome = $ENV{ORACLE_HOME};
  my $dbName = $ENV{ORACLE_SID};

  !system "$oracleHome/bin/orapwd file=$oracleHome/dbs/orapw.$dbName password=change_on_install entries=30"
    or die "Cannot create password file!";

  EMD_PERL_DEBUG("db_clone.createPasswordFile(): Password file $oracleHome/dbs/orapw.$dbName has been created.");
  EMD_PERL_DEBUG("db_clone.createPasswordFile(): *** END ***");
}

# Copy password file to temporary location,
#1) Look for orapw<inst>.
#2) If not found look for orapw<inst prefix>, where <inst prefix> is the inst name minus the trailing "_<#". This will cover the policy-managed RAC case, where the instance names are <db_unique_name>_<#>, and the pw file is orapw<db_unique_name>
#3) If not found look for orapw.

# For NT the password file format will be pwd.ora or pwd<inst prefix>.  Hence adding the .ora exists check.
sub copyPasswordFile
{
  EMD_PERL_DEBUG("db_clone.copyPasswordFile(): *** START ***");

  my($srcfile, $tempfile, $instance, $passwordFileUsage) = @_;
  my $oraExists = "N";
  if(rindex(lc(basename($srcfile)), ".ora") != -1)
  {
    $oraExists = "Y";
  }
  EMD_PERL_DEBUG("db_clone.copyPasswordFile(): oraExists = $oraExists");

  EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfile = $srcfile");
  EMD_PERL_DEBUG("db_clone.copyPasswordFile(): tempfile = $tempfile");
  EMD_PERL_DEBUG("db_clone.copyPasswordFile(): instance = $instance");
  EMD_PERL_DEBUG("db_clone.copyPasswordFile(): passwordFileUsage = $passwordFileUsage");

  # $srcfile is of the form orapw<instance>;
  if((-e $srcfile) && (-f $srcfile) )
  {
    if($passwordFileUsage eq "Y")
    {
      !copyFile($srcfile, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile"));
    }
    else #try to copy
    {
      !copyFile2($srcfile, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile"));
    }
  }
  else
  {
    EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfile with orapw<instance> or pwd<instance> in case if it is NT is not found as it is $srcfile");

    #2) If not found look for orapw<inst prefix>, where <inst prefix> is the inst name minus the trailing "_<#". This will catch policy-managed RAC case where inst names should be <db_unique_name>_<#>.
    my($index) = rindex(basename($srcfile), '_');
    my $srcfileChopped;
    my $wasChopped = 0;

    if($index != -1)
    {
      $wasChopped = 1;
      my($nindex) = rindex($srcfile, '_');
      $srcfileChopped = substr($srcfile,0,$nindex);
      if($oraExists eq "Y")
      {
        $srcfileChopped .= ".ora";
      }
    }

    EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfileChopped= $srcfileChopped");

    if($wasChopped && (-e $srcfileChopped) && (-f $srcfileChopped) )
    {
      EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfileChopped exists and it is $srcfileChopped");
      if($passwordFileUsage eq "Y")
      {
        !copyFile($srcfileChopped, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile"));
      }
      else #try to copy
      {
        !copyFile2($srcfileChopped, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile"));
      }
    }
    else
    {
      EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfileChopped is also not found, srcfile is $srcfile");

      #if it doesn't exist, truncate down to orapw and look for that
      my($idx) = rindex($srcfile,$instance);
      if($idx == -1)
      {
        EMD_PERL_DEBUG("Error parsing file name $srcfile");
        die "Error parsing file name $srcfile";
      }
      $srcfile = substr($srcfile,0,$idx);
      if($oraExists eq "Y")
      {
        $srcfile .= ".ora";
      }
      EMD_PERL_DEBUG("db_clone.copyPasswordFile(): srcfile = $srcfile ");

      if($passwordFileUsage eq "Y")
      {
        !copyFile($srcfile, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile"));
      }
      else
      {
        !copyFile2($srcfile, $tempfile) || (EMD_PERL_DEBUG("Error copying $srcfile to $tempfile") && (die "Error copying $srcfile to $tempfile"));
      }
    }
  }

  EMD_PERL_DEBUG("db_clone.copyPasswordFile(): *** END ***");
}

# Prepare the destination instance for cloning
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# clonePrepare(pfileFullName, spfile)
sub clonePrepare
{
  EMD_PERL_DEBUG("db_clone.clonePrepare(): *** START ***");

#  if($clonePurpose eq "STANDBY_NO_RECOVERY")
#  {
#    EMD_PERL_DEBUG("db_clone.clonePrepare(): No clone prepare has been done since clone purpose is: $clonePurpose");
#    EMD_PERL_DEBUG("db_clone.clonePrepare(): *** END ***");
#    return;
#  }

  my ($pfileFullName, $spfile) = @_;

  #create password file
  #Currently we copy the source password file
  #createPasswordFile();

  #kill the old instance
  my $sql_string = "";
  $sql_string .= "shutdown abort;\n";
  $sql_string .= "exit;\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.clonePrepare(): Old instance has been killed.");

  #Create the server parameter file and start instance
  $sql_string = "";
  $sql_string .= "shutdown abort\n";
  if($spfile eq "Y")
  {
    $sql_string .= "create spfile from pfile='$pfileFullName';\n";
    $sql_string .= "startup force nomount\n";
  }
  else
  {
    $sql_string .= "startup force nomount pfile='$pfileFullName';\n";
  }
  $sql_string .= "exit;\n";

  ($fh, $filename) = &runSqlOnDestination($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.clonePrepare(): *** END ***");
}

# Create init parameter file based on source DB's v$parameter
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# createSourceInitParamFile(pfileFullName, filteredParams)
sub createSourceInitParamFile
{
  EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): *** START ***");

  my ($pfileFullName, $filteredParamNames) = @_;

  EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): pfileFullName: $pfileFullName");
  EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): filteredParamNames: $filteredParamNames");

  my @filteredParamNames = split /$DELIMITER/, $filteredParamNames;

  #get names and values from source DB v$parameter
  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "set serveroutput on;\n";
  $sql_string .= "set linesize 512;\n";
  $sql_string .= "declare\n";
  $sql_string .= "  i    binary_integer := 1;\n";
  $sql_string .= "  bigvalue varchar(4096);\n";
  $sql_string .= "  value varchar(255);\n";
  $sql_string .= "BEGIN\n";
  $sql_string .= "  dbms_output.enable(2000000);\n";
  $sql_string .= "  FOR dfrec IN (SELECT name, value FROM V\$PARAMETER WHERE ISDEFAULT = 'FALSE' )\n";
  $sql_string .= "  LOOP\n";
  $sql_string .= "    IF (dfrec.value IS NULL) then\n";
  $sql_string .= "      dfrec.value := '\"\"';\n";
  $sql_string .= "    end if;\n";
  $sql_string .= "    dbms_output.put_line('Parameter ' || i || ':');\n";
  $sql_string .= "    bigvalue := 'param# ' || dfrec.name || '=' || dfrec.value;\n";
  $sql_string .= "    IF (length(bigvalue) > 255) then\n";
  $sql_string .= "      while (length(bigvalue) > 255) LOOP\n";
  $sql_string .= "        value := substr(bigvalue, 1, 255);\n";
  $sql_string .= "        bigvalue := substr(bigvalue, 256);\n";
  $sql_string .= "        dbms_output.put_line(value);\n";
  $sql_string .= "      END LOOP;\n";
  $sql_string .= "      dbms_output.put_line(bigvalue);\n";
  $sql_string .= "    else\n";
  $sql_string .= "      dbms_output.put_line(bigvalue);\n";
  $sql_string .= "    end if;\n";
  $sql_string .= "    i := i + 1;\n";
  $sql_string .= "  END LOOP;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n";

  (my $fh, my $filename) = &runSqlOnSource($sql_string);

  #Make the $pfileFullName based on souce v$parameter and filter certain
  #parameters
  open (CREATE_SOURCE_INIT_PARAM, "$filename") || die "Unable to open tempfile for CREATE_SOURCE_INIT_PARAM\n";
  open(INIT_ORA, ">$pfileFullName") || die "Cannot open $pfileFullName";
  my $nameValue = "";
  my $isPrint = "true";
  while (<CREATE_SOURCE_INIT_PARAM>)
  {
    if ($_=~/\bparam#/)
    {
      chomp($_);
      $nameValue = substr $_, 7;  #7 is the position name starts

      foreach $filteredParamNames (@filteredParamNames)
      {
        if ($nameValue =~ /\b$filteredParamNames/)
        {
          $isPrint = "false";
          last;
        }
      }

      if($isPrint eq "true")
      {
        #nls_ parameters need quotes
        if($nameValue =~ /\bnls_/i)
        {
          my @values = split /=/, $nameValue;
          my $name = $values[0];
          my $value = "='".$values[1]."'";
          print INIT_ORA "$name"."$value\n";
        }

        #handle empty os_authent_prefix
        elsif($nameValue =~ /\bos_authent_prefix/i)
        {
          my @values = split /=/, $nameValue;
          my $name = $values[0];
          my $value = "=".$values[1];
          if(&trim($values[1]) eq '')
          {
            $value = "='".$values[1]."'";
          }
          print INIT_ORA "$name"."$value\n";
        }

        #control files could be on multiple lines
        elsif($nameValue =~ /\bcontrol_files/i)
        {
          print INIT_ORA "$nameValue\n";

          my $oneLine = "";
          open (FOR_CONTROL_FILE, "$filename") || die "Unable to open tempfile for FOR_CONTROL_FILE\n";
          my $value = "";
          while ($oneLine = <FOR_CONTROL_FILE>)
          {
            if (($oneLine=~/\bparam#/) && ($oneLine=~/\bcontrol_files/i))
            {
              my $oneControlFile = "";
              while (($oneControlFile = <FOR_CONTROL_FILE>) && (!($oneControlFile =~ /\bparam#/)
                    && !($oneControlFile =~ /\bParameter/)))
              {
                $value = $oneControlFile;
                print INIT_ORA "$value";
              }
              last;
            }
          }
          close FOR_CONTROL_FILE;
        }

        #dispatchers could be on multiple lines
        elsif($nameValue =~ /\bdispatchers/i)
        {
          my $name = substr $nameValue, 0, 12; #position 0 to 11 contain "dispatchers="
          my $value = substr $nameValue, 12;  #12 is the position value starts
          if($value =~ /,/)
          {
            my @values = split /,/, $value;
            $value = "\"".$values[0]."\",";
          }
          else
          {
            $value = "\"".$value."\"";
          }
          print INIT_ORA "dispatchers=$value\n";

          my $oneLine = "";
          open (FOR_DISPATCHERS, "$filename") || die "Unable to open tempfile for FOR_DISPATCHERS\n";
          my $value = "";
          while ($oneLine = <FOR_DISPATCHERS>)
          {
            if (($oneLine=~/\bparam#/) && ($oneLine=~/\bdispatchers/i))
            {
              my $oneDispatchers = "";
              while (($oneDispatchers = <FOR_DISPATCHERS>) && (!($oneDispatchers =~ /\bparam#/)
                    && !($oneDispatchers =~ /\bParameter/)))
              {
                $value = $oneDispatchers;
                if($value =~ /,/)
                {
                  my @values = split /,/, $value;
                  $value = "\"".$values[0]."\",".$values[1];
                }
                else
                {
                  my @values = split /\n/, $value;
                  $value = "\"".$values[0]."\"\n";
                }
                print INIT_ORA "$value";
              }
              last;
            }
          }
          close FOR_DISPATCHERS;
        }

        #mts_dispatchers could be on multiple lines
        elsif($nameValue =~ /\bmts_dispatchers/i)
        {
          my $name = substr $nameValue, 0, 16; #position 0 to 15 contain "mts_dispatchers="
          my $value = substr $nameValue, 16;  #16 is the position value starts
          if($value =~ /,/)
          {
            my @values = split /,/, $value;
            $value = "\"".$values[0]."\",";
          }
          else
          {
            $value = "\"".$value."\"";
          }
          print INIT_ORA "mts_dispatchers=$value\n";

          my $oneLine = "";
          open (FOR_MTS_DISPATCHERS, "$filename") || die "Unable to open tempfile for FOR_MTS_DISPATCHERS\n";
          my $value = "";
          while ($oneLine = <FOR_MTS_DISPATCHERS>)
          {
            if (($oneLine=~/\bparam#/) && ($oneLine=~/\bmts_dispatchers/i))
            {
              my $oneDispatchers = "";
              while (($oneDispatchers = <FOR_MTS_DISPATCHERS>) && (!($oneDispatchers =~ /\bparam#/)
                    && !($oneDispatchers =~ /\bParameter/)))
              {
                $value = $oneDispatchers;
                if($value =~ /,/)
                {
                  my @values = split /,/, $value;
                  $value = "\"".$values[0]."\",".$values[1];
                }
                else
                {
                  my @values = split /\n/, $value;
                  $value = "\"".$values[0]."\"\n";
                }
                print INIT_ORA "$value";
              }
              last;
            }
          }
          close FOR_MTS_DISPATCHERS;
        }
        else
        {
          # generic look-ahead for parameters that are broken-up across
          #  more than one line
          my $oneLine = "";
          open (LOOK_AHEAD, "$filename") || die "Unable to open tempfile for LOOK_AHEAD\n";
          while ($oneLine = <LOOK_AHEAD>){
            if(($oneLine=~/\bparam#/) && ($oneLine=~/\b$nameValue/i)){
                my $addline = "";
                while (($addline = <LOOK_AHEAD>) && ($addline !~ /\bparam#/)
                       && ($addline !~ /\bParameter/) && ($addline !~ /^\n$/)){
                  chomp($addline);
                  $nameValue = $nameValue.$addline;
                }
                last;
            }
          }
          close LOOK_AHEAD;

          print INIT_ORA "$nameValue\n";
        }
      }
      else
      {
        $isPrint = "true";
      }
    }
  }
  close CREATE_SOURCE_INIT_PARAM;

  close INIT_ORA || die "Cannot close $pfileFullName";

  #For debug
  if($ENV{EMAGENT_PERL_TRACE_LEVEL} >= 0)
  {
    open (INIT_ORA_FILE, "$pfileFullName") || die "Unable to open tempfile for INIT_ORA_FILE\n";
    my @file_content = <INIT_ORA_FILE>;
    my $file_string = "@file_content";
    close INIT_ORA_FILE;
    EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): INIT_ORA_FILE:\n $file_string");
  }

  close $fh;

  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.createSourceInitParamFile(): *** END ***");
}

# Modify init parameter file based on destination host properties
# modifyInitFile(modifiedParamNames, modifiedParamValues, pfileFullName)
sub modifyInitFile
{
  EMD_PERL_DEBUG("db_clone.modifyInitFile(): *** START ***");

  my ($modifiedParamNames, $modifiedParamValues, $pfileFullName) = @_;

  ## For Data Guard; *_file_name_convert params are comma-separated
  ##  lists; the commas will mess-up the array parsing above, so
  ##  they come in from the java as # instead of commans. Need
  ##  to put them back to commas here
  $modifiedParamValues =~ s/%%%/'/g;
  $modifiedParamValues =~ s/\'#\'/\',\'/g;
  $modifiedParamValues =~ s/ROLES#ONLINE/ROLES,ONLINE/g;
  $modifiedParamValues =~ s/ROLE#STANDBY/ROLE,STANDBY/g;

  if(($modifiedParamNames eq "") || ($modifiedParamNames eq "$DELIMITER"))
  {
    EMD_PERL_DEBUG("db_clone.modifyInitFile(): NO PARAMETER TO MODIFY, returning");
    EMD_PERL_DEBUG("db_clone.modifyInitFile(): *** END ***");
    return;
  }

  #Replace the "###" with "'" in $modifiedParamValues since job system reserves ","
  #This is for control file name delimiters
  #$modifiedParamValues =~ s/###/'/g;

  EMD_PERL_DEBUG("db_clone.modifyInitFile(): modifiedParamNames: $modifiedParamNames");
  EMD_PERL_DEBUG("db_clone.modifyInitFile(): modifiedParamValues: $modifiedParamValues");

  my @modifiedParamNames = split /$DELIMITER/, $modifiedParamNames;
  my @modifiedParamValues = split /$DELIMITER/, $modifiedParamValues;

  open(INIT_ORA, ">>$pfileFullName") || die "Cannot open $pfileFullName";

  #Add modified init parameters (name=value) in the materialized init.ora
  foreach (0 .. $#modifiedParamNames)
  {
    print INIT_ORA "$modifiedParamNames[$_]=$modifiedParamValues[$_]\n";
  }

  close INIT_ORA || die "Cannot close $pfileFullName";

  #For debug
  if($ENV{EMAGENT_PERL_TRACE_LEVEL} >= 0)
  {
    open (INIT_ORA_FILE, "$pfileFullName") || die "Unable to open tempfile for INIT_ORA_FILE\n";
    my @file_content = <INIT_ORA_FILE>;
    my $file_string = "@file_content";
    close INIT_ORA_FILE;
    EMD_PERL_DEBUG("db_clone.modifyInitFile(): INIT_ORA_FILE:\n $file_string");
  }

  EMD_PERL_DEBUG("db_clone.modifyInitFile(): *** END ***");
}

# Check if an spfile is used
# return spfile name: if empty, no spfile is being used.
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# spfileUsed()
sub spfileUsed
{
  EMD_PERL_DEBUG("db_clone.spfileUsed(): *** START ***");

  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "set serveroutput on;\n";
  $sql_string .= "set linesize 512;\n";
  $sql_string .= "variable rowcount number;\n";
  $sql_string .= "variable spfile_val varchar2(256);\n";
  $sql_string .= "BEGIN\n";
  $sql_string .= "  dbms_output.enable(2000000);\n";
  $sql_string .= "  select count(*) INTO :rowcount from v\$parameter where name = 'spfile';\n";
  $sql_string .= "  dbms_output.put_line('Row count: '||:rowcount);\n";
  $sql_string .= "  IF (:rowcount > 0) then\n";
  $sql_string .= "    select value INTO :spfile_val from v\$parameter where name = 'spfile';\n";
  $sql_string .= "    IF NOT (:spfile_val IS NULL) then\n";
  $sql_string .= "      dbms_output.put_line('Start printing spfile value: ');\n";
  $sql_string .= "      dbms_output.put_line('spfile# '||:spfile_val);\n";
  $sql_string .= "    end if;\n";
  $sql_string .= "  end if;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n";

  (my $fh, my $filename) = &runSqlOnSource($sql_string);

  my $spfileName = "";
  open (SPFILE, "$filename") || die "Unable to open tempfile for SPFILE\n";
  while (<SPFILE>)
  {
    if ($_=~/\bspfile#/)
    {
      chomp($_);
      $spfileName = substr $_, 8; #spfile name starts from position 8
    }
  }

  close SPFILE;
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.spfileUsed(): spfile name: $spfileName");
  EMD_PERL_DEBUG("db_clone.spfileUsed(): *** END ***");

  return $spfileName;
}

# Shutdown DB with specified option.
# Option: normal, immediate, transactional, abort.
# Create pfile based on DB v$parameter if "pfileFullName" is defined.
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# shutdownDB(option, pfileFullName, filteredParams, modifiedNames, modifiedValues)
sub shutdownDB
{
  EMD_PERL_DEBUG("db_clone.shutdownDB(): *** START ***");

  my ($option) = (@_[0]);

  EMD_PERL_DEBUG("db_clone.shutdownDB(): option: $option");

  if(defined(@_[1]))
  {
    my ($filteredParams, $modifiedNames, $modifiedValues) = ("", "", "");
    if(defined(@_[2]))
    {
      $filteredParams = @_[2];
    }
    if(defined(@_[3]))
    {
      $modifiedNames = @_[3];
    }
    if(defined(@_[4]))
    {
      $modifiedValues = @_[4];
    }
    &createSourceInitParamFile(@_[1], $filteredParams);
    &modifyInitFile($modifiedNames, $modifiedValues, @_[1]);
    EMD_PERL_DEBUG("db_clone.shutdownDB(): created pfile: @_[1]");
  }

  my $sql_string = "";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "shutdown $option\n";
  $sql_string .= "EXIT;\n";

  my ($fh, $filename) = &runSqlOnSource($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.shutdownDB(): *** END ***");
}

# Start DB to specified mode.
# Mode: nomount, mount, open (""), restrict, force.
# Use the specified pfile if "pfileFullName" is defined.
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# startupDB(mode, pfileFullName)
sub startupDB
{
  EMD_PERL_DEBUG("db_clone.startupDB(): *** START ***");

  my ($mode) = (@_[0]);

  if($mode =~ /open/i)
  {
    $mode = "";
  }

  EMD_PERL_DEBUG("db_clone.startupDB(): mode: $mode");

  my $pfile = "";
  if(defined(@_[1]))
  {
    $pfile = "pfile='@_[1]'";
    EMD_PERL_DEBUG("db_clone.startupDB(): pfile: @_[1]");
  }

  my $sql_string = "";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "startup $mode $pfile;\n";
  $sql_string .= "EXIT;\n";

  my ($fh, $filename) = &runSqlOnSource($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.startupDB(): *** END ***");
}

# Modify spfile with given parameter names and values
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# modifySpfile(modifiedNames, modifiedValues)
sub modifySpfile
{
  EMD_PERL_DEBUG("db_clone.modifySpfile(): *** START ***");

  my ($modifiedNames, $modifiedValues) = @_;

  if(($modifiedNames eq "") || ($modifiedNames eq "$DELIMITER"))
  {
    EMD_PERL_DEBUG("db_clone.modifySpfile(): NO PARAMETER TO MODIFY, returning");
    EMD_PERL_DEBUG("db_clone.modifySpfile(): *** END ***");
    return;
  }

  #Replace the "###" with "'" in $modifiedParamValues since job system reserves ","
  #This is for control file name delimiters
  $modifiedValues =~ s/%%%/'/g;

  #Replace the ";" with "," in $modifiedValues since job system reserves ","
  #my $position = index($modifiedValues, ";");
  #my $replacedLength = 1;
  #while($position  > 0)
  #{
  #  substr($modifiedValues, $position, $replacedLength) = ",";
  #  $position = index($modifiedValues, ";");
  #}

  EMD_PERL_DEBUG("db_clone.modifySpfile(): parameter names: $modifiedNames");
  EMD_PERL_DEBUG("db_clone.modifySpfile(): parameter values: $modifiedValues");

  my @modifiedNames = split /$DELIMITER/, $modifiedNames;
  my @modifiedValues = split /$DELIMITER/, $modifiedValues;

  my $sql_string = "";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";

  foreach (0 .. $#modifiedNames)
  {
    $sql_string .= "ALTER SYSTEM SET $modifiedNames[$_]=$modifiedValues[$_] SCOPE=SPFILE; \n";
  }

  $sql_string .= "EXIT;\n";

  my ($fh, $filename) = &runSqlOnSource($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.modifySpfile(): *** END ***");
}

# Create init parameter file based on source DB's v$parameter
# For "STANDBY_NO_RECOVERY or "STANDBY_RECOVERY, the control file is backed up
# as ctlFileName for standyby.  This option is for data guard to create
# physical or logical standby database.  To clone a normal database, the
# control file is not backed up.
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# createSourceInitParamFileAndStandbyCtlFile(pfileFullName, filteredParams,
# ctlFileFullName)
sub createSourceInitParamFileAndStandbyCtlFile
{
  EMD_PERL_DEBUG("db_clone.createSourceInitParamFileAndStandbyCtlFile(): *** START ***");

  my ($pfileFullName, $filteredParamNames, $ctlFileFullName) = @_;

  EMD_PERL_DEBUG("db_clone.createSourceInitParamFileAndStandbyCtlFile(): filteredParamNames: $filteredParamNames");
  EMD_PERL_DEBUG("db_clone.createSourceInitParamFileAndStandbyCtlFile(): ctlFileName: $ctlFileFullName");

  &createSourceInitParamFile("$pfileFullName", $filteredParamNames);

  EMD_PERL_DEBUG("db_clone.createSourceInitParamFileAndStandbyCtlFile(): *** END ***");
}

# Create control file for standby (physical or logical)
sub createStandbyCtlFile
{
  EMD_PERL_DEBUG("db_clone.createStandbyCtlFile(): *** START ***");

  my ($ctlFileFullName) = @_;

  EMD_PERL_DEBUG("db_clone.createStandbyCtlFile(): ctlFileName: $ctlFileFullName");

  #Backup control file
  my $sql_string = "";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";


  if(($clonePurpose eq "STANDBY_NO_RECOVERY") || ($clonePurpose eq "STANDBY_RECOVERY"))
  {
    $sql_string .= "ALTER DATABASE CREATE STANDBY CONTROLFILE AS '$ctlFileFullName';\n";
  }
  else
  {
    ## Different controlfile commands for logical standby 9i & 10i
    if($clonePurpose eq "LOGICAL_STANDBY")  {
      $sql_string .= "ALTER DATABASE BACKUP CONTROLFILE TO '$ctlFileFullName';\n";
    }
    elsif($clonePurpose =~ /^LOGICAL_STANDBY_10I$/i)  {
      $sql_string .= "ALTER DATABASE CREATE LOGICAL STANDBY CONTROLFILE AS '$ctlFileFullName';\n";
    }
    else #for normal DBClone
    {
      $sql_string .= "ALTER DATABASE BACKUP CONTROLFILE TO '$ctlFileFullName';\n";
    }
  }

  $sql_string .= "EXIT;\n";
  (my $fh, my $filename) = &runSqlOnSource($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.createStandbyCtlFile(): *** END ***");
}

# Backup one datafile and store it as given backupPieceName
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# backupDatafile(backupPieceName, fileNum)
sub backupDatafile
{
  EMD_PERL_DEBUG("db_clone.backupDatafile(): *** START ***");

  my ($backupPieceName, $fileNum) = @_;

  EMD_PERL_DEBUG("db_clone.backupDatafile(): temp file to store backup: $backupPieceName");
  EMD_PERL_DEBUG("db_clone.backupDatafile(): fileNum to be backed up: $fileNum");

  #backup one datafile
  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";

  $sql_string .= "variable devicename varchar2(255);\n";
  $sql_string .= "variable set_stamp  number;\n";
  $sql_string .= "variable set_count  number;\n\n";

  $sql_string .= "declare\n";
  $sql_string .= "  pieceno binary_integer;\n";
  $sql_string .= "  done    boolean;\n";
  $sql_string .= "  handle  varchar2(256);\n";
  $sql_string .= "  comment varchar2(256);\n";
  $sql_string .= "  media   varchar2(256);\n";
  $sql_string .= "  concur  boolean;\n";
  $sql_string .= "  params  varchar2(256);\n\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.backupSetDataFile(:set_stamp, :set_count);\n";
  $sql_string .= "  dbms_backup_restore.backupDataFile($fileNum);\n";
  $sql_string .= "  dbms_backup_restore.backupPieceCreate('$backupPieceName',\n";
  $sql_string .= "                                        pieceno, done,\n";
  $sql_string .= "                                        handle, comment,\n";
  $sql_string .= "                                        media, concur,\n";
  $sql_string .= "                                        params, reuse=>true);\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnSource($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.backupDatafile(): *** END ***");
}

# To replace backupDatafile()
# Backup one datafile and store it as given backupPieceName
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# backupOneDatafile(backupPieceName, fileNum)
sub backupOneDatafile
{
  EMD_PERL_DEBUG("db_clone.backupOneDatafile(): *** START ***");

  my ($backupPieceName, $fileNum) = @_;

  EMD_PERL_DEBUG("db_clone.backupOneDatafile(): fileNum to be backed up: $fileNum");
  EMD_PERL_DEBUG("db_clone.backupOneDatafile(): fileName to store backup piece: $backupPieceName");

  #backup one datafile
  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";

  $sql_string .= "variable devicename varchar2(255);\n";
  $sql_string .= "variable set_stamp  number;\n";
  $sql_string .= "variable set_count  number;\n\n";

  $sql_string .= "declare\n";
  $sql_string .= "  pieceno binary_integer;\n";
  $sql_string .= "  done    boolean;\n";
  $sql_string .= "  handle  varchar2(256);\n";
  $sql_string .= "  comment varchar2(256);\n";
  $sql_string .= "  media   varchar2(256);\n";
  $sql_string .= "  concur  boolean;\n";
  $sql_string .= "  params  varchar2(256);\n\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.backupSetDataFile(:set_stamp, :set_count);\n";
  $sql_string .= "  dbms_backup_restore.backupDataFile($fileNum);\n";
  $sql_string .= "  dbms_backup_restore.backupPieceCreate('$backupPieceName',\n";
  $sql_string .= "                                        pieceno, done,\n";
  $sql_string .= "                                        handle, comment,\n";
  $sql_string .= "                                        media, concur,\n";
  $sql_string .= "                                        params, reuse=>true);\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnSource($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.backupOneDatafile(): *** END ***");
}

# Backup multiple datafiles and store it (name: PIECE) in a given temp location
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# backupMultipleDatafiles(backupPieceName, fileNums)
sub backupMultipleDatafiles
{
  EMD_PERL_DEBUG("db_clone.backupMultipleDatafiles(): *** START ***");

  my ($backupPieceName, $fileNums) = @_;

  EMD_PERL_DEBUG("db_clone.backupMultipleDatafiles(): fileNum to be backed up: $fileNums");
  EMD_PERL_DEBUG("db_clone.backupMultipleDatafiles(): Backup piece name: $backupPieceName");

  my @datafileNums = split /$DELIMITER/, $fileNums;

  #backup one datafile
  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";

  $sql_string .= "variable devicename varchar2(255);\n";
  $sql_string .= "variable set_stamp  number;\n";
  $sql_string .= "variable set_count  number;\n\n";

  $sql_string .= "declare\n";
  $sql_string .= "  pieceno binary_integer;\n";
  $sql_string .= "  done    boolean;\n";
  $sql_string .= "  handle  varchar2(256);\n";
  $sql_string .= "  comment varchar2(256);\n";
  $sql_string .= "  media   varchar2(256);\n";
  $sql_string .= "  concur  boolean;\n";
  $sql_string .= "  params  varchar2(256);\n\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.backupSetDataFile(:set_stamp, :set_count);\n";

  my $datafileNums;
  foreach $datafileNums (@datafileNums)
  {
    $sql_string .= "  dbms_backup_restore.backupDataFile($datafileNums);\n";
  }

  $sql_string .= "  dbms_backup_restore.backupPieceCreate('$backupPieceName',\n";
  $sql_string .= "                                        pieceno, done,\n";
  $sql_string .= "                                        handle, comment,\n";
  $sql_string .= "                                        media, concur,\n";
  $sql_string .= "                                        params, reuse=>true);\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnSource($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.backupMultipleDatafiles(): *** END ***");
}

# Backup archived logs in one piece and store it (name: ARCH) in a given temp
# location
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# backupLog(backupPieceName, maxSeqNum)
sub backupLog
{
  EMD_PERL_DEBUG("db_clone.backupLog(): *** START ***");

  my ($backupPieceName, $maxSeqNum) = @_;

  EMD_PERL_DEBUG("db_clone.backupLog(): Log max sequence number: $maxSeqNum");
  EMD_PERL_DEBUG("db_clone.backupLog(): Backup piece name: $backupPieceName");

  ## Logical standby: build dictionary prior to log backup (9i only)
  if($clonePurpose eq "LOGICAL_STANDBY")  {
    prepareLogical();
  }

  #backup all archived logs to one piece
  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";

  $sql_string .= "ALTER SYSTEM ARCHIVE LOG CURRENT;\n\n";

  $sql_string .= "variable devicename varchar2(255);\n";
  $sql_string .= "variable set_stamp  number;\n";
  $sql_string .= "variable set_count  number;\n\n";

  $sql_string .= "declare\n";
  $sql_string .= "  pieceno binary_integer;\n";
  $sql_string .= "  done    boolean;\n";
  $sql_string .= "  handle  varchar2(256);\n";
  $sql_string .= "  comment varchar2(256);\n";
  $sql_string .= "  media   varchar2(256);\n";
  $sql_string .= "  concur  boolean;\n";
  $sql_string .= "  params  varchar2(256);\n\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.backupSetArchivedLog(:set_stamp, :set_count);\n";

  $sql_string .= "  FOR alrec IN (SELECT name, recid, stamp FROM V\$ARCHIVED_LOG \n";
 # $sql_string .= "                             WHERE sequence#>$maxSeqNum and standby_dest = 'NO')\n";
  $sql_string .= "                             WHERE $maxSeqNum < next_change# and standby_dest = 'NO' and ARCHIVED = 'YES' and status = 'A' and RESETLOGS_CHANGE# = (select RESETLOGS_CHANGE# from V\$DATABASE))\n";
  $sql_string .= "  LOOP\n";
  $sql_string .= "    dbms_backup_restore.backupArchivedLog(alrec.recid, alrec.stamp);\n";
  $sql_string .= "  END LOOP;\n\n";

  $sql_string .= "  dbms_backup_restore.backupPieceCreate('$backupPieceName',\n";
  $sql_string .= "                                        pieceno, done,\n";
  $sql_string .= "                                        handle, comment,\n";
  $sql_string .= "                                        media, concur,\n";
  $sql_string .= "                                        params, reuse=>true);\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnSource($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.backupLog(): *** END ***");
}

# Run logical standby preparate procedure
sub prepareLogical
{
  EMD_PERL_DEBUG("db_clone.prepareLogical(): *** START ***");

  my($sql_string) = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";
  $sql_string .= "set serveroutput on;\n";

  $sql_string .= "variable state VARCHAR2(9);\n";

  $sql_string .= "BEGIN\n";

  ## Quiesce
  $sql_string .= "SELECT active_state into :state from v\$instance;\n";
  $sql_string .= "IF (:state = 'NORMAL') then\n";
  $sql_string .= "  EXECUTE IMMEDIATE 'ALTER SYSTEM QUIESCE RESTRICTED';\n";
  $sql_string .= "  dbms_output.put_line('db is quiesced');\n";
  $sql_string .= "END IF;\n";

  ## Logical dictionary build
  $sql_string .= "dbms_output.put_line('Building data dictionary');\n";
  $sql_string .= "DBMS_LOGMNR_D.BUILD(options => DBMS_LOGMNR_D.STORE_IN_REDO_LOGS);\n";

  ## Switch a log to to seed the stream w/ the dictionary log
  $sql_string .= "EXECUTE IMMEDIATE 'ALTER SYSTEM ARCHIVE LOG CURRENT';\n";

  ## Unquiesce
  $sql_string .= "SELECT active_state into :state from v\$instance;\n";
  $sql_string .= "IF (:state != 'NORMAL') then\n";
  $sql_string .= "  EXECUTE IMMEDIATE 'ALTER SYSTEM UNQUIESCE';\n";
  $sql_string .= "  dbms_output.put_line('db is unquiesced');\n";
  $sql_string .= "END IF;\n";

  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  (my $fh, my $filename) = &runSqlOnSource($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.prepareLogical(): *** END ***");
}


# Restore one datafile to the given working directory
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# restoreDatafile(workDir, backupPieceName, fileNum)
sub restoreDatafile
{
  EMD_PERL_DEBUG("db_clone.restoreDatafile(): *** START ***");

  my ($workDir, $backupPieceName, $fileNum) = @_;
  my $dbName = $ENV{ORACLE_SID};

  my @tempAry = convertOneDatafileNum($dbName, $workDir, $fileNum);
  my $datafileName = $tempAry[0];

  EMD_PERL_DEBUG("db_clone.restoreDatafile(): Backup piece name: $backupPieceName");
  EMD_PERL_DEBUG("db_clone.restoreDatafile(): File number to be restored: $fileNum");
  EMD_PERL_DEBUG("db_clone.restoreDatafile(): The restored file name: $datafileName");

  #restore one datafile
  my $sql_string = "";
  $sql_string .= "set echo off\n";

  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "declare\n";
  $sql_string .= "  done boolean;\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.restoreSetDataFile;\n";
  $sql_string .= "  dbms_backup_restore.restoreDataFileTo($fileNum, '$datafileName');\n";
  $sql_string .= "  dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";

  $sql_string .= "  IF done then\n";
  $sql_string .= "    dbms_backup_restore.deletefile('$backupPieceName');\n";
  $sql_string .= "  end if;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.restoreDatafile(): *** END ***");
}

# Restore one datafile to the given working directory
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# restoreDatafileSameHost(workDir, backupPieceName, fileNum)
sub restoreDatafileSameHost
{
  EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): *** START ***");

  my ($workDir, $backupPieceName, $fileNum) = @_;
  my $dbName = $ENV{ORACLE_SID};

  my @tempAry = convertOneDatafileNum($dbName, $workDir, $fileNum);
  my $datafileName = $tempAry[0];

  EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): Backup piece name: $backupPieceName");
  EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): File number to be restored: $fileNum");
  EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): The restored file name: $datafileName");

  #restore one datafile
  my $sql_string = "";
  $sql_string .= "set echo off\n";

  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "declare\n";
  $sql_string .= "  done boolean;\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.restoreSetDataFile;\n";
  $sql_string .= "  dbms_backup_restore.restoreDataFileTo($fileNum, '$datafileName');\n";
  $sql_string .= "  dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.restoreDatafileSameHost(): *** END ***");
}

# To replace restoreDatafile()
# Restore one datafile to the given working directory
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# restoreOneDatafile(backupPieceName, fileNum, fileName)
sub restoreOneDatafile
{
  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): *** START ***");

  my ($backupPieceName, $fileNum, $fileName) = @_;

  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): Backup piece name: $backupPieceName");
  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): File number to be restored: $fileNum");
  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): The restored file name: $fileName");

  #restore one datafile
  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "declare\n";
  $sql_string .= "  done boolean;\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.restoreSetDataFile;\n";
  $sql_string .= "  dbms_backup_restore.restoreDataFileTo($fileNum, '$fileName');\n";
  $sql_string .= "  dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";

  $sql_string .= "  IF done then\n";
  $sql_string .= "    dbms_backup_restore.deletefile('$backupPieceName');\n";
  $sql_string .= "  end if;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): *** END ***");
}

# To replace restoreDatafileSameHost()
# Restore one datafile as the given fileName
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# restoreOneDatafileSameHost(backupPieceName, fileNum, fileName)
sub restoreOneDatafileSameHost
{
  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): *** START ***");

  my ($backupPieceName, $fileNum, $fileName) = @_;

  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): Backup piece name: $backupPieceName");
  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): File number to be restored: $fileNum");
  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): The restored file name: $fileName");

  #restore one datafile
  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "declare\n";
  $sql_string .= "  done boolean;\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.restoreSetDataFile;\n";
  $sql_string .= "  dbms_backup_restore.restoreDataFileTo($fileNum, '$fileName');\n";
  $sql_string .= "  dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.restoreOneDatafile(): *** END ***");
}

# Restore multiple datafiles to the given working directory
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# restoreMultipleDatafiles(workDir, backupPieceName, fileNums)
sub restoreMultipleDatafiles
{
  EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): *** START ***");

  my ($workDir, $backupPieceName, $fileNums) = @_;
  my $dbName = $ENV{ORACLE_SID};

  my @datafileNums = split /$DELIMITER/, $fileNums;
  my @datafileNames = convertDatafileNums($dbName, $workDir, $fileNums);

  EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): Backup piece name: $backupPieceName");
  EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): File number to be restored: $fileNums");
  EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): The restored file names: @datafileNames");

  #restore one datafile
  my $sql_string = "";
  $sql_string .= "set echo off\n";

  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "declare\n";
  $sql_string .= "  done boolean;\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.restoreSetDataFile;\n";

  my $datafileNum;
  my $index = 0;
  foreach $datafileNum (@datafileNums)
  {
    $sql_string .= "  dbms_backup_restore.restoreDataFileTo($datafileNum, '$datafileNames[$index]');\n";
    $index ++;
  }

  $sql_string .= "  dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";

  $sql_string .= "  IF done then\n";
  $sql_string .= "    dbms_backup_restore.deletefile('$backupPieceName');\n";
  $sql_string .= "  end if;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.restoreMultipleDatafiles(): *** END ***");
}

# Restore backup piece containing archived logs to the given working directory
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# restoreLog(backupPieceName, restoreLogName)
sub restoreLog
{
  EMD_PERL_DEBUG("db_clone.restoreLog(): *** START ***");

  my ($backupPieceName, $restoreLogName) = @_;

  EMD_PERL_DEBUG("db_clone.restoreLog(): Log backup piece name: $backupPieceName");
  EMD_PERL_DEBUG("db_clone.restoreLog(): The prefix of file name to be restored: $restoreLogName");

  #restore logs
  my $sql_string = "";
  $sql_string .= "set echo off\n";

  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "declare\n";
  $sql_string .= "  done boolean;\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.restoreSetArchivedLog('$restoreLogName');\n";
  $sql_string .= "  dbms_backup_restore.restoreArchivedLogRange();\n";
  $sql_string .= "  dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";

  $sql_string .= "  IF done then\n";
  $sql_string .= "    dbms_backup_restore.deletefile('$backupPieceName');\n";
  $sql_string .= "  end if;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.restoreLog(): *** END ***");
}

# Restore backup piece containing archived logs to the given working directory
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# restoreLogSameHostExistingBS(backupPieceName, restoreLogName)
sub restoreLogSameHostExistingBS
{
  EMD_PERL_DEBUG("db_clone.restoreLogSameHostExistingBS(): *** START ***");

  my ($backupPieceName, $restoreLogName) = @_;

  EMD_PERL_DEBUG("db_clone.restoreLogSameHostExistingBS(): Log backup piece name: $backupPieceName");
  EMD_PERL_DEBUG("db_clone.restoreLogSameHostExistingBS(): The prefix of file name to be restored: $restoreLogName");

  #restore logs
  my $sql_string = "";
  $sql_string .= "set echo off\n";

  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "declare\n";
  $sql_string .= "  done boolean;\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.restoreSetArchivedLog('$restoreLogName');\n";
  $sql_string .= "  dbms_backup_restore.restoreArchivedLogRange();\n";
  $sql_string .= "  dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";

  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.restoreLogSameHostExistingBS(): *** END ***");
}

# Restore backup piece containing archived logs to the given working directory
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# restoreLogSameHost(backupPieceName, restoreLogName)
sub restoreLogSameHost
{
  EMD_PERL_DEBUG("db_clone.restoreLogSameHost(): *** START ***");

  my ($backupPieceName, $restoreLogName) = @_;

  EMD_PERL_DEBUG("db_clone.restoreLogSameHost(): Log backup piece name: $backupPieceName");
  EMD_PERL_DEBUG("db_clone.restoreLogSameHost(): The prefix of file name to be restored: $restoreLogName");

  #restore logs
  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "CONNECT $userID AS SYSDBA;\n";

  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "declare\n";
  $sql_string .= "  done boolean;\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.restoreSetArchivedLog('$restoreLogName');\n";
  $sql_string .= "  dbms_backup_restore.restoreArchivedLogRange();\n";
  $sql_string .= "  dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";

  #$sql_string .= "  IF done then\n";
  #$sql_string .= "    dbms_backup_restore.deletefile('$backupPieceName');\n";
  #$sql_string .= "  end if;\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnSource($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.restoreLogSameHost(): *** END ***");
}

# Depricated
# Restore one datafile to the given working directory for remote operations
# restoreDatafileWithWaiting(oracleHome, dbName, workDir, tempDir, fileNum)
sub restoreDatafileWithWaiting
{
  my ($oracleHome, $dbName, $workDir, $tempDir, $fileNum) = @_;
  $ENV{ORACLE_HOME} = "$oracleHome";
  $ENV{ORACLE_SID} = "$dbName";
  my $backupPieceName = "$tempDir/PIECE";
  my $datafileName = "$workDir/$dbName"."_file"."$fileNum".".dbf";

  my $dir = tempdir( CLEANUP => 1);
  (my $fh, my $filename) = tempfile( DIR => $dir );

  #restore one datafile
  open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename")
    || die "Cannot open pipe for RESTORE_DATAFILE";

  print SQL_SCRIPT "set echo off\n";
  print SQL_SCRIPT "connect / as sysdba;\n";
  print SQL_SCRIPT "set serveroutput on;\n\n";

  print SQL_SCRIPT "variable devicename varchar2(255);\n";

  print SQL_SCRIPT "declare\n";
  print SQL_SCRIPT "  done boolean;\n";
  print SQL_SCRIPT "  i    binary_integer := 1;\n";
  print SQL_SCRIPT "  j    binary_integer := 0;\n";
  print SQL_SCRIPT "  ora_19624 EXCEPTION;\n";
  print SQL_SCRIPT "  PRAGMA EXCEPTION_INIT(ora_19624, -19624);\n";

  print SQL_SCRIPT "BEGIN\n";
  print SQL_SCRIPT "  dbms_output.enable(2000000);\n";
  print SQL_SCRIPT "  dbms_output.put_line(' ');\n";
  print SQL_SCRIPT "  dbms_output.put_line(' RESTORE: Allocating device... ');\n";
  print SQL_SCRIPT "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  print SQL_SCRIPT "  dbms_output.put_line(' RESTORE: Specifing datafiles... ');\n";
  print SQL_SCRIPT "  dbms_backup_restore.restoreSetDataFile;\n";
  print SQL_SCRIPT "  dbms_backup_restore.restoreDataFileTo($fileNum, '$datafileName');\n";

  print SQL_SCRIPT "  LOOP\n";
  print SQL_SCRIPT "    j := 0;\n";
  print SQL_SCRIPT "    <<retry>>\n";
  print SQL_SCRIPT "    j := j + 1;\n";
  print SQL_SCRIPT "    dbms_output.put_line(' RESTORE: Restoring piece ');\n";
  print SQL_SCRIPT "    BEGIN\n";
  print SQL_SCRIPT "      dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";
  print SQL_SCRIPT "    EXCEPTION\n";
  print SQL_SCRIPT "      WHEN ora_19624 THEN\n";
  print SQL_SCRIPT "        IF j < 400\n";
  print SQL_SCRIPT "        THEN\n";
  print SQL_SCRIPT "          dbms_backup_restore.sleep(30);\n";
  print SQL_SCRIPT "          GOTO retry;\n";
  print SQL_SCRIPT "        ELSE\n";
  print SQL_SCRIPT "          dbms_output.put_line(' RESTORE: Too many retries. Giving up.');\n";
  print SQL_SCRIPT "          RAISE;\n";
  print SQL_SCRIPT "        END IF;\n";
  print SQL_SCRIPT "      RAISE;\n";
  print SQL_SCRIPT "      WHEN others THEN\n";
  print SQL_SCRIPT "        RAISE;\n";
  print SQL_SCRIPT "    END;\n";

  print SQL_SCRIPT "    IF done then\n";
  print SQL_SCRIPT "      dbms_backup_restore.deletefile('$backupPieceName');\n";
  print SQL_SCRIPT "      dbms_output.put_line(' RESTORE: Restore datafile done.');\n";
  print SQL_SCRIPT "      EXIT;\n";
  print SQL_SCRIPT "    else\n";
  print SQL_SCRIPT "      dbms_output.put_line(' RESTORE: Restore datafile failed');\n";
  print SQL_SCRIPT "      EXIT;\n";
  print SQL_SCRIPT "    end if;\n";
  print SQL_SCRIPT "  END LOOP;\n";
  print SQL_SCRIPT "END;\n";
  print SQL_SCRIPT "/\n";
  print SQL_SCRIPT "EXIT;\n\n";

  close SQL_SCRIPT || die "Bad SQL_SCRIPT_RESTORE_DATAFILE";

  open (RESTORE_DATAFILE, "$filename") || die "Unable to open tempfile\n";
  while (<RESTORE_DATAFILE>)
  {
    print;
  }
  close RESTORE_DATAFILE;

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }
}

# Depricated
# Restore backup piece containing archived logs to the given working directory
# for remote operations
# restoreLogWithWaiting(oracleHome, dbName, workDir, tempDir)
sub restoreLogWithWaiting
{
  my ($oracleHome, $dbName, $workDir, $tempDir) = @_;
  $ENV{ORACLE_HOME} = "$oracleHome";
  $ENV{ORACLE_SID} = "$dbName";
  #my $restoreLogName = "$workDir/ARCH";
  my $restoreLogName = "$tempDir/ARCH";
  my $backupPieceName = "$tempDir/ARCH";

  my $dir = tempdir( CLEANUP => 1);
  (my $fh, my $filename) = tempfile( DIR => $dir );

  #restore one datafile
  open(SQL_SCRIPT, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$filename")
    || die "Cannot open pipe for RESTORE_LOG";

  print SQL_SCRIPT "set echo off\n";
  print SQL_SCRIPT "connect / as sysdba;\n";
  print SQL_SCRIPT "set serveroutput on;\n\n";

  print SQL_SCRIPT "variable devicename varchar2(255);\n";

  print SQL_SCRIPT "declare\n";
  print SQL_SCRIPT "  done boolean;\n";
  print SQL_SCRIPT "  i    binary_integer := 1;\n";
  print SQL_SCRIPT "  j    binary_integer := 0;\n";
  print SQL_SCRIPT "  ora_19624 EXCEPTION;\n";
  print SQL_SCRIPT "  PRAGMA EXCEPTION_INIT(ora_19624, -19624);\n";

  print SQL_SCRIPT "BEGIN\n";
  print SQL_SCRIPT "  dbms_output.enable(2000000);\n";
  print SQL_SCRIPT "  dbms_output.put_line(' ');\n";
  print SQL_SCRIPT "  dbms_output.put_line(' RESTORE: Allocating device... ');\n";
  print SQL_SCRIPT "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  print SQL_SCRIPT "  dbms_output.put_line(' RESTORE: Specifing archived logs... ');\n";
  print SQL_SCRIPT "  dbms_backup_restore.restoreSetArchivedLog('$restoreLogName');\n";
  print SQL_SCRIPT "  dbms_backup_restore.restoreArchivedLogRange();\n";

  print SQL_SCRIPT "  LOOP\n";
  print SQL_SCRIPT "    j := 0;\n";
  print SQL_SCRIPT "    <<retry>>\n";
  print SQL_SCRIPT "    j := j + 1;\n";
  print SQL_SCRIPT "    dbms_output.put_line(' RESTORE: Restoring piece ');\n";
  print SQL_SCRIPT "    BEGIN\n";
  print SQL_SCRIPT "      dbms_backup_restore.restoreBackupPiece('$backupPieceName', done);\n\n";
  print SQL_SCRIPT "    EXCEPTION\n";
  print SQL_SCRIPT "      WHEN ora_19624 THEN\n";
  print SQL_SCRIPT "        IF j < 400\n";
  print SQL_SCRIPT "        THEN\n";
  print SQL_SCRIPT "          dbms_backup_restore.sleep(30);\n";
  print SQL_SCRIPT "          GOTO retry;\n";
  print SQL_SCRIPT "        ELSE\n";
  print SQL_SCRIPT "          dbms_output.put_line(' RESTORE: Too many retries. Giving up.');\n";
  print SQL_SCRIPT "          RAISE;\n";
  print SQL_SCRIPT "        END IF;\n";
  print SQL_SCRIPT "      RAISE;\n";
  print SQL_SCRIPT "      WHEN others THEN\n";
  print SQL_SCRIPT "        RAISE;\n";
  print SQL_SCRIPT "    END;\n";

  print SQL_SCRIPT "    IF done then\n";
  print SQL_SCRIPT "      dbms_backup_restore.deletefile('$backupPieceName');\n";
  print SQL_SCRIPT "      dbms_output.put_line(' RESTORE: Restore archivedLogs done.');\n";
  print SQL_SCRIPT "      EXIT;\n";
  print SQL_SCRIPT "    else\n";
  print SQL_SCRIPT "      dbms_output.put_line(' RESTORE: Restore archivedLogs failed');\n";
  print SQL_SCRIPT "      EXIT;\n";
  print SQL_SCRIPT "    end if;\n";
  print SQL_SCRIPT "  END LOOP;\n";
  print SQL_SCRIPT "END;\n";
  print SQL_SCRIPT "/\n";
  print SQL_SCRIPT "EXIT;\n\n";

  close SQL_SCRIPT || die "Bad SQL_SCRIPT_RESTORE_LOG";

  open (RESTORE_LOG, "$filename") || die "Unable to open tempfile\n";
  while (<RESTORE_LOG>)
  {
    print;
  }
  close RESTORE_LOG;

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }
}

#convert datafile numbers to datafile names.
#This will be done in java code later for more complicated cases.
#convertDatafileNums(dbName, workDir, fileNumArray)
sub convertDatafileNums()
{
  my ($dbName, $workDir, $fileNumArray) = @_;
  my @datafileNums = split /$DELIMITER/, $fileNumArray;
  my @datafileNames = ();
  my $count = 0;
  my $datafileNums;
  foreach $datafileNums (@datafileNums)
  {
    $datafileNames[$count] = "$workDir/$dbName"."_file"."$datafileNums".".dbf";
    $count ++;
  }
  return @datafileNames;
}

#convert one datafile number to datafile names.
#Remove any white space around the datafile number
#convertOneDatafileNum(dbName, workDir, fileNum)
sub convertOneDatafileNum()
{
  my ($dbName, $workDir, $fileNum) = @_;
  my @datafileNums = split /\s+/, $fileNum;
  my @datafileNames = ();
  my $count = 0;
  my $datafileNums;
  foreach $datafileNums (@datafileNums)
  {
    if($datafileNums ne (""))
    {
      $datafileNames[$count] = "$workDir/$dbName"."_file"."$datafileNums".".dbf";
      $count ++;
    }
  }
  return @datafileNames;
}

#convert logfile names
#This will be done in java code later for more complicated cases.
#convertLogfileName(dbName, workDir, logGroupArrray)
sub convertLogfileName()
{
  my ($dbName, $workDir, $logGroupArrray) = @_;
  my @logGroupNums = split /$DELIMITER/, $logGroupArrray;
  my @logfileNames = ();
  my $count = 0;
  my $logGroupNums;
  foreach $logGroupNums (@logGroupNums)
  {
    $logfileNames[$count] = "$workDir/$dbName"."_redo"."$logGroupNums".".log";
    $count ++;
  }
  return @logfileNames;
}

# Create a new control file
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# createControlFile(dfDir, lfDir, fileNumArray, pfileFullName, spfile)
sub createControlFileBk
{
  EMD_PERL_DEBUG("db_clone.createControlFile(): *** START ***");

  my ($dfDir, $lfDir, $fileNumArray, $pfileFullName, $spfile) = @_;
  my $dbName = $ENV{ORACLE_SID};

  my @datafileNames = &convertDatafileNums($dbName, $dfDir, $fileNumArray);
  #set two log groups for the moment
  my @logfileNames = &convertLogfileName($dbName, $lfDir, "1:2:");

  EMD_PERL_DEBUG("db_clone.createControlFile(): Restored datafile names: @datafileNames");
  EMD_PERL_DEBUG("db_clone.createControlFile(): Created online redo log files: @logfileNames");

  #open the database with resetlogs
  my $sql_string = "";

  if($spfile eq "Y")
  {
    $sql_string .= "startup force nomount;\n";
  }
  else
  {
    $sql_string .= "startup force nomount pfile='$pfileFullName';\n";
  }

  $sql_string .= "create controlfile reuse set database $dbName resetlogs archivelog\n";
  $sql_string .= "datafile\n";
  foreach (0 .. $#datafileNames - 1)
  {
    $sql_string .= "    '$datafileNames[$_]'  reuse,\n";
  }
  $sql_string .= "    '$datafileNames[$#datafileNames]'  reuse\n";

  $sql_string .= "logfile\n";
  foreach (0 .. $#logfileNames - 1)
  {
    $sql_string .= "  group $_ '$logfileNames[$_]' size 20M reuse,\n";
  }
  $sql_string .= "  group $#logfileNames '$logfileNames[$#logfileNames]' size 20M reuse\n";

  $sql_string .= "maxinstances 2\n";
  $sql_string .= "maxdatafiles 100\n";
  $sql_string .= "maxlogfiles 10\n";
  $sql_string .= "maxlogmembers 5\n";
  $sql_string .= "maxarchlogs 500\n";
  $sql_string .= "character set \"WE8DEC\";\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.createControlFile(): *** END ***");
}

# To replace createControlFile()
# Create a new control file
# One file name in logfileArray represents one GROUP of log file, this will be
# changed later.
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# createControlFile(dbName, datafileArray, loggroupArray, logfileArray, maxInfoArray, pfileFullName, spfile)
sub createControlFile
{
  EMD_PERL_DEBUG("db_clone.createControlFile(): *** START ***");

  my ($dbName, $datafileArray, $loggroupArray, $logfileArray, $maxInfoArray, $pfileFullName, $spfile) = @_;

  my @datafileNames = split /$DELIMITER/, $datafileArray;
  my @loggroupNames = split /$DELIMITER/, $loggroupArray;
  my @logfileNames = split /$DELIMITER/, $logfileArray;
  my @maxInfo = split /$DELIMITER/, $maxInfoArray;

  EMD_PERL_DEBUG("db_clone.createControlFile(): Database Name: $dbName");
  EMD_PERL_DEBUG("db_clone.createControlFile(): Restored datafile names: @datafileNames");
  EMD_PERL_DEBUG("db_clone.createControlFile(): Created online redo log groups: @loggroupNames");
  EMD_PERL_DEBUG("db_clone.createControlFile(): Created online redo log files: @logfileNames");
  EMD_PERL_DEBUG("db_clone.createControlFile(): Control file max info: @maxInfo");

  #open the database with resetlogs
  my $sql_string = "";

  #$sql_string .= "shutdown abort; \n";

  if($spfile eq "Y")
  {
    $sql_string .= "startup force nomount;\n";
  }
  else
  {
    $sql_string .= "startup force nomount pfile='$pfileFullName';\n";
  }

  $sql_string .= "create controlfile reuse set database $dbName resetlogs archivelog\n";
  $sql_string .= "datafile\n";
  foreach (0 .. $#datafileNames - 1)
  {
    $sql_string .= "    '$datafileNames[$_]'  reuse,\n";
  }
  $sql_string .= "    '$datafileNames[$#datafileNames]'  reuse\n";

  my $prev_group = "";
  my $curr_group = "";
  my $next_group = "";
  $sql_string .= "logfile\n";
  foreach (0 .. $#loggroupNames - 1)
  {
    $curr_group = $loggroupNames[$_];
    $next_group = $loggroupNames[$_ + 1];
    if(($curr_group ne $prev_group) && ($curr_group ne $next_group))
    {
      $sql_string .= "  group $loggroupNames[$_] '$logfileNames[$_]' size 20M reuse,\n";
    }
    elsif(($curr_group ne $prev_group) && ($curr_group eq $next_group))
    {
      $sql_string .= "  group $loggroupNames[$_] ('$logfileNames[$_]',\n";
    }
    elsif(($curr_group eq $prev_group) && ($curr_group ne $next_group))
    {
      $sql_string .= "  '$logfileNames[$_]') size 20M reuse,\n";
    }
    elsif(($curr_group eq $prev_group) && ($curr_group eq $next_group))
    {
      $sql_string .= "  '$logfileNames[$_]',\n";
    }

    $prev_group = $curr_group;
  }
  $curr_group = $loggroupNames[$#loggroupNames];
  if($curr_group ne $prev_group)
  {
    $sql_string .= "  group $loggroupNames[$#loggroupNames] '$logfileNames[$#logfileNames]' size 20M reuse\n";
  }
  else
  {
    $sql_string .= "  '$logfileNames[$#logfileNames]') size 20M reuse\n";
  }

  #$sql_string .= "maxinstances 2\n";
  #$sql_string .= "maxdatafiles 100\n";
  #$sql_string .= "maxlogfiles 10\n";
  #$sql_string .= "maxlogmembers 5\n";
  #$sql_string .= "maxarchlogs 500\n";
  #$sql_string .= "character set \"WE8DEC\";\n";

  $sql_string .= "maxlogfiles $maxInfo[0]\n";
  $sql_string .= "maxlogmembers $maxInfo[1]\n";
  $sql_string .= "maxdatafiles $maxInfo[2]\n";
  $sql_string .= "maxinstances $maxInfo[3]\n";
  $sql_string .= "maxarchlogs $maxInfo[4]\n";
  $sql_string .= "character set $maxInfo[5];\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.createControlFile(): *** END ***");
}

# Recover datafiles from log files generated during cloning process
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# recoverDatafile(backupArchName, pfileFullName, spfile)
sub recoverDatafile
{
  EMD_PERL_DEBUG("db_clone.recoverDatafile(): *** START ***");

  if($clodClone eq "Y")
  {
    EMD_PERL_DEBUG("db_clone.recoverDatafile(): No recovery has been done since cold clone flag is: $clodClone");
    EMD_PERL_DEBUG("db_clone.recoverDatafile(): *** END ***");
    return;
  }

  my ($backupArchName, $pfileFullName, $spfile) = @_;

  #get the archivedLog files used to recover the database
  my @logfileNames = glob "$backupArchName*";

  #Handle NFS mount case
  my $startStr = substr($backupArchName, 0, 2);
  EMD_PERL_DEBUG("db_clone.recoverDatafile(): StartStr: $startStr");
  if($NT && ($startStr eq "\\\\"))
  {
    my $length = length($backupArchName);
    my $path = substr($backupArchName, 0, $length-4);
    my $glob = "dir /b $backupArchName*";
    @logfileNames = `$glob`;
    foreach (0 .. $#logfileNames)
    {
      $logfileNames[$_]=$path.$logfileNames[$_];
      chomp($logfileNames[$_]);
    }
  }

  EMD_PERL_DEBUG("db_clone.recoverDatafile(): Transferred archived log files: @logfileNames");

  #open the database with resetlogs
  my $sql_string = "";

  if($spfile eq "Y")
  {
    $sql_string .= "startup force mount;\n";
  }
  else
  {
    $sql_string .= "startup force mount pfile='$pfileFullName';\n";
  }

  $sql_string .= "alter database recover database until cancel using backup controlfile;\n";

  my $logfileNames;
  foreach $logfileNames (@logfileNames)
  {
    $sql_string .= "alter database recover logfile '$logfileNames';\n";
  }

  $sql_string .= "alter database recover cancel;\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.recoverDatafile(): Ignore errors ORA-00278, ORA-00279, ORA-00280, and ORA-00289.  These are normal messages.");

  EMD_PERL_DEBUG("db_clone.recoverDatafile(): *** END ***");
}

# Nullify the dbid for all datafiles
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# nullifyDBid()
sub nullifyDBid
{
  EMD_PERL_DEBUG("db_clone.nullifyDBid(): *** START ***");

  #nullify the dbid for all datafiles
  my $sql_string = "";
  $sql_string .= "set echo off\n";

  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "begin\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.zeroDbid(0);\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.nullifyDBid(): *** END ***");
}

# Restore multiple datafiles to the given working directory
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# nullifyDatafilesDBid(fileNums, fileNames)
sub nullifyDatafilesDBid
{
  EMD_PERL_DEBUG("db_clone.nullifyDatafilesDBid(): *** START ***");

  my ($fileNums, $fileNames) = @_;

  my @datafileNums = split /$DELIMITER/, $fileNums;
  my @datafileNames = split /$DELIMITER/, $fileNames;

  EMD_PERL_DEBUG("db_clone.nullifyDatafilesDBid(): File number to be restored: $fileNums");
  EMD_PERL_DEBUG("db_clone.nullifyDatafilesDBid(): The restored file names: @datafileNames");

  my $sql_string = "";
  $sql_string .= "variable devicename varchar2(255);\n";

  $sql_string .= "BEGIN\n";
  $sql_string .= "  :devicename := dbms_backup_restore.deviceAllocate;\n";
  $sql_string .= "  dbms_backup_restore.restoreSetDataFile;\n";

  my $datafileNum;
  my $index = 0;
  foreach $datafileNum (@datafileNums)
  {
    $sql_string .= "  dbms_backup_restore.restoreDataFileTo($datafileNum, '$datafileNames[$index]');\n";
    $sql_string .= "  dbms_backup_restore.zeroDbid($datafileNum);\n";
    $index ++;
  }

  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.nullifyDatafilesDBid(): *** END ***");
}

# Open the cloned database with resetlogs
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# openDBwithResetLog(pfileFullName, spfile)
sub openDBwithResetLog
{
  EMD_PERL_DEBUG("db_clone.openDBwithResetLog(): *** START ***");

  my ($pfileFullName, $spfile) = @_;

  #open the database with resetlogs
  my $sql_string = "";
  $sql_string .= "set echo off\n";

  if($spfile eq "Y")
  {
    $sql_string .= "startup force mount;\n";
  }
  else
  {
    $sql_string .= "startup force mount pfile='$pfileFullName';\n";
  }

  $sql_string .= "alter database open resetlogs;\n";
  $sql_string .= "SELECT STATUS FROM V\$INSTANCE;\n";

  if($clodClone eq "Y")
  {
    $sql_string .= "shutdown immediate;\n";
    if($spfile eq "Y")
    {
      $sql_string .= "startup mount;\n";
    }
    else
    {
      $sql_string .= "startup mount pfile='$pfileFullName';\n";
    }
    $sql_string .= "alter database NOARCHIVELOG;\n";
    $sql_string .= "alter database open;\n";
    $sql_string .= "SELECT STATUS FROM V\$INSTANCE;\n";
  }

  $sql_string .= "exit;\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.openDBwithResetLog(): *** END ***");
}

# Add tempfiles after opening the cloned database
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# addTempfiles(fileNames, tablespaces, sizes, autoextends)
sub addTempfiles
{
  EMD_PERL_DEBUG("db_clone.addTempfiles(): *** START ***");

  my ($fileNames, $tablespaces, $sizes, $autoextends) = @_;

  my $db_version = "";

  #check DB version
  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "set serveroutput on;\n\n";
  $sql_string .= "variable db_version varchar2(20);\n";
  $sql_string .= "BEGIN\n";
  $sql_string .= "  select version INTO :db_version from v\$instance;\n";
  $sql_string .= "  dbms_output.put_line('Get DB version');\n";
  $sql_string .= "  dbms_output.put_line('DBversion#: '||:db_version);\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  open (DB_VERSION, "$filename") || die "Unable to open tempfile for DB_VERSION\n";
  while (<DB_VERSION>)
  {
    if ($_=~/\bDBversion#/)
    {
      chomp($_);
      $db_version = substr $_, 12;  #12 is the position that version starts
      EMD_PERL_DEBUG("db_clone.addTempfiles(): Database version: $db_version");
    }
  }
  close DB_VERSION;
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  if($fileNames eq "$DELIMITER"){
    cleanupDB($db_version);
    EMD_PERL_DEBUG("db_clone.addTempfiles(): NO TEMPFILES TO ADD, returning");
    EMD_PERL_DEBUG("db_clone.addTempfiles(): *** END ***");
    return;
  }

  #Tempfiles are only supported in DB above 9.0.1 version
  if(&compareVer($db_version, "9.0.1") < 0)
  {
    EMD_PERL_DEBUG("db_clone.addTempfiles(): DB version less than 9.0.1.  No tempfiles added.");
    return;
  }

  EMD_PERL_DEBUG("db_clone.addTempfiles(): DB version is or above 9.0.1.  Tempfiles are added.");

  my @tempTablespaces = split /$DELIMITER/, $tablespaces;
  my @tempFileNames = split /$DELIMITER/, $fileNames;
  my @tempSizes = split /$DELIMITER/, $sizes;
  my @tempAutoextends = split /$DELIMITER/, $autoextends;

  #add multiple tempfiles
  $sql_string = "";

  my $tempFileNames;
  my $index = 0;
  foreach $tempFileNames (@tempFileNames)
  {
    $sql_string .= "ALTER TABLESPACE \"$tempTablespaces[$index]\" \n";
    $sql_string .= "ADD TEMPFILE '$tempFileNames' \n";
    $sql_string .= "SIZE $tempSizes[$index] REUSE $tempAutoextends[$index];\n";
    $index ++;
  }
  $sql_string .= "EXIT;\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  cleanupDB($db_version);

  EMD_PERL_DEBUG("db_clone.addTempfiles(): *** END ***");
}

# Cleanup the cloned database.
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# cleanupDB(version)
sub cleanupDB
{
  EMD_PERL_DEBUG("db_clone.cleanupDB(): *** START ***");

  my ($db_version) = @_;

  #Cleanup is only supported in DB above 10.1.0.2 version
  if(&compareVer($db_version, "10.1.0.2") < 0)
  {
    EMD_PERL_DEBUG("db_clone.cleanupDB(): DB version less than 10.1.0.2.  No cleanup is done.");
    return;
  }

  EMD_PERL_DEBUG("db_clone.cleanupDB(): DB version is or above 10.1.0.2.  Cleanup will be performed.");

  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "set serveroutput on;\n\n";
  $sql_string .= "BEGIN\n";
  $sql_string .= "  dbms_swrf_internal.cleanup_database(cleanup_local => FALSE);\n";
  $sql_string .= "END;\n";
  $sql_string .= "/\n";
  $sql_string .= "EXIT;\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.cleanupDB(): *** END ***");
}

# Set the password for DBSNMP user for the cloned database.
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# setDBSNMPpassword(password)
sub setDBSNMPpassword
{
  EMD_PERL_DEBUG("db_clone.setDBSNMPpassword(): *** START ***");

  my ($password) = @_;

  my $sql_string = "";
  $sql_string .= "set echo off\n";
  $sql_string .= "ALTER USER DBSNMP IDENTIFIED BY \"${password}\";\n";
  $sql_string .= "EXIT;\n";

  my $notHideOutput;
  (my $fh, my $filename) = &runSqlOnDestination($sql_string, $notHideOutput, "hideSQL");
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.setDBSNMPpassword(): *** END ***");
}


# Recover the database
# recoverDatabase(dbName, datafileArray, loggroupArray, logfileArray, maxInfoArray, backupArchName, pfileFullName, spfile)
sub recoverDatabase
{
  EMD_PERL_DEBUG("db_clone.recoverDatabase(): *** START ***");

  if($clonePurpose eq "STANDBY_NO_RECOVERY" || $clonePurpose =~ /LOGICAL_STANDBY/)
  {
    EMD_PERL_DEBUG("db_clone.recoverDatabase(): No recovery has been done since clone purpose is: $clonePurpose");
    EMD_PERL_DEBUG("db_clone.recoverDatabase(): *** END ***");
    return;
  }

  my ($dbName, $datafileArray, $loggroupArray, $logfileArray, $maxInfoArray, $backupArchName, $pfileFullName, $spfile) = @_;

  &createControlFile($dbName, $datafileArray, $loggroupArray, $logfileArray, $maxInfoArray, $pfileFullName, $spfile);
  &recoverDatafile($backupArchName, $pfileFullName, $spfile);
  &nullifyDBid();
  &createControlFile($dbName, $datafileArray, $loggroupArray, $logfileArray, $maxInfoArray, $pfileFullName, $spfile);
  &openDBwithResetLog($pfileFullName, $spfile);

  EMD_PERL_DEBUG("db_clone.recoverDatabase(): *** END ***");
}

# Add tempfiles after the database is opened.
# The temp files are sorted by file IDs before being passed in.
# addTempFilesToDB(fileNames, tablespaces, sizes, autoextends)
sub addTempFilesToDB
{
  EMD_PERL_DEBUG("db_clone.addTempFilesToDB(): *** START ***");

  if($clonePurpose eq "STANDBY_NO_RECOVERY" || $clonePurpose =~ /LOGICAL_STANDBY/)
  {
    EMD_PERL_DEBUG("db_clone.addTempFilesToDB(): No temp files will be added since clone purpose is: $clonePurpose");
    EMD_PERL_DEBUG("db_clone.addTempFilesToDB(): *** END ***");
    return;
  }

  my ($fileNames, $tablespaces, $sizes, $autoextends) = @_;

  &addTempfiles($fileNames, $tablespaces, $sizes, $autoextends);

  EMD_PERL_DEBUG("db_clone.addTempFilesToDB(): *** END ***");
}

# Create multiple directoris for given dir names
# If certain dir's exist, skip them.
# Return an array containing OK (created) and NOK (exist or failed).
# createNewDirs(onlineDatafileArray, offlineDatafileArray, controlfileArray,
# logfileArray, tempfileArray, destTempDir, destBackupCtlFile, adminDumpDirs)
sub createNewDirs
{
  my ($onlineDatafileArray, $offlineDatafileArray, $controlfileArray,
      $logfileArray, $tempfileArray, $destTempDir, $destBackupCtlFile,
      $adminDumpDirs) = @_;

  my @onlineDatafiles = split /$DELIMITER/, $onlineDatafileArray;
  my @offlineDatafiles = split /$DELIMITER/, $offlineDatafileArray;
  my @controlfiles = split /$DELIMITER/, $controlfileArray;
  my @logfiles = split /$DELIMITER/, $logfileArray;
  my @tempfiles = split /$DELIMITER/, $tempfileArray;
  my @adminDirs = split /$DELIMITER/, $adminDumpDirs;

  my $onlineDatafiles;
  my $offlineDatafiles;
  my $controlfiles;
  my $logfiles;
  my $tempfiles;
  my $adminDirs;
  my $ret;

  #check and create datafile directories if not exist
  foreach $onlineDatafiles (@onlineDatafiles)
  {
    $ret = &mkDir(&getDirname($onlineDatafiles));
    if($ret eq "NOK"){
      exit(1);
    }
  }
  foreach $offlineDatafiles (@offlineDatafiles)
  {
    $ret = &mkDir(&getDirname($offlineDatafiles));
    if($ret eq "NOK"){
      exit(1);
    }
  }

  #check and create controlfiles directories if not exist
  foreach $controlfiles (@controlfiles)
  {
    $ret = &mkDir(&getDirname($controlfiles));
    if($ret eq "NOK"){
      exit(1);
    }
  }

  #check and create logfiles directories if not exist
  foreach $logfiles (@logfiles)
  {
    $ret = &mkDir(&getDirname($logfiles));
    if($ret eq "NOK"){
      exit(1);
    }
  }

  #check and create tempfiles directories if not exist
  foreach $tempfiles (@tempfiles)
  {
    $ret = &mkDir(&getDirname($tempfiles));
    if($ret eq "NOK"){
      exit(1);
    }
  }

  #check and create destTempDir directory if not exist
  $ret = &mkDir($destTempDir);
  if($ret eq "NOK"){
    exit(1);
  }

  #check and create destBackupCtlFile directory if not exist
  $ret = &mkDir(&getDirname($destBackupCtlFile));
  if($ret eq "NOK"){
    exit(1);
  }

  #check and create admin dump directories if not exist
  foreach $adminDirs (@adminDirs)
  {
    $ret = &mkDir($adminDirs);
    if($ret eq "NOK"){
      exit(1);
    }
  }
}

# This method is not being used currently.  DG might be interested in this.
# Add offline datafile to control file after opening the cloned database
# missingFileNames are queried from v$datafile (sorted by id)
# offlineFileNames are got from DatabaseFileAttributes (sorted by id)
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# addOfflineDatafileToControlfile(missingFileNames, offlineFileNames)
sub addOfflineDatafileToControlfile
{
  EMD_PERL_DEBUG("db_clone.addOfflineDatafileToControlfile(): *** START ***");

  my ($missingFileNames, $offlineFileNames) = @_;

  if(($missingFileNames eq "$DELIMITER") || ($offlineFileNames eq "$DELIMITER")){
    EMD_PERL_DEBUG("db_clone.addOfflineDatafileToControlfile(): NO MISSING FILE TO ADD, returning");
    EMD_PERL_DEBUG("db_clone.addOfflineDatafileToControlfile(): *** END ***");
    return;
  }

  my @missingFiles = split /$DELIMITER/, $missingFileNames;
  my @offlineFiles = split /$DELIMITER/, $offlineFileNames;

  my $sql_string = "";

  my $missingFiles;
  my $index = 0;
  foreach $missingFiles (@missingFiles)
  {
    $sql_string .= "ALTER DATABASE RENAME FILE '$missingFiles' TO '$offlineFiles[$index]'; \n";
    $index ++;
  }
  $sql_string .= "EXIT;\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.addOfflineDatafileToControlfile(): *** END ***");
}

##########################################################
## Start a listener
##########################################################
sub startLsnr{
  EMD_PERL_DEBUG("db_clone.startLsnr(): *** START ***");

	my($lname, $lfile) = @_;
	my($lsnrexec) = "$ENV{ORACLE_HOME}${S}bin${S}lsnrctl";

	## Since the listener.ora file may not be the default one,
	##  set TNS_ADMIN to whatever directory the file is in
	my($idx) = rindex($lfile,"$S");
	if($idx == -1){
    EMD_PERL_DEBUG("db_clone.startLsnr(): Cannot determine TNS_ADMIN");
    exit(1);
	}

	my($tns) = substr($lfile,0,$idx);
	$ENV{TNS_ADMIN} = "$tns";
  EMD_PERL_DEBUG("db_clone.startLsnr(): TNS_ADMIN = $ENV{TNS_ADMIN}");

  #remember the original OH
  my $original_oracle_home = $ENV{ORACLE_HOME};
  my $reset_env = "FALSE";

  my $lsnrctl = &getLsnrctl($tns);
  if($lsnrctl ne "")
  {
    $lsnrexec = $lsnrctl;
    $reset_env = "TRUE";
  }

  #A temp solution for hanging: redirect output to a temp file
  (my $fh, my $filename) = &create_temp_file();
  if($NT)
  {
    $filename = "$TEMP\\"."dbclone.$$";
  }
  EMD_PERL_DEBUG("db_clone.startLsnr(): Temp pfile: $filename");

	## If status is bad, means it's not running
	my($cmd) = "$lsnrexec status $lname";
        EMD_PERL_DEBUG("db_clone.startLsnr(): Command: $cmd");
	my(@res) = `$cmd >$filename 2>&1`;

  my $mesg = getOutputFromFile($filename);
  print STDOUT $mesg;

	if(!$?){
    EMD_PERL_DEBUG("db_clone.startLsnr(): listener $lname currently running");
    $cmd = "$lsnrexec reload $lname";
    EMD_PERL_DEBUG("db_clone.startLsnr(): Command: $cmd");
    @res = `$cmd >$filename 2>&1`;

    $mesg = getOutputFromFile($filename);
    print STDOUT $mesg;

		#if(system($cmd)){
    if($?){
      EMD_PERL_DEBUG("db_clone.startLsnr(): lsnrctl reload failed");
      exit(1);
		}

    close $fh;
    if($NT)
    {
	&removeFile($filename);
    }

    #set env back since getLsnrctl set it to a new home
    if($reset_env eq "TRUE")
    {
      &set_env($original_oracle_home, $ENV{ORACLE_SID});
    }

    EMD_PERL_DEBUG("db_clone.startLsnr(): listener $lname reloaded");
    EMD_PERL_DEBUG("db_clone.startLsnr(): *** END ***");
    return;
	}

	## Can't try the start too soon on NT
	if($NT){
		sleep 5;
	}

	$cmd = "$lsnrexec start $lname";
        EMD_PERL_DEBUG("db_clone.startLsnr(): Command: $cmd");
  @res = `$cmd >$filename 2>&1`;

  $mesg = getOutputFromFile($filename);
  print STDOUT $mesg;

	#system($cmd);
	if($?){
    EMD_PERL_DEBUG("db_clone.startLsnr(): ${cmd} failed: $!");
		exit(1);
	}

  if($NT)
  {
    my $lsnrService = getLsnrService($mesg, $lname);
    if($lsnrService ne "")
    {
      $cmd = $ENV{ComSpec}." /c";
      $cmd = "$cmd sc config ".${lsnrService}." start= auto";
      EMD_PERL_DEBUG("db_clone.startLsnr(): config service: $cmd");
      @res = `$cmd >$filename 2>&1`;
      EMD_PERL_DEBUG("db_clone.startLsnr(): config service output: @res");
      $mesg = getOutputFromFile($filename);
      print STDOUT $mesg;
    }
  }

  close $fh;
  if($NT)
  {
     &removeFile($filename);
  }

  #set env back since getLsnrctl set it to a new home
  if($reset_env eq "TRUE")
  {
    &set_env($original_oracle_home, $ENV{ORACLE_SID});
  }

  EMD_PERL_DEBUG("db_clone.startLsnr(): listener $lname started");
  EMD_PERL_DEBUG("db_clone.startLsnr(): *** END ***");
}

# Return "/abc/../bin/lsnrctl" if the given file path is "/abc/../network/admin/listener.ora"
# getLsnrctl(tns)
sub getLsnrctl
{
  my ($tns) = @_;

  my $lsnrctl = "";
  my $exe = "";
  my $levelOneDirName = "";
  my $levelTwoDirName = "";
  my $levelOneDirPath = "";
  my $levelTwoDirPath = "";

  $levelOneDirName = &getBasename($tns);
  $levelOneDirPath = &getDirname($tns);
  if((uc $levelOneDirName) ne (uc "admin"))
  {
    EMD_PERL_DEBUG("db_clone.getLsnrctl(): $levelOneDirName is not 'admin'");
    return $lsnrctl;
  }

  $levelTwoDirName = &getBasename($levelOneDirPath);
  $levelTwoDirPath = &getDirname($levelOneDirPath);
  if((uc $levelTwoDirName) ne (uc "network"))
  {
    EMD_PERL_DEBUG("db_clone.getLsnrctl(): $levelTwoDirName is not 'network'");
    return $lsnrctl;
  }

  if($NT)
  {
    $exe = ".exe";
  }

  $lsnrctl = "${levelTwoDirPath}${S}bin${S}lsnrctl${exe}";

  if(! -e "$lsnrctl")
  {
    EMD_PERL_DEBUG("db_clone.getLsnrctl(): $lsnrctl does not exist!");
    return "";
  }

  &set_env($levelTwoDirPath, $ENV{ORACLE_SID});

  EMD_PERL_DEBUG("db_clone.getLsnrctl(): lsrnctl is: $lsnrctl");
  return $lsnrctl;
}

# Get listener service name by parsing the "lsnrctl start" output
# getLsnrService(mesg, lname)
sub getLsnrService
{
  my $mesg = $_[0];
  my $lname = $_[1];

  EMD_PERL_DEBUG("db_clone.getLsnrService(): lsnrctl start output: $mesg");
  EMD_PERL_DEBUG("db_clone.getLsnrService(): listener name: $lname");

  if(uc($lname) eq "LISTENER")
  {
    $lname = "";
  }

  my $firstPart = "<Oracle";
  my $secondPart = "TNSListener".${lname}.">";
  my $startPos = index($mesg, $firstPart);
  my $endPos = index($mesg, $secondPart);
  if(($startPos < 0) || ($endPos < 0))
  {
    EMD_PERL_DEBUG("db_clone.getLsnrService(): startPos: $startPos");
    EMD_PERL_DEBUG("db_clone.getLsnrService(): endPos: $endPos");
    return "";
  }

  $firstPart = substr($mesg, $startPos + 1, ($endPos - $startPos - 1));
  $secondPart = substr($secondPart, 0, (length($secondPart) -1));
  my $lsnrService = ${firstPart}.${secondPart};
  EMD_PERL_DEBUG("db_clone.getLsnrService(): listener service name: $lsnrService");

  return $lsnrService;
}

# Run given ftp script on a local machine
# The caller is responsible to close the returned fileHandle
# runFTP(remoteHost, ftpScript, hideOutput) will hide standard output for any
# defined parameter "hideOutput"
# runFTP(remoteHost, ftpScript) will print standard output
sub runFTP
{
  EMD_PERL_DEBUG("db_clone.runFTP(): *** START ***");

  my $ftp;
  if($OS eq "SOL2")
  {
    $ftp = '/bin/ftp';
  }
  elsif($NT)
  {
    $ftp = 'ftp';
  }
  else
  {
    $ftp = '/usr/bin/ftp';
  }

  my ($remoteHost, $ftp_string) = @_;
  my $ftp_string_debug = &filterHostCredentials($ftp_string);
  EMD_PERL_DEBUG("db_clone.runFTP(): FTP:\n$ftp_string_debug");

  EMD_PERL_DEBUG("db_clone.runFTP(): start creating temp file");

  (my $fh, my $filename) = &create_temp_file();
  if($NT)
  {
    $filename = "$TEMP\\"."dbclone.$$";
  }

  EMD_PERL_DEBUG("db_clone.runFTP(): Start ftp ...");

  open(FTP_SCRIPT, "|$ftp -n $remoteHost >$filename")
    || die "Cannot open pipe for FTP_SCRIPT";
  print FTP_SCRIPT $ftp_string;
  close FTP_SCRIPT || die "Bad FTP_SCRIPT";

  #Open the temp file to print output to standard output and debug trace file
  open (OUT_PUT, "$filename") || die "Unable to open tempfile for OUT_PUT\n";
  my @output_content = <OUT_PUT>;
  my $output_string = "@output_content";
  close OUT_PUT;

  if(!defined(@_[2]))
  {
    print STDOUT $output_string;
  }

  EMD_PERL_DEBUG("db_clone.runFTP(): OUT_PUT:\n$output_string");
  #&parseOutput($output_string);

  EMD_PERL_DEBUG("db_clone.runFTP(): *** END ***");

  return ($fh, $filename);
}

# Filter out host credentials from ftp script, which is written to the trace file
# filterHostCredentials(ftpScript)
sub filterHostCredentials
{
  my ($ftpScript) = @_;
  my $position1 = index($ftpScript, "user");
  my $position2 = index($ftpScript, "\n");
  my $replacedLength = $position2 - $position1 - 5;
  substr($ftpScript, $position1 + 5, $replacedLength) = "username/password(Hiden intentionally)";

  return $ftpScript;
}

# Get multiple files from a remote machine to the local directory.
# Login to the remote machine, then cd to the directory of a specified file,
# then do a remote get of that file.
# ftpGet(remoteHost, username, password, remoteFileNames, localFileNames, formatTypes)
sub ftpGet
{
  EMD_PERL_DEBUG("db_clone.ftpGet(): *** START ***");

  my ($remoteHost, $username, $password, $remoteFileNames, $localFileNames, $formatTypes) = @_;

  my @remoteFiles = split /$DELIMITER/, $remoteFileNames;
  my @localFiles = split /$DELIMITER/, $localFileNames;
  my @formats = split /$DELIMITER/, $formatTypes;

  my $ftp = 'ftp';
  my $lcd = 'lcd';
  my $cd = 'cd';
  my $get = 'get';

  my $remoteFile;
  my $localFile;
  my $remoteDirName;
  my $localDirName;
  my $format;
  my $index = 0;

  my $ftp_string = "";
  #quote password to handle spaces within it
  $ftp_string .= "user $username \"$password\"\n";

  foreach $remoteFile (@remoteFiles)
  {
    $remoteDirName = &getDirname($remoteFile);

    if($NT)
    {
      #the virtue directory name is hard-configured as a top dir "backup",
      #which is mapped to the source working directory (not including the dynamically
      #generated sub dir name

      #for testing ftp, which does not have sub dir created yet, this should be called
      #so, we should pass in one more defined argument in testing ftp connection case.
      if(defined($_[7]))
      {
        $remoteDirName = "/backup";
      }
      #this should be called in clone job since we have a sub dir generated dynamically
      else
      {
        $remoteDirName = &getBasename($remoteDirName); #get the sub dir name
        $remoteDirName = "/backup/$remoteDirName";
      }

    }

    $ftp_string .= "$cd $remoteDirName\n";

    $localFile = $localFiles[$index];
    $localDirName = &getDirname($localFile);
    $ftp_string .= "$lcd $localDirName\n";

    $format = $formats[$index];
    $ftp_string .= "$format\n";

    $remoteFile = &getBasename($remoteFile);
    $localFile = &getBasename($localFile);
    $ftp_string .= "$get $remoteFile $localFile\n";

    $index ++;
  }
  $ftp_string .= "close\n";
  $ftp_string .= "bye\n";

  (my $fh, my $filename) = &runFTP($remoteHost, $ftp_string, @_[6]);
  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.ftpGet(): *** END ***");
}

# Copy init parameter file from OH/dbs to admin/pfile.
# copyInitFile(srcfile, destfile, spfile)
sub copyInitFile
{
  EMD_PERL_DEBUG("db_clone.copyInitFile(): *** START ***");

  my($srcfile, $destfile, $spfile) = @_;
  my $new_srcfile = $srcfile;
  my $new_destfile = $destfile;

  if(-e $srcfile)
  {
    ## Do not remove original init.ora file for Data Guard
    if(($spfile eq "Y") && ($clonePurpose ne "STANDBY_NO_RECOVERY") && ($clonePurpose !~ /LOGICAL_STANDBY/))
    {
      $new_srcfile = $srcfile.".saved";
      $new_destfile = $destfile.".saved";
      !copyFile($srcfile, $new_srcfile) || (EMD_PERL_DEBUG("db_clone.copyInitFile(): Error copying $srcfile to $new_srcfile") && (die "db_clone.copyInitFile(): Error copying $srcfile to $new_srcfile"));
      removeFile($srcfile);
    }
    !copyFile($new_srcfile, $new_destfile) || (EMD_PERL_DEBUG("db_clone.copyInitFile(): Error copying $new_srcfile to $new_destfile") && (die "db_clone.copyInitFile(): Error copying $new_srcfile to $new_destfile"));
  }
  else
  {
    EMD_PERL_DEBUG("db_clone.copyInitFile(): File $srcfile does not exist!");
    die "db_clone.copyInitFile(): File $srcfile does not exist!";
  }

  if($clonePurpose =~ /STANDBY/)
  {
    EMD_PERL_DEBUG("db_clone.copyInitFile(): Data Guard: returning");
    EMD_PERL_DEBUG("db_clone.copyInitFile(): *** END ***");
    return;
  }

  #remove OH/dbs/init.ora and create a link to admin/pfile/init.ora
  if($OS ne "NT")
  {
    removeFile($new_srcfile);
    symlink $new_destfile, $new_srcfile
      or warn "db_clone.copyInitFile(): Can not symlink $new_destfile to $new_srcfile: $!";
  }

  if($NT)
  {
    if (-w "$new_srcfile")
    {
      EMD_PERL_DEBUG("db_clone.copyInitFile(): Add ifile entry to $new_srcfile");
      open(INIT_FILE, ">>$new_srcfile") || die "Cannot open $new_srcfile";

      #Append ifile entry to avoid duplication
      print INIT_FILE "ifile = $new_destfile\n";

      close INIT_FILE || die "Cannot close $new_srcfile";
		}
    else
    {
      EMD_PERL_ERROR("db_clone.copyInitFile(): NO write permission to file $new_srcfile");
    }
  }

  EMD_PERL_DEBUG("db_clone.copyInitFile(): *** END ***");
}

# Add datafiles for testing purpose
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# addDatafilesForTesting(fileNamePattern, tablespace, size, startSuffix, endSuffix)
sub addDatafilesForTesting
{
  EMD_PERL_DEBUG("db_clone.addDatafilesForTesting(): *** START ***");

  my ($fileNamePattern, $tablespace, $size, $startSuffix, $endSuffix) = @_;

  #add multiple datafiles
  my $sql_string = "";

  my $index = $startSuffix;
  my $fileName = "";
  while ($index <= $endSuffix)
  {
    $fileName = "$fileNamePattern"."$index";
    $sql_string .= "ALTER TABLESPACE \"$tablespace\" \n";
    $sql_string .= "ADD DATAFILE '$fileName' \n";
    $sql_string .= "SIZE $size REUSE;\n";
    $index ++;
  }
  $sql_string .= "EXIT;\n";

  (my $fh, my $filename) = &runSqlOnDestination($sql_string);

  close $fh;
  if($NT)
  {
    &removeFile($filename);
  }

  EMD_PERL_DEBUG("db_clone.addDatafilesForTesting(): *** END ***");
}

# Get output from file
# getOutputFromFile(filename)
sub getOutputFromFile
{
  EMD_PERL_DEBUG("db_clone.getOutputFromFile(): *** START ***");

  my ($filename) = @_;

  #Open the file to get output
  open (OUT_PUT, "$filename") || die "Unable to open file $filename for OUT_PUT\n";
  my @output_content = <OUT_PUT>;
  my $mesg = "@output_content";
  close OUT_PUT;

  EMD_PERL_DEBUG("db_clone.getOutputFromFile(): *** END ***");

  return $mesg;
}

# compareVer compares 2 version numbers (v1 and v2)
# The match compares v1 to the number of significant
# digits in v2; for example, assume the v1 is 8.0.2.0.1:
#      if v2 = 8.0     returns 0
#      if v2 = 8.0.2   returns 0
#      if v2 = 8.0.1   returns 1
#      if v2 = 8.0.2.1 returns -1
# compareVer(version1, version2)
sub compareVer
{
  EMD_PERL_DEBUG("db_clone.compareVer(): *** START ***");
  my ($version1, $version2) = @_;

  EMD_PERL_DEBUG("db_clone.compareVer(): version1: $version1");
  EMD_PERL_DEBUG("db_clone.compareVer(): version2: $version2");

  my $compareValue = 0;
  my $separator = '\.';
  my @version1s = split /$separator/, $version1;
  my @version2s = split /$separator/, $version2;
  my ($v1, $v2);
  my $index = 0;

  while(($#version1s >= $index) && ($#version2s >= $index))
  {
    $v1 = $version1s[$index];
    $v2 = $version2s[$index];

    if($v1 < $v2)
    {
      $compareValue = -1;
      last;
    }
    elsif ($v1 > $v2)
    {
      $compareValue = 1;
      last;
    }

    $index ++;
  }

  EMD_PERL_DEBUG("db_clone.compareVer(): compareValue: $compareValue");
  EMD_PERL_DEBUG("db_clone.compareVer(): *** END ***");
  return $compareValue;
}

1;

#Tests
#removed main_dbclone
