# 
# dgcreate.pl
# 
# Copyright (c) 2002, 2005, Oracle. All rights reserved.  
#
#    NAME
#      dgcreate.pl
#
#    DESCRIPTION
#      Data Guard physical/logical post-DBClone standby creation
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    sjconnol    06/17/05 - Bug 4439779
#    sjconnol    06/03/05 - Bug 4407481: revert previous changes
#    sjconnol    05/12/05 - Change to DGexecuteSQLPlus
#    sjconnol    08/30/04 - Logical auto delete
#    sjconnol    08/18/04 - OMF/ASM changes
#    sjconnol    08/10/04 - Change mount command for 10g standby
#    sjconnol    08/03/04 - Change addTempfiles to addTempfilesDG
#    sjconnol    07/29/04 - Move tempfile deletion before database open
#    sjconnol    07/15/04 - Bug 3736070
#    sjconnol    06/28/04 - Don't use conn descriptor in stby connections
#    sjconnol    06/28/04 - Fix duplicate CLEAR LOGFILE commands 
#    sjconnol    06/14/04 - Bug 3655231
#    sjconnol    06/11/04 - Tempfile changes for 10.2
#    pbantis     05/18/04 - Exit on SQLPLUS errors. 
#    sjconnol    03/10/04 - Archive log auto deletion
#    sjconnol    12/03/03 - NT fixes
#    sjconnol    11/20/03 - Fix indexing in addTempFiles
#    sjconnol    10/30/03 - Bug 3227245: change phys stby recovery 
#    sjconnol    09/26/03 - Put back use of TNS descriptor for stby connect
#    sjconnol    09/15/03 - Bug 3130235, 3134578
#    sjconnol    06/13/03 - Bug 2964554
#    sjconnol    04/29/03 - Bug 2930030
#    sjconnol    02/10/03 - Change separator
#    sjconnol    01/31/03 - Conditionalize last fix for 10i
#    sjconnol    01/29/03 - Use local dest instead of standby_archive_dest
#    sjconnol    01/09/03 - Move waitForDG() to dgutil.pl
#    sjconnol    01/02/03 - Do set of display name after enable
#    pbantis     12/04/02 - Move handleError/Warning subroutine to dgutil
#    sjconnol    11/21/02 - Existing backup support
#    sjconnol    11/17/02 - sjconnol_dg1115
#    sjconnol    11/12/02 - Logical 10i support
#    sjconnol    10/29/02 - RAC compatibility changes
#    sjconnol    10/15/02 - Creation
# 
require "flush.pl";

use DBI;

$TEST_101 = 1;
require "$ENV{EMDROOT}/sysman/admin/scripts/db/dg/dgutil.pl";
require "$ENV{EMDROOT}/sysman/admin/scripts/db/rman/rman.pl";

$SIG{__DIE__} = \&handleError;
$SIG{__WARN__} = \&handleWarning;

$DESTSQL =  "select destination from v\$archive_dest where dest_name='STANDBY_ARCHIVE_DEST'";

# Exit on SQLPLUS errors.
$IGNORE_SQLPLUS_ERROR = 0;

sub setPrmyCreds{
  my ($username, $password, $tns) = @_;
  my $mode = 2; # SYSDBA
	if(!$tns){
    $prmyLDA = DBI->connect('dbi:Oracle:', "$username", "$password", 
                            {ora_session_mode => $mode,
                             PrintError => 0,
                             RaiseError => 1});
	}
	else{
    ## Make a DBI connection to primary
    $prmyLDA = DBI->connect('dbi:Oracle:', "$username@".$tns , "$password", 
                            {ora_session_mode => $mode,
                             PrintError => 0,
                             RaiseError => 1});
  }
  debug("dgcreate.setPrmyCreds: User Name: $username");
}

sub setStbyCreds{
  ($stbyUser, $stbyPW, $stbyTNS) = @_;
  my $mode = 2; # SYSDBA
#	if(!$stbyTNS){
    $stbyLDA = DBI->connect('dbi:Oracle:', "$stbyUser", "$stbyPW", 
                            {ora_session_mode => $mode,
                             PrintError => 0,
                             RaiseError => 1});
#	}
#	else{
#    $stbyLDA = DBI->connect('dbi:Oracle:', "$stbyUser@".$stbyTNS , "$stbyPW", 
#                            {ora_session_mode => $mode,
#                             PrintError => 0,
#                             RaiseError => 1});
#	}
  debug("dgcreate.setStbyCreds: User Name: $stbyUser");
}

sub disconnect{
  debug("dgcreate.disconnect");
  $prmyLDA->disconnect;
  $stbyLDA->disconnect;
}

sub createStandby{
  debug("dgcreate.createStandby");
  my($instance, $ohome, $lfile, $initfile, $islogical, $fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $dgopts, $newdbname) = @_;

  ## environment setup
  createAdmin($instance, $ohome, $lfile, $initfile, $islogical, $dgopts);

  if($isLogical){
    if($is10i){
      createLogical10i($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname);
    }
    else{
      createLogical9i($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname);
    }
  }
  else{
    createPhysical($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends);
  }

  ## Add the resource and enable
  addResource();

  disconnect();
}
    
sub createPhysical{
  debug("dgcreate.createPhysical");
  my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends) = @_;

  ##init.ora files for physical
  putIfile();
  
  ## Remote old SPFILE
  rmInitFiles();
  
  ## bounce standby
  bounceStby();
  
  ## Rename files
  renameFiles($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups);

  ## Harvest the recovery SCN
  getRecoverySCN();

  ## Recover the standby
  recoverStby($tempNames);
  
  ## Move the real init.ora file in place
  mvIfile();
  
  ## Create the SPFILE
  createSpfile();
  
  ## Create tempfiles
  addTempfiles_DG($tempNames, $tablespaces, $sizes, $autoextends);
  
  ## bounce standby
  bounceStby();

  ## Online offlined datafiles
  onlineDatafiles();

  ## Setup autodeletion policy 
  ## (Non-essential operation; do not process return val)
  setAutoDelete();
}

## 9i Logical creation
sub createLogical9i{
  debug("dgcreate.createLogical9i");
  my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_;

  ## Modify init.ora files for physical
  putIfile($newdbname);
  
  ## Remote old SPFILE
  rmInitFiles();
  
  ## bounce standby, startup mount
  bounceStby();
  
  ## Rename files
  renameFiles($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups);

  ## Harvest the recovery SCN
  getRecoverySCN();
  
  ## Recover the standby, open resetlogs
  recoverStby();
  
  ## Create tempfiles
  addTempfiles_DG($tempNames, $tablespaces, $sizes, $autoextends);

  ## Start logical apply (9i only)
  startLogical();

  ## bounce, startup mount
  bounceStby();

  ## DB ID/name change steps
  if(!$devMode){
    ## Run NID to change DB ID/name
    nid($newdbname);
    
    ## Move the real init.ora file in place
    mvIfile();
  }
    
  ## Create the SPFILE
  createSpfile();

  ## shutdown
  shutdownStby();

  if(!$devMode){
    ## Create new orapwd file
    orapwd();
  }

  ## startup
  startupStby();

  ## Open resetlogs (only if NID was run)
  if(!$devMode){
    openStby();
  }
}

## 10i Logical creation
sub createLogical10i{
  debug("dgcreate.createLogical9i");
  my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_;

  ## Modify init.ora files for physical
  putIfile($newdbname);
  
  ## Remote old SPFILE
  rmInitFiles();
  
  ## bounce standby, startup mount
  bounceStby();
  
  ## Rename files
  renameFiles($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups);

  ## Harvest the recovery SCN
  getRecoverySCN();
  
  ## Recover the standby, open resetlogs
  recoverStby();
  
  ## Create tempfiles
  addTempfiles_DG($tempNames, $tablespaces, $sizes, $autoextends);

  ## bounce, startup mount
  bounceStby();

  ## DB ID/name change steps
  if(!$devMode){
    ## Turn off broker before running nid
    my($sql) = "alter system set dg_broker_start=false scope=memory";
    printDebug("dgcreate.nid: $sql");
    my($dbcur) = $stbyLDA->prepare($sql);
    $dbcur->execute;

    ## Disconnect before running nid
    $stbyLDA->disconnect;

    ## Run NID to change DB ID/name; 10i NID shuts down database
    nid($newdbname);

    ## Database is now shutdown; create new password file
    orapwd();

    ## Move the real init.ora file in place
    mvIfile();

    ## Restart
    startupStby();
  }
    
  ## Create the SPFILE
  createSpfile();

  ## Bounce
  bounceStby();

  ## Open resetlogs (only if NID was run)
  if(!$devMode){
    openStby();
  }

  ## Setup autodeletion policy 
  ## (Non-essential operation; do not process return val)
  setAutoDelete();
}

sub getStbyDest{
  my($stbydest);
  if($is10i){
    my $recarea = get_init_param($stbyLDA, "db_recovery_file_dest");
    ## If the recovery area is in use, no need to return
    ##  a value; recovery will automatically use it
    if(defined($recarea) && ($recarea =~ /\S/)){
      debug("dgcreate.getStbyDest: using recovery area: $recarea");
      return undef;
    }
    ## Otherwise, get the value of standby archive dest
    my $dbcur = $stbyLDA->prepare($DESTSQL);
    $dbcur->execute;
    my @row = $dbcur->fetchrow_array();
    $stbydest = $row[0];
  }
  else{
    $stbydest = get_init_param($stbyLDA, "standby_archive_dest");
  }
  debug("dgcreate.getStbyDest: standby_archive_dest: $stbydest");
  return $stbydest;
}

sub recoverStby{
  my($tempfiles) = @_;
  my($sql, $dbcur);
  debug("dgcreate.recoverStby: tempfiles = $tempfiles");

  ## For physical, only do recovery if there are tempfiles
  if(defined($tempfiles) && ($tempfiles eq "$DELIMITER")){
    debug("dgcreate.recoverStby: no recovery to be done, returning");
    return;
  }

  ## 10.2: tempfile entries are retained in stby controlfile;
  ##  run drop cmd for as many entries as are known
  if($dbVer eq "10.2"){
    $sql = "SELECT FILE# from v\$tempfile";
    printDebug("dgcreate.recoverStby: $sql");
    $dbcur = $stbyLDA->prepare($sql);
    $dbcur->execute;
    my @row = $dbcur->fetchrow_array();
    for($i = 0; $i <= $#row; $i++){
      $sql = "ALTER DATABASE TEMPFILE $row[$i] DROP";    
      printDebug("dgcreate.recoverStby: $sql");
      $dbcur = $stbyLDA->prepare($sql);
      $dbcur->execute;
    }    
  }

  my $fromclause = "";
  my $stbydest = getStbyDest();
  ## If using recovery area (which will be true for OMF/ASM), no
  ## need to specify FROM clause
  if(defined($stbydest)){
    $fromclause = "FROM '$stbydest'";
  }

  $sql = "ALTER DATABASE RECOVER AUTOMATIC $fromclause STANDBY DATABASE UNTIL CHANGE $pitSCN";

  ## Backup controlfile used only for 9i logical
  if($isLogical && !$is10i){
    $sql = "ALTER DATABASE RECOVER AUTOMATIC FROM '$stbydest' DATABASE UNTIL CHANGE $pitSCN USING BACKUP CONTROLFILE";
  }

  ## Only disable exception handling for physical
  if(!$isLogical){
    ## Want to ignore all ORA-00278, ORA-00279, ORA-00289, ORA-00280,
    ##  ORA-00310 , ORA-00334 errors during this type of recovery, as they
    ##  are normal
    ## Turn off all exception handling, turn on logging of errors;
    ##  if there's real error (not one of the ones above), we'll
    ##  catch it in the open
    $stbyLDA->{RaiseError} = 0;
    $stbyLDA->{PrintError} = 1;
  }

  printDebug("dgcreate.recoverStby: $sql");

  $dbcur = $stbyLDA->prepare($sql);
  $dbcur->execute;

  if($isLogical){
    ## 10i logical: ACTIVATE (not open), bounce, open
    if($is10i){
      $sql = "alter database activate standby database";
      printDebug("dgcreate.recoverStby: $sql");

      $dbcur = $stbyLDA->prepare($sql);
      $dbcur->execute;

      bounceStby();

      $sql = "ALTER DATABASE OPEN";

      printDebug("dgcreate.recoverStby: $sql");

      $dbcur = $stbyLDA->prepare($sql);
      $dbcur->execute;
    }
    ## 9i logical: Turn on guard prior to open
    else{
      $sql = "ALTER DATABASE GUARD ALL";
      printDebug("dgcreate.recoverStby: $sql");

      $dbcur = $stbyLDA->prepare($sql);
      $dbcur->execute;

      $sql = "ALTER DATABASE OPEN RESETLOGS";

      printDebug("dgcreate.recoverStby: $sql");

      $dbcur = $stbyLDA->prepare($sql);
      $dbcur->execute;
    }
  }
  ## Physical
  else{
    $sql = "ALTER DATABASE RECOVER CANCEL";

    printDebug("dgcreate.recoverStby: $sql");

    $dbcur = $stbyLDA->prepare($sql);
    $dbcur->execute;

    ## Turn exception handling back on
    $stbyLDA->{RaiseError} = 1;
    $stbyLDA->{PrintError} = 0;
    
    $sql = "ALTER DATABASE OPEN READ ONLY";

    printDebug("dgcreate.recoverStby: $sql");
    
    $dbcur = $stbyLDA->prepare($sql);
    $dbcur->execute;
  }

  debug("dgcreate.recovery: recovery complete to $pitSCN");
}

sub getRecoverySCN{
  my($sql, $dbcur);

  ## 10i Logical: Recovery SCN is found on the standby, not the primary
  if($isLogical && $is10i){
    $sql = "SELECT DI2LR_SCN FROM X\$KCCDI2";
    $dbcur = $stbyLDA->prepare($sql);
  }
  ## Otherwise, recovery SCN found on primary
  else{
    if($isLogical){
      $sql = "SELECT MAX(FIRST_CHANGE#) FROM V\$ARCHIVED_LOG WHERE DICTIONARY_BEGIN = 'YES'";
    }
    else{
      $sql = "SELECT MAX(NEXT_CHANGE#) FROM V\$ARCHIVED_LOG";
      #$sql = "SELECT CHECKPOINT_CHANGE# FROM V\$DATABASE";
    }
    $dbcur = $prmyLDA->prepare($sql);
  }

  printDebug("dgcreate.getRecoverySCN: $sql");

  $dbcur->execute;

  my @row = $dbcur->fetchrow_array();
  $pitSCN = $row[0];
  debug("dgcreate.getRecoverySCN: pitSCN: $pitSCN");
}
  
sub getPrmyID{
  my $sql = "SELECT DBID from v\$database";

  printDebug("dgcreate.getPrmyID: $sql");

  my $dbcur = $prmyLDA->prepare($sql);
  $dbcur->execute;

  my @row = $dbcur->fetchrow_array();
  my($dbID) = $row[0];
  debug("dgcreate.getPrmyID: db ID: $dbID");
  return($dbID);
}

sub startLogical(){
  debug("dgcreate.startLogical: *** START ***");
  my($dbID) = getPrmyID();

  my $sql = "Begin DBMS_LOGSTDBY.PRIMARY_DBID($dbID); End;";

  printDebug("dgcreate.startLogical: $sql");

  my $dbcur = $stbyLDA->prepare($sql);
  $dbcur->execute;

  if($is10i){
    $sql = "ALTER DATABASE START LOGICAL STANDBY APPLY";
  }
  else{
    $sql = "ALTER DATABASE START LOGICAL STANDBY APPLY INITIAL $pitSCN";
  }

  printDebug("dgcreate.startLogical: $sql");

  $dbcur = $stbyLDA->prepare($sql);
  $dbcur->execute;

  $sql = "ALTER DATABASE STOP LOGICAL STANDBY APPLY";

  printDebug("dgcreate.startLogical: $sql");

  $dbcur = $stbyLDA->prepare($sql);
  $dbcur->execute;
  
  debug("dgcreate.startupLogical: *** END ***");
}
  
sub onlineDatafiles{
  my($file, $dbcur);
  foreach $file (@OFFLINE_FILES){
    my($sql) = "ALTER DATABASE DATAFILE '$file' ONLINE";
    printDebug("dgcreate.onlineDatafiles: $sql");
    $dbcur = $stbyLDA->prepare($sql);
    $dbcur->execute;
  }
}  

sub renameFiles{
  my ($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups) = @_;

  my(@fromDatafiles) = split /$DELIMITER/, $fromDatafiles;
  my(@toDatafiles) = split /$DELIMITER/, $toDatafiles;
  my(@fromLogfiles) = split /$DELIMITER/, $fromLogfiles;
  my(@toLogfiles) = split /$DELIMITER/, $toLogfiles;
  my(@logGroups) = split /$DELIMITER/, $logGroups;
  my($from, $dbcur);

  debug("dgcreate.renameFiles: *** START ***");
  ## rename datafiles
  my($count) = 0;
  foreach $from (@fromDatafiles){ 
    my($to) = $toDatafiles[$count];
    $count++;
    ## Skip rename if files are the same name
    ##  (Case insensitive match for NT)
    if(($from eq $to) || ($NT && (lc($from) eq lc($to)))){
      debug("dgcreate.renameFiles: Skipping rename for same name: $from $to");      
      next;
    }

    my $sql = "ALTER DATABASE RENAME FILE '$from' to '$to'";
    printDebug("dgcreate.renameFiles: $sql");
    $dbcur = $stbyLDA->prepare($sql);
    $dbcur->execute;

    ## Bug 3736070: If the file is offline on the primary, take it offline
    ## on the standby so recovery doesn't try to recover it
    $sql = "SELECT STATUS FROM V\$DATAFILE WHERE NAME = '$from'";
    printDebug("dgcreate.renameFiles: $sql");
    $dbcur = $prmyLDA->prepare($sql);
    $dbcur->execute;
    my @row = $dbcur->fetchrow_array();
    if($row[0] =~ /OFFLINE|RECOVER/i){
      printDebug("dgcreate.renameFiles: datafile $from is offline on primary; taking $to offline on standby");
      push(@OFFLINE_FILES, $to);
      $sql = "ALTER DATABASE DATAFILE '$to' OFFLINE DROP";
      printDebug("dgcreate.renameFiles: $sql");
      $dbcur = $stbyLDA->prepare($sql);
      $dbcur->execute;
    }
  }

  $count = 0;
  foreach $from (@fromLogfiles){ 
    my($to) = $toLogfiles[$count];
    $count++;
    ## Skip rename if files are the same name
    ##  (Case insensitive match for NT)
    if(($from eq $to) || ($NT && (lc($from) eq lc($to)))){
      debug("dgcreate.renameFiles: Skipping rename for same name: $from $to");      
      next;
    }
    my $sql = "ALTER DATABASE RENAME FILE '$from' to '$to'";
    printDebug("dgcreate.renameFiles: $sql");
    $dbcur = $stbyLDA->prepare($sql);
    $dbcur->execute;
  }
  
  debug("dgcreate.renameFiles: Data/log file rename complete");
  
  my($group, %donegroups);
  foreach $group (@logGroups){ 
    if(defined($donegroups{$group})){
      printDebug("dgcreate.renameFiles: already processed group $group");      
      next;
    }
    $donegroups{$group} = 1;
    my $sql = "ALTER DATABASE CLEAR LOGFILE GROUP $group";
    printDebug("dgcreate.renameFiles: $sql");
    $dbcur = $stbyLDA->prepare($sql);
    $dbcur->execute;
  }
  
  debug("dgcreate.renameFiles: Log groups created");
}

## Shutdown the standby
sub shutdownStby
{
  debug("dgcreate.shutdownStby: *** START ***");

  ## disconnect 
  $stbyLDA->disconnect;

  push(@SQLCMDS, "shutdown immediate");
  #executeSQLPlusSYSDBA($stbyUser, $stbyPW, $stbyTNS);
  executeSQLPlusSYSDBA($stbyUser, $stbyPW, '');
  
  debug("dgcreate.shutdownStby: *** END ***");
  undef @SQLCMDS;
}

## Shutdown the standby abort
sub shutdownStbyAbort
{
  debug("dgcreate.shutdownStbyAbort: *** START ***");

  ## disconnect 
  $stbyLDA->disconnect;

  push(@SQLCMDS, "shutdown abort");
  #executeSQLPlusSYSDBA($stbyUser, $stbyPW, $stbyTNS);
  executeSQLPlusSYSDBA($stbyUser, $stbyPW, '');
  
  debug("dgcreate.shutdownStbyAbort: *** END ***");
  undef @SQLCMDS;
}
  
## Open resetlogs (logical only)
sub openStby
{
  debug("dgcreate.openStby: *** START ***");
  
  push(@SQLCMDS, "ALTER DATABASE OPEN RESETLOGS"); 
  #executeSQLPlusSYSDBA($stbyUser, $stbyPW, $stbyTNS);
  executeSQLPlusSYSDBA($stbyUser, $stbyPW, '');
  
  debug("dgcreate.openStby: *** END ***");
  undef @SQLCMDS;
}

## Startup the standby 
##  mount for logical
##  nomount for physical
sub startupStby
{
  debug("dgcreate.startupStby: *** START ***");
  
  if($is10i || $isLogical){
    push(@SQLCMDS, "startup mount"); 
  }
  else{
    push(@SQLCMDS, "startup nomount"); 
    push(@SQLCMDS, "ALTER DATABASE MOUNT STANDBY DATABASE"); 
  }

  #executeSQLPlusSYSDBA($stbyUser, $stbyPW, $stbyTNS);
  executeSQLPlusSYSDBA($stbyUser, $stbyPW, '');

  ## Reconnect to standby
  setStbyCreds($stbyUser, $stbyPW, $stbyTNS);
  
  debug("dgcreate.startupStby: *** END ***");
  undef @SQLCMDS;
}

sub bounceStby{
  shutdownStby();
  startupStby();
}

sub createSpfile(){
  debug("dgcreate.createSpfile: *** START ***");
  
  my $sql = "CREATE SPFILE FROM PFILE='$Ifile'";
  printDebug("dgcreate.createSpfile: $sql");
  my $dbcur = $stbyLDA->prepare($sql);
  $dbcur->execute;
  
  debug("dgcreate.createSpfile: *** END ***");
}

sub addTempfiles_DG{
  debug("dgcreate.addTempfiles: *** START ***");

  my ($fileNames, $tablespaces, $sizes, $autoextends) = @_;
  if($fileNames eq "$DELIMITER"){
    debug("dgcreate.addTempfiles: NO TEMPFILES TO ADD, returning");
    return;
  }

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

  my $tempFileNames;
  my $index = 0;
  my($sql, $dbcur);

  foreach $tempFileNames (@tempFileNames)
  {
    $sql = "ALTER TABLESPACE $tempTablespaces[$index] ADD TEMPFILE '$tempFileNames' SIZE $tempSizes[$index] REUSE $tempAutoextends[$index]";
    printDebug("dgcreate.addTempfiles: $sql");
    $dbcur = $stbyLDA->prepare($sql);
    $dbcur->execute;
    $index++;
  }

  debug("dgcreate.addTempfiles: *** END ***");
}

sub addResource{
  my($state);
  if($is10i){
    $state = "PHYSICAL";
    if($isLogical){
      $state = "LOGICAL";
    }
  }
  else{
    $state = "PHYSICAL-APPLY-ON";
    if($isLogical){
      $state = "LOGICAL-APPLY-ON";
    }
  }

  my($indoc);

  ## For 10i, first remove the site (and database), then re-add it
  ##  so that the property harvest happens
  if($is10i){
    $indoc = "<DO_CONFIGURE_SITE><REMOVE><SITE_ID site_id=\"$siteID\"/></REMOVE></DO_CONFIGURE_SITE>";
    
    debug("dgcreate.addResource: $indoc");
    processStatus(get_dg_document($prmyLDA, $indoc));
    debug("dgcreate.addResource: Database $resName removed");
    
    $indoc = "<DO_CONFIGURE_DRC><ADD><DATABASE name=\"$resName\" connect=\"$stbyTNS\" role=\"$state\"/></ADD></DO_CONFIGURE_DRC>";
    
    debug("dgcreate.addResource: $indoc");
    processStatus(get_dg_document($prmyLDA, $indoc));
    debug("dgcreate.addResource: Database $resName re-added");
  }
  ## For 9i, add the resource
  else{
    $indoc = "<DO_CONFIGURE_SITE><ADD><SITE_RESOURCE><SITE_ID site_id=\"$siteID\"/><RESOURCE name=\"$resName\" parent_id=\"$siteID\"><RESOURCE_TMPL_ID res_tmpl_id=\"256\"/><THREADED_PROPERTIES parent_state=\"PRIMARY\" default_state=\"READ-WRITE-XPTON\"/><THREADED_PROPERTIES parent_state=\"STANDBY\" default_state=\"$state\"/></RESOURCE></SITE_RESOURCE></ADD></DO_CONFIGURE_SITE>";
    
    debug("dgcreate.addResource: $indoc");
    processStatus(get_dg_document($prmyLDA, $indoc));
    debug("dgcreate.addResource: Resource $resName added");
  }

  ## Turn off all exception handling on the primary, turn on logging of errors;
  ##  we don't want to error the whole job because of an enable problem
  $prmyLDA->{RaiseError} = 0;
  $prmyLDA->{PrintError} = 1;

  $indoc = "<DO_CONTROL><DO_COMMAND type=\"Enable\" object_id=\"$siteID\"/></DO_CONTROL>";
  
  ## Give broker time to start on the standby
  waitForDG($stbyLDA);

  debug("dgcreate.addResource: $indoc");
  processStatus(get_dg_document($prmyLDA, $indoc));
  debug("dgcreate.addResource: Resource $resName enabled");

  ## For 10i, fix up the display name
  if($is10i){
    $indoc = "<DO_CONFIGURE_SITE><EDIT><RESOURCE_ID res_id=\"$dbObjID\"/><EDIT_PROPERTY_LIST><VALUE name=\"DbDisplayName\" value=\"$displayName\"/></EDIT_PROPERTY_LIST></EDIT></DO_CONFIGURE_SITE>";

    debug("dgcreate.addResource: $indoc");
    processStatus(get_dg_document($prmyLDA, $indoc));
    debug("dgcreate.addResource: Display name set to $displayName for DB object");
  }
}
  
sub processStatus{
  my($status) = @_;
  my @dbres_status_tokens = split(/[><]+/, $status);
  # Token 0 is "", token 1 is "RESULT ", token 2 is "MESSAGE "
  my($dbres_status) = $dbres_status_tokens[3];
  my @temp_tokens = split(/\s+/, $dbres_status);
  $dbres_status = $temp_tokens[0];
  EMD_PERL_DEBUG("dbres_status=$dbres_status.");

  if ($dbres_status eq "SUCCESS")
  {
    debug("dgcreate.processStatus: DG operation successful");
    return;
  }
  else
  {
    # Parse out the error from the big status.
    my $dbres_status_token;
    my $token_count = 0;
    my($dbres_status_text);
    foreach $dbres_status_token (@dbres_status_tokens)
    {
      # EMD_PERL_DEBUG("dbres_status_token=$dbres_status_token.");
      if ($dbres_status_token eq "ERROR_TEXT ")
      {
        $dbres_status_text = $dbres_status_tokens[$token_count + 1];
        last;
      }
      $token_count++;
    }
    if ($dbres_status eq "WARNING"){
      debug("dgcreate.processStatus: DG operation produced warning: $dbres_status_text");
    }
    elsif ($dbres_status eq "FAILURE"){
      die("$dbres_status_text");
    }
  }
}
  
##########################################################
## Setup for standby creation
##########################################################
sub createAdmin{
	debug("dgcreate.createAdmin: @_");
	## globals
	($Instance,	$OracleHome, $listenerFile, $initFile, $cloneType, $dgOpts) = @_;
  
  ## Harvest DG options
  ##  1) dev mode
  ##  2) site ID
  ##  3) DB resource name
  my(@dgopts) = split(/:/, $dgOpts);

  # SJC 9/18/03: Disable development mode
  #$devMode = $dgopts[0];
  $devMode = 0;

  $siteID = $dgopts[1];
  $resName = $dgopts[2];
  ## displayName is 10i only
  $displayName = $dgopts[3];
  ## dbObjID is 10i only; distinct from siteID
  $dbObjID = $dgopts[4];
  ## autoDelete is 10i only; determines if rec area uses auto deletion
  $autoDelete = $dgopts[5];

  if($devMode){
    debug("dgcreate.createAdmin: Development mode");
  }
  elsif(-e "/tmp/dgNoNewID"){
    debug("dgcreate.createAdmin: No NID mode");
    $devMode = 1;
  }	

  if($cloneType =~ /LOGICAL_STANDBY/){
    debug("dgcreate.createAdmin: Logical Standby");
    $isLogical = 1;
  }
  else{
    debug("dgcreate.createAdmin: Physical Standby");
    $isLogical = 0;
  }

  debug("dgcreate.createAdmin: site ID: $siteID");
  debug("dgcreate.createAdmin: resource name: $resName");

  ## Figure out DB Version (primary and standby will be same)
  $dbver = get_dbversion($prmyLDA);
  if($dbver =~ /^102/){
    debug("dgcreate.createAdmin: Database version: 10gR2");
    $is10i = 1;
    $dbVer = "10.2";
  }
  elsif($dbver =~ /^101|100/){
    debug("dgcreate.createAdmin: Database version: 10gR1");
    $is10i = 1;
    $dbVer = "10.1";
  }
  else{
    debug("dgcreate.createAdmin: Database version: 9iR2");
    $is10i = 0;
    $dbVer = "9.2";
  }

	## Environmentals
	$ENV{ORACLE_HOME} = "$OracleHome";
	debug("ORACLE_HOME = $ENV{ORACLE_HOME}");

	## Since the listener.ora file may not be the default one,
	##  set TNS_ADMIN to whatever directory the file is in
  if(defined($listenerFile) && $listenerFile){
    my($idx) = rindex($listenerFile,"$S");
    if($idx == -1){
      die("ERROR_TNS_ADMIN:$listenerFile");
      return;
    }
		
    my($tns) = substr($listenerFile,0,$idx);
    $ENV{TNS_ADMIN} = "$tns";
    debug("TNS_ADMIN = $ENV{TNS_ADMIN}");
  }

	if(defined($ENV{LD_LIBRARY_PATH})){
		$ENV{LD_LIBRARY_PATH} = "${OracleHome}${S}lib:$ENV{LD_LIBRARY_PATH}";
	}
	else{
		$ENV{LD_LIBRARY_PATH} = "${OracleHome}${S}lib";
	}
	debug("LD_LIBRARY_PATH = $ENV{LD_LIBRARY_PATH}");
	if(defined($ENV{PATH})){
		$ENV{PATH} = "${OracleHome}${S}bin:$ENV{PATH}";
	}
	else{
		$ENV{PATH} = "${OracleHome}${S}bin";
	}
	debug("PATH = $ENV{PATH}");

	$InstanceHome = "${OracleHome}${S}dbs";	
	if($NT){
		$InstanceHome = "${OracleHome}${S}database";
	}

  if(defined($initFile) && $initFile){
    $Ifile = $initFile;
    $IfileForMV = substr($Ifile, rindex($Ifile,"$S") + 1);
    ## Beware that the value of $Ifiletmp will be inferred as 
    ##  "${Ifile}tmp" by the client, so any changes here must be performed
    ##  in the client code, too.
    $Ifiletmp1 = "${Ifile}tmp1";
    $Ifiletmp2 = "${Ifile}tmp2";
  }

	if(!(-e "$InstanceHome")){
		die("ERROR_DIR_EXIST:$InstanceHome");
		return;
	}
}


##########################################################
## Move the "real" init.ora file to the correct name 
##########################################################
sub mvIfile{
	local($SIG{'CHLD'}) = 'DEFAULT';
  my($file) = $_[0];
  ## default: move temp file #1 to permanent slot
  if(!defined($file)){
    $file = $Ifiletmp1;
  }
	debug("dgcreate.mvfile: $_[0]");
	my($cmd) = "$MV $file $Ifile";
	if($NT){
		# For NT, first delete the destination file.
		my($delcmd) = "del $Ifile";
		debug("\t$delcmd");
		&runCmd($delcmd);	
		$cmd = "$MV $file $IfileForMV";
	}
	debug("$cmd");
	if(&runCmd($cmd)){
		return(1);
	}
}

##########################################################
## Install a new init.ora file
##########################################################
sub putIfile{
	debug("dgcreate.putIfile");
	my($db_name) = @_;
	my($line);

	## Create main init.ora file for standby ($Ifiletmp1). It will actually be 
	##  created under the temporary file name, initially.
	open(IFILETMP1,">$Ifiletmp1") || die("ERROR_FILE_OPEN:$Ifiletmp1");

	## Create temp init.ora ($Ifiletmp2) file w/o db_file_name_convert, 
	##  log_file_name_convert, or standby_file_management. This is 
	##  so standby can start w/o these params set in order to do the
	##  file renaming. Also, strip log_archive_format parameter, so
	##  that if a manual recovery is done by the create process, a
	##  user specified format doesn't get in the way (log_archive_format
	##  will be preserved in the real spfile).
	open(IFILETMP2,">$Ifiletmp2") || die("ERROR_FILE_OPEN:$Ifiletmp2");

  ## Open the file copied over by DB Clone for reading, from which
  ##  the above two files will be created.
	open(IFILE,"$Ifile") || die("ERROR_FILE_OPEN:$Ifile");    

  while($line = <IFILE>){
    chomp($line);
    ## For Logical, substitute the new db_name, so it will match
    ##  the name set by the NID utility
    if((defined($db_name)) && $line =~ /^\s*(db_name)/i){
      debug("\tadding new db_name=$db_name for logical stby");
      print IFILETMP1 "db_name=$db_name\n";
      ## Keep old db_name in tempfile
      print IFILETMP2 "$line\n";
      next;
    }
    ## remove lock_name_space from main file; don't need it after db name change
    elsif((defined($db_name)) && $line =~ /^\s*lock_name_space/i){
      print IFILETMP2 "$line\n";
      next;
    }

    ## remove db/log_file_name_convert from temp file
		if($line =~ /^\s*(db_file_name_convert|log_file_name_convert|standby_file_management)/i){
      print IFILETMP1 "$line\n";
      next;
    }

    print IFILETMP1 "$line\n";
    print IFILETMP2 "$line\n";
  }

	close(IFILE);
	close(IFILETMP1);
	close(IFILETMP2);

  ## Now that we're done producing the two init.ora files, move
  ##  the temp one into place.
  ## Return with no output if error; mvIfile will output an error messages
  if(&mvIfile("$Ifiletmp2")){
    return;
  }

}

	
##########################################################
## Create a remote login passwordfile
##########################################################
sub orapwd{
	local($SIG{'CHLD'}) = 'DEFAULT';
	debug("dgcreate.orapwd");

  my($password) = $stbyPW;
	my($pwfile) = "${OracleHome}${S}dbs${S}orapw${Instance}";
	my($orapwexec) = "${OracleHome}${S}bin${S}orapwd";

	if($NT){
		$pwfile = "${OracleHome}${S}database${S}PWD${Instance}.ora";
		$orapwexec = "${OracleHome}${S}bin${S}orapwd.exe";
	}

	my($pwcmd) = "$orapwexec file=$pwfile password=$password entries=10";
	my($display) = "$orapwexec file=$pwfile password=(password) entries=10";
	debug("$display");		
	
	## If the executable is there, and the file isn't, create it
	if((-e "$orapwexec") && !(-e "$pwfile")){
		if(&runCmd($pwcmd)){
      exit(1);      
		}
		debug("Created file $pwfile");
		&printDebug("$display");
	}
	## else, if file is there, remove it and recreate
	elsif(-e "$pwfile"){
		debug("Removing file $pwfile");
		if(unlink($pwfile)){
			if(&runCmd($pwcmd)){
        exit(1);
			}
			debug("Created file $pwfile");
      &printDebug("$display");
		}
		## If remove fails, that's an error 
		else{
			die("ERROR_RM_FILE:$pwfile");
		}
	}
	## 
	## else, there's a problem (probably no orapwd exec)
	else{
		die("ERROR_EXEC:orapwd");
	}
}

##########################################################
## Run the NID utility to change the database name. (Called
##  only to change dbname for a logical standby.)
##########################################################
sub nid{
	local($SIG{'CHLD'}) = 'DEFAULT';
	debug("dgcreate.nid: $_[0]");
	my($dbname) = @_;

	my($nidexec) = "${OracleHome}${S}bin${S}nid";

	if($NT){
		$nidexec = "${OracleHome}${S}bin${S}nid.exe";
	}

	## If a connect alias isn't specified, NID will connect
	##  to whatever ORACLE_SID is set to
	$ENV{ORACLE_SID} = "$Instance";

	my($cmd) = "echo Y | $nidexec TARGET=/ DBNAME=$dbname";
      
	debug("$cmd");		
	
	## If the executable is there, and the file isn't, create it
	if(-e "$nidexec"){
		if(&runCmd($cmd)){
      exit(1);
		}
	}
	else{
		die("ERROR_EXEC:nid");
	}
}

##########################################################
## Remove the SPFILE file for an instance
## 
##  Since this is run at create time, the globals $Instance and
##  $OracleHome will be set when it's called.
##########################################################
sub rmInitFiles{
	debug("dgcreate.rmInitFiles");	
	my($file);
	
	my($file) = "${OracleHome}${S}dbs${S}spfile${Instance}.ora";	
	if($NT){
		$file = "${OracleHome}${S}database${S}spfile${Instance}.ora";
	}
	debug("\tlooking for $file");
	if(-e "$file"){
		debug("\tremoving file for for ${Instance}: $file");
		unlink($file)
			|| die("ERROR_RM_FILE:$file");
	}
}

##########################################################
## Setup the arch log file auto deletion policy using RMAN
## $autoDelete global set in createAdmin
##########################################################
sub setAutoDelete{
	debug("dgcreate.setAutoDelete");	

  ## Logical auto delete feature (10.2 only); always set this
  if($isLogical && ($dbVer eq "10.2")){
    my $sql = "Begin DBMS_LOGSTDBY.APPLY_SET('LOG_AUTO_DELETE','TRUE'); End;";
    printDebug("dgcreate.setAutoDelete: $sql");
    my $dbcur = $stbyLDA->prepare($sql);
    $dbcur->execute;
  }

  if(!$is10i || ($autoDelete !~ /true/i)){
    return;
  }

  $target_home = $OracleHome;
  $target_sid = $Instance;
  $db_10_or_higher = "YES";
  $rman_script = "CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;";
  ## Don't want fatal error here to fail the job; trap any errors
  my($status) = eval{rman("NO", "");}; 
  if($@){
    debug("dgcreate.setAutoDelete: RMAN script failed: $@");	
  }

  debug("dgcreate.setAutoDelete: RMAN returned $status");	
}
 
sub runCmd{
	local($SIG{'CHLD'}) = 'DEFAULT';
	my($cmd) = $_[0];
	my(@res) = `$cmd 2>&1`;
	debug("@res ");
	if($?){
		my($err) = $!;
		## If there is no system error, just put out the output
		if($err eq undef || ($err eq "")){
			$err = "@res";
			## To make multiline errors fit on one line (so it
			##  makes it back to the client) replace all \n with tabs.
			## The client will in turn replace the tabs with newlines
			##  before presenting the error to the user.
			$err =~ s/\n/\t/g;
		}
		chomp($err);
		## Send back only the OS components of error to client
		##  to minimize translation issues
    if($cmd !~ /password/i){
      printDebug("${cmd}: $err");
    }
		return(1);
	}
	return(0);
}
 

1;


  

