# 
#
# okaroot.pl
# 
# Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      okaroot.pl
#
#    DESCRIPTION
#      install/uninstall OKA components from the distribution files
#
#    NOTES
#      okaroot install -h [-s|-v] -l
#          install OKA kernel drivers and commands.
#      okaroot uninstall -h [-s|-v] -p
#          uninstall OKA kernel drivers and commands.
#      okaroot version_check -h -l
#          check if OKA components are available for installation.
#      okaroot enable [-h] [-s | -v ]
#          enable OKA CRS resources.
#      okaroot disable [-h] [-s | -v ]
#          disable OKA CRS resources.
#      okaroot stop [-h] [-s | -v ]
#          stop OKA CRS resources.
#      okaroot activate [-h] [-s | -v ]
#          activate OKA CRS resources.#
#      okaroot deactivate [-h] [-s | -v ]
#          deactivate OKA CRS resources.

#
#    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 OKA 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 English;
use okalib;
use acfslib;
use osds_acfsroot;
use osds_okaroot;

# 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
                             # -h : help

  # user flags. See description above or usage message output
  my (%flags) = ( install       => 'hsvk:l:',
                  uninstall     => 'hsvp',
                  enable        => 'hs:v',
                  disable       => 'hsv:',
                  version_check => 'hl:k:',
                  stop          => 'hs:v',
                  activate      => 'hs:v',
		  deactivate    => 'hs:v',
                );

  # process the command line for acfsutil cmdlog
  # We could just use "acfsutil cmdlog -s @ARGV"
  # but that wouldn't give us the absolute command path name;
  my ($opt, $opts);

  $opts = "";
  foreach $opt (@ARGV)
  {
    $opts = abs_path($0) if $opts eq "";  # command name
    $opts .= " $opt";
  }

  # 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);

  # check if OKA supported
  if (!lib_oka_supported() && defined($sub_command))
  {
    # OSD specific message generated in lib_oka_supported().

    if ($sub_command eq "install")
    {
      # Resolve ORACLE_HOME in the "wrapper scripts".
      # Currently, there are no user space tools shipped in KA
      osds_fix_oka_wrapper_scripts();
      okaroot_exit(USM_NOT_SUPPORTED);
    }
    elsif ($sub_command eq "enable")
    {
      # Enable requires a previous installation.
      okaroot_exit(USM_NOT_SUPPORTED);
    }
    else
    {
      # all other sub-commands fall through.
    }
  }

  # sub commands "install", "uninstall", or "version_check"
  # must be supplied by the user.
  if (defined($sub_command))
  {
    if (!(($sub_command eq 'install') ||
          ($sub_command eq 'uninstall') ||
          ($sub_command eq 'enable') ||
          ($sub_command eq 'disable') ||
          ($sub_command eq 'version_check') ||
          ($sub_command eq 'stop') ||
          ($sub_command eq 'activate') ||
	  ($sub_command eq 'deactivate')))
    {
      # illegal sub-command
      usage("invalid", 0);
      okaroot_exit(USM_FAIL);
    }
  }
  else
  {
    # no sub-command
    usage("invalid", 0);
    okaroot_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_noalert(9388,
        "An absolute path name must be specified for the alternate location.");
      okaroot_exit(USM_FAIL);
    }
  }
  if ($opt{'h'})
  {
    # print help information
    usage($sub_command, 0);
    okaroot_exit(USM_SUCCESS);
  }

  if ($opt{'s'} && $opt{'v'})
  {
    lib_error_print_noalert(9160,
      "Can not use the silent and verbose options at the same time.");
      okaroot_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;
  }

  # determine $ORACLE_HOME for this system
  # TODO-SOMA - will OKA need this? Need to check in install area
  #get_oracle_home();

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

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

  # use the default location for media unless the user specified otherwise
  if (!defined($install_files_loc))
  {
    $install_files_loc = $OKA_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);
    okaroot_exit($return_code);
  }

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

  if ($sub_command eq 'install')
  {
    # install the USM components
    $return_code = install($install_kernel_vers,
                                          $install_files_loc, $sub_command);
  }
  else
  {
    if ($sub_command eq 'uninstall')
    {
      # 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 OKA resources
        $return_code = enable();
      }
      else
      {
        if ($sub_command eq 'disable')
        {
          # disable the OKA resources
          $return_code = disable();
        }
        else
        {
          if ($sub_command eq 'stop')
          {
            # stop the OKA resources
            $return_code = stop();
          }
          else
          {
            if ($sub_command eq 'activate')
            {
              # activate the OKA resources
              $return_code = activate();
            }
	    else
	    {
	      if ($sub_command eq 'deactivate')
	      {
		# deactivate the OKA resources
                $return_code = deactivate();
              }
	    }
          }
        }
      }
    }
  }

  okaroot_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;

  if (defined($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.
  $return_code = osds_oka_search_for_distribution_files($install_files_loc);
  if ($return_code == USM_SUCCESS)
  {
    lib_inform_print_noalert(627, "OKA distribution files found.");
  }
  else
  {
    lib_error_print_noalert(628, "OKA installation cannot proceed:");
    if (defined($install_files_loc))
    {
      lib_error_print_noalert(617,
                     "No OKA distribution media detected at " .
                     "location: '%s'", $install_files_loc);
    }
    else
    {
      lib_error_print_noalert(9303,
         "No installation files found for OS kernel version " .
         "%s.", $kernel_version);
    }
    return USM_FAIL;
  }

  # Can't continue if the currently loaded drivers can't be unloaded
  $return_code = lib_unload_oka_drivers($install_files_loc, $sub_command);
  if ($return_code != USM_SUCCESS)
  {
    if ($return_code == USM_REBOOT_RECOMMENDED)
    {
      lib_error_print(629, 
          "Failed to unload OKA drivers. A system reboot is recommended.");
      return $return_code;
    }
    else
    {
      lib_error_print_noalert(630,
          "Installation cannot proceed: Failed to unload OKA drivers.");
      return $return_code;
    }
  }

  # Search for a previous installation and remove it if found.
  if (lib_check_any_oka_driver_installed())
  {
    # 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_noalert(631, "OKA installation cannot proceed:");
      lib_error_print_noalert(9306, "Failed to uninstall previous installation.");
      return USM_FAIL;
    }
  }

  # We have distribution files and no USM components are currently 
  # installed or loaded - we can proceed with the installation.
  lib_inform_print_noalert(636, "Installing requested OKA software.");

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

  if ($return_code == USM_SUCCESS)
  {
    if ($no_load == 0)
    {
      lib_inform_print_noalert (637, "Loading installed OKA drivers.");
    }

    # ensure that all utilities and drivers are where they are expected to be
    $return_code = osds_load_and_verify_oka_state($no_load);
    if ($return_code == USM_SUCCESS)
    {
      lib_inform_print_noalert(638, "OKA installation correctness verified.");
    }
    else
    {
      lib_error_print_noalert(653, "Failed to load OKA drivers.");
      lib_error_print_noalert(639, "OKA installation failed.");
      $return_code = USM_REBOOT_RECOMMENDED;
    }
  }
  else
  {
    lib_error_print_noalert(640, "Failed to install OKA files.");
    lib_error_print_noalert(639, "OKA installation failed.");
    $return_code = USM_FAIL;
  }

  return $return_code;
} # end install

# Enable OKA 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 OKA supports this OS.

  if (lib_check_oka_drivers_installed() == 0)
  {
    lib_error_print(9167,
              "OKA is not installed or loaded. Run 'okaroot 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_noalert(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_noalert(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;
  
  $ret = create_oka_drivers_resource_type();

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

    $ret1 = start_oka_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 OKA drivers and registry resources
sub disable
{
  my $ret = USM_SUCCESS;

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

  my $ret1 = delete_oka_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 stop
{
  lib_trace( 9176, "Entering '%s'", "stop");

  my $ret = stop_oka_drivers_resource();

  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'", "stop");
  return $ret;
}

sub activate
{
  lib_trace( 9176, "Entering '%s'", "activate");

  my $ret = activate_oka_drivers_resource();

  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'", "activate");
  return $ret;
}

sub deactivate
{
  lib_trace( 9176, "Entering '%s'", "deactivate");

  my $ret = deactivate_oka_drivers_resource();

  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'", "deactivate");
  return $ret;
}

sub uninstall
{
  my ($install_files_loc, $preserve, $sub_command) = @_;
  my ($return_code);

  # Search for a previous installation
  if (lib_check_any_oka_driver_installed())
  {
    lib_inform_print_noalert(632, "Existing OKA installation detected.");
  }
  else
  {
    lib_error_print_noalert(633, "No OKA installation detected.");
    return USM_SUCCESS;
  }

  # Can't continue if the currently loaded drivers can't be unloaded
  $return_code = lib_unload_oka_drivers(undef, $sub_command);
  if ($return_code != USM_SUCCESS)
  {
    return $return_code;
  }

  lib_inform_print_noalert(634, "Removing previous OKA installation.");

  # Pass $install_files_loc to osds_usm_uninstall() as distribution files may
  # be needed during uninstall process
  $return_code = osds_oka_uninstall($install_files_loc, $preserve);
  if ($return_code == USM_SUCCESS)
  {
    lib_inform_print_noalert(635,
                       "Previous OKA components successfully removed."); 
  }

  return $return_code;
} # end uninstall

sub version_check
{
  my ($install_kernel_vers, $install_files_loc) = @_;
  my ($return_code);
  my ($kernel_version) = osds_get_kernel_version();

  $return_code = osds_oka_search_for_distribution_files($install_files_loc);

  if ($return_code == 0)
  {
    lib_inform_print_noalert(616, 
          "Valid OKA distribution media detected at: '%s'",
          $install_files_loc);
  }
  else
  {
    lib_error_print_noalert(617, "No OKA distribution media detected at " .
                          "location: '%s'", $install_files_loc);
  }

  return $return_code;
} # end check

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

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

  if ($sub_command eq "install")
  {
    lib_error_print_noalert(601, " okaroot install: Install OKA components.");

    lib_error_print_noalert(602,
		    " %s [-h] [-s | -v] [-l <directory>]", 
		    "Usage: okaroot install");
    lib_error_print_noalert(603, 
		    "        [-h]             - print help/usage information");
    lib_error_print_noalert(604,
		    "        [-s]             - silent mode" .   
		    " (error messages only)");
    lib_error_print_noalert(605,
		    "        [-v]             - verbose mode");
    lib_error_print_noalert(606, 
		    "        [-l <directory>] - location of the" .   
		    " installation directory");
  }
  elsif ($sub_command eq "uninstall")
  {
    lib_error_print_noalert(607, " okaroot uninstall: Uninstall OKA" .
		    " components.");
    lib_error_print_noalert(608, " Usage: okaroot uninstall [-h] [-s | -v]");
    lib_error_print_noalert(603, 
		    "        [-h]             - print help/usage information");
    lib_error_print_noalert(604,
		    "        [-s]             - silent mode" .   
		    " (error messages only)");
    lib_error_print_noalert(605,
		    "        [-v]             - verbose mode");
    lib_error_print_noalert(609, 
		    "        [-p]             - preserve tunable parameters");
  }
  elsif ($sub_command eq "version_check")
  {
    lib_error_print_noalert(610, " okaroot version_check: Check OKA version.");
    lib_error_print_noalert(611, 
		    " Usage: okaroot version_check [-h] [-l <location>]");
    lib_error_print_noalert(603, 
		    "        [-h]             - print help/usage information");
    lib_error_print_noalert(606, 
		    "        [-l <directory>] - location of the" .   
		    " installation directory");
    lib_error_print_noalert(612, 
		    "                           (if not the default" .
		    " location)");
    lib_error_print_noalert(613, 
		    "                           (if not the current kernel" . 
		    " version)");
  }
  elsif ($sub_command eq "enable")
  {
    lib_error_print_noalert(9164, " okaroot enable: Enable OKA CRS resources.");
    lib_error_print_noalert(9184, " %s [-h] [-s | -v ]",
                    "Usage: okaroot 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");

  }
  elsif ($sub_command eq "disable")
  {
    lib_error_print_noalert(9165," okaroot disable: Disable OKA CRS resources.");
    lib_error_print_noalert(9184, " %s [-h] [-s | -v ]",
                    "Usage: okaroot 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");
  }elsif ($sub_command eq "stop")
  {
    lib_error_print_noalert(9184, " %s [-h] [-s | -v ]",
                    "Usage: okaroot stop");
    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");

  }elsif ($sub_command eq "activate")
  {
    lib_error_print_noalert(9184, " %s [-h] [-s | -v ]",
                    "Usage: okaroot activate");
    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");

  }
  elsif ($sub_command eq "deactivate")
  {
    lib_error_print_noalert(9184, " %s [-h] [-s | -v ]",
                    "Usage: okaroot deactivate");
    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");

  }
  else
  {
    lib_error_print_noalert(652,
              "Usage: okaroot {install | uninstall | version_check | enable | disable} [<arguments>]");
    lib_error_print_noalert(615, " For more information, use okaroot <command> -h");
  }

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

# okaroot_exit does not return
#
sub okaroot_exit
{
  my ($ret) = @_;
  # call acfsutil cmdlog - $optc handles the Windows/Unix cmd switch '-' or '/'.
  # the 't' option logs the termination of the command.
  #my ($cmd) =
  #        sprintf "$OKA_DFLT_CMD_LOC/acfsutil cmdlog %st $ret $COMMAND", $optc;
  #system($cmd);
  exit $ret;
}

main();
