MINI MINI MANI MO

Path : /opt/oracle/product/18c/dbhomeXE/lib/
File Upload :
Current File : //opt/oracle/product/18c/dbhomeXE/lib/osds_unix_linux_acfslib.pm

#
#
# osds_unix_linux_acfslib.pm
# 
# Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
#
#
#    NAME
#      osds_unix_linux_acfslib.pm - Linux Unix OSD library components.
#
#    DESCRIPTION
#      Purpose
#          Linux Unix OSD library functions for the install/runtime scripts.
#
#    NOTES
#      All user visible output should be done in the common code.
#      this will ensure a consistent look and feel across all platforms.
#
#

use strict;
use Cwd;
use File::Path;
use acfslib;
package osds_unix_linux_acfslib;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(
                 lib_osds_krncmp
                 lib_osds_am_root
                 lib_osds_control_devices_accessible
                 lib_osds_create_mount_point
                 lib_osds_device_from_mountpoint
                 lib_osds_get_advm_mounts
                 lib_osds_get_asm_user
                 lib_osds_get_drive_info
                 lib_osds_mount
                 lib_osds_mountpoint_descriptors
                 lib_osds_run_as_user
                 lib_osds_unmount
                 lib_osds_usm_supported
                 lib_osds_verify_usm_devices
                 lib_osds_is_db_home
                 lib_osds_is_abs_path
                 lib_osds_are_same_file
                 get_dev_mntpt_from_mount_line
                 $ACFSUTIL
                 $ADVMUTIL
                 $TMPDIR
                 $REDIRECT
                 AVD_DIR
                 AVD_CTL_DEV
                 OFS_CTL_DEV
                 AVD_IDX
                 OFS_IDX
                 OKS_IDX
                 OPT_CHR
                 USM_FAIL
                 USM_SUCCESS
                 USM_SUPPORTED
                 USM_NOT_SUPPORTED
                 USM_REBOOT_RECOMMENDED
                 USM_TRANSIENT_FAIL
                 lib_check_nfs_path
                 lib_install_nfs_path
                 lib_uninstall_nfs_path
                 );

use Config;
my ($OSNAME) = $Config{osname};
chomp($OSNAME);
my ($UNAME_A) = `uname -a`;
chomp($UNAME_A);

# return/exit codes
#
# USM_TRANSIENT_FAILures are those that can be easily filed by the admin.
# In the case of "acfsroot install", the admin could fix the error and then
# resume a grid install, for example, from the checkpoint.
use constant USM_SUCCESS            => 0;
use constant USM_FAIL               => 1;
use constant USM_NOT_SUPPORTED      => 2;
use constant USM_REBOOT_RECOMMENDED => 3;
use constant USM_TRANSIENT_FAIL     => 1; # failures that can be easily fixed
use constant USM_SUPPORTED          => 5;

use constant OPT_CHR => "-";            # Linux/Unix option character

use constant AVD_DIR     => "/dev/asm";         # created when AVD loaded
use constant AVD_CTL_DEV => "/dev/asm/.asm_ctl_spec"; # created when AVD loaded
use constant OFS_CTL_DEV => "/dev/ofsctl";      # created when OFS loaded

use constant AVD_IDX => 0;                      # index into driver_ccomponents
use constant OKS_IDX => 1;                      # index into driver_ccomponents
use constant OFS_IDX => 2;                      # index into driver_ccomponents

# On Windows, in the CRS environment, this 
# redirection does not work.  It fails with 
# "cannot open file descriptor" or "cannot open pipe NOWAIT".
our ($REDIRECT) = "2>&1";

my ($CACHED_INSTALLED_DRIVERS);               # saved find /lib/modules output
my ($CACHED_LOADED_DRIVERS);                  # saved find lsmod output

our ($ACFSUTIL) = "/sbin/acfsutil";           # Linux
$ACFSUTIL       = "/sbin/acfsutil" if ($OSNAME eq "aix");
$ACFSUTIL       = "/usr/lib/fs/acfs/acfsutil" if ($OSNAME eq "solaris");

our ($ADVMUTIL) = "/sbin/advmutil";

# /sbin/fuser     ... RH Enterprise Linux
# /bin/fuser      ... Suse
# /usr/sbin/fuser ... AIX, HPUX, Solaris
# /usr/sbin/lsof  ... RH Enterprise Linux
# /sbin/modprobe  ... RH Enterprise Linux
my $ORACLE_HOME = $ENV{ORACLE_HOME};
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin:" . $ORACLE_HOME;

if (! -e $ACFSUTIL)
{
  # Not installed yet.
  $ACFSUTIL = "$ORACLE_HOME/usm/install/cmds/bin/acfsutil";
}

our ($TMPDIR) = "/tmp";

#
# HANFS Constants
#

use constant FALSE        => 0;
use constant TRUE         => 1;

my ($hostname)    = $ENV{HOSTNAME};
my ($SBIN_DIR)    = "/sbin";
my ($USR_SBIN_DIR)= "/usr/sbin";
my ($MKFS)   = "$SBIN_DIR/mkfs";
my ($MKFS_ACFS) = "$SBIN_DIR/mkfs.acfs";


# lib_osds_am_root
#
# verify root access
#
sub lib_osds_am_root
{
  if ($>) # get euid
  {
    # not zero (root)
    # If this is AIX, let the grid user in.
    if ($OSNAME eq "aix ")
    {
      my $tusr=`/usr/bin/grep ORACLE_OWNER $ORACLE_HOME/crs/install/crsconfig_params|/usr/bin/cut -f2 -d '='`;
      my $me=`/bin/id -un`;
      $tusr =~ s/\s+$//;
      $me =~ s/\s+$//;
      if ($me eq $tusr)
      {
        return 1;
      } else
      {
        return 0;
      }
    }

    return 0;
  }

  return 1;
} # end lib_osds_am_root

# lib_osds_mount
#
# Mount the specified file system
#
sub lib_osds_mount
{
  my ($device, $mount_point, $options) = @_;
  my ($asmadmin);
  my ($result);
  my ($fs_switch);

  if ($OSNAME eq "linux")
  {
    $fs_switch = "-t";
  }
  elsif ($OSNAME eq "solaris")
  {
    $fs_switch = "-F";
  }
  elsif ($OSNAME eq "aix")
  {
    $fs_switch = "-v";
  }
  else
  {
    # should never get here if lib_osds_usm_supported() did its job.
    lib_error_print(9125,
                    "ADVM/ACFS is not supported on this OS: '%s'", $OSNAME);
    return USM_FAIL;
  }

  if ($options ne "none")
  {
    $options = "-o " . $options;
  }
  else
  {
    $options = "";
  }
  $result = system("mount $fs_switch acfs $options $device $mount_point");
  if ($result)
  {
    return USM_FAIL;
  }

  # set mount point attributes if the defaults have not been changed.
  my ($dev,$ino,$mode,$nlink,$uid,$gid) = stat($mount_point);
  if (($mode == 040755) && ($uid == 0) && ($gid == 0))
  {
    # The user has not changed the defaults.
    $asmadmin = acfslib::lib_get_asm_admin_name();
    system ("chmod 0770 $mount_point");
    system ("chgrp $asmadmin $mount_point");
  }

  # If the mount point is to be exported, do it here.
  # This will be a no-op if the mount point is not in /etc/exports
  acfslib::lib_osds_exportfs($mount_point);

  return USM_SUCCESS;
} # end lib_osds_mount

# lib_osds_mountpoint_descriptors
#
# called with action = 1 when the user calls "clean" for force any open file
# references to be cleared from a mount point to ensure that the unmount
# will succeed.
#
# called with action = 0 to print open references on the mount point.
#
sub lib_osds_mountpoint_descriptors
{
  my ($mountpoint, $action) = @_;
  my ($fuser) = "fuser";
  my ($have_fuser) = 1;
  my ($lsof) = "lsof";
  my ($have_lsof) = 1;
  my ($str);                       # temp work space
  my ($descriptor_list) = "";      # returned if action == 0       
  my ($pid) = "";
  my ($retval) = USM_SUCCESS;
  my ($busyCmd);                  
  my @CLEAN_DO_NOT_KILL_LIST = (
    '^/\S*/acfs_script.sh',
    '^asm_.*\+ASM\d$',
    '^\[asmError0]$',
    '^\[asmIntervalTime]$',
    '^\[asmShutdown]$',
    '^/\S*/crsd',
    '^/\S*/cssdmonitor',
    '^/\S*/diskmon',
    '^/\S*/evmlogger',
    '^/\S*/evmd',
    '^/\S*/gipcd',
    '^/\S*/gpnpd',
    '^/\S*/mdnsd',
    '^/\S*/ocssd',
    '^/\S*/octssd',
    '^/\S*/ohasd',
    '^/\S*/ologgerd',
    '^/\S*/ons',
    '^/\S*/oraagent',
    '^/\S*/orarootagent',
    '^/\S*/osysmond',
    '^/\S*/tnslsnr',
  );


  # All systems are "supposed" to have fuser (LSB_Core), but who really
  # knows.  Either fuser or lsof can do the job. Let's see what we have.
  open (WHICH, "which $fuser 2>&1 |");
  $str = <WHICH>;
  close (WHICH);
  if ($str =~ /no $fuser in/)
  {
    $have_fuser = 0;
  }

  open (WHICH, "which $lsof 2>&1 |");
  $str = <WHICH>;
  close (WHICH);
  if ($str =~ /no $lsof in/)
  {
    $have_lsof = 0;
  }

  # for debugging, uncomment the appropriate line below.
  # $have_fuser = 1; $have_lsof = 0;
  # $have_fuser = 0; $have_lsof = 1;

  if (($have_fuser == 0) && ($have_lsof == 0))
  {
    print "This system has neither fuser nor lsof in its command search path\n";
    if ($action)
    {
      return USM_FAIL;
    }
    else
    {
      return $descriptor_list;
    }
  }

  #
  #  Search for processes with open files on the target mount point
  #  using fuser.  If found, list or kill them.  fuser should be on
  #  most operating systems.
  #

  if ($have_fuser)
  {

    # If the mountpoint got unmounted before we run fuser, we could get all
    # pids on the system. To avoid this, we run lib_osds_is_mounted() after
    # fuser completes to ensure that the data is still valid. We used to call
    # lib_osds_is_mounted() and then fuser - which opened a race condition.
    #
    # If fuser gets an error, it will appear on a separate line so we need
    # to handle that. For example:
    #      Cannot stat file /proc/6318/fd/6: Stale NFS file handle
    #      /mnt1:               14134
    #
    # You won't believe this but on the line:
    #      /mnt1:               14134
    # "/mnt1: goes to STDERR and "14134" to STDOUT. So, we can't do 
    # something like "open (FUSER, "$fuser -c $mountpoint 2> /dev/null |");".

    if ($OSNAME eq "linux")
    {
      # Only linux has the -a flag to fuser.
      open (FUSER, "$fuser -ac $mountpoint 2>&1 |");
    }
    else
    {
      open (FUSER, "$fuser -c $mountpoint 2>&1 |");
    }

    my ($fuser_line);
    while ($fuser_line = <FUSER>)
    {
      my (@fuser_array) = split(/\s+/, $fuser_line);

      last if (! acfslib::lib_osds_is_mounted($mountpoint) );

      EACH_FUSER_PID: foreach $pid (@fuser_array)
      {
        if ($pid =~ /$mountpoint/)
        {
          # The list begins with "<mountpoint>:". Ignore that.
          next;
        }

        my ($first_char) = substr($pid, 0, 1);
        if ($first_char =~ /\D/)
        {
          # non-digit first character
          # e.g., Cannot stat file /proc/6318/fd/6: Stale NFS file handle
          next;
        }

        # strip non digit characters from (base 10) pid.
        #
        # fuser output: <mountpoint>: [<pid>[n]>
        #    where [n] is one or more of the following:
        # 'c' current directory symbol
        # 'e' executable running symbol
        # 'f' open file symbol
        # 'F' open for write symbol
        # 'm' mmap'ed file symbol
        # 'n' holding non-blocking lock (Solaris)  
        # 'o' open file (Solaris)  
        # 'r' root directory symbol
        # 's' shared library file (AIX)
        # 't' text file (Solaris)
        # 'y' controlling terminal (Solaris)
        $pid =~ s/[[:alpha:]]+//g;

        # Does the PID contain non-digits?
        if ($pid =~ /\D/)
        {
          # This is not a PID. Likely an error from fuser.
          next;
        }

        #
        #  This process has a file open on the mount point.  Get it's
        #  name and log it, but not in alert.log.
        #

        $busyCmd = qx(ps -p $pid -o args=);
        next EACH_FUSER_PID if ( ! $busyCmd );
        chomp $busyCmd;

        # Not sending to alert.log, it just recives the list of PIDs
        # from acfsregistrymount.pl
        acfslib::lib_inform_print_noalert(9153,
          "Program '%s' with OS process ID '%s' is using mount point '%s'.",
          $busyCmd, $pid, $mountpoint );

        #
        #  Perform the requested action: list or kill.
        #

        if ($action == 1)    # clean
        {
          #
          # If process is our "safe list" don't kill it.
          #
          foreach my $safe ( @CLEAN_DO_NOT_KILL_LIST )
          {
            if ( $busyCmd =~ /$safe/ )
            {
              # This isn't going to end well ...
              acfslib::lib_inform_print(9152,
                "Program '%s' with OS process ID '%s' will not be terminated.",
                $busyCmd, $pid );
              $retval = USM_FAIL;
              next EACH_FUSER_PID;
            }
          }

          acfslib::lib_inform_print(9126,
            "Attempting to terminate the program '%s' with OS process ID '%s'.",
            $busyCmd, $pid);
          my $killRet = system("kill -9 $pid");
          if ( $killRet )
          {
            # The kill command failed. Either the process is already gone
            # or we can't kill it.
            my (%info) = lib_get_pid_info($pid);
            if (defined($info{'PID'}))
            {
              # The process is still alive.
              $retval = USM_FAIL;
              acfslib::lib_inform_print(9136,
                                       "PID %s could not be killed.", $pid);
              report_pid_info(%info);
            }
          }
        }
        else
        {
          $descriptor_list .= "$pid ";
        }
      }
    }
    close (FUSER);
  } # end if have_fuser

  #
  #  Search for processes with open files on the target mount point
  #  using lsof.  If found, list or kill them.  lsof isn't found on some
  #  operating system installations.
  #

  if ($have_lsof)
  {

    my $skipToNextProcess = 0;

    #
    # lsof may block. We may want to use the -b option to
    # avoid kernel calls that might block which can cause hangs
    # for several minutes. 
    #
    # Use '-F pn' option to get parsable output (pid, file name)
    #

    open (LSOF, "$lsof -b -F pn 2>/dev/null |");
    LSOF_LINE: while($str = <LSOF>)
    {

      # If pid line, save it and continue.
      if ( $str =~ /^p\d+$/ )
      {
        $pid = $str;
        $pid =~ s/^p//;
        chomp $pid;
        $skipToNextProcess = 0;
        next LSOF_LINE;
      }

      #
      # We should be looking at a file name line. ( "n/abc/def/ghi" );
      #
      # If we've already performed the desired action against the
      # process, skip to the next process.
      #

      next LSOF_LINE if ( $skipToNextProcess == 1 );

      # Next if this file is not on the target mount point.
      next LSOF_LINE if ( $str !~ /^n$mountpoint/ );

      #
      # This process has a file open on the mount point.  Get it's name
      # and log it.
      #

      $busyCmd = qx(ps -p $pid -o args=);
      if ( ! $busyCmd )
      {
        # Maybe it exited between the time lsof found it and now.
        $skipToNextProcess = 1;
        next  LSOF_LINE;
      }
      chomp $busyCmd;
      acfslib::lib_inform_print_noalert(9153,
        "Program '%s' with OS process ID '%s' is using mount point '%s'.",
        $busyCmd, $pid, $mountpoint );

      #
      #  Perform the requested action: list or kill.
      #

      if ($action == 1)    # clean
      {
        #
        # Don't kill this process if it's on our "safe list". 
        #
        foreach my $safe ( @CLEAN_DO_NOT_KILL_LIST )
        {
          if ( $busyCmd =~ /$safe/ )
          {
            # This isn't going to end well ...
            acfslib::lib_inform_print(9152,
              "Program '%s' with OS process ID '%s' will not be terminated.",
              $busyCmd, $pid );
            $skipToNextProcess = 1;
            $retval = USM_FAIL;
            next LSOF_LINE;
          }
        }

        acfslib::lib_inform_print(9126,
           "Attempting to terminate the program '%s' " .
           "with OS process ID '%s'.", $busyCmd, $pid);
        my $killRet = system("kill -9 $pid");
        if ( $killRet )
        {
          # The kill command failed. Either the process is already gone
          # or we can't kill it.
          my (%info) = lib_get_pid_info($pid);
          if (defined($info{'PID'}))
          {
            # The process is still alive.
            $retval = USM_FAIL;
            acfslib::lib_inform_print(9136,
                                     "PID %s could not be killed.", $pid);
            report_pid_info(%info);
          }
        }
      }
      else
      {
        $descriptor_list .= "$pid ";
      }

      #
      # A process can have multiple files open on the target mount
      # point. We only want to list or kill the process once.
      #

      $skipToNextProcess = 1;

    }
    close (LSOF);
  }

  # Split the string by spaces.
  my @words= split / /, $descriptor_list;
  # New hash.
  my %newwords;
  # Put the values in the string as the hash keys, assign a value of 1.
  # This prevents duplicates as they hash to the same value.
  for (@words) { $newwords{$_}=1 }
  # Join the keys back into a list.  This effectively removes duplicate
  # process id's in the string.
  $descriptor_list = join ' ', keys(%newwords);

  if ($action)
  {
    return $retval;
  }
  else
  {
    return $descriptor_list;
  }
} # end lib_osds_ mountpoint_descriptors

# lib_osds_control_devices_accessible
#
# We test the USM control device accessibility by opening them
# Note that this will work on all Linux/Unix - but not Windows
#
# return true (1) or false (0)
#
sub lib_osds_control_devices_accessible
{
  my ($ret) = 1;  # assume true

  open AVD, "<" . AVD_CTL_DEV or $ret = 0;
  if ($ret)
  {
    close AVD;
    # AVD open worked, now try OFS
    open OFS, "<" . OFS_CTL_DEV or $ret = 0;
    if ($ret)
    {
      # we can talk to both AVD and OFS control devices - success
      close OFS;
    }
  }
  return $ret;
} # end lib_osds_control_devices_accessible

# lib_osds_create_mount_point
#
# Create the mount point directory.
#
sub lib_osds_create_mount_point
{
  my $mount_point = shift;

  acfslib::lib_inform_print(9255, "Creating '%s' mount point.", $mount_point);
  eval File::Path::mkpath($mount_point);
  if ($@)
  {
    acfslib::lib_error_print(9256, "Failed to create mountpoint '%s'.", $@);
    return USM_FAIL;
  }
  return USM_SUCCESS;
}

# lib_osds_device_from_mountpoint
#
# return the device name given a mountpoint
#
sub lib_osds_device_from_mountpoint
{
  my ($mountpoint) = @_;
  my ($device) = "";
  my ($line);

  open (MOUNT, "mount |");
  while ($line = <MOUNT>)
  {
    my ($dev, $mntpt) = get_dev_mntpt_from_mount_line($line);    

    if ($mntpt eq $mountpoint)
    {
      $device = $dev;
      last;
    }
  }
  close (MOUNT);

  return $device;
} # end lib_osds_device_from_mountpoint

# lib_osds_get_advm_mounts
#
# return an doubly dimensioned array of devices and mountpoints
# of all currently mounted OFS file systems
# array element[0] is the device and array element[1] is the mountpoint
#
sub lib_osds_get_advm_mounts
{
  my (@array);
  my ($i) = 0;
  my ($line);

  open (MOUNT, "mount |");
  while ($line = <MOUNT>)
  {
    my ($device, $mount_point) = get_dev_mntpt_from_mount_line($line);    
    if ($device =~ /^\/dev\/asm\//)
    {
      push @{$array[$i]}, $device, $mount_point;
      $i += 1;
    }
  }
  close(MOUNT);
  return \@array;
} #end lib_osds_get_advm_mounts

# osds_get_asm_user
#
# Get the user name and group id that we use to connect to ASM
#
sub lib_osds_get_asm_user
{
  my (@out_array);
  my ($group);
  my ($line);

  if ($OSNAME eq "solaris")
  { 
    open(PS, "ps -z `/bin/zonename` -o user,gid,comm | grep asm_pmon | grep -v grep |");
  }
  elsif ($OSNAME eq "linux")
  {
    open(PS, "ps -eo user,gid,cmd | grep asm_pmon | grep -v grep |");
  }
  elsif ($OSNAME eq "aix")
  {
    # On AIX the asm_pmon process shows up as "oracle" whem using -o comm.
    # When using -fe the command sometimes does not return a string,
    # using -eo user,gid,args works
    open(PS, "ps -eo user,gid,args | grep asm_pmon | grep -v grep |") or do
    {
      acfslib::lib_error_print(9999, 
                               "Unable to get the output for ps command: $! "); 
      close(PS);
      return (@out_array);
    }
  }
  while ($line = <PS>)
  {
    # Element 0 is the ASM user name, element 1 is the ASM gid.
    $line =~ s/^\s+//; # Remove leading whitespace from the line
    @out_array = split(/\s+/, $line);
    last;
  }
  close(PS);

  return (@out_array);
} # end lib_osds_get_asm_user

# lib_osds_run_as_user
#
# For now, ASM connections require euid of the ASM user - bug 6900692.
#
sub lib_osds_run_as_user
{
  my ($user_name, $cmd) = @_;
  my ($return_code);
  my (@args);

  ##### special case for usm_dbhome
  ##### usm_dbhome can be called as non-root - in which case we simmply pass
  ##### the command on without changing the user ID
  my ($euid) = $>;
  my ($asm_user) = lib_osds_get_asm_user();

  if (( $euid == 0) || ($user_name ne $asm_user))
  {
    @args = ('su', $user_name, '-c', "$cmd");
  }
  else
  {
    @args = $cmd;
  }
  $return_code = system(@args);

  return $return_code;

  #### once we drop usm_dbhome, the function can revert back to the below #####

  @args = ('su', $user_name, '-c', "$cmd");
  $return_code = system(@args);

  return $return_code;
}

# lib_osds_unmount
#
# unmount the specified file system
#
sub lib_osds_unmount
{
  my ($mountpoint) = @_;
  my ($result);
  my (@nfs_export_list) = acfslib::lib_osds_remove_exports($mountpoint);

  $result = system("umount $mountpoint 2> /dev/null");
  if ($result)
  {
    # The unmount failed. Restore previously existing NFS exports, if any.
    acfslib::lib_osds_restore_exports(@nfs_export_list);
    return USM_FAIL;
  }  
  return USM_SUCCESS;
} # end lib_osds_unmount


# lib_osds_usm_supported.
#
# The fact that we got here means that there is some support for
# this platform. However, perhaps not all releases are supported.
# We make that determination here.
#
# return true or false
#
sub lib_osds_usm_supported
{
  my ($arch) = $Config{archname};   # Machine architecture - e.g., i386
  chomp($arch);

  $osds_acfslib::configuration{os} = $OSNAME;
  $osds_acfslib::configuration{arch} = $arch;

  # For the time being, of all the "Unix" versions,
  # only Linux, AIX, and Solaris are supported
  if (!(($OSNAME eq "linux") || ($OSNAME eq "aix") || ($OSNAME eq "solaris")))
  {
    # TODO for other operating systems
    acfslib::lib_error_print(9125,
                            "ADVM/ACFS is not supported on this OS: '%s'.",
                            $OSNAME);
    return 0;
  }

  $osds_acfslib::configuration{unamea} = $UNAME_A;

  if (!(($arch =~ /^i686/) || ($arch =~ /^x86_64/)
        || ($arch =~ /^aix-thread-multi-64all/)
        || ($arch =~ /^i86pc/)
        || ($arch =~ /^sun4-solaris/)))
  {
    # TODO for other architectures
    acfslib::lib_error_print(9120,
                     "The '%s' machine architecture is not supported.", $arch);
    return 0;
  }

  # $type is "EL5", for example
  my ($type) = osds_acfslib::lib_osds_get_os_type(@_);
  if ($type =~ /not supported/)
  {
    return 0;
  }
  return 1;
} # end lib_osds_usm_supported

# lib_osds_verify_usm_devices
#
# Verify that the USM drivers are loaded and running by checking the
# existence of the device control files for the drivers.
#
sub lib_osds_verify_usm_devices
{
  my ($asmadmin) = acfslib::lib_get_asm_admin_name();

  # Make sure that the proper /dev files get created
  my ($found) = 0;
  my ($max_wait_seconds) = 60;

  my ($device) = AVD_CTL_DEV;
  acfslib::lib_inform_print(9156, "Detecting control device '%s'.", $device);

  while (($max_wait_seconds > 0) && (!$found))
  {
    if (! -e $device)
    {
      sleep 1;
      $max_wait_seconds -= 1;
    }
    else
    {
      $found = 1;
      # Solaris - Control device permissions come from the original add_drv
      # command, which is called with a '-m' switch to set the permissions for 
      #  subsequent devfsadm calls to load the driver.
      # Linux - control device permissions come from a udev rule file.
      # AIX - control device permissions come from cfgadvmctl\cfgacfsctl and
      #   the mknod command there.
      open LSDIR, "ls -Lla $device |";
      while (<LSDIR>)
      {
        acfslib::lib_trace( 9999, "VUD: control device permissions: \n\t". $_);
      }                                         
      close(LSDIR);
    }
  }

  if (!$found)
  {
    acfslib::lib_error_print(9121, "Failed to detect control device '%s'.", $device);
    return USM_FAIL;
  }
  else
  {
    # The ADVM driver creates /dev/asm/* and not (directly) /dev/asm.
    my $rcode;
    $rcode = system("chgrp $asmadmin " . AVD_DIR);
    if($rcode > 0)
    {
      acfslib::lib_error_print(10624, "User '%s' is not member of the group '%s'.","root",$asmadmin);
      return USM_FAIL;
    }  
 
    system("chmod 0770 " . AVD_DIR);
  }

  $max_wait_seconds = 60;
  $found = 0;
  $device = OFS_CTL_DEV;
  acfslib::lib_inform_print(9156, "Detecting control device '%s'.", $device);

  while (($max_wait_seconds > 0) && (!$found))
  {
    if (! -e $device)
    {
      sleep 1;
      $max_wait_seconds -= 1;
    }
    else
    {
      $found = 1;
      # Solaris - Control device permissions come from the original add_drv
      # command, which is called with a '-m' switch to set the permissions for 
      #  subsequent devfsadm calls to load the driver.
      # Linux - control device permissions come from a udev rule file.
      # AIX - control device permissions come from cfgadvmctl\cfgacfsctl and
      #   the mknod command there.
      open LSDIR, "ls -Lla $device |";
      while (<LSDIR>)
      {
        acfslib::lib_trace( 9999, "VUD: control device permissions: \n\t". $_);
      }
      close (LSDIR);
    }
  }

  if (!$found)
  {
     acfslib::lib_error_print(9121, "Failed to detect control device '%s'.", $device);
    return USM_FAIL;
  }
  # In some cases, udev doesn't apply the rules.
  # A touch of the devices will change the permissions to match udev rules.
  my $ctl_path = AVD_DIR . "/.asm*";
  system("touch $ctl_path");

  lib_osds_trigger_udev_rules($ctl_path, $asmadmin);
  # We only warn if persistent logging can't be started.
  `$ACFSUTIL plogconfig -d`;
  if ($?)
  {
     acfslib::lib_inform_print(9225, "Failed to start OKS persistent logging.");
  }                
} #end lib_osds_verify_usm_devices


###### static routines only after this point ##########

# lib_get_pid_info
#
# Collect and return information, for a given PID.
# Returns an undefined %pid_hash if the PID is not found.
#
# Linux, Solaris, and AIX all support ps -fe and output in the same format.
# If a future supported Unix does not, we'll have to make changes.
#
sub lib_get_pid_info
{
  my ($pid) = @_;    # target PID
  my ($ps_info);     # output line from the ps(1) command
  my (%pid_hash);    # return: hashed output from the ps(1) command for $pid

  open PS, "/bin/ps -fe 2>&1 |"
          or warn ("Failed to run '/bin/ps -fe': $!"), return %pid_hash;
  # ps -fe format is:
  # UID PID PPID C STIME TTY TIME CMD
  #   0   1   2  3   4    5   6   7 
  while ($ps_info = <PS>)
  {
    my (@array) = split /\s+/, $ps_info;
    %pid_hash = (
      USER    => $array[0],
      PID     => $array[1],
      PPID    => $array[2],
      C       => $array[3],
      STIME   => $array[4],
      TTY     => $array[5],
      TIME    => $array[6],
      CMD     => $array[7],
    );

    if ($pid eq $pid_hash{'PID'})
    {
      close(PS);
      return %pid_hash;
    }
  }

  # Target PID not found.
  close (PS);
  undef %pid_hash;
  return %pid_hash;
} # end lib_get_pid_info

# 9999 messages are not formatted - WYSIWYG.
#
sub report_pid_info
{
  my (%info) = @_;
  acfslib::lib_inform_print(9141, "         COMMAND %s", $info{'CMD'});
  acfslib::lib_inform_print(9143, "         USER %s", $info{'USER'});
  acfslib::lib_inform_print(9144, "         CPU_TIME %s", $info{'TIME'});
  acfslib::lib_inform_print(9142, "         STATUS %s", $info{'C'});
}

# get_dev_mntpt_from_mount_line
#
# Get the device and mountpoint from a mount command output line.
#
# Different Unixs have different mount command output formats.
#  mount | grep mnt1
#    /dev/asm/foo-123 on /mnt1 ...           Linux
#    /dev/asm/foo-123 /mnt1    ...           AIX
#    /mnt1 on /dev/asm/foo-123 ...           Solaris
#
sub get_dev_mntpt_from_mount_line
{
  my ($line) = @_;
  my ($device, $on, $mountpoint);

  if ($OSNAME eq "linux")
  {
    # /dev/asm/xxx_yyy on /oracle_base/ofsdata/foo type ofs (rw)
    ($device, $on, $mountpoint) = split(/ /, $line);
  }
  elsif ($OSNAME eq "solaris")
  {
   # /dev/asm/xxx_yyy on /oracle_base/ofsdata/foo <options> on <date>
    ($mountpoint, $on, $device) = split(/ /, $line);
  }
  elsif ($OSNAME eq "aix")
  {
    # <node> /dev/asm/xxx_yyy /oracle_base/ofsdata/foo <type> <date> <options>
    my ($node);

    if ($line =~ /^ /)
    {
      # if the first char of the line is blank, we don't have a node.
      $line =~ s/^\s+//;                   # remove leading spaces
      $line =~ s/\s+/ /g;                  # remove extra spaces between fields
      ($device, $mountpoint) = split(/ /, $line);
    }
    else
    {
      $line =~ s/\s+/ /g;                  # remove spaces between fields
      ($node, $device, $mountpoint) = split(/ /, $line);
    }
  }
  else
  {
    # should never get here
    lib_error_print(9149,
             "unable to determine device mount status - unsupported OS name '%s'",
             $OSNAME);
  }

  return ($device, $mountpoint);
} # end get_dev_mntpt_from_mount_line

# This is a Windows function
sub lib_osds_get_drive_info
{
    return 0;
}

# lib_osds_is_abs_path
sub lib_osds_is_abs_path
{
  my $path = shift;
  if ( $path =~ /^\// )
  {
    return 1;
  }
  return 0;
}

#Function used to avoid comparing kernels as strings
#Just like strcmp in C, if a data, in this case numbers, returns the difference
#between the first non-equal elements, being positive if the first argument's
#is greater than the second's.
#Perl's stringwise operators appear to compare each character of the string to
#see if it has a higher ascii value than the other.  So, "76" < "8".  "101 <
#800".  "501" > "4900".  Thus, we need to compare each value using a numeric
#compare.

sub lib_osds_krncmp {
  my ($kver_a, $kver_b, $splitter) = @_;
  
  #We use the same functions for dotted and dashed kernel values
  if(!defined($splitter)){
    $splitter = "\\.";
  }
  my (@splitted_a) = split(/$splitter/, $kver_a);
  my (@splitted_b) = split(/$splitter/, $kver_b);

  #Let's compare each part, assuming that significance decreases to the right.
  for my $i (0 .. $#splitted_a) {
    if (scalar @splitted_b <= $i) 
    {
      #We have more elements in a.
      last;
    }
    if($splitted_a[$i] =~ /-/ || $splitted_b[$i] =~ /-/) {
      # Values have dashes 
      my $dashed = lib_osds_krncmp($splitted_a[$i], $splitted_b[$i], "-");
      if($dashed != 0) {
        return $dashed
      }
    }
    else {
      #String Values
      if (($splitted_a[$i] =~ /\D/) &&
          ($splitted_b[$i] =~ /\D/))
      {
        return $splitted_a[$i] cmp $splitted_b[$i];
      }
      #A is string compares vs 0
      elsif ($splitted_a[$i] =~ /\D/)
      {
        return 0 - ($splitted_b[$i]+0);
      }
      #B is string compares vs 0
      elsif ($splitted_b[$i] =~ /\D/)
      {
        return ($splitted_a[$i]+0);
      }
      # Numeric Values
      # +0 forces it to treat the value as a number.
      elsif (($splitted_a[$i]+0) - ($splitted_b[$i]+0) != 0){
        return ($splitted_a[$i]+0) - ($splitted_b[$i]+0);
      }
    }
  }

  #If we checked all elements and they're equal or the 2nd kernel has more
  #values then it means a bigger kernel version.
  return scalar @splitted_a - scalar @splitted_b;
}

# Returns 1 if both files are the same, either through a hard link or through 
# a symbolic link path. Linux/Unix specific. 
use File::stat;
sub lib_osds_are_same_file
{
  my ($source, $target) = @_;
  my ($s_sb, $t_sb);

  if ( -e $source && -e $target ){
    $t_sb = stat($target);
    $s_sb = stat($source);

    if ( $t_sb->dev==$s_sb->dev && $t_sb->ino==$s_sb->ino ){
      return 1;
    }
  }

  return 0;
}

#After we touch the devices, we'll check for the right permissions
#In Linux, permissions come from a udev rule file
#If the permissions are not right, we'll try to reload the rules
sub lib_osds_trigger_udev_rules
{
  my ($ctl_path, $asmadmin) = @_;
  if ($OSNAME eq "linux")
  {
    my $max_wait_seconds = 10;
    my $found = 0;
    acfslib::lib_trace( 9999, "Detecting permissions for devices using udev rules.");
    while (($max_wait_seconds > 0) && (!$found))
    {
      my ($ctl_line);
      open LSCTLDIR, "ls -Lla $ctl_path |";
      while ($ctl_line = <LSCTLDIR>)
      {
        chomp($ctl_line);
        #Line has to include the permissions "brwxrwx---" and asmadminname
        if ((index($ctl_line, "brwxrwx---") < 0) ||
            (index($ctl_line, $asmadmin) < 0))
        {
          #Udev is not working. Reload rules
          $found = 0;
          acfslib::lib_trace( 9999, "Wrong Permissions: \t". $ctl_line);
          # For restarting udev rules, we'll run the following commands
          my $type = osds_acfslib::lib_osds_get_os_type();
          if ($type =~ /EL5/)
          {
            system("udevcontrol start_exec_queue");
            system("udevcontrol reload_rules");
            system("udevtrigger");
          }
          else
          {
            system("udevadm control --start-exec-queue");
            if ($type =~ /SLES12/)
            {
              system("udevadm control --reload");
            }
            else
            {
              system("udevadm control --reload-rules");
            }
            system("udevadm trigger");
          }
          sleep 1;
          $max_wait_seconds -= 1;
          last;
        }
        else
        {
          $found = 1;
        }
      }
      close (LSCTLDIR);                                
    }         
  }    
}

#
# HANFS Unix Locking
#

sub lib_osds_get_nfs_path
{
  if ($OSNAME eq "linux")
  {
     return "/var/lib/nfs";
  }
  elsif ($OSNAME eq "solaris")
  {
     return "/var/share/nfs";
  }
  #TODO: AIX Support
  acfslib::lib_error_print(9615, "NFS_PATH is not supported.");
  return "";
}

# We need to check and create the following directories for HANFS
# Directories only for Linux
sub lib_osds_get_nfs_directories
{
  if ($OSNAME eq "linux")
  {
    return ("v4root","v4recovery","rpc_pipefs");
  }
  return ();
}

#lib_check_nfs_path
#
# Check if $NFS_PATH is an ACFS File Sytem.
#

sub lib_check_nfs_path
{
  my ($result) = 0;
  my $NFS_PATH = lib_osds_get_nfs_path();
  if (acfslib::lib_is_mounted($NFS_PATH))
  {
    my ($cmd_out);
    my $cmd = "$ACFSUTIL info fs " . OPT_CHR . "o ismountpoint " . $NFS_PATH;
    $cmd_out= `$cmd`;
    if (!defined($cmd_out))
    {
      $cmd_out="<No Error Text Returned>";
    }
    if ($? == 0)
    {
      # Execution successful, cmd_out will hold 0 or 1.
      if ($cmd_out != 1 )
      {
          $result = 0;
      }
      else
      {   #mount is available.
          $result = 1;
      }
    }
    else
    {
        $result = 0;
    }
  }

  return $result;
} #end lib_check_nfs_path

# lib_install_nfs_path
#
# Install HANFS Locking support
#
sub lib_install_nfs_path
{
  my ($force, $volume)  = (@_);
  my ($result) = USM_FAIL;
  my ($rc) = 0;
  my ($output) = "";
  my $isNFSPathMounted = FALSE;
  my $isNFSPathSymlinked = FALSE;
  my $isVolumeMounted = FALSE;
  my $NFS_PATH = lib_osds_get_nfs_path();
  my $zip_file_name = "/tmp/hanfs.nfs.zip";
  my $cmd = "";

  if (acfslib::lib_is_mounted($NFS_PATH))
  {
    $isNFSPathMounted = TRUE;
  }
  if (-l $NFS_PATH)
  {
    $isNFSPathSymlinked = TRUE;
  }

  if (acfslib::lib_is_mounted($volume))
  {
    $isVolumeMounted = TRUE;
  }

  # Ensure that an NFS resource does not already exist. If it does, either 
  #   # '-addnode' must be run, or NFSv4 locking is already configured in this cluster.
  my ($volumeResourceDependency, $nfsVolume, $isrunningNFS, $existsNFS) = getNFSResourceStatus();
  if ($existsNFS == -1)
  {
    return USM_FAIL;
  }

  # Check for filesystem resource  
  my $isrunningFS = 0;
  my $existsFS = 0;
  open SRVCTL_FS, "$ORACLE_HOME/bin/srvctl status filesystem -d $volume |";
  while (<SRVCTL_FS>)
  {
    if (/PRKO-2012/)
    {
      # "No Oracle Clusterware components configured."
      acfslib::lib_error_print_noalert(5219,
                              "Cannot proceed because CRS stack is not up.");
      $result = USM_FAIL;
      last;
    }
    elsif (/PRCA-1070/)
    {
      # "Could not find resource filesystem"
      $result = USM_SUCCESS;
      last;
    }
    elsif (/ADVM-03180/)
    {
      #  Unable to obtain ASM volume device information for volume
      $result = USM_SUCCESS;
      last;
    }
    elsif (/is mounted on nodes/)
    {
      $existsFS = 1;
      $isrunningFS = 1;
      $result = USM_SUCCESS;
    }
    elsif (/is not mounted/)
    {
      $existsFS = 1;
      $isrunningFS = 0;
      $result = USM_SUCCESS;
    }
  }
  close SRVCTL_FS;

  if ($result == USM_FAIL)
  {
    return $result;
  }
  elsif (!$force)
  {
    # If !force, warn the user that the following actions will be taken:
    #    Modification of NFS OS Startup scripts so that NFS does not 
    #    automatically start.
    #    Modification of $NFS_PATH to be on stable storage.
    #    Format of the volume.
    #    Creation of a file system resource.
    #    Short message regarding ongoing management of NFS by the CRS stack. 
    #    (Print a list of appropriate srvctl commands to use.)
    #
    acfslib::lib_inform_print_noalert(9603, "The script will do the following actions:");
    acfslib::lib_inform_print_noalert(9604,
    " - Update the operating system startup procedure so that NFS does not automatically start.");
    acfslib::lib_inform_print_noalert(9605,
    "   Management of the NFS daemons will be moved to Oracle Clusterware.");
    acfslib::lib_inform_print_noalert(9606, " - Format the volume: %s.", $volume);
    acfslib::lib_inform_print_noalert(9607,
    " - Create an ACFS resource for the file system.");
    acfslib::lib_inform_print_noalert(9608,
    " - Mount the ACFS file system on '%s'.", $NFS_PATH);
    acfslib::lib_inform_print_noalert(9609, " Continue the installation? [1=yes,2=no]:");
    # Request confirmation of intent to continue, unless ???force option 
    # is provided.
    my $answer = <STDIN>;
    chomp($answer);
    if ($answer == 1)
    {
      $result = USM_SUCCESS;
    }
    else
    {
      return USM_FAIL;
    }
  }

  #Before starting with the installation, will do a couple of validations.
  my $isFirstNode = 0;
  if (!$existsNFS && !$existsFS)
  {
    $isFirstNode = 1;
  }
  elsif ($existsNFS && $existsFS)
  {
    $isFirstNode = 0;
  }
  elsif ($existsNFS && !$existsFS)
  {
    #This is weird but possible, we'll need to create the filesystem resource
  }
  elsif (!$existsNFS && $existsFS)
  {
    # Possible but file system should not be used for any other application
    # We'll send an error.
    acfslib::lib_error_print_noalert( 9610,
    "Error - the ACFS resource for the specified file system is already in use.");
    return USM_FAIL;
  }

  # (First node) Ensure that an ACFS file system is not mounted on or 
  #symlinked from $NFS_PATH.
  if ($isFirstNode && ($isNFSPathMounted || $isNFSPathSymlinked))
  {
    acfslib::lib_error_print_noalert(9602,
      "Installation cannot proceed: path '%s' is mounted or it is a symlink.",
      $NFS_PATH);
    return USM_FAIL;
  }
  # (First node) Ensure that the volume is not mounted.
  if ($isFirstNode && $isVolumeMounted)
  {
    acfslib::lib_error_print_noalert(2125, "volume '%s' is already in use", $volume);
    return USM_FAIL;
  }

  # (All nodes) Stop the OS NFS daemons.
  acfslib::lib_inform_print_noalert(9612, "Stopping NFS Service.");
  if (!actionNFSServer("stop"))
  {
    $result = USM_FAIL;
  }

  # Before to mount, create a zip file in /tmp/ including all $NFS_PATH files.
  # We want to mantain the same structure that the customer has.
  if ($result != USM_FAIL)
  {
    my $current_directory = Cwd::cwd();
    my $ziptool = "/usr/bin/zip";
  
    if (-e $zip_file_name)
    {
      unlink($zip_file_name);
    }
    
    #Check the zip tool
    if (!-e $ziptool)
    {
       # If the absolute path doesn't exist, we'll try with the rleative command
       $ziptool = "zip";
    }

    chdir($NFS_PATH);
    $cmd = "$ziptool -qr $zip_file_name *";
    $rc = system($cmd);
    acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', " .
                                       "output = '%s'", $cmd, ($? >> 8));
    chdir($current_directory);
  }

  my ($disk_group) = "";
  my ($vol) = "";
  # (All nodes) Verify the volume device is enabled.
  open ADVMUTIL, "$ADVMUTIL volinfo $volume |";
  if ($?)
  {
    # It's not enabled or it's not an ADVM Volume
    acfslib::lib_error_print_noalert( 520, "%s is not an ADVM volume.", $volume);
    $result = USM_FAIL;
  }
  else
  {
    while (<ADVMUTIL>)
    {
      if (/Disk Group/)
      {
        $disk_group = ($_);
        chomp($disk_group);
        $disk_group = acfslib::trim(substr($disk_group, index($disk_group, ':') + 1));
        $disk_group =~ s/^\s+//;
      }
      elsif (/Volume/)
      {
        $vol = ($_);
        chomp($vol);
        $vol = acfslib::trim(substr($vol, index($vol, ':') + 1));
        $vol =~ s/^\s+//;
      }
    }
  }

  # If NFS Resource exists, we need to check that 
  # they are using the same Volume Resource Dependency
  if ($existsNFS &&
     (lc($volumeResourceDependency) ne lc("ora.$disk_group.$vol.acfs")))
  {
    # If the NFS Resource exists, we'll use the dependency from there
    ($volume, $isrunningFS, $existsFS) = getVolumeName($volumeResourceDependency);
    if ($existsFS == -1)
    {
      $result = USM_FAIL;
    }
    # if the filesystem Resource exists and NFS Resource exist
    #     # will check the volume path
    if ($existsFS &&
        (lc($volume) ne lc($nfsVolume)))
    {
      $result = USM_FAIL;
    }
  }
  if ($result != USM_FAIL)
  {
    # (First node only) Format the volume provided.
    if (!$existsFS || !$isrunningFS)
    {
      acfslib::lib_inform_print_noalert(9614, "Formatting the volume device.");
      # For Linux
      $cmd = $MKFS_ACFS . " " . OPT_CHR . "f " . $volume;
       
      if ($OSNAME eq "solaris")
      {
          $cmd = $MKFS . " " . OPT_CHR . "F acfs "  . OPT_CHR . "o f " . $volume;
      }
                                       
      $rc = system($cmd);
      acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'", $cmd, ($? >> 8));
      if ($rc != 0)
      {
        $result = USM_FAIL;
      }
    }
  }

  if ($result != USM_FAIL)
  {
    # (First node only) Create a file system resource for the storage path and 
    # configure it to be mounted on $NFS_PATH.  
    # Create it globally disabled, enablig it on a node by node basis.
    if (!$existsFS)
    {
      if (!actionFileSystemResource("add", $volume))
      {
        $result = USM_FAIL;
      }
    }
  }

  # As far I can see, we don't need to use a temporary path. 
  # We can mount directly in $NFS_PATH and the information will not be lost.

  # The previous contents (if any), owner, and mode of dir become invisible, 
  # and as long as this filesystem remains mounted, the pathname dir refers 
  # to the root of the filesystem on device.
  
  # So maybe, we don't need to run the following steps.
  # (First node only) Mount the file system in a temporary location.
  # (First node only) Copy $NFS_PATH to file system.
  # (All nodes) Move $NFS_PATH to $NFS_PATH.old_before_oracle_hanfs_<date>
  # (All nodes) Create a new $NFS_PATH
  # (First node only) Unmount the temporary location of the file system.

  if ($result != USM_FAIL)
  {
    # (All nodes) Enable and start the file system resource ??? on current node only
    if (!actionFileSystemResource("start", $volume))
    {
      $result = USM_FAIL;
    }
  }

  if ($result != USM_FAIL)
  {
    if (-e $zip_file_name)
    {
      my $unziptool = "/usr/bin/unzip";
       
      #Check the unzip tool
      if (!-e $unziptool)
      {
        # If the absolute path doesn't exist, we'll try with the rleative command
        $unziptool = "unzip";
      }
      
      $cmd = "$unziptool -uoq $zip_file_name -d $NFS_PATH";
      $rc = system($cmd);
      acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', " .
                                         "output = '%s'", $cmd, ($? >> 8));
    }

    # Filesystem is mounted, we need to check for NFS Customer and 
    # NFSv4 Directories. We could have duplicated entries but it doesn't
    # matter. If the directory exists we skip it.
    my @directories = lib_osds_get_nfs_directories();
    foreach my $directory (@directories)
    {
      if (!(-e "$NFS_PATH/$directory"))
      {
        $cmd = "mkdir $NFS_PATH/$directory";
        $rc = system($cmd);
        acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', " .
                                           "output = '%s'", $cmd, ($? >> 8));
        if ($?)
        {
          lib_error_print(9345, "Unable to create directory: '%s'.",
                                "$NFS_PATH/$directory");
          return USM_FAIL;
        }
      }
    }
  }

  # (First node only) Call appropriate srvctl command to add the NFS resource 
  # and adjust current export dependencies.
  if ($result != USM_FAIL)
  {
    if (!$existsNFS)
    {
      #Add NFS Resouce
      if (!actionNFSResource("add", $volume))
      {
        $result = USM_FAIL;
      }
    }
  }

  # (All nodes) Start the NFS resource on current node.
  acfslib::lib_inform_print_noalert(9611, "Starting NFS Service.");
  if (!actionNFSServer("start"))
  {
    $result = USM_FAIL;
  }
  
  #  Before starting the NFS resource, make sure the 
  #   service is online, in Solaris the server does not get online
  #   if there no share resources or there is no work to do.
  #   in that case create a share folder with limited access.
  if ( $OSNAME eq "solaris" )
  {
     # Check that there is no share resources
     $output = `wc -l < /etc/dfs/sharetab`;
     chomp($output);
     if ( $output eq '1' )
     {
       $cmd = "/usr/bin/mkdir $NFS_PATH/nfs_share";
       $rc = system($cmd);
       acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
                            $cmd, ($? >> 8));
       $cmd = "$USR_SBIN_DIR/share -F nfs -o ro,anon=-1 $NFS_PATH/nfs_share";
       $rc = system($cmd);     
       acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
                            $cmd, ($? >> 8));
     }
  }
   
  if ($result != USM_FAIL)
  {
    if ($isrunningNFS)
    {
      #Stop
      if (!actionNFSResource("stop"))
      {
         $result = USM_FAIL;
      }
    }
    #Start
    if (!actionNFSResource("start"))
    {
       $result = USM_FAIL;
    }
  }

  return $result;
} #end lib_install_nfs_path


# lib_uninstall_nfs_path
#
# Uninstall HANFS Locking support
#
sub lib_uninstall_nfs_path
{
  my ($result) = USM_FAIL;
  my ($rc) = 0;
  my ($cmd) = "";
  my $NFS_PATH = lib_osds_get_nfs_path();
  my ($volumeResourceDependency, $nfsVolume, $isrunningNFS, $existsNFS) = getNFSResourceStatus();

  if ($existsNFS == -1)
  {
    return USM_FAIL;
  }

  # Check for filesystem resource
  my ($volume, $isrunningFS, $existsFS) = ("", 0, 0);
  if ($existsNFS)
  {
     ($volume, $isrunningFS, $existsFS) = getVolumeName($volumeResourceDependency);
  }
  if ($existsFS == -1)
  {
    $result = USM_FAIL;
  }
  else
  {
    $result = USM_SUCCESS;
  }
  
  #  In solaris a share a folder with limited access is created in order 
  #   to get the service online. Here we unshare the folder so the system
  #   returns to a clean state.
  if ( $OSNAME eq "solaris" )
  {
    open SHARE, "$USR_SBIN_DIR/share |";
    while (<SHARE>)
    {
      if (/nfs_share/)
      {
         $cmd = "$USR_SBIN_DIR/unshare $NFS_PATH/nfs_share";
         $rc = system($cmd);
         acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
                           $cmd, ($? >> 8));
      }
    }
  }

  # Stop the service
  if (!actionNFSServer("stop"))
  {
    $result = USM_FAIL;
  }
  # Even when services rpcidmapd and NFS are down, directory rpc_pipefs is 
  # still mounted, mainly in EL6. For Linux umount rpc_pipefs
  if ($OSNAME eq "linux")
  {
    lib_osds_unmount("$NFS_PATH/rpc_pipefs");
  }

  #    (All nodes)  Stop the NFS service using srvctl with the force option 
  #    on the current node.
  if ($result != USM_FAIL)
  {
    if ($isrunningNFS)
    {
      #Stop
      if (!actionNFSResource("stop"))
      {
        $result = USM_FAIL;
      }
    }
  }
  
  # (All nodes)  Stop the ACFS file system for stable storage on 
  # the current node only.
  if ($result != USM_FAIL)
  {
    if ($isrunningFS)
    {
      if (!actionFileSystemResource("stop", $volume))
      {
        # It could fail, if not running in this node
        # or if ACFS Resource can not umount
      }
    }
  }

  # Get the service online since restart only works with online services.
  # Start the service
  if (!actionNFSServer("start"))
  {
    $result = USM_FAIL;
  }

  #    (All nodes)  Disable the NFS service resource on this node.
  #    Just for srvctl
  
  #    (All nodes)  Disable the ACFS file system for stable storage on 
  #    the current node only.
  #    It doesn't work for node
  
  # We don't need to do these steps.
  #    (All nodes)  Move $NFS_PATH to a temporary directory.
  #    (All nodes)  Create a new $NFS_PATH.
  #    (All nodes)  Copy the old $NFS_PATH files from stable storage to 
  #    the new $NFS_PATH. 
  #     If the files don???t exist, use the backup in $NFS_PATH from 
  #     installation. 
  
  #   (Last node) Remove the NFS CRS agent using the appropriate srvctl remove 
  #    command. 
  #   (Last node) Remove the stable storage ACFS resource.
  if ($result != USM_FAIL)
  {
    if ($existsFS && isLastNode($volume))
    {
      if (!actionNFSResource("remove") ||
          !actionFileSystemResource("remove", $volume))
      {
        $result = USM_FAIL;
      }
    }
  }

  #    (All nodes) Start the NFS services using the OS commands.
  #    (All nodes)  Restore the OS management of NFS daemons.
  acfslib::lib_inform_print_noalert(9613, "Restarting NFS Service.");
  
  # In solaris if there is no shared resources the following action is 
  #  going to fail since the service is disabled and the restart option 
  #  only works with online services. The action is performed but the result is
  #  not going to affect the return status.
  actionNFSServer("restart");

  return $result;
} #end lib_uninstall_nfs_path

# actionNFSServer
# start/stop/restart NFS Server. 
# Parameters
#   $action - "stop"/"start"/"restart" actions for the NFS Server
# Returns TRUE if action was successful or FALSE if it failed
sub actionNFSServer
{
  my $action  = shift;
  my $cmd = "";
  my $auxcmd = "";
  my $rc = 0;

  if ($OSNAME eq "linux")
  {
     $cmd = "/sbin/service nfs ". $action;
     $auxcmd = "/sbin/service rpcidmapd ". $action;
     # In Linux, we need to stop and start rpc.idmapd before to
     # nfs service.
     $rc = system($auxcmd);
     acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
                               $auxcmd, ($? >> 8));
  }
  elsif ($OSNAME eq "solaris")
  {
     # Translate for solaris disable for stop and enable for start.
     #  NOTE: restart only works on services that are in the online 
     #        or degraded states
     if( $action eq "start" )
     {
        $action = "enable";
     }
     elsif( $action eq "stop" )
     {
        $action = "disable";
     }

     $cmd = "/sbin/svcadm ". $action . " network/nfs/server";
  }

  $rc = system($cmd);
  acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
                            $cmd, ($? >> 8));
  if ($rc != 0)
  {
    return FALSE;
  }
  return TRUE;
} #end actionNFSServer

# actionFileSystemResource
# add/start/stop/disable/remove Filesystem Resource
# Parameters
#   $action - "add"/"start"/"stop"/"disable"/"remove" actions 
#             for the Filesystem Resource
#   $volume - Device to use
# Returns TRUE if action was successful or FALSE if it failed
sub actionFileSystemResource
{
  my ($action)  = shift;
  my ($volume)  = shift;
  my ($rc) = 0;
  my $NFS_PATH = lib_osds_get_nfs_path();

  if ($action eq "add")
  {
    $rc = system("$ORACLE_HOME/bin/srvctl add filesystem -device " . $volume .
                 " -path " . $NFS_PATH);
    acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
                             "$ORACLE_HOME/bin/srvctl add filesystem -device " . 
                             $volume . " -path " . $NFS_PATH, ($? >> 8));
    if ($rc != 0)
    {
      return FALSE;
    }
  }
  else
  {
    my ($node_command) = "";
    if ($action eq "start" ||
        $action eq "stop")
    {
      $node_command = " -n $hostname";
    }
    $rc = system("$ORACLE_HOME/bin/srvctl " . $action . " filesystem -device " . 
                 $volume . $node_command);
    acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
                             "$ORACLE_HOME/bin/srvctl " . $action .
                             " filesystem -device " . $volume . $node_command,
                             ($? >> 8));
    if ($rc != 0)
    {
      return FALSE;
    }
  }
  return TRUE
} #end actionFileSystemResource

# actionNFSResource
# start/stop/remove/add NFS Resource
# Parameters
#   $action - "start"/"stop"/"remove"/"add" actions for the NFS Resource
#   $attr   - Attribute to add or modify. Just for add or modify action.
# Returns TRUE if action was successful or FALSE if it was failed
sub actionNFSResource
{
  my ($action) = shift;
  my ($rc) = 0;
  if ($action eq "add")
  {
    my $device = shift;
    my $command = "$ORACLE_HOME/bin/srvctl add netstorageservice -device "
                . $device;
    $rc = system($command);
    acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
                              $command, ($? >> 8));
    if ($rc != 0)
    {
      return FALSE;
    }
  }
  else
  {
    my $command = "$ORACLE_HOME/bin/srvctl ". $action . " netstorageservice";
    $rc = system($command);
    acfslib::lib_inform_print_noalert( 9179, "Command executed: '%s', output = '%s'",
                              $command, ($? >> 8));
    if ($rc != 0)
    {
      return FALSE;
    }
  }
  return TRUE
} #end actionNFSResource

# getNFSResourceStatus
# Status for NFS Resource
sub getNFSResourceStatus
{
  my $resource = "ora.netstorageservice";
  my $volumeResourceDependency = "";
  my $volume = "";
  my $isrunningNFS = 0;
  my $existsNFS = -1;
  open CRSCTL, "$ORACLE_HOME/bin/crsctl stat res $resource -f |";
  while (<CRSCTL>)
  {
    if (/CRS-4047/)
    {
      # "No Oracle Clusterware components configured."
      acfslib::lib_error_print_noalert(5219, "Cannot proceed because CRS stack is not up.");
      last;
    }
    elsif (/CRS-2613/)
    {
      # "Could not find resource ora.netstorageservice"
      $existsNFS = 0;
      last;
    }
    elsif (/STATE=OFFLINE/)
    {
      $existsNFS = 1;
    }
    elsif (/STATE=ONLINE/)
    {
      $existsNFS = 1;
      $isrunningNFS = 1;
    }
    elsif (/START_DEPENDENCIES/)
    {
      $existsNFS = 1;
      if (!(/START_DEPENDENCIES_RTE_INTERNAL/))
      {
        $volumeResourceDependency = ($_);
        chomp($volumeResourceDependency);
        $volumeResourceDependency = substr($volumeResourceDependency,
                                    index($volumeResourceDependency, '(') + 1);
        $volumeResourceDependency = substr($volumeResourceDependency,
                                    index($volumeResourceDependency, '(') + 1);
        $volumeResourceDependency = substr($volumeResourceDependency, 0,
                                    length($volumeResourceDependency) - 1);
      }
    }
    elsif (/STABLE_STORAGE/)
    {
      $volume = ($_);
      chomp($volume);
      $volume = substr($volume, index($volume, '=') + 1);
    }
  }
  close CRSCTL;

  return ($volumeResourceDependency, $volume, $isrunningNFS, $existsNFS);
} # end getNFSResourceStatus

sub getVolumeName
{
  my ($volumeResourceDependency) = shift;

  # Check for filesystem resource
  my $isrunningFS = 0;
  my $existsFS = -1;
  my $volume = '';
  open CRSCTL, "$ORACLE_HOME/bin/crsctl stat res $volumeResourceDependency -f |";
  while (<CRSCTL>)
  {
    if (/CRS-4047/)
    {
      # "No Oracle Clusterware components configured."
      acfslib::lib_error_print_noalert(5219, "Cannot proceed because CRS stack is not up.");
      last;
    }
    elsif (/CRS-2613/)
    {
      # "Could not find resource
      $existsFS = 0;
      last;
    }
    elsif (/STATE=OFFLINE/)
    {
      $existsFS = 1;
    }
    elsif (/STATE=ONLINE/)
    {
      $existsFS = 1;
      $isrunningFS = 1;
    }
    elsif (/VOLUME_DEVICE/)
    {
      $volume = ($_);
      chomp($volume);
      $volume = substr($volume, index($volume, '=') + 1);
    }
  }
  close CRSCTL;

  return ($volume, $isrunningFS, $existsFS);
}

sub isLastNode
{
  my ($volume) = shift;

  # Check for filesystem resource
  open SRVCTL_FS, "$ORACLE_HOME/bin/srvctl status filesystem -d $volume |";
  while (<SRVCTL_FS>)
  {
    if (/PRKO-2012/)
    {
      # "No Oracle Clusterware components configured."
      acfslib::lib_error_print_noalert(5219, "Cannot proceed because CRS stack is not up.");
      last;
    }
    elsif (/PRCA-1070/)
    {
      # "Could not find resource filesystem"
      last;
    }
    elsif (/ADVM-03180/)
    {
      #  Unable to obtain ASM volume device information for volume
      last;
    }
    elsif (/is mounted on nodes/) #We stop the resource on this node
    {
      last;
    }
    elsif (/is not mounted/)
    {
      return TRUE;
    }
  }
  close SRVCTL_FS;
  return FALSE;
}


# vim:ts=2:expandtab

OHA YOOOO