# 
#
# acfsroot.pl
# 
# Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      acfsroot.pl
#
#    DESCRIPTION
#      install/uninstall USM components from the distribution files
#      See section 4.2.1 of Design Note Supporting USM Installation
#
#    NOTES
#      acfsroot install [-h] [-s | -v | -t <0,1,2>] [-l <directory>] 
#                       [-m Domain|Member|SHMI]
#          install USM kernel drivers and commands.
#      acfsroot uninstall [-h] [-s | -v | -t <0,1,2>]
#          uninstall USM kernel drivers and commands.
#      acfsroot version_check [-h] [-t <0,1,2>]
#          check if USM components are available for installation.
#      acfsroot enable [-h] [-s | -v | -t <0,1,2>]
#          enable ADVM/ACFS CRS resources.
#      acfsroot disable [-h] [-s | -v | -t <0,1,2>]
#          disable ADVM/ACFS CRS resources.
#      acfsroot patch_verify [-l <directory>]
#          verify acfsroot installation
#      acfsroot transport_config {-r|-s}
#          configure acfs remote transport
#      acfsroot transport_list
#          list acfs remote transports
#
#    INSTALL ACTIONS
#      - verify that the user has root privs.
#      - checks that the proper install files exists
#      - unloads currently loaded drivers (if any).
#      - removes currently installed USM install files (if any).
#      - installs from the selected distribution files
#      - Loads the drivers to make sure that they load properly.
#      - Performs any required OSD actions, if needed.
#
#      User output should be done in this file when possible to ensure
#      cross platform similarity in command look and feel. However, it is 
#      understood that some OSD actions are specific to one platform only,
#      in which case it makes sense to do the output there.
#
# 

use strict;
use Getopt::Std;
use Cwd 'abs_path';
use File::Basename;
use File::Copy;
use English;
use acfslib;
use osds_acfsroot;

# acfsutil command line option switch.
use Config;
my ($optc);
$optc = '-';
$optc = '/' if ($Config{osname} =~ /Win/);

sub main
{
  my ($sub_command);         # start or stop
  my ($preserve) = 0;        # uninstall: preserve tunable files - default = no
  my ($return_code);         # as the name implies 
  # user options
  my (%opt);                 # user options hash - used by getopts()
  my ($install_kernel_vers); # -k : install kernel version (other than current)
  my ($install_files_loc);   # -l : location(s) of the distribution files 
                             # -s : silent operation - no console output 
                             # -v : verbose operation - additional output
                             # -m : Member - enables SHMU
                             #      Domain - enables SHML
                             #      SHMI   - enables SHMI      
                             # -h : help

  # user flags. See description above or usage message output
  my (%flags) = ( install       => 'hsvk:t:l:m:',
                  uninstall     => 'hsvpt:',
                  enable        => 'hst:v',
                  disable       => 'hsvt:',
                  version_check => 'ht:k:',
                  transport_config => 's:r:',
                  transport_list => '',
                  patch_verify  => 'vl:',
                );

  # supplied by the front end driver and 'guaranteed' to be there.
  # command is what the user actually typed in (sans directories).
  $COMMAND = shift(@ARGV);

  # supplied by user
  $sub_command = shift(@ARGV);

  # Enable command line access to library functions
  if (defined($sub_command) && ($sub_command eq 'lib_run_func'))
  {
    $return_code = lib_run_func "@ARGV";
    acfsroot_exit($return_code);
  }

  if (!lib_usm_supported() && defined($sub_command))
  {
    # OSD specific message generated in lib_usm_supported().

    if ($sub_command eq "install")
    {
      # Resolve ORACLE_HOME in the "wrapper scripts".
      osds_fix_wrapper_scripts();
      acfsroot_exit(USM_NOT_SUPPORTED);
    }
    elsif ($sub_command eq "enable")
    {
      # Enable requires a previous installation.
      acfsroot_exit(USM_NOT_SUPPORTED);
    }
    else
    {
      # all other sub-commands fall through.
    }
  }

  # sub commands "install", "uninstall", "enable", "disable", or "version_check"
  # must be supplied by the user.
  if (defined($sub_command))
  {
    if ($sub_command eq "-h")
    {
      usage("invalid", 0);
      acfsroot_exit(USM_SUCCESS);
    }
    elsif (!(($sub_command eq 'install')          ||
             ($sub_command eq 'uninstall')        ||
             ($sub_command eq 'enable')           ||
             ($sub_command eq 'disable')          ||
             ($sub_command eq 'version_check')    || 
             ($sub_command eq 'transport_config') || 
             ($sub_command eq 'transport_list')   || 
             ($sub_command eq 'patch_verify')))
    {
      # Illegal sub-command
      #If we are here. This is an invalid option.
      #Removing leading "-" in $sub_command. I'm getting the following error:
      # (Bad argc for usm:acfs-532)
      $sub_command =~ s/^[-]+//;
      if (!($sub_command eq "help"))
      {
        lib_error_print(532, "invalid option: %s", $sub_command);
      }
      usage("invalid", 0);
      acfsroot_exit(USM_FAIL);
    }
  }
  else
  {
    # no sub-command
    usage("invalid", 0);
    acfsroot_exit(USM_FAIL);
  }

  # parse user options
  %opt=();
  getopts($flags{$sub_command}, \%opt) or usage($sub_command, 1);
  if ($opt{'k'})
  {
    $install_kernel_vers = $opt{'k'};
  }
  if ($opt{'p'})
  {
    $preserve = 1;
  }
  if ($opt{'l'})
  {
    $install_files_loc = $opt{'l'};
    $install_files_loc =~ s/(\/|\\)$//;
    if (! File::Spec->file_name_is_absolute($install_files_loc))
    {
      lib_error_print(9388,
        "An absolute path name must be specified for the alternate location.");
      acfsroot_exit(USM_FAIL);
    }
  }
  if ($opt{'h'})
  {
    # print help information
    usage($sub_command, 0);
    acfsroot_exit(USM_SUCCESS);
  }

  if ($opt{'s'} && $opt{'v'})
  {
    lib_error_print(9160,
      "Can not use the silent and verbose options at the same time.");
    acfsroot_exit(USM_FAIL);
  }

  if ($opt{'s'})
  {
    # do not generate console output - default is not silent.
    $SILENT = 1;
  }
  if ($opt{'v'})
  {
    # Generate additional console output - default is not verbose.
    $VERBOSE = 1;
  }
  
  if(defined ($ENV{'USM_ENABLE_CCMB_INSTALL'}) && 
     $ENV{'USM_ENABLE_CCMB_INSTALL'} == '1')
  {
      # Add the content of $Config{osname} when the OS becomes supported
      my (%supportedOSforODA) = ('linux' => 1);
      
      # This is the default install case. In order to determine if we need
      # to setup acfs remote, we need to go and do a series of checks in 
      # the crsconfig_params file. Based on this table we will then define
      # this flag with the appropriate mode, if necessary. 
      # 1) Local ASM Member Cluster - No SHIM Mode
      # 2) Local ASM Cluster        - No SHIM Mode
      # 3) App Member Cluster       - SHIMU Mode
      # 4) DB Member Cluster        - SHIMU Mode
      # 5) ODA domU                 - SHIMU Mode
      # 6) OPC domU                 - SHIMU Mode
      # 7) ODA dom1                 - SHIML Mode
      # 8) OPC dom0                 - SHIML Mode
      # 9) Domain Cluster           - SHIML Mode  
      # 10) SHMI
      #
      # We only check, if the O.S. is supported
      if ((!$opt{'m'}) &&
          ($supportedOSforODA{$Config{osname}}))
      {
        if( isDomainClass() || isODA() || isOPCDom0() )
        {
            $opt{'m'} = 'Domain';
        }
        elsif ( (isMemberClass || isODADomu() || isOPCDomu()) )
        {
            if(defined($osds_acfsroot::asm_storage_mode) )
            {
                $osds_acfsroot::asm_storage_mode = 
                    acfslib::getParam("ASM_CONFIG");
                if ($osds_acfsroot::asm_storage_mode eq 'near')
                {
                    $opt{'m'} = 'Standalone';
                }
                else
                {
                    $opt{'m'} = 'Member';
                }
            }
        }
        elsif( isSHMI())
        {
            $opt{'m'} = 'SHMI';
        }
      }

      if ($opt{'m'})
      {
        if ($supportedOSforODA{$Config{osname}})
        {
          # When modifying the supported modes, make sure you update
          # usm/src/cmds/internal/acfspatch/acfspatchinstall.pl  
          my %supportedModes;
          
          if (defined($ENV{ADE_VIEW_ROOT}))
          {
              
              %supportedModes = ('Standalone' => 0,
                                 'Domain'     => 1,
                                 'Member'     => 2,
                                 'SHMI'       => 3);
          }
          else
          {
              %supportedModes = ('Standalone' => 0,
                                 'Domain'     => 1,
                                 'Member'     => 2);
          }
          if(exists($supportedModes{$opt{'m'}}) && 
             defined($osds_acfsroot::DOM))
          {
            $osds_acfsroot::DOM = $supportedModes{$opt{'m'}};
          }
          else
          {
            lib_error_print(9192, "unknown installation mode: %s", 
                            $opt{'m'});
            acfsroot_exit(USM_FAIL);
          }
        } 
        else 
        {
          lib_error_print(9193, 
                          "Use of the -m flag is not supported in this OS.");
          acfsroot_exit(USM_FAIL);
        }
      }

  }

  $_ORA_USM_TRACE_ENABLED = 0;
  $_ORA_USM_TRACE_LEVEL = 0;
  if ( defined $opt{'t'}  )
  {
    if ($opt{'v'} || $opt{'s'})
    {
      lib_error_print(9188,
      "cannot use the trace option with the silent or verbose options");
      acfsroot_exit(USM_FAIL);
    }
    $_ORA_USM_TRACE_LEVEL = $opt{'t'};

    if( $_ORA_USM_TRACE_LEVEL == 0){
        $SILENT = 1;
        $VERBOSE = 0;
    }elsif( $_ORA_USM_TRACE_LEVEL == 1){
        $VERBOSE = 1;
        $SILENT = 0;
    }elsif( $_ORA_USM_TRACE_LEVEL == 2){
        $VERBOSE = 1;
        $SILENT = 0;
        $_ORA_USM_TRACE_ENABLED = 1;
    }else{
        lib_error_print( 9175, "Invalid trace level. Valid values for trace level are 0, 1 or 2.");      
        acfsroot_exit(USM_FAIL);    
    }
  } 
  # determine $ORACLE_HOME for this system
  get_oracle_home();

  ##### command parsing complete #####

  # perform required OSD initialization, if any.
  $return_code = osds_initialize($install_kernel_vers, $sub_command);
  if ($return_code != USM_SUCCESS)
  {
    # error messages generated by osds_initialize().
    acfsroot_exit(USM_FAIL);
  }

  # use the default location for media unless the user specified otherwise
  if (!defined($install_files_loc))
  {
    lib_trace(9999, "Setting install_files_loc to default for main"); 
    $install_files_loc = $USM_DFLT_DRV_LOC; 
  }

  # version availability checks don't require privileged access
  if ($sub_command eq 'version_check')
  {
    # check the availability of USM components
    $return_code = version_check($install_kernel_vers, $install_files_loc);
    acfsroot_exit($return_code);
  }

  # verify root access
  if (!lib_am_root())
  {
    lib_error_print(9130, "Root access required");
    acfsroot_exit(USM_FAIL);
  }

  if ($sub_command eq 'install')
  {
    # During an install, which happens as part of an upgrade, utilize commands 
    # out of the install area.  This prevents system installed commands, 
    # such as acfsutil, from the previous version throwing an error when we 
    # call it with a new option.
    # For 12.1, cmdlog is one such option which is not found in previous 
    # releases. 
    lib_trace( 9180, "Sub-command is '%s'", "install" );
    if (!defined($ENV{ADE_VIEW_ROOT}))
    {
      $ACFSUTIL = File::Spec->catfile($USM_DFLT_CMD_LOC, "acfsutil");
      lib_verbose_print_noalert(9505,  
                        "Using acfsutil executable from location: '%s'", 
                        $ACFSUTIL); 
    }

    # install the USM components
    $return_code = install($install_kernel_vers,
                                          $install_files_loc, $sub_command);
  }
  else
  {
    if ($sub_command eq 'uninstall')
    {
      # Set ACFSUTIL if not ADE Environment. If we remove /sbin/acfsutil
      # we might need to log actions after this.
      if (!defined($ENV{ADE_VIEW_ROOT}))
      {
        $ACFSUTIL = File::Spec->catfile($USM_DFLT_CMD_LOC, "acfsutil");
        lib_verbose_print_noalert(9505,  
                          "Using acfsutil executable from location: '%s'", 
                          $ACFSUTIL); 
      }

      # uninstall the USM components
      # pass $install_files_loc to uninstall() as distribution files may be
      # needed during uninstall process
      $return_code = uninstall($install_files_loc, $preserve, $sub_command);
    }
    else
    {
      if ($sub_command eq 'enable')
      {
        # enable the ACFS resources
        $return_code = enable();
      }
      else
      {
        if ($sub_command eq 'disable')
        {
          # disable the ACFS resources
          $return_code = disable();
        }
        elsif ($sub_command eq 'patch_verify')
        {
          # verify patch installation
          osds_search_for_distribution_files($install_files_loc);
          $return_code = osds_patch_verify();
          if($return_code == USM_SUCCESS)
          {
            lib_inform_print (9999,"Patch verify: SUCCESS");
          }
          else
          {
            lib_inform_print (9999,"Patch verify: FAILED");
          }
        }
      }
    }
  }
  if ($sub_command eq 'transport_config')
  {
      # TODO: real transport config
      $return_code = USM_SUCCESS; 
  }
  if ($sub_command eq 'transport_list')
  {
      # TODO: real transport list
      # This is fake output 
      lib_inform_print (9999,"ISCSI: INUSE : IQN:foo.123");
      lib_inform_print (9999,"BLK: NOTINUSE : 1111-XXXX");
      $return_code = USM_SUCCESS; 
  }

  acfsroot_exit($return_code);
} # end main

sub install
{
  my ($install_kernel_vers, $install_files_loc, $sub_command) = @_;
  my ($no_load)  = 0;            # Do not load the newly installed bits.
  my ($preserve) = 1;            # Any tunable files are preserved.
  my ($return_code);
  my ($kernel_version) = osds_get_kernel_version();
  my ($reboot_recommended) = 0;
  my ($previous_install_detected_msg) = 0; # Do not print out the "previous
                                           # install detected" message when
                                           # calling lib_check_uninstall_\
                                           # required() because it is going
                                           # to get printed out when we call
                                           # uninstall().
  my ($alt_files_loc) = $install_files_loc;
  my ($tmp) = -1;
  lib_trace( 9176, "Entering '%s'", "install");

  if (defined($install_kernel_vers))
  {
    lib_trace( 9181, "Kernel version is '%s'", $install_kernel_vers);
    # 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.
    $kernel_version = $install_kernel_vers;
    $no_load = 1;
  }  
  
  # First, find the distribution files from which to install, 
  # no point in going on if they can't be found.
  if (!defined($install_files_loc)) 
  {
    lib_trace(9999, "Setting install_files_loc to default for install");
    $install_files_loc = $USM_DFLT_DRV_LOC;                   
  }
  $return_code = osds_search_for_distribution_files($install_files_loc);
  
  # We try again instead of failing right away, using $install_files_loc
  if (($return_code != USM_SUCCESS) && (defined($install_files_loc))) 
  {
    # Windows compatibility
    $install_files_loc =~ s/\\/\//g;

    do 
    {
      $tmp = index($install_files_loc, "/install", $tmp+1);
    } while (index($install_files_loc, "/install", $tmp+1) != -1);
    if ($tmp != -1)
    {
      $alt_files_loc = substr($install_files_loc, 0, $tmp+8);
      lib_inform_print(9507, "Searching the alternative location: '%s'",
                             $alt_files_loc);
      $return_code = osds_search_for_distribution_files($alt_files_loc);
    }
  } # Done trying again

  if ($return_code == USM_SUCCESS)
  {
    $install_files_loc = $alt_files_loc;
    lib_inform_print(9300, "ADVM/ACFS distribution files found.");
  }
  else
  {
    lib_error_print(9301, "ADVM/ACFS installation cannot proceed:");
    if (defined($install_files_loc))
    {
      lib_error_print(9317,
                     "No ADVM/ACFS distribution media detected at " .
                     "location: '%s'", $install_files_loc);
    }
    else
    {
      lib_error_print(9303,
         "No installation files found for OS kernel version %s.", $kernel_version);
    }
    lib_trace( 9178, "Return code = %s", "USM_FAIL");
    lib_trace( 9177, "Return from '%s'", "install");
    return USM_FAIL;

  }

  # Do not erase the oracleadvm.conf file 


  # Can't continue if the currently loaded drivers can't be unloaded
  $return_code = lib_unload_usm_drivers($install_files_loc, $sub_command);
  if (($return_code != USM_SUCCESS) || (testFailMode() == 1))
  {
    if (($return_code == USM_REBOOT_RECOMMENDED) || (testFailMode() == 1))
    {
      lib_error_print(9427, "Failed to unload ADVM/ACFS drivers. A system reboot is recommended.");
      lib_trace( 9178, "Return code = %s", "USM_REBOOT_RECOMMENDED");
      lib_trace( 9177, "Return from '%s'", "install");
      return $return_code;
    }
    else
    {
      lib_error_print(9304,
          "Installation cannot proceed: Failed to unload ADVM/ACFS drivers.");
      lib_trace( 9178, "Return code = %s", "NOT USM_REBOOT_RECOMMENDED");
      lib_trace( 9177, "Return from '%s'", "install");
      return $return_code;
    }
  }

  # Pass $install_files_loc to uninstall() as distribution files may be
  # needed during uninstall process
  if (uninstall($install_files_loc, $preserve, $sub_command) == USM_FAIL)
  {
    lib_error_print(9305, "ADVM/ACFS installation cannot proceed:");
    lib_error_print(9306, "Failed to uninstall previous installation.");
    lib_trace( 9178, "Return code = %s", "USM_FAIL");
    lib_trace( 9177, "Return from '%s'", "install");
    return USM_FAIL;
  }

  # Check tunable files
  check_tunable_files();

  # We have distribution files and no USM components are currently 
  # installed or loaded - we can proceed with the installation.
  lib_inform_print(9307, "Installing requested ADVM/ACFS software.");

  # osds_search_for_distribution files() has set which files need
  # to be installed.
  $return_code = osds_install_from_distribution_files($reboot_recommended);

  if ($return_code == USM_SUCCESS)
  {
    if ($no_load == 0)
    {
      lib_inform_print (9308, "Loading installed ADVM/ACFS drivers.");
    }

    # ensure that all utilities and drivers are where they are expected to be
    $return_code = osds_load_and_verify_usm_state($no_load);
    if (($return_code == USM_SUCCESS) && (testFailMode() != 2))
    {

      lib_inform_print(9309, "ADVM/ACFS installation correctness verified.");

      # TODO Check for return code.
      osds_configure_acfs_remote();

    }
    else
    {
      lib_error_print(9428,
         "Failed to load ADVM/ACFS drivers. A system reboot is recommended.");
      lib_error_print(9310, "ADVM/ACFS installation failed.");
      lib_trace( 9178, "Return code = %s", "USM_REBOOT_RECOMMENDED");
      $return_code = USM_REBOOT_RECOMMENDED;
    }
  }
  else
  {
    if ($return_code == USM_REBOOT_RECOMMENDED)
    {
      lib_error_print(9428,
         "Failed to load ADVM/ACFS drivers. A system reboot is recommended.");
      lib_error_print(9310, "ADVM/ACFS installation failed.");
      lib_trace( 9178, "Return code = %s", "USM_REBOOT_RECOMMENDED");
      $return_code = USM_REBOOT_RECOMMENDED;
    } 
    else
    {
      lib_error_print(9429, "Failed to install ADVM/ACFS files.");
      lib_error_print(9310, "ADVM/ACFS installation failed.");
      lib_trace( 9178, "Return code = %s", "USM_FAIL");
      $return_code = USM_FAIL;
    }
  }

  

  lib_trace( 9177, "Return from '%s'", "install");
  return $return_code;
} # end install

# Enable ACFS drivers and registry resources
sub enable
{
  my $ret = USM_SUCCESS;
  my $ret1 = USM_SUCCESS;

  lib_trace( 9176, "Entering '%s'", "enable");
  # We are guaranteed here that the ADVM/ACFS supports this OS.

  if ((lib_check_drivers_installed() == 0) || (lib_check_drivers_loaded() == 0))
  {
    lib_error_print(9167,
              "ADVM/ACFS is not installed or loaded. Run 'acfsroot install'.");
    lib_trace( 9178, "Return code = %s", "USM_FAIL");
    lib_trace( 9177, "Return from '%s'", "enable");
    return USM_FAIL;
  }

  if (!((-e <$ORACLE_HOME/bin/crsctl*>) || (-l <$ORACLE_HOME/bin/crsctl*>)))
  {
    lib_error_print(5062, "cannot query CRS resource");
    lib_trace( 9178, "Return code = %s", "USM_FAIL");
    lib_trace( 9177, "Return from '%s'", "enable");
    return USM_FAIL;
  }

  # For some reason, crsctl will sometimes return 0 even if
  # crs is down.  I suppose this is because the command 
  # executed successfully.
  open CRSCTL, "$ORACLE_HOME/bin/crsctl check crs |";
  while (<CRSCTL>)
  {
    if (/CRS-4639/)
    {
      lib_error_print(5062, "cannot query CRS resource");
      close CRSCTL;
      lib_trace( 9178, "Return code = %s", "USM_FAIL");
      lib_trace( 9177, "Return from '%s'", "enable");
      return USM_FAIL;
    }
  }
  close CRSCTL;

  if (usm_resource_exists("drivers") == USM_SUCCESS)
  {
    # Upgrade the resources.
    $ret1 = modify_usm_drivers_resource();
    if ($ret1 != USM_SUCCESS)
    { 
      $ret = USM_FAIL;
    }
  }
  else
  {
    # Install and start the resources.
    $ret1 = add_usm_drivers_resource();
    if ($ret1 != USM_SUCCESS)
    {
      $ret = USM_FAIL;
    }

    $ret1 = start_usm_drivers_resource();
    if ($ret1 != USM_SUCCESS)
    {
      $ret = USM_FAIL;
    }
  }
  if( $ret == USM_SUCCESS){
      lib_trace( 9178, "Return code = %s", "USM_SUCCESS");
  }elsif( $ret == USM_FAIL){
      lib_trace( 9178, "Return code = %s", "USM_FAIL");
  }else{
      lib_trace( 9178, "Return code = %s", "$ret");
  }
  lib_trace( 9177, "Return from '%s'", "enable");

  return $ret;
} 

# Disable ACFS drivers and registry resources
sub disable
{
  my $ret = USM_SUCCESS;

  lib_trace( 9176, "Entering '%s'", "disable");

  my $ret1 = delete_usm_drivers_resource();
  if ($ret1 != USM_SUCCESS)
  {
    $ret = USM_FAIL;
  }

  if( $ret == USM_SUCCESS){
      lib_trace( 9178, "Return code = %s", "USM_SUCCESS");
  }elsif( $ret == USM_FAIL){
      lib_trace( 9178, "Return code = %s", "USM_FAIL");
  }else{
      lib_trace( 9178, "Return code = %s", "$ret");
  }
  
  lib_trace( 9177, "Return from '%s'", "disable");
  return $ret;
}

sub uninstall
{
  my ($install_files_loc, $preserve, $sub_command) = @_;
  my ($return_code);
  my ($previous_install_detected_msg) = 1; # print out the "previous
                                           # installation detected" message
                                           # when we call:
                                           # lib_check_uninstall_required
  lib_trace( 9176, "Entering '%s'", "uninstall");
  # If we're executing an uninstall and a previous installation does not exist
  if (($sub_command eq "uninstall") 
    && (!lib_check_uninstall_required($previous_install_detected_msg)))
  {
    lib_error_print(9313, "No ADVM/ACFS installation detected.");
    lib_trace( 9178, "Return code = %s", "USM_SUCCESS");
    lib_trace( 9177, "Return from '%s'", "uninstall");
    return USM_SUCCESS;
  }

  # Can't continue if the currently loaded drivers can't be unloaded
  $return_code = lib_unload_usm_drivers(undef, $sub_command);
  if ($return_code != USM_SUCCESS)
  {
    lib_trace( 9178, "Return code = %s", "NOT USM_SUCCESS");
    lib_trace( 9177, "Return from '%s'", "uninstall");
    return $return_code;
  }

  lib_inform_print(9314, "Removing previous ADVM/ACFS installation.");

  # Pass $install_files_loc to osds_usm_uninstall() as distribution files may
  # be needed during uninstall process
  $return_code = osds_usm_uninstall($install_files_loc, $preserve);
  if ($return_code == USM_SUCCESS)
  {
    lib_inform_print(9315,
                       "Previous ADVM/ACFS components successfully removed."); 
  }

  if( $return_code == USM_SUCCESS){
      lib_trace( 9178, "Return code = %s", "USM_SUCCESS");
  }elsif( $return_code == USM_FAIL){
      lib_trace( 9178, "Return code = %s", "USM_FAIL");
  }else{
      lib_trace( 9178, "Return code = %s", "$return_code");
  }
  lib_trace( 9177, "Return from '%s'", "uninstall");

  return $return_code;
} # end uninstall

sub version_check
{
  lib_trace( 9176, "Entering '%s'", "vers check");
  my ($install_kernel_vers, $install_files_loc) = @_;
  my ($return_code);
  my ($kernel_version) = osds_get_kernel_version();

  $return_code = osds_search_for_distribution_files($install_files_loc);

  if ($return_code == 0)
  {
    lib_inform_print(9316, 
          "Valid ADVM/ACFS distribution media detected at: '%s'",
          $install_files_loc);
  }
  else
  {
    if (!defined($install_files_loc))
    {
      $install_files_loc = "";
    }
    lib_error_print(9317, "No ADVM/ACFS distribution media detected at " .
                          "location: '%s'", $install_files_loc);
  }

  lib_trace( 9178, "Return code = %s", "$return_code");
  lib_trace( 9177, "Return from '%s'", "vers check");
  return $return_code;
} # end check

################################################
# The following are static functions.
################################################

# There are 3 different ways of getting ORACLE_HOME:
# 1) From the environment.  This may be set by rootcrs or the user.
# 2) From the current location of the command.
# 3) From the crsconfig_params file.

# deinstall tree - Oracle provides a package of software
#     that contains all the tools necessary to remove the 
#     Oracle software - this is called the deinstall.
#     In the event that you remove the Grid Home from 
#     your system, this is the standalone deinstall,
#     although it currently doesn't work.  Our deinstall
#     is part of this throught the deinstall mapfiles.

# There are 5 different uses of the env ORACLE_HOME:
# 1) From the deinstall tree - if the Grid Home that is being removed
#    is gone, it will point to non-existent Grid Home.
# 2) From the deinstall tree, pointing to the Grid Home to remove.
# 3) From the rootcrs.pl during install.
# 4) From the rootcrs.pl during deinstall, but not from the deinstall tree.
# 5) And the final one, manually during our patching procedures.

# In all cases, we don't fully trust the passed in ORACLE_HOME, as the
# user may have incorrectly set it.  We also don't really want to force
# the user to set it, except in certain situations.

# So, we rely on a heuristic, which should catch all cases:
# 1) Find out where we are running from.  In most cases, acfsroot.pl
#    will run from ORACLE_HOME/lib.  (The only case this isn't true for
#    is an ACFS only patch or a command line manual install.)
# 2) Find out what the passed in ORACLE_HOME is.
# 3) See if either discovered or passed in ORACLE_HOME has a crsconfig_params
#    file.
# 4) Get ORACLE_HOME from the params file.
# 5) Use the param file ORACLE_HOME for comparison if it exists, or the env
#    if it doesn't.
# 6) Convert both the passed in  or params ORACLE_HOME (if it exists) and the
#    discovered "ORACLE_HOME" to an absolute path, which will
#    correctly dereference all symlinks in the path.  Compare these
#    two values.  If they match, then use the passed in\params ORACLE_HOME,
#    and assume that it is correct (most times it will be coming from 
#    rootcrs).  This will cover most normal installs, and will
#    catch symlinks.
# 7) If they do not match, then use the discovered value.  This is 
#    because during deinstall, one of two things can happen:
#    a) We are running from the deinstall tree, and the ORACLE_HOME 
#       that is passed in is not valid.  We want to use our tools out
#       of the deinstall tree.
#    b) We are running from the deinstall tree, and the ORACLE_HOME
#       that is passed in is valid, but it doesn't matter to us,
#       we still use our tools out of the deinstall tree.
#    And during install, one of 3 things can happen:
#    a) We are running from rootcrs, and the value matches.
#    b) We are running from the command line during a manual patch\install
#       and the user has the incorrect ORACLE_HOME specified.  In 
#       this case, assume we are running from the GridHome. (However,
#       this case will be covered by getting it from the params file.)

# Bug 11833948 was a bug where the OH was a symlink, yet we used the
# real path of the directory.  This resulted in not being able to contact
# the ASM instance for some reason.  Changing the OH to the symlinked 
# path made things work again.

sub get_oracle_home
{
  my ($dir) = dirname($0);          # $0 is built in acfstoolsdriver.{sh,bat}

  my $discovered_ORACLE_HOME;       # the ORACLE_HOME from bin location.
  my $param_ORACLE_HOME;            # the ORACLE_HOME in the param file.
  my $env_ORACLE_HOME;              # the ORACLE_HOME in the env.
  my $compare_ORACLE_HOME;          # the final choice we are comparing against.
  my $paramfile  = "";              # The location of the parameter file.

  lib_trace( 9176, "Entering '%s'", "get ora home");
  if ((defined($ENV{SRCHOME})) && ($ENV{SRCHOME} ne ""))
  {
    # We're in a development environment, we'll use that $ORACLE_HOME.
    $ORACLE_HOME = $ENV{ORACLE_HOME};
    lib_trace( 9182, "Variable '%s' has value '%s'", "ORACLE_HOME", "$ORACLE_HOME");
    return;
  }

  # We're in a production environment.

  # This file lives in $ORACLE_HOME/lib - drop the trailing /lib
  $dir =~ s/\/lib$//;

  # This is where we are running from.
  $discovered_ORACLE_HOME = $dir;

  # Remove any trailing '\n's.
  chomp($discovered_ORACLE_HOME);

  # Now the env location, for safety.
  if (defined($ENV{ORACLE_HOME}))
  {
    $env_ORACLE_HOME = $ENV{ORACLE_HOME};
    chomp($env_ORACLE_HOME);
  }

  # Now we try to get the information from the params file,
  # just in case it matches somewhere else.
  # We use this param file in a few places now... should we
  # have a function to access it and get info?

  #  Most times we are running out of the grid home, or a place with
  # a crsconfig_params.
  $paramfile = $discovered_ORACLE_HOME . "crs/install/crsconfig_params";

  if ( ! -e $paramfile )
  {
    # Try the location of the env ORACLE_HOME for kicks.
    $paramfile = $env_ORACLE_HOME . "/crs/install/crsconfig_params";
  }
  if ( -e $paramfile )
  {
    open PARAMS, $paramfile;
    
    while (<PARAMS>)
    {
       if (m/^ORACLE_HOME/)
       {
          my @LINE = split /=/;
          $param_ORACLE_HOME = $LINE[$#LINE];
          # Remove any trailing '\n's
          chomp($param_ORACLE_HOME);
          last;
       }
    }

    close (PARAMS);
  }

  # Now, compare the env and the param file.  If they are different, use 
  # the param file (assuming it is not null).
  # If they are the same, use the param file.
  # If we couldn't get to the param file, use the env location.
  if (defined($param_ORACLE_HOME) )
  {
    $compare_ORACLE_HOME = $param_ORACLE_HOME;
    lib_verbose_print(9500, "Location of Oracle Home is '%s' " .
                      "as determined from the internal configuration data", 
                      $ORACLE_HOME);
    
  }
  else  #param is not defined.
  {
    if (defined($env_ORACLE_HOME))
    {
      $compare_ORACLE_HOME = $env_ORACLE_HOME;
      lib_verbose_print(9501, "Location of Oracle Home is '%s' " .
                        "as determined from the ORACLE_HOME " . 
                        "environment variable", 
                        $ORACLE_HOME);
    }
  }


  # Now, compare the abs_path of all dirs found and use the one we trust.
  if (abs_path($compare_ORACLE_HOME) eq abs_path($discovered_ORACLE_HOME))
  {
    # This will take into account symlinks.
    $ENV{ORACLE_HOME} = $compare_ORACLE_HOME;
  }
  else
  {
    # They differed (after abs_path), so assume the user 
    # had something wrong somewhere, and use what we know
    # to be true.
    # Or the user is running deinstall, where ORACLE_HOME can point
    # to some invalid location not consistent with where we are
    # running out of.
    #  This is okay - the system location of our files won't 
    #   change, and that's where we want to remove things from.
    #  OUI can handle cleaning up the ORACLE_HOME, wherever it is.
    $ENV{ORACLE_HOME} = $discovered_ORACLE_HOME;
    lib_verbose_print(9502, "Location of Oracle Home is '%s' " .
                      "as determined from the location of the Oracle " . 
                      "library files", 
                      $ORACLE_HOME);
  }
  $ORACLE_HOME = $ENV{ORACLE_HOME};
  lib_trace( 9182, "Variable '%s' has value '%s'", "ORACLE_HOME", "$ORACLE_HOME");
  lib_trace( 9177, "Return from '%s'", "get ora home");
}

sub usage
{
  my ($sub_command, $abort) = @_;

  lib_trace( 9176, "Entering '%s'", "usage");
  if ($sub_command eq "install")
  {
    lib_error_print_noalert(9161, 
                    " acfsroot install: Install ADVM/ACFS components.");

    lib_error_print_noalert(9185, 
                    " %s [-h] [-s | -v | -t <0,1,2>] [-l <directory>]", 
		    "Usage: acfsroot install");
    lib_error_print_noalert(9132, 
		    "        [-h]             - print help/usage information");
    lib_error_print_noalert(9131,
		    "        [-s]             - silent mode" .   
		    " (error messages only)");
    lib_error_print_noalert(9159,
		    "        [-v]             - verbose mode");
    lib_error_print_noalert(9332, 
		    "        [-l <directory>] - location of the" .   
		    " installation directory");
    lib_error_print_noalert(9189, 
                    "        [-t <0,1,2> ]    - trace level");    
    
  }
  elsif ($sub_command eq "uninstall")
  {
    lib_error_print_noalert(9162, " acfsroot uninstall: Uninstall ADVM/ACFS" .
		    " components.");
    lib_error_print_noalert(9186,
                     " Usage: acfsroot uninstall [-h] [-s | -v | -t <0,1,2>]");
    lib_error_print_noalert(9132, 
		    "        [-h]             - print help/usage information");
    lib_error_print_noalert(9131,
		    "        [-s]             - silent mode" .   
		    " (error messages only)");
    lib_error_print_noalert(9159,
		    "        [-v]             - verbose mode");
    lib_error_print_noalert(9387, 
		    "        [-p]             - preserve tunable parameters");
    lib_error_print_noalert(9189,
                    "        [-t <0,1,2> ]    - trace level");

  }
  elsif ($sub_command eq "version_check")
  {
    lib_error_print_noalert(9163, 
                    " acfsroot version_check: Check ADVM/ACFS version.");
    lib_error_print_noalert(9191, 
	            " Usage: acfsroot version_check [-h] [-t <0,1,2>]");
    lib_error_print_noalert(9132, 
		    "        [-h]             - print help/usage information");
    lib_error_print_noalert(9189,
                    "        [-t <0,1,2> ]    - trace level");

  }
  elsif ($sub_command eq "enable")
  {
    lib_error_print_noalert(9164, 
                    " acfsroot enable: Enable ADVM/ACFS CRS resources.");
    lib_error_print_noalert(9184, " %s [-h] [-s | -v | -t <0,1,2>]",  
		    "Usage: acfsroot enable");
    lib_error_print_noalert(9132, 
		    "        [-h]             - print help/usage information");
    lib_error_print_noalert(9131,
		    "        [-s]             - silent mode" .   
		    " (error messages only)");
    lib_error_print_noalert(9159,
		    "        [-v]             - verbose mode");
    lib_error_print_noalert(9189,
                    "        [-t <0,1,2> ]    - trace level");

  }
  elsif ($sub_command eq "disable")
  {
    lib_error_print_noalert(9165,
                    " acfsroot disable: Disable ADVM/ACFS CRS resources.");
    lib_error_print_noalert(9184, " %s [-h] [-s | -v | -t <0,1,2>]",  
		    "Usage: acfsroot disable");
    lib_error_print_noalert(9132, 
		    "        [-h]             - print help/usage information");
    lib_error_print_noalert(9131,
		    "        [-s]             - silent mode" .   
		    " (error messages only)");
    lib_error_print_noalert(9159,
		    "        [-v]             - verbose mode");
    lib_error_print_noalert(9189,
                    "        [-t <0,1,2> ]    - trace level");
  }
  elsif ($sub_command eq "transport_config")
  {
    lib_error_print_noalert(9850, 
                " acfsroot transport_config: Configure ACFS Remote transport.");
    lib_error_print_noalert(9849, " %s {-s|-r}",
                            "Usage: acfsroot transport_config");
    lib_error_print_noalert(9851, " -r      - refresh transport");
    lib_error_print_noalert(9852, " -s      - scan transport");
  }
  elsif ($sub_command eq "transport_list")
  {
    lib_error_print_noalert(9999, 
                " acfsroot transport_list: List ACFS Remote transports.");
    lib_error_print_noalert(9999,
                            "Usage: acfsroot transport_list");
  }
  else
  {
    lib_error_print_noalert(9185, 
                    " %s [-h] [-s | -v | -t <0,1,2>] [-l <directory>]", 
		    "Usage: acfsroot install");
    lib_error_print_noalert(9186,
                     " Usage: acfsroot uninstall [-h] [-s | -v | -t <0,1,2>]");
    lib_error_print_noalert(9191, 
	             " Usage: acfsroot version_check [-h] [-t <0,1,2>]");
    lib_error_print_noalert(9184, " %s [-h] [-s | -v | -t <0,1,2>]",
                                                     "Usage: acfsroot enable");
    lib_error_print_noalert(9184, " %s [-h] [-s | -v | -t <0,1,2>]",
                                                     "Usage: acfsroot disable");
  }

  lib_trace( 9177, "Return from '%s'", "usage");

  if ($abort)
  {
    acfsroot_exit(USM_FAIL);
  }
} # end usage

# acfsroot_exit does not return
#
sub acfsroot_exit
{
  my ($ret) = @_;
  lib_trace( 9176, "Entering '%s'", "acroot ex");
  lib_trace( 9178, "Return code = %s", "$ret");
  lib_trace( 9177, "Return from '%s'", "acroot ex");
  exit $ret;
}

# A function for error path testing; returns 0 if we are not testing an error path
sub testFailMode
{
  lib_trace( 9176, "Entering '%s'", "fail mode");
  if (defined($ENV{ADE_VIEW_ROOT}) && defined($ENV{_ORA_ACFSROOT_TEST}))
  {
    # 1 = Simulates failure during unload of drivers
    # 2 = Simulates failure during load of drivers
    lib_trace( 9178, "Return code = %s", "$ENV{_ORA_ACFSROOT_TEST}");
    lib_trace( 9177, "Return from '%s'", "fail mode");
    return ($ENV{_ORA_ACFSROOT_TEST});
  }
  lib_trace( 9178, "Return code = %s", "0");
  lib_trace( 9177, "Return from '%s'", "fail mode");
  return 0;
}

# check_tunable_files
#
# Check if tunables files exists in ORACLE_HOME and copy to O.S. Path
sub check_tunable_files
{
  #Check if $USM_TUNE_ORA_DIR exists
  unless (-d $USM_TUNE_ORA_DIR)
  {
    File::Path::make_path($USM_TUNE_ORA_DIR);
  }

  foreach my $tunefile ("acfstunables", "advmtunables")
  {
    my ($OS_TUNABLE_FILE) = "$USM_TUNE_OS_DIR/$tunefile";
    my ($ORA_TUNABLE_FILE) = "$USM_TUNE_ORA_DIR/$tunefile";

    #If tunables file exists in the ORA_HOME location
    if (-e $ORA_TUNABLE_FILE)
    {
      unless (-l $ORA_TUNABLE_FILE)
      {
        if ((-e $OS_TUNABLE_FILE) ||
            (-l $OS_TUNABLE_FILE))
        {
          system("rm $OS_TUNABLE_FILE");
        }
        unless (copy($ORA_TUNABLE_FILE,$OS_TUNABLE_FILE))
        {
          #Error
          lib_error_print(9999,
                    "Failed copying $ORA_TUNABLE_FILE to $OS_TUNABLE_FILE: $!.");
        } 
      }
    }
    # In the else case, ORA_TUNABLE_FILE doesn't exists
    # If OS_TUNABLE_FILE exists, we'll copy to ORA_TUNABLE_FILE
    elsif (-e $OS_TUNABLE_FILE)
    {
      if ((-e $ORA_TUNABLE_FILE) ||
          (-l $ORA_TUNABLE_FILE))
      {
        system("rm $ORA_TUNABLE_FILE");
      }
      copy($OS_TUNABLE_FILE,$ORA_TUNABLE_FILE);
      chmod 0664, $ORA_TUNABLE_FILE;
    }
  }
}

main();
