#!/usr/bin/perl

# ------------------------------------------------------------------------
# coldclone -- create a clone of an existing Oracle Database
#            G. Patterson, Sep 2002

# Set the common parameters (contained in common.pl)
use File::Basename;
$MyPath = dirname($0);
require "$MyPath\\common.pl";
use Time::Local;

# ------------------------------------------------------------------------

# Write a log message to the logfile
sub LogMsg{
	my @t = localtime(time);
	printf LOG "%04d-%02d-%02d %02d:%02d %s\n",
		$t[5]+1900,$t[4]+1,$t[3],$t[2],$t[1],$_[0];
}

# ------------------------------------------------------------------------

# We have a problem ... cleanup and exit
sub problem{
	LogMsg "ERROR: $_[0]";
	die "$_[0]\n";
}

# ------------------------------------------------------------------------

# Run the given SQL in svrmgrl
sub run_svrmgr{
	# construct an SQL file
	open SQL,">$sqltmp" || die "cannot open $sqltmp";
	print SQL "connect internal/$passwd\n";
	print SQL "$_[0]\n";
	print SQL "exit\n";
	close SQL || die "$!";
	# EXEC as a backticks block and return the output in list context
	my @t = `$svrmgrl \@$sqltmp`;
	return( @t);
}

# ------------------------------------------------------------------------

# database MUST be DOWN!
sub chk_nodb{
	# Try and run a query ... any query
	my @t = run_svrmgr 'select * from dual;';
	# If we did not get an error something is wrong!
	die "Cannot $MyName a running database. Shut It Down First!\n"
		unless (grep (/ORA-01034:/,@t) ||
		        grep (/ORA-09352:/,@t) ||
		        grep (/ORA-12154:/,@t) ||
		        grep (/ORA-12560:/,@t) ||
			grep (/ORA-12535:/,@t) );
}

# ------------------------------------------------------------------------

sub chkdir{
	return unless $_[0];
	problem "Missing Folder: $_[0]" unless (-d $_[0]);
}

# ------------------------------------------------------------------------

# retrieve parameter values from srcdir\\0header
sub read_header{
	# This parses the header file, created by coldarch
	open (HDR,"$srcdir\\0header") || die "Cannot open $srcdir\\0header\n";
	while (<HDR>){
		s/\s+$//;
		s/^\s+//;
		if (/\w+ started at /){
			$start_str = $';
			@start = split(' ',$start_str);
			@start = reverse (split( /-/,$start[0]),
					split(/:/,$start[1]));
			$start[4]--;
			$start[5] -= 1900;
		}
		# If the string FILES: is at EOL, previous word is "type"
		elsif (/(\w+) FILES:$/){
			# set up to push to an array $get_data
			$get_data = lc($1) . "_files";
		}
		# Blank line means another block of data in the header file
		elsif (/^$/){
			undef($get_data);
		}
		# Otherwise if there is a colon, it's an Oracle Parmameter
		elsif (/^(.+): /){
			$oraparm{$1} = $';
		}
		# Otherwise we must be gathering data
		elsif ($get_data){
			# NOTE: $get_data is the name of the array ... NOT the array
			push(@$get_data,$_);
		}
		else{
			# Umm, Houston ... Houston?
			print "$_";
			problem "problem with 0header";
		}
	}
}

# ------------------------------------------------------------------------

sub gen_ctrl_sql {
# Generate the SQL to create the control files -- put it in @gen_ctrl
	my $line = 0; my $logf = 0; my $data = 0; my $x; my $y;
	my $ctl_trace = "$srcdir\\$srcsid" . "_CTRL.FILE.trc";
	my $create_str = "CREATE CONTROLFILE REUSE";
	open (CTL_TRACE,$ctl_trace) || problem "Cannot read $ctl_trace";
	push ( @gen_ctrl, "connect internal/$passwd");
	push ( @gen_ctrl, "shutdown");
	push ( @gen_ctrl, "startup nomount pfile=$pfile{$orasid}");
	while(<CTL_TRACE>){
		$line++ if ( /^$create_str/);
		next unless ($line);
		s/\s*$//;
		if ( $line++ == 1){
			$_ = "$create_str set DATABASE \"$orasid\" RESETLOGS";
		}
		elsif ( /^LOGFILE$/){
			$logf++;
		}
		elsif ( /^DATAFILE$/){
			$data++;
			$logf = 0;
			push (@gen_ctrl,$_);
			push (@gen_ctrl,@data_files_str);
			next;
		}
		elsif ($logf){
			if (s/$srcsid/$orasid/ig){
				($x,$logf,$y) = split( /'/, "x $_ y");
				push @redo_files,$logf;
			}
		}
		elsif ($data){
			$data = 0 unless ( /,$/);
			next;
		}
		push (@gen_ctrl,$_);
		last if (/^;$/);
	}
	close (CTL_TRACE) || problem "Cannot close $ctl_trace";
	push (@gen_ctrl,"alter database open resetlogs;");
}

# ------------------------------------------------------------------------

sub set_oraparm {
	my $key; my $data;
	my $t = $_[0];
	($key,$data) = split( /=/,$t,2);
	$key =~ s/\s//g;
	$data =~  s/\s*$//;
	$data =~  s/^\s*//;
	if ( $data =~ s/^"location=//i){
		$data =~ s/"$//;
	}
	$oraparm{lc($key)} = $data;
}

# ------------------------------------------------------------------------

sub copy_pfile {
# Copy the Oracle pfile and ifiles ... substitute SID.
	# Add to this list, if you want these lines altered ...
	$P_alter{ifile}++;
	$P_alter{db_name}++;
	$P_alter{instance_name}++;
	$P_alter{service_names}++;
	$P_alter{control_files}++;
	$P_alter{log_archive_dest_1}   = 2;
	$P_alter{log_archive_dest}     = 2;
	$P_alter{background_dump_dest} = 2;
	$P_alter{user_dump_dest}       = 2;
	my $i; my $key;
	my $srcf = $_[0];
	my $tgtf = $_[0];
	$tgtf =~ s/$srcsid/$orasid/ig;
	my $tgtp = dirname($tgtf);
	my $src = basename($srcf);
	problem "Missing path: $tgtp" unless (-d $tgtp);
	my $t = `copy $srcdir\\$src $tgtf`;
	$t =~ s/\s+$//;
	problem "Cannot copy $srcdir\\$src to $tgtf ($t)"
		unless ($t =~ /1 file\(s\) copied/);
	open (PFILE,$tgtf) ||
		problem "copy_pfile cannot open: $tgtf";
	@pfile_data = (<PFILE>);
	for ( $i = 0; $i <= $#pfile_data; $i++){
		$line = $i + 1;
		next if ( $pfile_data[$i] =~ /^\s*#/);
		if ( $pfile_data[$i] =~ /=/){
			$key = lc((split(' ',$pfile_data[$i]))[0]);
			$key =~ s/\s*=.*$//;
		}
		next if ( $P_skip{$key} );
		if ($pfile_data[$i] =~ s/$srcsid/$orasid/ig) {
			problem "Parameter: $key line $line in $tgtf"
				unless ($P_alter{$key});
		}
		set_oraparm( $pfile_data[$i] ) if ($P_alter{$key} == 2);
	}
	open (PFILE,">$tgtf") ||
		problem "copy_pfile cannot write to: $tgtf";
	foreach $x(@pfile_data){
		print PFILE $x;
	}
	close (PFILE) || problem "Closing $tgtf";
}

# ------------------------------------------------------------------------

sub copy_dfile {
	my $src; my $tgt; my $cmd; my @df; my $t; my $d; my $i;
	# unlink (@data_files, @ctrl_files, @redo_files);
	foreach $src(@data_files){
		$src =~ s/ .*$//;
		$d = dirname( $src);
		$d =~ s/$srcsid/$orasid/ig;
		push (@df, $tgt = "$d\\" . basename($src));
		chdir $d || problem "copy_dfile folder: $d" ;
		$cmd = "$unzip $srcdir\\" . basename($src) . ".zip";
		LogMsg $t = `$cmd`;
	}
	for $i (0 .. $#df){
		$data_files_str[$i] = sprintf "  '%s'",$df[$i];
		$data_files_str[$i] .= "," unless ($i == $#df);
	}
	@data_files = @df;
}

# ------------------------------------------------------------------------

sub create_db {
	my $x; my $cmd; my $t; my $global_name;
	my $sqltmp = "$logdir\\ctrl_$orasid.sql";
	LogMsg $t = `$oradim -shutdown -sid $orasid -usrpwd $passwd -shuttype srvc,inst -shutmode a`;
	run_svrmgr "shutdown abort";
	LogMsg $t = `$oradim -delete -sid $orasid`;
	foreach $x(@data_files,@ctrl_files, @redo_files){ chkdir(dirname($x))}
	foreach $x($ctrl_files[0],@redo_files){
		chdir dirname($x) || problem "Cannot create: $x";
		$cmd = "$unzip $coldarc\\config.$srcsid.zip " . basename($x);
		LogMsg $t = `$cmd`;
	}
	LogMsg $t = `$oradim -new -sid $orasid -intpwd $passwd -startmode auto -pfile $pfile{$orasid}`;
	open SQL,">$sqltmp" || die "cannot open $sqltmp\n";
	foreach $x(@gen_ctrl){
		print SQL "$x\n";
	}
	print SQL "exit";
	close SQL || die "cannot close $sqltmp\n";
	LogMsg $t = `$svrmgrl \@$sqltmp`;
	# problem "Failed to create db" if grep(/ORA-/,($t));
	LogMsg $t = `$oradim -startup -sid $orasid -usrpwd $passwd -starttype srvc`;
	@t = run_svrmgr "select * from global_name;";
	foreach $i (0 .. $#t){
		if ($t[$i] =~ /^1 row/){
			$global_name = $t[$i-1];
			$global_name =~ s/\s*$//;
		}
	}	
	if ( $global_name =~ /^$srcsid\./){
		$global_name =~ s/$srcsid/$orasid/;
		@t = run_svrmgr "alter database
			rename global_name to $global_name;";
		problem "Cannot rename db" if grep(/ORA-/,($t));
	}
}

# ------------------------------------------------------------------------

sub chk_clone {
# Is this a clone -- i.e. new SID?
	my $i;
	$srcsid = $oraparm{ORACLE_SID};
	if ( uc($srcsid) eq uc($orasid) ){
		$srcsid = $orasid;
		problem "$myname must not be used to restore a database";
	}
	problem "No oradim specified for $srcsid\n"
		unless ($oradim = $ORADIM{$srcsid});
	problem "No svrmgrl specified for $srcsid\n"
		unless ( $svrmgrl = $SVRMGRL{$srcsid});
	problem "No sqlplus specified for $srcsid\n"
		unless ( $sqlplus = $SQLPLUS{$srcsid});
	# Sanity Checks -- over-ride with NoSanity{} entries in common.pl
	my $x = $pfile{$srcsid};
	$x =~ s/$srcsid/$orasid/ig;
	if ( uc($x) ne uc($pfile{$orasid}) ) {
		problem "Sanity: $x differs from $pfile{$orasid}"
			unless ($NoSanity{$pfile{$orasid}});
	}
	if ( uc("$coldarc\\$srcsid") ne uc($srcdir)){
		problem "Sanity: $coldarc\\$srcsid differs from $srcdir"
			unless ($NoSanity{$srcdir});
	}
	for $i (0 .. $#ctrl_files){$ctrl_files[$i] =~ s/$srcsid/$orasid/ig};
}

# ------------------------------------------------------------------------

sub chkdirs{
	chkdir($oraparm{log_archive_dest_1});
	chkdir($oraparm{log_archive_dest});
	chkdir($oraparm{background_dump_dest});
	chkdir($oraparm{user_dump_dest});
}

# ------------------------------------------------------------------------

# Main ... start here:
my $f;
$MyName = basename $0,"\.pl";
die "usage: $MyName src_dir intrnl_passwd db_name\n" unless (@ARGV == 3);
$userid = "sys";
$srcdir = $ARGV[0];
$passwd = $ARGV[1];
$orasid = $ARGV[2];
chk_env();
chk_nodb();
@start = localtime(time);
$start_str = sprintf "%04d-%02d-%02d %02d:%02d:%02d",$start[5]+1900,
		$start[4]+1,$start[3],$start[2],$start[1],$start[0];
$logfile = sprintf "%s\\%s_%s_%02d%02d%02d.log",$logdir,$orasid,$MyName,
		$start[5]%100,$start[4]+1,$start[3];
open(LOG,">>$logfile") || die "error opening $logfile\n";
LogMsg "commencing coldrest";
read_header();
chk_clone();
$coldarc = "$coldarc\\$srcsid";
foreach $f ( "$pfile{$srcsid}",@i_files ){
	copy_pfile($f);
}
chkdirs();
copy_dfile();
gen_ctrl_sql();
create_db();
LogMsg "$MyName completed normal";
close(LOG) || die "error closing $logfile\n";
