#
# $Header: usm/src/cmds/acfsroot/win/osds_acfsroot.pm /st_usm_11.2/7 2012/01/25 12:51:51 anjiwaji Exp $
#
# osds_acfsroot.pm
# 
# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. 
#
#
#    NAME
#      osds_acfsroot.pm - Windows OSD component of acfsroot.
#
#    DESCRIPTION
#      Purpose
#          Install/uninstall USM commands and drivers.
#
#    NOTES
#      All user visible output should be done in the common code when possible.
#      This will ensure a consistent look and feel across all platforms.
#      It is understood that due to the OSD nature of the file, some output
#      needs to be done here.
#
#    MODIFIED   (MM/DD/YY)
#    anjiwaji    01/24/12 - Backport anjiwaji_bug13446398 from main
#    averhuls    06/22/11 - For easily fixable errors, return
#                           USM_TRANSIENT_FAIL.
#    josmith     06/10/11 - Add acfsreplcrs.bat to be instantiated
#    averhuls    05/05/11 - Do not remove ORACLE_HOME components in uninstall.
#    averhuls    03/15/11 - Backport averhuls_bug-11849923 from main.
#    anjiwaji    12/09/10 - Return USM_FAIL and USM_SUCCESS in the
#                           run_acfsinstall subroutine
#    averhuls    12/09/10 - XbranchMerge averhuls_acfspatch_fix from main
#    averhuls    12/06/10 - Put quotes around acfsinstall driver location.
#    averhuls    09/02/10 - Add the "preserve" argument to uninstall to
#                           allow the preservation of the tunable parameters.
#    averhuls    07/26/10 - Do not rely in text output in run_acfsinstall() to
#                           test for errors due to NLS differences.
#    mcarrell    05/07/10 - Fix bug 9450703: Remove sub sc_query, which is
#                           never called, and is virtually identical to
#                           osds_acfslib.pm's sub query.
#    gsanders    10/04/30 - Merge forward 11.2.0.1 work
#    agraves     03/19/10 - Change instantiation logic to look for CRS_HOME=,
#                           instead of ORACLE_HOME= to support running perl
#                           from adifferent dbhome than CRS_HOME.
#    averhuls    01/13/10 - Typo fix acfsdriver_state -> acfsdriverstate.
#    averhuls    01/07/10 - OS_TYPE -> OS_SUBDIR.
#    averhuls    12/22/09 - Add replication commands.
#    averhuls    05/28/09 - Remove creation of empty $ORACLE_BASE/acfsmounts
#    agraves     04/30/09 - Make messages consistent with Bill Manry's review.
#    averhuls    04/22/09 - Rename the acfsdata drirectory to acfsmounts.
#    averhuls    04/15/09 - Rename usminstall to acfsinstall.
#    averhuls    03/26/09 - Remove support for obsolete usm_* wrapper scripts.
#    averhuls    03/16/09 - Export fix_wrapper_scripts.
#    averhuls    03/05/09 - Use '\\' for all directory references - not '/'.
#    averhuls    01/30/09 - rename usm_ -> acfs.
#    averhuls    01/20/09 - Trap usminstall errors and dump to terminal.
#    averhuls    01/13/09 - Driver name change oks.sys -> oracleoks.sys
#    averhuls    12/23/08 - Use message catalog.
#    averhuls    12/16/08 - Move win2000 to the unsupported list.
#    averhuls    11/07/08 - use usm_lib
#    averhuls    10/30/08 - 2 minor shiphome (only) fixes.
#    averhuls    10/20/08 - Remove "USM" from print statements.
#    averhuls    10/07/08 - use the environment (systeroot) to determine where
#                           to place the drivers.
#    averhuls    09/03/08 - Write ORACLE_HOME location to the USM .bat files.
#                           use PROCESSOR_ARCHITECTURE instead of depracated
#                           GetArchName().
#    ethibaul    08/30/08 - OFS->ACFS
#    averhuls    08/26/08 - convert constants implemented as variables to real
#                           constants. "sc stop oks" -> "sc stop 'oracle oks'".
#    mcarrell    06/24/08 - Remove gbmlog.
#    averhuls    06/13/08 - Change view RPM location for the new USM label.
#    averhuls    05/22/08 - move installed commands from \windows\system32 to
#                           $ORACLE_HOME/bin.
#    averhuls    05/16/08 - Initialize the OCR.
#    averhuls    04/30/08 - Add shiphome capability.
#    averhuls    04/11/08 - Make ORACLE_HOME global. add osds_initialize().
#    averhuls    03/13/08 - improve printing.
#    averhuls    11/10/07 - Creation
#

use strict;
use Win32;
use File::Spec::Functions;
require Win32API::File;
require Exporter;
package osds_acfsroot;
our @ISA = qw(Exporter);
our @EXPORT = qw(
                 osds_fix_wrapper_scripts
                 osds_get_kernel_version
                 osds_initialize
                 osds_install_from_distribution_files
                 osds_search_for_distribution_files
                 osds_load_and_verify_usm_state
                 osds_usm_uninstall
                 $MEDIA_FOUND
                 $ORACLE_HOME
                 $USM_DFLT_MEDIA_LOC
                 );

use osds_acfslib;
use acfslib;

use constant AVD_DRIVER => "oracleadvm.sys";
use constant OFS_DRIVER => "oracleacfs.sys";
use constant OKS_DRIVER => "oracleoks.sys";

our ($MEDIA_FOUND) = '';                      # path name to media

my (@DRIVER_COMPONENTS) = (
           "oracleadvm.sys",      "oracleoks.sys",        "oracleacfs.sys",
           );
my (@COMMAND_COMPONENTS) = (
           "acfsdismount.exe",       "acfsmountvol.exe",
           "advmutil.exe",           "acfsdbg.exe",
           "acfsformat.exe",         "acfsutil.exe",
           "acfsinstall.exe",        "acfschkdsk.exe",
           "acfsrepl_apply.exe",     "acfsrepl_initializer.exe",
           "acfsrepl_monitor.exe",   "acfsrepl_preapply.exe",
           "acfsrepl_transport.exe",
           );

# installed compoment locations
my ($SYSTEM_ROOT) = $ENV{SYSTEMROOT};
my ($DRIVER_DIR ) = "$SYSTEM_ROOT\\system32\\drivers";  # driver location
my ($COMMAND_DIR);  # installed command location - depends on ORACLE_HOME

# set by osds_search_for_distribution_files() and
# consumed by osds_install_from_distribution_files() 
my ($MEDIA_PATH);

our ($USM_DFLT_MEDIA_LOC);
our ($ORACLE_HOME);

# osds_initialize
#
# Perform OSD required initialization if any
#
sub osds_initialize
{
  $ORACLE_HOME = $ENV{ORACLE_HOME};

  # lib_osds_usm_supported() has already determined and verified that the
  # machine architecture ($ARCH) and OS type are supported
  # and are valid.
  
  # default location - over-ride with the -l option
  if (defined($ENV{SRCHOME}))
  {
    # development environment.
    $USM_DFLT_MEDIA_LOC =
          "$ENV{ADE_VIEW_ROOT}\\usm\\install\\Windows\\$OS_SUBDIR\\$ARCH\\bin";
  }
  else
  {
    # production environment
    $USM_DFLT_MEDIA_LOC = "$ORACLE_HOME\\install\\usm\\$OS_SUBDIR\\$ARCH\\bin";
  }

  if (defined($ORACLE_HOME))
  {
    $COMMAND_DIR = "$ORACLE_HOME\\bin";
  }
  else
  {
    lib_error_print(9339, "The environment variable ORACLE_HOME is not set.");
    return USM_TRANSIENT_FAIL;
  }

  return USM_SUCCESS;
}

# osds_get_kernel_version
#
sub osds_get_kernel_version
{
  return $OS_TYPE;
} # end osds_get_kernel_version

use File::Copy;

# osds_install_from_distribution_files
#
# Install the USM components from the specified distribution files
# The media has already been validated by the time we get here
# by osds_search_for_distribution_files(). Also, any previous USM installation
# will have been removed.
#
sub osds_install_from_distribution_files
{
  my ($component);                 # curent component being installed
  my ($driver_path);               # full path name of the driver source
  my ($acfsinstall);               # full path name of acfsinstall.exe
  my ($ret_val);                   # return code from system()
  my ($return_code) = USM_SUCCESS;

  # install commands
  # the commands have been verified to exist so the copy will not fail
  foreach $component (@COMMAND_COMPONENTS)
  {
    my ($source) = "$MEDIA_PATH\\$component";
    my ($target) = "$COMMAND_DIR\\$component";
    copy ($source, $target);
    
    lib_verbose_print (9504, "Copying file '%s' to the path '%s'", 
                       $source,
                       $target);
  }

  $acfsinstall = "$COMMAND_DIR\\acfsinstall.exe";

  # install the drivers
  $driver_path = $MEDIA_PATH;

  lib_verbose_print (9503, "ADVM and ACFS driver media location is '%s'", 
                        $MEDIA_PATH);
  
  # install the OKS driver
  $ret_val = run_acfsinstall(
                      $acfsinstall, "/i", "/l", "$driver_path\\" . OKS_DRIVER);
  if ($ret_val != USM_SUCCESS)
  {
    my ($driver) = "OKS";
    lib_error_print(9340, "failed to install %s driver.", $driver);
    $return_code = $ret_val;
  }

  # install the AVD driver
  $ret_val = run_acfsinstall(
                      $acfsinstall, "/i", "/a", "$driver_path\\" . AVD_DRIVER);
  if ($ret_val != USM_SUCCESS)
  {
    my ($driver) = "ADVM";
    lib_error_print(9340, "failed to install %s driver.", $driver);
    $return_code = $ret_val;
  }

  # install the ACFS driver
  $ret_val = run_acfsinstall(
                      $acfsinstall, "/i", "/o", "$driver_path\\" . OFS_DRIVER);
  if ($ret_val != USM_SUCCESS)
  {
    my ($driver) = "ACFS";
    lib_error_print(9340, "failed to install %s driver.", $driver);
    $return_code = $ret_val;
  }

  return $return_code;
} # end osds_install_from_distribution_files

# osds_search_for_distribution_files
#
# Search the location(s) specified by the user for valid media
# If a specific kernel and/or USM version is specified,
# look for that only that version.
#
sub osds_search_for_distribution_files

{
  my ($install_files_loc, $kernel_version, $usm_version) = @_;
  my ($component);

  if (-d $install_files_loc == 0)
  {
    return USM_FAIL;
  }

  # convert any '/' to '\'
  $install_files_loc =~ s/\//\\/g;

  # test that all of our expected components exist in the distribution
  foreach $component (@DRIVER_COMPONENTS, @COMMAND_COMPONENTS)
  {
    my ($target) = "$install_files_loc\\$component";
    if (-e $target == 0)
    {
      lib_error_print(9341, "Binary '%s' not found.", $target);
      return USM_FAIL;
    }
  }

  $MEDIA_PATH = $MEDIA_FOUND = $install_files_loc;
  $MEDIA_FOUND .= "\n";

  return USM_SUCCESS;
} # end osds_search_for_distribution_files

# osds_load_and_verify_usm_state
#
# If the install was for the current kerlel version, we load the drivers
# and test to see that the expected /dev entries get created.
#
sub osds_load_and_verify_usm_state
{
  my ($no_load) = @_;

  # Make sure that all components are in place
  my ($fail) = 0;
  my ($component);
  my ($return_val);

  # verify that the drivers reside in the target directory
  foreach $component (@DRIVER_COMPONENTS)
  {
    my ($driver_path) = "$DRIVER_DIR\\$component";

    if (! -e $driver_path)
    {
      $fail = 1;
      lib_error_print(9330, "Binary '%s' not installed.", $driver_path);
    }
  }

  # verify that the commands reside in the target directory
  foreach $component (@COMMAND_COMPONENTS)
  {
    my ($command_path) = "$COMMAND_DIR\\$component";
    
    if (! -e $command_path)
    {
      $fail = 1;
      lib_error_print(9330, "Binary '%s' not installed", $command_path);
    }
  }
 
  if ($fail)
  {
    return $fail;
  } 

  osds_fix_wrapper_scripts();

  if ($no_load)
  {
    # We're installing USM for another kernel version - do not attempt to
    # load the drivers. The presumed scenario is that the user wants to
    # install USM for an about to be upgraded kernel. This way, USM can
    # be up and running upon reboot. Dunno if anyone will ever use this.
    return USM_SUCCESS;
  }

  # make sure all drivers are loaded (running in windows speak)
  lib_inform_print(9327, "Verifying ADVM/ACFS devices.");
  $return_val = lib_osds_verify_usm_devices();
  if ($return_val != USM_SUCCESS)
  {
    # osds_verify_usm_devices() will print the specific error(s), if any;
    return $return_val;
  }

  # # should we unload the drivers at this point?
  # osds_unload_drivers();

  return USM_SUCCESS;
} # end osds_load_and_verify_usm_state

use File::Path qw(rmtree);
# osds_usm_uninstall
#
sub osds_usm_uninstall
{
  my (undef, $preserve) = @_;
  my ($return_code) = USM_SUCCESS;        # Assume success
  my ($ret_val);                          # return value from system()
  my ($component);
  my ($command);                          # Command being executed by system()
  my ($acfsinstall);                      # path to acfsinstall.exe

  if (!$preserve)
  {
    # Names MUST match the ASM_OSD_TUNABLE_FILE_NAME define in asmdefs.h
    # and OFS_OSD_TUNABLE_FILE_NAME in ofsXXXtunables.h
    #
    # Note that win/if/asmdefs.h has "C:WINDOWS" hard coded. This is a bad idea.
    my ($advm_tunables_dir) = "C:\\WINDOWS\\system32\\drivers\\advm";
    my ($acfs_tunables_dir) = "C:\\WINDOWS\\system32\\drivers\\acfs";
    my ($advm_tunables) = $advm_tunables_dir . "\\tunables";
    my ($acfs_tunables) = $acfs_tunables_dir . "\\tunables";

    # I'd like to use the more modern remove_tree() but it's not exported by
    # our File::Path. So we use the legacy (but supported) rmtree().

    if (-d $advm_tunables_dir)
    {
      rmtree $advm_tunables_dir;
      if (-d $advm_tunables_dir)
      {
        lib_inform_print(9348, "Unable to remove '%s'.", $advm_tunables_dir);
      }
    }
    if (-d $acfs_tunables_dir)
    {
      rmtree $acfs_tunables_dir;
      if (-d $acfs_tunables_dir)
      {
        lib_inform_print(9348, "Unable to remove '%s'.", $acfs_tunables_dir);
      }
    }
  } 

  # uninstall the drivers
  # the driver files are deleted by acfsinstall.exe

  $acfsinstall = "$COMMAND_DIR\\acfsinstall.exe"; 

  # we SHOULD have an installed acfsinstall.exe. But, just in case,
  # look in the default media distribution location if not. 
  if (! -e $acfsinstall)
  {
    $acfsinstall = "$USM_DFLT_MEDIA_LOC\\acfsinstall.exe";
  }
  if (! -e $acfsinstall)
  {
     lib_error_print(9341, "$%s not found", $acfsinstall);
     exit(1);
  }

  # uninstall OFS
  $ret_val = run_acfsinstall($acfsinstall, "/u", "/o");
  if ($ret_val != USM_SUCCESS)
  {
    my ($driver) = "ACFS";
    lib_error_print(9329, "Failed to uninstall driver: '%s'.", $driver);
    $return_code = $ret_val;
  }

  # uninstall ADVM 
  $ret_val = run_acfsinstall($acfsinstall, "/u", "/a");
  if ($ret_val != USM_SUCCESS)
  {
    my ($driver) = "ADVM";
    lib_error_print(9329, "Failed to uninstall driver: '%s'.", $driver);
    $return_code = $ret_val;
  }

  # uninstall OKS
  $ret_val = run_acfsinstall($acfsinstall, "/u", "/l");
  if ($ret_val != USM_SUCCESS)
  {
    my ($driver) = "OKS";
    lib_error_print(9329, "Failed to uninstall driver: '%s'.", $driver);
    $return_code = $ret_val;
  }

  # remove components if acfsinstall (above) worked for all drivers
  if (!$ret_val)
  {
    # remove drivers
    foreach $component (@DRIVER_COMPONENTS)
    {
      my ($file) = "$DRIVER_DIR\\$component";
      unlink($file); 
    }
  }

  return $return_code;
} # end osds_usm_uninstall

###############################
# internal #static" functions #
###############################

# osds_fix_wrapper_scripts
#
# We need to resolve ORA_CRS_HOME in the command wrapper scripts.
#
sub osds_fix_wrapper_scripts
{
  my ($prog); 
  my ($line);
  my (@buffer);
  my ($read_index, $write_index);
  my (@progs) = (
                "$ORACLE_HOME\\bin\\acfsdriverstate.bat",
                "$ORACLE_HOME\\bin\\acfsload.bat",
                "$ORACLE_HOME\\bin\\acfsregistrymount.bat",
                "$ORACLE_HOME\\bin\\acfssinglefsmount.bat",
                "$ORACLE_HOME\\bin\\acfsreplcrs.bat",
                );

  foreach $prog (@progs)
  {
    $read_index = 0;
    open READ, "<$prog" or next;
    while ($line = <READ>)
    {
      if ($line =~ m/^set CRS_HOME=/)
      {
         $line = "set CRS_HOME=$ORACLE_HOME\n";
      }
      $buffer[$read_index++] = $line;
    }
    close (READ);
    
    $write_index = 0;
    open WRITE, ">$prog";
    while($write_index < $read_index)
    {
      print WRITE "$buffer[$write_index++]";
    }
    close (WRITE);
  }
}
# end osds_fix_wrapper_scripts

# run_acfsinstall
#
sub run_acfsinstall
{
  my ($command_loc, $i_or_u, $driver, $driver_loc) = @_; 
  my ($line);
  my ($retval);

  if ($i_or_u eq "/i")
  {
   # quotes around $driver_loc in case the directory contains spaces.
   $line = `$command_loc /i $driver "$driver_loc" 2>&1`;
  }
  else           # "/u"
  {
    $line = `$command_loc /u $driver 2>&1`;
  }

  $retval = $?;

  if ($retval != 0)
  {
    # print whatever (already NLSed) output that acfsinstall.exe
    # may have generated
    lib_error_print(9999, $line);
    return USM_FAIL;
  }
  else
  {
    return USM_SUCCESS;
  }
}

1;
