#!/usr/local/bin/perl
# 
#
# olfsroot.pl
# 
# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      olfsroot.pl - <one-line expansion of the name>
#
#    DESCRIPTION
#      <short description of component this file declares/defines>
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
# 

my ($KVER);
my ($ARCH) = `uname -i`;                        # Machine architecture - i386
chomp($ARCH);
my ($UNAME_R) = `uname -r`;
chomp($UNAME_R);

our ($ORACLE_HOME) = $ENV{ORACLE_HOME};

my  ($SHIPHOME_BASE_DIR) = "$ORACLE_HOME/usm/install";
our ($OLFS_DFLT_CMD_LOC)  = "$SHIPHOME_BASE_DIR/cmds/bin";
our ($OLFS_DFLT_DRV_LOC);
my ($SBIN_DIR)         = "/sbin";
my ($DRIVER_DIR);
my ($COMMAND) = "No Command Specified";
my ($SILENT) = 0;
my ($VERBOSE) = 0;
my %olfs_values = ('AUI_PATH' => 0, 'AGI_PATH' => 1, 'OH_PATH' => 2);
my $INIT_DIR = "/etc/init.d";
my $RC_START = "S95";
my $RC_KILL = "K95";
my $RC_KILL_OLD = "K19";
my $RCSDIR = "/etc/rc.d/rc3.d /etc/rc.d/rc5.d";
my $RCKDIR = "/etc/rc.d/rc0.d /etc/rc.d/rc1.d /etc/rc.d/rc2.d /etc/rc.d/rc4.d /etc/rc.d/rc6.d";
my $RCALLDIR = "/etc/rc.d/rc0.d /etc/rc.d/rc1.d /etc/rc.d/rc2.d /etc/rc.d/rc3.d /etc/rc.d/rc4.d /etc/rc.d/rc5.d /etc/rc.d/rc6.d";

use strict;
use Getopt::Std;
use Cwd 'abs_path';
use File::Basename;
use English;
use Config;
use File::Path;
use File::Spec::Functions;

use acfslib;
use osds_acfslib;
use osds_acfsroot;
$acfslib::USM_CURRENT_PROD = USM_PROD_OLFS;

main();

sub olfs_initialize
{
    my ($install_kver, $sub_command) = @_;
    my ($type);
    my ($return_code) = USM_SUCCESS;

    if (!defined($install_kver))
    {
        # use the current kernel version
        $KVER = $UNAME_R;
    }
    else
    {
        # use the specified kernel version
        # TODO we'll want to do some sanity checking here.
        $KVER = $install_kver;
    }

    # we have already verified that we have a valid Linix type
    # stomar
    $type = lib_osds_get_os_type();

    my ($base) = $SHIPHOME_BASE_DIR;
    $OLFS_DFLT_DRV_LOC = get_install_home_from_base($base, $type, $ARCH, $KVER);
    return $return_code;
}

sub get_olfs_driver_dir
{
    my ($driver_build_version); # version that the driver was linked for.

    $driver_build_version = $UNAME_R;

    return "/lib/modules/$driver_build_version/extra/oracle";
}

sub olfsroot_exit
{
  my ($ret) = @_;

  exit $ret;
}

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); # -u : AUI path 
  my ($install_files_loc);   # -g : AGI path
                             # -m : Mount point path of OLFS
                             # -v : verbose operation - additional output
                             # -h : help

  # user flags. See description above or usage message output
  my (%flags) = ( install       => 'hu:g:m:',
                  uninstall     => 'h',
                );

  # 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);
  my ($aui_path) = "";
  my ($agi_path) = "";
  my ($mnt_path) = "";

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

  # must be supplied by the user.
  if (defined($sub_command))
  {
    if (!(($sub_command eq 'install') ||
          ($sub_command eq 'uninstall')))
    {
      # illegal sub-command
      usage("invalid", 0);
      olfsroot_exit(USM_FAIL);
    }
  }
  else
  {
    # no sub-command
    usage("invalid", 0);
    olfsroot_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.");
      olfsroot_exit(USM_FAIL);
    }
  }
  if ($opt{'h'})
  {
    # print help information
    usage($sub_command, 0);
    olfsroot_exit(USM_SUCCESS);
  }

  if ($opt{'s'} && $opt{'v'})
  {
    lib_error_print(9160,
      "Can not use the silent and verbose options at the same time.");
      olfsroot_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 ($sub_command eq 'install')
  {
      if ($opt{'u'})
      {
        $aui_path = $opt{'u'};
      }
      else
      {
        #print "AUI path not set\n";
        #usage($sub_command, 1);
      }
      if ($opt{'g'})
      {
        $agi_path = $opt{'g'};
      }
      else
      {
        #print "AGI path not set\n";
        #usage($sub_command, 1);
      }
      if ($opt{'m'})
      {
        $mnt_path = $opt{'m'};
      }
      else
      {
        #print "Mount path not set\n";
        #usage($sub_command, 1);
      }
      #print "AUI: '$aui_path', AGI: '$agi_path', OLFS: '$mnt_path'\n";
  }

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

  $return_code = olfs_initialize($install_kernel_vers, $sub_command);
  if ($return_code != USM_SUCCESS)
  {
    olfsroot_exit(USM_FAIL);
  }

  if (!defined($install_files_loc))
  {
    $install_files_loc = $OLFS_DFLT_DRV_LOC;
  }

  if ($sub_command eq 'install')
  {
    $return_code = install($install_kernel_vers, $install_files_loc,
                           $sub_command, $aui_path, $agi_path, $mnt_path);
  }
  else
  {
    if ($sub_command eq 'uninstall')
    {
        $return_code = uninstall($install_files_loc, $preserve, $sub_command);
    }
  }

  olfsroot_exit($return_code);
} # end main

sub install
{
    my ($install_kernel_vers, $install_files_loc, $sub_command, $aup, $agp, $mnt) = @_;
    my ($return_code);

    $return_code = olfs_search_for_distribution_files($install_files_loc);
    if ($return_code == USM_SUCCESS)
    {
    }

    $return_code = olfs_install_from_distribution_files(); 
    if ($return_code == USM_SUCCESS)
    {
    }

    $return_code = olfs_load_and_verify_olfs_state();
    if ($return_code == USM_SUCCESS)
    {
    }
    
    if ($aup ne '' && $agp ne '' && $mnt ne '')
    {
        $return_code = olfs_initialize_sbsfile($aup, $agp, $mnt);
        if ($return_code == USM_SUCCESS)
        {
        }
        else
        {
            uninstall($install_files_loc, 1, "uninstall");
        }
    }

    return ($return_code);
}

sub olfs_search_for_distribution_files
{
    my ($kernel_install_files_loc) = @_;
    my ($return_code) = USM_SUCCESS;

    if (!defined($kernel_install_files_loc))
    {
        return USM_FAIL;
    }

    $DRIVER_DIR = get_olfs_driver_dir();
    return $return_code;
}

sub olfs_install_from_distribution_files
{
    my ($return_code);
    my ($ret);
    my ($command);

    if (! -d $DRIVER_DIR)
    {
        mkpath($DRIVER_DIR, 0, 0755);
    }

    my ($source) = "$OLFS_DFLT_DRV_LOC/oracleolfs.ko"; 
    my ($target) = "$DRIVER_DIR/oracleolfs.ko";

    $command = "cp $source $target";
    $ret = system ($command);
    $return_code = USM_FAIL if $ret;
}

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

  # Search for a previous installation
  if (check_any_olfs_driver_installed())
  {
      # print "Existing OLFS installation detected.\n";
      # $$$: These should go into messages file. Check AFD messages file for
      # reference.
  }
  else
  {
    print "No OLFS installation detected.\n";
    return USM_SUCCESS;
  }

if (0)
{
  # Can't continue if the currently loaded drivers can't be unloaded
  $return_code = lib_unload_afd_drivers(undef, $sub_command);
  if ($return_code != USM_SUCCESS)
  {
    return $return_code;
  }
}
  $return_code = olfs_remove_rc_init();
  if ($return_code == USM_FAIL)
  {
      return $return_code;
  }
  # print "Removing previous OLFS installation.\n";

  # Pass $install_files_loc to osds_usm_uninstall() as distribution files may
  # be needed during uninstall process
  $return_code = olfs_uninstall($install_files_loc, $preserve);
  if ($return_code == USM_SUCCESS)
  {
      # print "Previous OLFS components successfully removed.\n";
  }

  return $return_code;
} # end uninstall

use File::Basename;
use File::Find;

sub olfs_uninstall
{
    my ($return_code);
    my $command;
    my $ret;

    $command = "/sbin/rmmod oracleolfs";

    $ret = system ($command);
    if ($ret)
    {
        $return_code = USM_FAIL;
        goto OUT;
    }
    else
    {
        $return_code = USM_SUCCESS;
    }

    open (CHECK, "find /lib/modules  2> /dev/null |");
    while (<CHECK>)
    {
        my ($driver) = split;
        if (($driver =~ m/\/oracle\/oracleolfs.ko/) )
        {
            if (! unlink $driver)
            {
                print "Unable to remove '%s'.", $driver;
                $return_code = USM_FAIL;
                goto OUT;
            }
        }
    }
    close(CHECK);
OUT:
    return $return_code;
}

sub check_any_olfs_driver_installed
{
    my ($usm_driver) = 'uninitialized';
    my ($return_code);

    # print "In check_any_olfs_driver_installed\n";

    open(OLFS, "find /lib/modules/ | grep olfs |");

    while (<OLFS>)
    {
        $usm_driver = $_; 
        $usm_driver = basename($usm_driver);
        chomp($usm_driver);
    }

    if ($usm_driver eq "oracleolfs.ko")
    {
        $return_code =  1;
    }
    else
    {
        $return_code =  0;
    }
    close(OLFS);

    return ($return_code);
}

sub olfs_load_and_verify_olfs_state()
{
    my ($return_code) = USM_SUCCESS; 
    my ($command);
    my ($ret);

    $command = "/sbin/insmod $DRIVER_DIR/oracleolfs.ko";

    $ret = system ($command);
    $return_code = USM_FAIL if $ret;

    return ($return_code);
}

sub olfs_initialize_sbsfile()
{
    my ($srcfile, $tmpfile, $rexp, $sub, $fh, $wh);
    $srcfile = catfile($ORACLE_HOME, "crs", "sbs", "olfs.sbs");
    $tmpfile = catfile("/", "tmp", "olfs");
    $rexp="[a-zA-Z_]+";
    my @myargs = @_;

    unless (open( $fh, '<', $srcfile))
    {
        print "Can't open $srcfile: $!\n";
        return USM_FAIL;
    }
    unless (open( $wh, '>', $tmpfile))
    {
        print "Can't open $tmpfile: $!\n";
        return USM_FAIL;
    }

    while ( my $line = <$fh> ) {
        if (my @matchlist = $line =~ /%(${rexp})%/g)
        {
            foreach my $match (@matchlist) {
                if (exists($olfs_values{$match}))
                {
                    $sub = $myargs[$olfs_values{$match}];
                }
                else
                {
                    $sub = $DRIVER_DIR;
                }
                $line =~ s/%(${match})%/$sub/g;
            }
        }
        print $wh $line;
    }
    close $fh;
    close $wh;
    
    my $return_code = olfs_copy_to_rc_init($tmpfile);
    my $cmd = "rm $tmpfile";
    my $ret = system($cmd);
    if ($ret)
    {
        print "Can't delete $tmpfile: $!\n";
    }
    return $return_code;
}

sub olfs_remove_file
{
    my $myfile = $_[0];
    my $return_code = USM_SUCCESS;
    if (-e $myfile || -l $myfile)
    {
        my $cmd = "rm $myfile";
        my $ret = system ($cmd);
        $return_code = USM_FAIL if $ret;
    }
    return $return_code;
}

sub olfs_set_perms
{
    my $myfile = $_[1];
    my $perms = $_[0];
    my $return_code = USM_SUCCESS;
    if (-e $myfile || -l $myfile)
    {
        my $cmd = "chmod $perms $myfile";
        my $ret = system ($cmd);
        $return_code = USM_FAIL if $ret;
    }
    return $return_code;
}

sub olfs_copy_to_rc_init
{
    my $srcfile = $_[0];
    my $destfile = "olfs";
    my $cmd;
    my $ret;
    my $destpath = catfile($INIT_DIR, $destfile);

    $cmd = "cp $srcfile $destpath";
    $ret = system ($cmd);
    if ($ret)
    {
        return USM_FAIL;
    }
    $ret = olfs_set_perms ("0755", $destpath);
    if ($ret == USM_FAIL)
    {
        print "Failed in set perms of $destpath";
        return $ret;
    }
    
    my @RCSDIRLIST = split (/ /, $RCSDIR);
    foreach my $rc (@RCSDIRLIST) 
    {
        $ret = olfs_remove_file ("$rc/$RC_START$destfile");
        if ($ret == USM_FAIL)
        {
            print("Removing $rc/$RC_START$destfile failed\n");
            return $ret;
        }

        symlink($destpath, catfile($rc, "$RC_START$destfile")) 
                || return USM_FAIL;
    }

    my @RCKDIRLIST = split (/ /, $RCKDIR);
    foreach my $rc (@RCKDIRLIST) 
    {
        $ret = olfs_remove_file ("$rc/$RC_KILL$destfile");
        if ($ret == USM_FAIL)
        {
            print("Removing $rc/$RC_KILL$destfile failed\n");
            return $ret;
        }

        symlink($destpath, catfile($rc, "$RC_KILL$destfile")) 
                || return USM_FAIL;
    }
    return USM_SUCCESS;
}

sub olfs_remove_rc_init
{
    my $myfile = "olfs";

    my ($rc, $rcStartFile, $rcKillFile, $rcKillOldFile, $initPath);
    my @RCALLDIRLIST = split (/ /, $RCALLDIR);

    foreach $rc (@RCALLDIRLIST) 
    {
        $rcStartFile = catfile ($rc, $RC_START . $myfile);
        olfs_remove_file ("$rcStartFile");

        $rcKillFile = catfile ($rc, $RC_KILL . $myfile);
        olfs_remove_file ("$rcKillFile");

        $rcKillOldFile = catfile ($rc, $RC_KILL_OLD . $myfile);
        olfs_remove_file ("$rcKillOldFile");
    }
    $initPath = catfile ($INIT_DIR, $myfile);
    olfs_remove_file($initPath);
    return USM_SUCCESS;
}

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

    if ($sub_command eq "install")
    {
        print "olfsroot install: Install OLFS.\n";
        #print "Usage: olfsroot install -u <AUI_PATH> -g <AGI_PATH> -m <OH_PATH>\n";
        print "Usage: olfsroot install\n";
    }

    if ($sub_command eq "uninstall")
    {
        print "olfsroot uninstall: Uninstall OLFS\n";
        print "Usage: olfsroot uninstall\n";
    }

    if ($sub_command eq "invalid")
    {
        print "olfsroot: Invalid command.\n";
        print "olfsroot {install | uninstall} [-h]\n";
    }

    if ($abort)
    {
        olfsroot_exit(USM_FAIL);
    }
}
