#
# $Header: usm/src/cmds/acfslib/acfslib.pm /st_usm_11.2/14 2011/07/25 13:22:33 averhuls Exp $
#
# osdsacfslib.pm
# 
# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. 
#
#
#    NAME
#      acfslib.pm - Common (non platform specific) functions used by
#                   the install/runtime scripts.
#
#    DESCRIPTION
#      Purpose
#          See above.
#
#    NOTES
#      All user visible output should be done in the common code.
#      this will ensure a consistent look and feel across all platforms.
#
#    MODIFIED   (MM/DD/YY)
#    averhuls    07/25/11 - XbranchMerge averhuls_install_from_load from main
#    averhuls    07/25/11 - Pass $COMMAND in lib_osds_usm_drivers().
#    averhuls    07/21/11 - XbranchMerge averhuls_crs_modify_and_more from main
#    averhuls    07/15/11 - Add usm_resource_exists(),
#                           modify_usm_{registry,drivers}_resource().
#    averhuls    07/11/11 - Minor rework of when USM_REBOOT_RECOMMENDED is
#                           issued.
#    gsanders    06/30/11 - Backport gsanders_bug-12692110 from main
#    averhuls    06/20/11 - Test lib_osds_unload_driver() return for
#                           USM_SUCCESS.
#    gsanders    05/11/11 - Backport gsanders_bug-12387330 from main
#    gsanders    04/04/11 - Backport gsanders_bug-11871207 from main
#    anjiwaji    03/24/11 - Backport anjiwaji_bug-11872430 from main
#    anjiwaji    03/23/11 - Add a VERBOSE flag to wrap message 9155.
#    mcarrell    03/21/11 - Backport mcarrell_bug-11874601 from main
#    anjiwaji    02/25/11 - Add debugging statements for loading and verifying
#                           driver installation.
#    anjiwaji    02/17/11 - Backport anjiwaji_bug-11744565 from main
#    anjiwaji    01/20/11 - Add subroutine to print header for system error
#                           messages.
#    gsanders    01/10/11 - XbranchMerge gsanders_bug-10229980 from main
#    gsanders    01/10/11 - spelling miss-match -> mismatch
#    aime        12/09/10 - XbranchMerge aime_w3 from main
#    rvadraha    11/30/10 - Bug9925648 Add lib_count_drivers_loaded()
#    mcarrell    11/19/10 - Change lib_recover_stale_mounts to only recover
#                           mount points in the registry when called from
#                           acfsregistrymount.
#    agraves     09/30/10 - Add option to not mount mount points if a nomounts
#                           file is present.
#    gsanders    09/17/10 - Remove temp debug statement
#    gsanders    08/20/10 - change reg res group rwx
#    gsanders    07/21/10 - lib_is_mount_available() strip backslash
#    gsanders    07/07/10 - add lib_create_mount_point()
#    plancian    07/06/10 - Skip lines from acfsutil that produce ACFS-#
#                           messages, instead of reading these errors as
#                           mountpoints
#    jinjche     07/06/10 - Fix a bug that DBHOME ACFS resource does not
#                           start
#    gsanders    06/22/10 - add lib_get_drive_info()
#    bonelso     01/21/10 - Stop trying to unload drivers if error encountered.
#    jinjche     06/02/10 - Add 7 functions for reregistering and unregistering
#                           ACFS resources
#    gsanders    05/20/10 - Add time stamps to logged output from clsecho
#    gsanders    04/30/10 - Merge forward 11.2.0.1 work
#    averhuls    04/28/10 - Errors will now go to the CRS user - if invoked by
#                           CRS and not just the logs.
#    jinjche     04/21/10 - Fix bug that $COMMAND was not initialized. Set it to
#                           "No Command Specified" as suggested by the reviewers.
#    agraves     04/19/10 - Fix message 9111 to not have Stale in it.
#    agraves     04/14/10 - Add missing comma on line 713
#    agraves     04/10/10 - Change stale to offline in message 9139.
#    agraves     04/08/10 - Add some extra error checking to
#                           lib_is_mount_available for cases when acfsutil info
#                           fs fails to run. This will get around perl throwing
#                           an exception to the CRS stack.
#    averhuls    03/22/10 - Improve debug output if the -s (silent) option is
#                           used.
#    averhuls    03/17/10 - Add USM_NOT_SUPPORTED.
#    averhuls    03/15/10 - change verify_message() to convert the incoming
#                           message ID  to the acfsus.msg 5 char format.
#    averhuls    02/26/10 - lib_load_usm_drivers(): separate the detect and
#                           load steps into two phases - bug 9345437.
#    anjiwaji    02/10/10 - Add function to check if any drivers are installed.
#    agraves     01/19/09 - Remove 2>&1 output redirection for execution from
#                           the srvctl environment.  Apparently on Windows
#                           the redirection fails, and the execution dies. 
#    certan      11/24/09 - Add optional argument to lib_unload_usm_drivers
#                           that specifies the location of new install files.
#                           Utilities inside of new install files may be used
#                           to unload drivers if old utilities cannot be found
#    averhuls    11/18/09 - if asm_connect() fails, chech that the gis is
#                           correct.
#    averhuls    09/21/09 - Report error if osdbagrp returns NULL.
#    agraves     07/28/09 - Remove is_db_home function.
#    averhuls    07/15/09 - Add time stamp to the check_in_progress file.
#    averhuls    07/02/09 - Change SQL select to improve performance.
#                           Remove unused lib_asm_create_volume() and
#                           lib_asm_delete_volume().
#    averhuls    06/23/09 - Allow lib_check_in_progress() to handle multi-level
#                           directories.
#    averhuls    06/01/09 - Add lib_check_in_progress().
#    agraves     05/15/09 - Add function lib_is_db_home heuristic check.
#    averhuls    05/07/09 - Unformatted messages go thru clsecho too.
#    averhuls    05/01/09 - Delete lib_verify_resource_online.
#    averhuls    04/30/09 - Get clsecho from ORA_CRS_HOME.
#    agraves     04/29/09 - Update the messages in this file to go hand in hand
#                           with the messages in the mesg file, following Bill
#                           Manry's review of them.
#    averhuls    03/31/09 - make lib_verify_ora_asm_online() generic.
#    averhuls    03/23/09 - Add CLSAGFW_ return codes.
#    averhuls    03/13/09 - Pass the nodename to crsctl stat - not the
#                           hostname.
#    averhuls    02/19/09 - Export constants from osds_acfslib.
#    averhuls    02/17/09 - Fix format for msg 9101.
#    averhuls    02/11/09 - Add lib_verify_ora_asm_online.
#                           Use acfsutil info fs -o mountpoints,isavailable
#                           to check for stale mounts.
#    averhuls    01/29/09 - rename usm_lib to acfslib.
#    agraves     01/28/09 - Add lib_is_mount_available to see if device is
#                           available, not offline or otherwise on vacation.
#    averhuls    12/22/08 - convert to use message catalog.
#    averhuls    11/03/08 - Creation
#

use strict;
package acfslib;
require Exporter;

our @ISA = qw(Exporter);
our @EXPORT = qw(
                 lib_am_root
                 lib_asm_connect
                 lib_asm_disconnect
                 lib_asm_enable_volume
                 lib_asm_mount_diskgroup
                 lib_check_drivers_installed
                 lib_check_any_driver_installed
                 lib_check_drivers_loaded
                 lib_count_drivers_loaded
                 lib_check_loaded_drivers_mismatch
                 lib_check_in_progress
                 lib_control_devices_accessible
                 lib_create_mount_point
                 lib_device_from_mountpoint
                 lib_end_check_in_progress
                 lib_error_print
                 lib_get_advm_mounts
                 lib_get_asm_admin_name
                 lib_get_asm_user
                 lib_get_drive_info
                 lib_inform_print
                 lib_is_mounted
                 lib_is_mount_available
                 lib_load_usm_drivers
                 lib_mount
                 lib_mountpoint_descriptors
                 lib_recover_stale_mounts
                 lib_run_as_user
                 lib_run_func
                 lib_unload_usm_drivers
                 lib_unmount
                 lib_usm_supported
                 lib_verify_usm_devices
                 lib_is_abs_path
		 lib_print_cmd_header
                 trim
                 $COMMAND
                 $SILENT
                 $VERBOSE
                 $ACFSUTIL
                 AVD_IDX
                 OFS_IDX
                 OKS_IDX
                 OPT_CHR
                 CHECK_IN_PROGRESS_NO
                 CHECK_IN_PROGRESS_YES
                 CHECK_IN_PROGRESS_TIMEOUT
                 CLSAGFW_AE_SUCCESS
                 CLSAGFW_AE_FAIL
                 CLSAGFW_ONLINE
                 CLSAGFW_UNPLANNED_OFFLINE
                 CLSAGFW_PLANNED_OFFLINE
                 CLSAGFW_UNKNOWN
                 CLSAGFW_PARTIAL
                 CLSAGFW_FAILED
                 USM_FAIL
                 USM_TRANSIENT_FAIL
                 USM_SUCCESS
                 USM_NOT_SUPPORTED
                 USM_REBOOT_RECOMMENDED
                 add_acfs_registry
                 delete_acfs_registry
                 start_acfs_registry
                 stop_acfs_registry
                 add_usm_drivers_resource
                 usm_resource_exists
                 modify_usm_registry_resource
                 modify_usm_drivers_resource
                 delete_usm_drivers_resource
                 start_usm_drivers_resource
                );

use DBI;
use osds_acfslib;
use File::Spec::Functions;

our ($SILENT) = 0;                       # input option (-s)
our ($VERBOSE) = 0;                      # input option (-v)
our ($COMMAND) = "No Command Specified";                     # currently executing command

my ($ADE_VIEW_ROOT) = $ENV{ADE_VIEW_ROOT};
my ($ORACLE_HOME) = $ENV{ORACLE_HOME};
my ($ORA_CRS_HOME) = $ENV{ORA_CRS_HOME};
my ($CRS) = $ENV{_ORA_AGENT_ACTION};     # defined if invoked via CRS
my ($CACHED_ASMADMIN);                   # avoid calling osdbagrp -a repeatedly
# '-l' = write to CRS alert log
my ($CLSECHO) = catfile($ORA_CRS_HOME, "bin", "clsecho") . " -p usm -f acfs -l";

# CRS check function return codes from crs/agentfw/include/clsagfw.h
use constant CLSAGFW_ONLINE            => 0;
use constant CLSAGFW_UNPLANNED_OFFLINE => 1;
use constant CLSAGFW_PLANNED_OFFLINE   => 2;
use constant CLSAGFW_UNKNOWN           => 3;
use constant CLSAGFW_PARTIAL           => 4;
use constant CLSAGFW_FAILED            => 5;

# CRS return codes for functions other than check()
use constant CLSAGFW_AE_SUCCESS        => 0;
use constant CLSAGFW_AE_FAIL           => 1;

use constant PRINT_INFORM              => 0;
use constant PRINT_ERROR               => 1;

use Config;
my ($OSNAME) = $Config{osname};
chomp($OSNAME);

# lib_am_root
# 
# call into lib_osds_am_root
#
sub lib_am_root
{
  return lib_osds_am_root();
} # end lib_am_root

#
# connect to the ASM instance
#
sub lib_asm_connect
{
  my ($dbh);
  my (%session_mode);
  my ($driver) = 'dbi:Oracle:';
  my ($usr) = '';
  my ($pswd) = '';

  # $session_mode{'ora_session_mode'} = 2;    # sysdba
  $session_mode{'ora_session_mode'} = 32768;  # sysasm
  $session_mode{'PrintError'} = 0;

  $dbh = DBI->connect($driver, $usr, $pswd, \%session_mode);
  warn "$DBI::errstr\n" unless defined ($dbh);
  if (!defined ($dbh))
  {
    if (defined($ENV{ADE_VIEW_ROOT}))
    {
      # On non-Linux platforms, ADE changes your gid when you enter a view.
      # This program is called as root, which then does a "su <user>" in
      # order to perform the ASM functions. The "su" command will revert
      # your gid back to the original gid. This means that you can no longer
      # "talk" to ASM. The -no_newgid option on your "ade useview <view>"
      # command will prevent the gid switch.
      my ($my_gid_list) = $(;
      my ($asm_user, $asm_gid) = lib_get_asm_user();
      my (@array) = split(/ /, $my_gid_list);
      my ($my_gid) = $array[0];

      # On NT, $asm_gid comes back as 0 from lib_get_asm_user().
      if (($my_gid != $asm_gid) && $asm_gid)
      {
        # No internationalization of the message because this is devlopment
        lib_error_print(9999, "\nASM/user gid mismatch ($asm_gid/$my_gid).");
        lib_error_print(9999,
         "You may need the -no_newgrp option on the ade useview command.");
      }
    }
    return USM_FAIL;
  }

  return $dbh;
}

#
# connect to the ASM instance
#
sub lib_asm_disconnect
{
  my ($dbh);

  if (defined($dbh))
  {
    $dbh->disconnect();
  }
} # end_lib_asm_disconnect

# lib_asm_enable_volume
#
# If the specified volume is not ASM enabled - enable it.
#
# The caller must have verified that the diskgroup is mounted.
#
sub lib_asm_enable_volume
{
  my ($dbh, $diskgroup, $volume) = @_;
  my ($diskgroup_uc, $volume_uc);
  my ($qry);                  # SQL query
  my ($dg_state);             # ASM state of the diskgroup
  my ($sth);                  # SQL statement handle
  my ($row);                  # SQL table row
  my ($found_diskgroup) = 0;
  my ($found_volume) = 0;
  my ($return_val) = USM_SUCCESS;

  # ASM returns select data in upper case and so our comparisons have to 
  # match case.
  $diskgroup_uc = uc($diskgroup);
  $volume_uc = uc($volume);

  # Now see if the volume exists. Enable it if needed.
  $qry = "select NAME, VOLUME_NAME, x.STATE from V\$ASM_VOLUME x join
    V\$ASM_DISKGROUP_STAT on x.GROUP_NUMBER = V\$ASM_DISKGROUP_STAT.GROUP_NUMBER";

  $sth = asm_select_stmt($dbh, $qry);
  while (defined ($row = asm_fetch_row($sth)))
  {
    if (($row->{'NAME'} eq $diskgroup_uc) &&
        ($row->{'VOLUME_NAME'} eq $volume_uc))
    {
      $found_volume = 1;
      if ($row->{'STATE'} ne 'ENABLED')
      {
        # enable the volume
        lib_inform_print(9103, "Enabling volume '%s' on diskgroup '%s'.",
                                                       $volume, $diskgroup);
        $qry = "alter diskgroup $diskgroup enable volume $volume";
        $return_val = asm_do_stmt($dbh, $qry);
        if ($return_val == USM_FAIL)
        {
          lib_error_print(9104, "Enable of volume '%s' failed.", $volume);
        }
      }
      last;
    }
  }
  eval { $sth->finish(); };

  if (! $found_volume)
  {
    lib_error_print(9105, "Volume '%s' not found in '%s'.", $volume, $diskgroup);
    $return_val = USM_FAIL;
  }

  return $return_val
} # end asm_enable_volume

# lib_asm_mount_diskgroup
#
# If the specified diskgroup is not ASM mounted - mount it.
#
sub lib_asm_mount_diskgroup
{
  my ($dbh, $diskgroup) = @_;
  my ($diskgroup_uc);
  my ($qry);                  # SQL query
  my ($dg_state);             # ASM state of the diskgroup
  my ($sth);                  # SQL statement handle
  my ($row);                  # SQL table row
  my ($found_diskgroup) = 0;
  my ($return_val);

  # ASM returns select data in upper case and so our comparisons have to 
  # match case.
  $diskgroup_uc = uc($diskgroup);

  $return_val = USM_SUCCESS;

  # see if the diskgroup exists
  $qry= "select name,state from v\$asm_diskgroup";
  $sth = asm_select_stmt($dbh, $qry);
  while (defined ($row = asm_fetch_row($sth)))
  {
    if ($row->{'NAME'} eq $diskgroup_uc)
    {
      $dg_state = $row->{'STATE'};
      $found_diskgroup = 1;
      last;
    }
  }
  eval { $sth->finish(); };

  if (! $found_diskgroup)
  {
    lib_error_print(9106, "Diskgroup '%s' not found.", $diskgroup);
    return USM_FAIL;
  }

  if ($dg_state eq 'DISMOUNTED')
  {
    lib_inform_print(9107, "ASM mounting diskgroup '%s'.", $diskgroup);
    $qry = "alter diskgroup $diskgroup mount";
    $return_val = asm_do_stmt($dbh, $qry);
    if ($return_val == USM_FAIL)
    {
      lib_error_print(9108, "ASM mount of diskgroup '%s' failed.", $diskgroup);
      return USM_FAIL;
    }
  }
  return $return_val;
} # end lib_asm_mount_and_enable

# lib_check_drivers_installed
#
sub lib_check_drivers_installed
{
  my ($driver);
  my ($num_drivers_installed) = 0;

  foreach $driver ($DRIVER_COMPONENTS[OKS_IDX],
                   $DRIVER_COMPONENTS[AVD_IDX], $DRIVER_COMPONENTS[OFS_IDX])
  {
    if($VERBOSE)
    {
      lib_inform_print(9155, "Checking for existing '%s' driver " .
                 "installation.", 
                 $driver);
    }
    if (lib_osds_check_driver_installed($driver))
    {
      $num_drivers_installed++;
    }
  }

  if ($num_drivers_installed != 3)
  {
    return 0;
  }

  return 1;
} # end lib_check_drivers_installed

# lib_check_any_driver_installed
#
sub lib_check_any_driver_installed
{
  my ($driver);

  foreach $driver ($DRIVER_COMPONENTS[OKS_IDX],
                   $DRIVER_COMPONENTS[AVD_IDX], $DRIVER_COMPONENTS[OFS_IDX])
  {
    if($VERBOSE)
    {
      lib_inform_print(9155, "Checking for existing '%s' driver " .
                 "installation.", 
                 $driver);
    }
    if (lib_osds_check_driver_installed($driver))
    {
      return 1;
    }
  }

  return 0;

} # end lib_check_any_driver_installed

# lib_count_drivers_loaded
#
sub lib_count_drivers_loaded
{
  my ($driver);
  my ($num_drivers_loaded) = 0;

  foreach $driver ($DRIVER_COMPONENTS[OKS_IDX],
                   $DRIVER_COMPONENTS[AVD_IDX], $DRIVER_COMPONENTS[OFS_IDX])
  {
    if (lib_osds_check_driver_loaded($driver))
    {
      $num_drivers_loaded++;
    }
  }

  return $num_drivers_loaded;
} # end lib_osds_count_drivers_loaded

# lib_check_drivers_loaded
#
sub lib_check_drivers_loaded
{
  my ($num_drivers_loaded) = 0;
  my ($return_val);

  $num_drivers_loaded = lib_count_drivers_loaded();

  if ($num_drivers_loaded != 3)
  {
    $return_val = 0;
  }
  else
  {
    $return_val = 1;
  }

  return $return_val;
} # end lib_osds_check_drivers_loaded

# lib_check_loaded_drivers_mismatch
#
# Determine whether or not the installed drivers match the drivers that
# are loaded in the kernel.  This can happen during Solaris which
# doesn't reliably unload drivers.

sub lib_check_loaded_drivers_mismatch
{
    return lib_osds_check_loaded_drivers_mismatch();
}

use constant CHECK_IN_PROGRESS_NO      => 0;
use constant CHECK_IN_PROGRESS_YES     => 1;
use constant CHECK_IN_PROGRESS_TIMEOUT => 2;

# lib_check_in_progress
#
# Use a temp file to mark a check being in progress 
#
# Returns:
#     CHECK_IN_PROGRESS_NO - no previous check currently in progress
#     CHECK_IN_PROGRESS_YES - previous check currently in progress
#     CHECK_IN_PROGRESS_TIMEOUT - previous check in progress timeout exceeded
#
# We return a CHECK_IN_PROGRESS_NO even if the directory or the file creation
# fails. This, effectively, disables the function because the caller will 
# think that there is no check in progress and so the check proceeds normally.
# This is better than returning a 1, which would make the caller think that
# is a check in progress - even if there isn't one.
#
sub lib_check_in_progress
{
  my ($fname) = @_;                     # temp file name
  my ($pname);                          # full path name for temp file name
  my ($retcode) = CHECK_IN_PROGRESS_NO; # return value
  my ($time_stamp) = get_day_time_in_seconds();

  if (! -d $TMPDIR)
  {
    mkdir $TMPDIR,0777 or warn ("failed to create $TMPDIR: $!"), $retcode = 1;
    if ($retcode eq 1)
    {
      return CHECK_IN_PROGRESS_NO;
    }
  }

  $pname = build_check_filename($TMPDIR, $fname);
  if (-e $pname)
  {
    # A check for this $pname is already in progress.
    my ($time_stamp);
    my ($time_limit) = $ENV{_ORA_CHECK_TIMEOUT};

    # If the $pname creation timestamp is greater than the check timeout value,
    # we return CHECK_IN_PROGRESS_TIMEOUT.
    open FILE, "<$pname"
          or warn ("failed to open $pname: $!"),
                  $retcode = CHECK_IN_PROGRESS_YES;
    if ($retcode ne CHECK_IN_PROGRESS_NO)
    {
      # We could not open the file to read the time stamp. We're root
      # and we can't open a file that we created.... hmmm.
      # Well, there is a small race between the exist check and the open.
      # Remove the file (if it still exists). It will be created on next check.
      unlink $pname;
      return $retcode;
    }

    $time_stamp = <FILE>;
    if(!defined($time_stamp))
    {
      # Apparently the file got damaged. delete the file and return
      # CHECK_IN_PROGRESS_YES. The file will be recreated on the next check.
      unlink $pname;
      return CHECK_IN_PROGRESS_YES;
    }
    chomp($time_stamp);
    close(FILE);

    if (!defined($time_limit))
    {
      # We could not get the _ORA_SCRIPT_TIMEOUT from the environment.
      # use the default falue from ./has/crs/template/registry.acfs.type.
      $time_limit = 300;
    }

    if (time_limit_exceeded($time_stamp, $time_limit))
    {
      $retcode = CHECK_IN_PROGRESS_TIMEOUT;
    }
    else
    {
      $retcode = CHECK_IN_PROGRESS_YES;
    }
  }
  else
  {
    # Marking check in progress.
    open FILE, ">$pname" or warn ("failed to create $pname: $!"), $retcode = 1;
    if ($retcode eq 0)
    {
      printf FILE "%05s\n", $time_stamp;
      close FILE;
    }
    $retcode = CHECK_IN_PROGRESS_NO;
  }

  return $retcode;
} # end lib_check_in_progress

# lib_end_check_in_progress
#
# returns 0 (success) or 1 (failed to remove existing tmp file)
#
# This is also called from start() when no temp file exists (should exist)
# to guarantee a "clean slate".
#
sub lib_end_check_in_progress
{
  my ($fname) = @_;               # temp file name
  my ($pname);                    # full path name for temp file name
  my ($retcode) = 0;              # return value

  $pname = build_check_filename($TMPDIR, $fname);
  if (-e $pname)
  {
    unlink $pname or warn ("failed to remove $pname: $!"), $retcode = 1; 
  }
  return $retcode;
} # end lib_end_check_in_progress

# lib_control_devices_accessible
# 
# call into control_devices_accessible
#
sub lib_control_devices_accessible
{
  return lib_osds_control_devices_accessible();
} # end lib_control_devices_accessible

# lib_get_asm_admin_name
#
# Get the group name of the ASM administrator
# NOTE: not called from Windows
# This is not really OSD code - but it's not (yet) needed on Windows 
# and is not installed there.
#
sub lib_get_asm_admin_name
{ 

  if (defined($CACHED_ASMADMIN))
  {
    return $CACHED_ASMADMIN;
  }

  my ($asmadmin) = 'dba';

  # get the current system ASM admin group name
  if ((defined($ORACLE_HOME)) && (-e "$ORACLE_HOME/bin/osdbagrp"))
  {
    open (ASMADMIN, "$ORACLE_HOME/bin/osdbagrp -a |");
    $asmadmin = <ASMADMIN>;
    close (ASMADMIN);
    if (!defined($asmadmin))
    {
      lib_error_print(9115, "The command '%s' returned an unexpected value.",
                                           "$ORACLE_HOME/bin/osdbagrp -a");
      lib_error_print(9135, "%s installation aborted.", $COMMAND);
      # This is unrecoverable - fail. Unfortunately, there's no graceful way
      # of failing without major redesign - and this is a VERY rare event.
      exit USM_FAIL;
    }
  }

  $CACHED_ASMADMIN = $asmadmin;
  return $CACHED_ASMADMIN;
} # end lib_get_asm_admin_name

# lib_get_advm_mounts
# 
# call into lib_osds_get_advm_mounts
#
sub lib_get_advm_mounts
{
  return lib_osds_get_advm_mounts();
} # end lib_get_advm_mounts

# lib_get_asm_user
# 
# call into lib_osds_get_asm_user
#
sub lib_get_asm_user
{
  return lib_osds_get_asm_user();
} # end lib_get_asm_user

# lib_get_drive_info
# 
# call into lib_osds_get_drive_info
#
sub lib_get_drive_info
{
  return lib_osds_get_drive_info(@_);
} # end lib_get_drive_info

# lib_load_usm_drivers
#
# Load the drivers if not already loaded. Silently ignore if a driver is loaded
# 
# We do this in two phases because we found that on Solaris, sometimes the
# oracleacfs driver got loaded when the advm driver got loaded by devfsadm(1M).
# Then the next time through the loop, lib_osds_check_driver_loaded(oracleacfs)
# would not get called. This prevented /dev/ofsctl was from being created. 
#
sub lib_load_usm_drivers
{
  my ($driver);
  my (@loaded);
  my ($idx);

  # determine which drivers are already loaded (if any).
  foreach $idx (OKS_IDX, AVD_IDX, OFS_IDX)
  {
    $driver = $DRIVER_COMPONENTS[$idx];
    $loaded[$idx] = 0;
    if (lib_osds_check_driver_loaded($driver))
    {
      $loaded[$idx] = 1;
    }
  }

  # Load the not already loaded drivers.
  # The order is important - OKS must be first.
  foreach $idx (OKS_IDX, AVD_IDX, OFS_IDX)
  {
    if (!$loaded[$idx])
    {
      my ($return_val);
      $driver = $DRIVER_COMPONENTS[$idx];
      
      lib_inform_print(9154, "Loading '%s' driver.", $driver);
      $return_val = lib_osds_load_driver($driver, $COMMAND);
      if ($return_val == USM_FAIL)
      {
        lib_error_print(9109, "%s driver failed to load.", $driver);
        return USM_FAIL;
      }
    }
  }
 
  return USM_SUCCESS;
} # end lib_load_usm_drivers

# lib_mount
# 
# call into lib_osds_mount
#
sub lib_mount
{
  my ($device, $mount_point, $options) = @_;

  # The following nomounts file can be used to prevent 
  # automatic resources from mounting file systems
  # that may need to be fsck'd - resulting in a panic.
  my $nomounts = "" ;
  if (defined($ENV{'TEMP'}) && (-f "$ENV{'TEMP'}/oracle_nomounts" ))
  {
    $nomounts = "$ENV{'TEMP'}/oracle_nomounts";
  }
  elsif (defined($ENV{'TMP'}) && (-f "$ENV{'TMP'}/oracle_nomounts" ))
  {
    $nomounts = "$ENV{'TMP'}/oracle_nomounts";
  }
  elsif (-f "/tmp/oracle_nomounts" )
  {
    $nomounts = "/tmp/oracle_nomounts";
  }

  if ( $nomounts ne "" )
  {
    lib_inform_print(9151, 
           "Ignoring request to mount due to existence of \"oracle_nomounts\" file: %s",
           $nomounts);
    return USM_FAIL;
  }

  return lib_osds_mount($device, $mount_point, $options);
} # end lib_mount

# lib_mountpoint_descriptors
# 
# call into lib_osds_mountpoint_descriptors
#
sub lib_mountpoint_descriptors
{
  my ($mount_point, $action) =  @_;
  return lib_osds_mountpoint_descriptors($mount_point, $action);
} # end lib_mountpoint_descriptors

# lib_recover_stale_mounts
#
# call acfsutil info fs and look for mountpoints marked Offline.
# Attempt to unmount the mount point.
#
# An acfsutil info fs -o mountpoints,isavailable entry looks like this: 
# /mnt
# 1
#
sub lib_recover_stale_mounts
{
  my ($recover_specific_mountpoint) = @_;    # set by usm_singlefs_mount only
  my ($offline) = 0;
  my ($recovered_list) = "";
  my ($line) = "";
  my ($device);
  my ($mountpoint);
  my ($ret_val);
  my ($acfsutil_info_fs) =
                "$ACFSUTIL info fs " . OPT_CHR . "o mountpoints,isavailable";

  my ($switch) = 0;

  # TODO: test the failure case.
  # this used to have a 2>&1 here.  Windows in CRS env does not seem to
  # like this sort of redirection, so it got booted.
  # 
  # Do we need to check the error stream for -03036?  Or can we just 
  # assume that there are no file systems if there is no output?
  # There doesn't seem to be any checks for other errors.
  open(INFO , "$acfsutil_info_fs $REDIRECT  |") or die "acfsutil info fs failed: $!";
  while ($line = <INFO>)
  {
    chomp($line);

    if ($line =~ /ACFS-03036/)
    {
      # no mounted file systems
      last;
    }

    if ($line =~/^\s*acfsutil info fs: (ACFS|CLSU)-\d{5}/)
    {
      # any ACFS-# error message
      lib_error_print(9150,
              "Unexpected output from 'acfsutil info fs': '%s'.", $line);
      next;
    }

    if ($switch == 0)
    {
      $mountpoint = $line;
      $switch = 1;
      next;
    }
    else
    {
      $switch = 0;
      if ($line eq 1)
      {
        #online
        next;
      }
    } 
   
    $device = lib_osds_device_from_mountpoint($mountpoint);
    if (!defined($device))
    {
      lib_error_print(9122,
            "ADVM device not determined from mount point '%s'.", $mountpoint);
      return USM_FAIL;
    }

    # usm_mount wants to recover all stale mounts (if any) and will
    # not pass an argument. usm_singlefs_mount wants to recover only
    # the mount point it is interested in (so as not to confuse
    # usm_mount and its state file).
    if (defined($recover_specific_mountpoint))
    {
      if ($recover_specific_mountpoint ne $mountpoint)
      {
        # looking for a specific mountpoint and this isn't it.
        next;
      }
    }
    else
    {
      # 
      #  Recover any stale mount points in the registry.
      #
      my $cmd_out = "";
      my $acfsutil_registry = "$ACFSUTIL registry " . OPT_CHR . "l $mountpoint";
      open(REGISTRY, "$acfsutil_registry $REDIRECT |") or do
      {
        lib_error_print(9999, "executing $acfsutil_registry failed: $!");
        next;
      };
      $cmd_out = <REGISTRY>;
      close(REGISTRY);
      if ( ! defined ( $cmd_out ) )
      {
        next;
      }
      if ($cmd_out =~ /ACFS-03135/)
      {
        # called from acfsregistrymount and mount point isn't in the registry so
        # so we don't want to recover it.
        next;
      }
      if ($cmd_out =~ /ACFS-/)
      {
        # Unexpected error from acfsutil
        lib_error_print(9999,
           "Unexpected error from $acfsutil_registry. err=$cmd_out");
        next;
      }
      my $mountpointQM = quotemeta ( $mountpoint ); # for Windows
      if ( $cmd_out !~ /Mount Point\s+:\s+$mountpointQM\s+:/i )
      {
        # We probably shouldn't get here.  Our check for ACFS-03135
        # should have caught this condition.  At any rate we don't see
        # the mount point in the acfsutil registry output so it isn't
        # registered (or there's some other problem).
        lib_error_print(9999,
           "Unexpected output from $acfsutil_registry. err=$cmd_out");
        next;
      }
    }

    lib_inform_print(9139, 
          "Attempting recovery of offline mount point '%s'",
          $mountpoint);

    $ret_val = lib_osds_unmount($mountpoint);

    if ($ret_val == 0)
    {
      # The unmount succeeded! Remove the mount point from the temp file
      # so it will simply be treated as a new mount in check().
      lib_inform_print(9110, "Offline mount point '%s' was dismounted for recovery.",
                                                             $mountpoint);
      $recovered_list .= "$device ";
    }
    else
    {
      # The unmount failed.
      # Find and report the open references on the mountpoint
      my ($refs) = lib_osds_mountpoint_descriptors($mountpoint, 0);
      lib_error_print (9112,
       "The following process IDs have open references on mount point '%s':", $mountpoint);
      lib_error_print(9999, $refs);  # message 9999 is not formatted
      lib_error_print(9113, "These processes will now be terminated.");

      # terminate any open descriptors
      $ret_val = lib_osds_mountpoint_descriptors($mountpoint, 1);
      lib_error_print(9114, "completed");

      if ($ret_val == USM_SUCCESS)
      {
        # OK try the unmount again
        $ret_val = lib_osds_unmount($mountpoint);
      }

      if ($ret_val == USM_SUCCESS)
      {
        # The unmount succeeded! Remove the mount point from the temp file
        # so it will simply be treated as a new mount in check().
        lib_inform_print(9110, "Offline mount point '%s' was dismounted for recovery.",
                                                             $mountpoint);
        $recovered_list .= "$device ";
      }
      else
      {
        # should never get here......... but......
        lib_error_print (9116,
                   "Offline mount point '%s' was not recovered.", $mountpoint);
        lib_error_print(9117, "Manual intervention is required.");
      }
    }
  }
  close (INFO);

  return $recovered_list;
      
} # end lib_recover_stale mounts

# lib_run_as_user
# 
# call into lib_osds_run_as_user
#
sub lib_run_as_user
{
  my ($user_name, $cmd) = @_;
  return lib_osds_run_as_user($user_name, $cmd);
} # end lib_run_as_user

# lib_unmount
# 
# call into lib_osds_unmount
#
sub lib_unmount
{
  my ($mount_point) = @_;
  return lib_osds_unmount($mount_point);
} # end lib_unmount

# lib_unload_usm_drivers
#
# Unload the USM drivers. Return error if any driver fails to unload.
#
sub lib_unload_usm_drivers
{
  # Optional argument: location of new install files.  Utilities from new
  # install files may be used to unload drivers if old utilities cannot be
  # found
  my ($install_files_loc) = @_;

  my ($driver);
  my ($return_val) = USM_SUCCESS;

  foreach $driver ($DRIVER_COMPONENTS[OFS_IDX],
                   $DRIVER_COMPONENTS[AVD_IDX], $DRIVER_COMPONENTS[OKS_IDX])
  {
    # nothing to do if the driver is not loaded
    if (lib_osds_check_driver_loaded($driver))
    {
      # test to see that the driver is not being used
      if (lib_osds_check_driver_inuse($driver))
      {
        lib_error_print (9118, "%s driver in use - cannot unload.", $driver);
        $return_val = USM_SUCCESS;
        last;
      }

      $return_val = lib_osds_unload_driver($driver, $install_files_loc);
      if ($return_val != USM_SUCCESS)
      {
        lib_error_print(9119, "%s driver failed to unload.", $driver);
        $return_val = USM_REBOOT_RECOMMENDED;
        last;
      }
    }
  }

  return $return_val;
} # end lib_unload_usm_drivers

# lib_usm_supported
# 
# call into lib_osds_usm_supported
#
sub lib_usm_supported
{
  return lib_osds_usm_supported();
} # end lib_usm_supported

# lib_usm_supported
# 
# call into lib_osds_usm_supported
#
sub lib_verify_usm_devices
{
  return lib_osds_verify_usm_devices();
} # end lib_verify_usm_devices

# lib_inform_print
# If $silent is set, messages are not displayed.
#
sub lib_inform_print
{
  my (@arg_array) = @_;
  common_print(PRINT_INFORM, @arg_array);

  return USM_SUCCESS;
}

# lib_create_mount_point
# 
# call into lib_osds_create_mount_point
#
sub lib_create_mount_point
{
  my $mount_point = shift;
  return lib_osds_create_mount_point($mount_point);
} # end lib_create_mount_point

# lib_device_from_mountpoint
# 
# call into lib_osds_device_from_mountpoint
#
sub lib_device_from_mountpoint
{
  my ($mount_point) = @_;
  return lib_osds_device_from_mountpoint($mount_point);
} # end lib_device_from_mountpoint

sub lib_error_print
{
  my (@arg_array) = @_;
  common_print(PRINT_ERROR, @arg_array);

  return USM_SUCCESS;
}

# lib_is_mounted
# 
# call into lib_osds_is_mounted
#
sub lib_is_mounted
{
  my ($mount_point) = @_;
  return lib_osds_is_mounted($mount_point);
} # end lib_is_mounted

#
# Check if a file system is offline or otherwise
# unavailable. (0 , not available, 1, available, -1, other error)
#
sub lib_is_mount_available
{
    my ($mount_point) = @_;         # mount point to test
    my ($avail) = 0;                # assume not available
    my ($cmd_out);                  # Capture output of acfsutil command.

    #
    #  On windows performing acfsutil against a drive letter
    #  specification which includes a trailing backslash (e.g. "p:\")
    #  when the filesystem is stale will yield a bunch of errors rather
    #  than returning the availability state.  Strip the trailing
    #  backslash if any. This code is harmless on non-Windows.
    #

    $mount_point = substr ($mount_point,0,2)
       if ( length($mount_point) == 3 && substr($mount_point,1,2) eq ":\\" );

    # ACFSUTIL defined in lib_osds_usm.pm
    my $cmd = "$ACFSUTIL info fs " . OPT_CHR . "o isavailable $mount_point ";
    $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 )
      {
          $avail = 0;
      }
      else
      {   #mount is available.
          $avail = 1;
      }
    }
    else
    {
      # We had an error running acfsutil.
      # Probable error: Not an acfs file system 
      # Probable error #2: Mount point no longer exists.
      lib_error_print(9138, 
            "command '%s' completed with an error: %s",
            $cmd, $cmd_out);

      $avail=-1;
    }
    return $avail; 
}

###########################################
######## Local only static routines #######
###########################################

# common_print
#
# common print routine shared by lib_inform_print() and lib_error_print
#
sub common_print
{
  my ($message_type) = shift(@_);         # PRINT_ERROR, PRINT_INFORM
  my ($message_id) = shift(@_);
  my ($message) = shift(@_);
  my (@message_args) = @_;
  my ($debug) = $ENV{'ACFS_DEBUG'};

  if (defined($debug))
  {
    my (@args) = @message_args;
    my ($msg) = $message;
    open DBG, ">>/tmp/acfs_debug" or warn "/tmp/acfs_debug: $!";
    while (@args)
    {
      my ($arg) = shift(@args);
      $msg =~ s/\%s/$arg/;
    }
    print DBG "$COMMAND: $msg\n";
    close DBG;
  }

  if ($SILENT && ($message_type == PRINT_INFORM))
  {
    # do not print if the message is not an error and the -s option is used.
    return USM_SUCCESS;
  }

  # special case: message 9999 is not formatted
  # The message may be a list of PIDs, for instance, or an error message
  # from another command that may already have been I18N'ed.
  if ($message_id == 9999)
  {
# TODO - disable until bug 9664524 gets fixed.
undef $CRS;
# end TODO
    if (($message_type == PRINT_ERROR) && (defined($CRS)))
    {
      $message = "CRS_ERROR:" . $message;
    }
    system("$CLSECHO \"$message\"");
    return;
  }

  if (defined($ADE_VIEW_ROOT))
  {
    # If we are in a devlopment environment, we compare the message
    # in the program to the message in acfsus.msg and flag a mis-match.
    verify_message($message_id, $message);
  }

  my ($arg_list) = "";

  # process message arguments
  while (@message_args)
  {
    my ($arg) = shift(@message_args);
    $arg_list .= "\"$arg\" ";
  }

  # Create the clsecho options string
  my ($echo_string) = "-m $message_id ";

  # Set the severity level (-c option)
  if ($message_type == PRINT_ERROR)
  {
# TODO - disable until bug 9664524 gets fixed.
undef $CRS;
# end TODO
    if (defined($CRS))
    {
      # Force errors to go to the terminal, not just the logs.
      # Normally, the messages would just go to the logs, but when AGFW
      # sees the "CRS_ERROR:" "decoration string", it strips that off and
      # sends the remaining string to the terminal and the logs. See
      # ./has/src/crs/agentfw/framework/clsAgfwScript.cpp.
      #
      # See has/include/clsem.h for "decoration string" guidelines. For example,
      # if the string, anywhere, contains 'f', it will be converted into the
      # "one digit fractional secs." - what you see may not be what you get.
      $echo_string .= "-c err -d 'CRS_ERROR: ' ";
    }
    else
    {
      # We're called interactively.
      $echo_string .= "-c err ";
    }
  }
  else
  {
    # lib_inform_print()
    $echo_string .= "-c info ";
  }
  # timestamp user message
  $echo_string .= "-t ";
  # write to log and console, with timestamps in log but not on console.
  $echo_string .= "-z ";
  # finally append the message values arg_list 
  $echo_string .= $arg_list;
  # Send the message
  system ("$CLSECHO $echo_string");

} # end common_print

# verify_message
#
# Verify that the message in the print statement matches the message catalog.
#  
# Called only when ADE_VIEW_ROOT is set in the environment
#
sub verify_message
{
  my ($message_id, $message) = @_;

  # verify that the message matches the (acfsus.msg) catalog
  open CATALOG, "<$ORACLE_HOME/usm/mesg/acfsus.msg"
                                         or die "can't open msg file: $!";
  my ($line);
  while ($line = <CATALOG>)
  {
    my ($len) = length $message_id;

    # Convert the incoming msg id to the acfsus.msg 5 character format - if
    # needed. If the msg id is 5 chars (or more, [future]), no work is needed.
    if ($len < 5)
    {
      $message_id = sprintf("%05s", $message_id);
    }

    if ($line =~ /^$message_id/)
    {
      # the "split" separates the message in the file from what
      # preceeds it (e.g., 1234, 0, ") 
      my (@acfsus_msg) = split(/"/, $line);
      chomp($acfsus_msg[1]);
      # lose the trailing quote
      $acfsus_msg[1] =~ s/"$//;

      if ($acfsus_msg[1] ne $message)
      {
        print "message $message_id format mismatch:\n";
        print "acfsus.msg:\t>$acfsus_msg[1]<\n";
        print "$COMMAND:\t>$message<\n";
      }
      last;
    }
  }
  close (CATALOG);
} # end verify_message
 
#
# used for "sql alter diskgroup....
#
sub asm_do_stmt
{
  my ($dbh, $qry) = @_;
  my ($sth);
  my ($rv) = USM_SUCCESS;

  $rv = $dbh->do($qry);
  warn "$DBI::errstr\n" unless defined ($rv);

  if (!defined ($rv))
  {
    $rv = USM_FAIL;
  }

  return ($rv);
}

#
# used for sql select
#
sub asm_select_stmt
{
  my ($dbh, $qry) = @_;
  my ($sth);
  my ($rv);

  eval { $sth = $dbh->prepare($qry); };
  if (!defined ($sth))
  {
    return USM_FAIL;
  }

  eval { $rv = $sth->execute(); };
  warn "$DBI::errstr\n" unless defined ($rv);

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

  return ($sth);
}

#
# Fetch the next row on the table
#
sub asm_fetch_row
{
  my $sth = shift;
  my $row;
  return undef unless(defined $sth);
  eval { $row = $sth->fetchrow_hashref; };
  if ( $@ )
  {
     # We can't talk to the data base. Maybe ASM died.
     undef $row;
  }
  return $row;
}


# build_check_filename
#
# Return the name of the check_in_progress file name - 
# Input:
#   $tmp_dir - typically /tmp or \temp.
#   $name of the file - this could include directories. 
#
# Remove any semblance of directory structure in $name. So, on Unix, a 
# name of /one/two/three will return /tmp/_one_two_three_check.
#
sub build_check_filename
{
  my ($tmp_dir, $name) = @_;

  $name =~ s/\//_/g;
  $name =~ s/\\/_/g;
  my ($full_file_name) = catfile($tmp_dir, $name . "_check");
  return $full_file_name;
} # end build_check_filename

# time stamp ops

use constant SECONDS_PER_DAY => 86400;

# get_day_time_in_seconds
#
# Return number of seconds since local midnight.
#
sub get_day_time_in_seconds
{
  my ($sec, $min, $hour) = localtime(time);
  my ($seconds) = ($hour * 3600) + ($min * 60) + $sec;

  return $seconds;
} # end get_day_time_in_seconds

# time_limit_exceeded
#
# The main reason for a separate function is to handle time wrapping.
#
sub time_limit_exceeded
{
  my ($time_stamp, $time_limit) = @_;
  my ($current_time) = get_day_time_in_seconds();
  my ($time_diff);

  if ($current_time>= $time_stamp)
  {
    $time_diff= $current_time - $time_stamp;
  }
  else
  {
    # the timer has wrapped
    $time_diff= (SECONDS_PER_DAY - $time_stamp) + $current_time;
  }

  if ($time_diff< $time_limit)
  {
    # time limit not exceeded
    return 0;
  }

  return 1;
} # end time_limit_exceeded

sub trim($)
{
  my $string=shift;
  $string =~ s/^\s+//;
  $string =~ s/\s+$//;
  return $string;
}

#
# ACFS resource utility functions
#

# Add the ACFS registry resource and its type
sub add_acfs_registry
{
  my $owner = "root";
  my $asmgrp = getParam("ORA_ASM_GROUP");
  chomp $asmgrp;
  my $crsctl       = File::Spec->catfile($ORACLE_HOME, "bin", "crsctl");

  if ($asmgrp eq "")
  {
    # getParam failed.
    lib_error_print(9367, "Adding ACFS registry resource failed.");
    return USM_FAIL;
  }

  if ($OSNAME eq "Windows_NT" || $OSNAME eq "MSWin32")
  {
    $owner = "NT AUTHORITY\\SYSTEM";
  }

  # Add the ora.registry.acfs.type type
  my @cmd = ($crsctl, 'add', 'type', 'ora.registry.acfs.type',
             '-basetype', 'ora.local_resource.type',
             '-file', "$ORACLE_HOME/crs/template/registry.acfs.type");
  my $ret1 = system(@cmd);

  # Add the ora.registry.acfs resource
  @cmd = ($crsctl, "add", "resource", "ora.registry.acfs", "-attr",
          "ACL='owner:$owner:rwx,pgrp:$asmgrp:rwx,other::r--'",
          "-type", "ora.registry.acfs.type", "-f");
  my $ret = system(@cmd);

  if ($ret != 0)
  {
    lib_error_print(9367, "Adding ACFS registry resource failed.");
    return USM_FAIL;
  }
  else
  {
    lib_inform_print(9368, "Adding ACFS registry resource succeeded.");
    return USM_SUCCESS;
  }
}

# Delete the ACFS registry resource and its type
sub delete_acfs_registry
{
  my $crsctl      = File::Spec->catfile($ORACLE_HOME, "bin", "crsctl");
  my @cmd = ($crsctl, "delete", "resource", "ora.registry.acfs", "-f");
  my $ret1 = system(@cmd);

  @cmd = ($crsctl, 'delete', 'type', 'ora.registry.acfs.type');
  my $ret = system(@cmd);

  if ($ret != 0)
  {
    lib_error_print(9369, "Deleting ACFS registry resource failed.");
    return USM_FAIL;
  }
  else
  {
    lib_inform_print(9370, "Deleting ACFS registry resource succeeded.");
    return USM_SUCCESS;
  }
}

# Start the ACFS registry resource
sub start_acfs_registry
{
  my $crsctl      = File::Spec->catfile($ORACLE_HOME, "bin", "crsctl");
  my @cmd = ($crsctl, 'start', 'res', 'ora.registry.acfs');
  my $ret = system(@cmd);

  if ($ret != 0)
  {
    lib_error_print(9371, "Starting ACFS registry resource failed.");
    return USM_FAIL;
  }
  else
  {
    lib_inform_print(9372, "Starting ACFS registry resource succeeded.");
    return USM_SUCCESS;
  }
}

# Stop the ACFS registry resource
sub stop_acfs_registry
{
  my $crsctl      = File::Spec->catfile($ORACLE_HOME, "bin", "crsctl");
  my @cmd = ($crsctl, 'stop', 'res', 'ora.registry.acfs');
  my $ret = system(@cmd);

  if ($ret != 0)
  {
    lib_error_print(9373, "Stopping ACFS registry resource failed.");
    return USM_FAIL;
  }
  else
  {
    lib_inform_print(9374, "Stopping ACFS registry resource succeeded.");
    return USM_SUCCESS;
  }
}

# Add the USM drivers resource.
sub add_usm_drivers_resource
{
  my $crsctl      = File::Spec->catfile($ORACLE_HOME, "bin", "crsctl");
  my $asmgrp = getParam("ORA_ASM_GROUP");
  chomp $asmgrp;
  my $owner = "root";
  my $CRSDUSER = getParam("ORACLE_OWNER");
  chomp $CRSDUSER;

  if (($CRSDUSER eq "") || ($asmgrp eq ""))
  {
    # getParam failed.
    lib_error_print(9375, "Adding ADVM/ACFS drivers resource failed.");
    return USM_FAIL;
  }

  if ($OSNAME eq "Windows_NT" || $OSNAME eq "MSWin32")
  {
    $owner = "NT AUTHORITY\\SYSTEM";
  }

  my $ret = system($crsctl, "add", "resource", "ora.drivers.acfs", "-attr", "ACL='owner:$owner:rwx,pgrp:$asmgrp:r-x,other::r--,user:$CRSDUSER:r-x'", "-type", "ora.drivers.acfs.type","-init");

  if ($ret != 0)
  {
    lib_error_print(9375, "Adding ADVM/ACFS drivers resource failed.");
    return USM_FAIL;
  }
  else
  {
    lib_inform_print(9376, "Adding ADVM/ACFS drivers resource succeeded.");
    return USM_SUCCESS;
  }
}

# Delete the USM drivers resource.
sub delete_usm_drivers_resource
{
  my $crsctl      = File::Spec->catfile($ORACLE_HOME, "bin", "crsctl");
  my @cmd = ($crsctl, "delete", "resource", "ora.drivers.acfs", "-f", "-init");
  my $ret = system(@cmd);

  if ($ret != 0)
  {
    lib_error_print(9377, "Deleting ADVM/ACFS drivers resource failed.");
    return USM_FAIL;
  }
  else
  {
    lib_inform_print(9378, "Deleting ADVM/ACFS drivers resource succeeded.");
    return USM_SUCCESS;
  }
}

# Start the USM drivers resource.
sub start_usm_drivers_resource
{
  my $crsctl      = File::Spec->catfile($ORACLE_HOME, "bin", "crsctl");
  my @cmd = ($crsctl, "start", "resource", "ora.drivers.acfs", "-init");
  my $ret = system(@cmd);

  if ($ret != 0)
  {
    lib_error_print(9379, "Starting ADVM/ACFS drivers resource failed.");
    return USM_FAIL;
  }
  else
  {
    lib_inform_print(9380, "Starting ADVM/ACFS drivers resource succeeded.");
    return USM_SUCCESS;
  }
}

# usm_resource_exists
#     returns USM_SUCCESS if the specified resource exists.
#     returns USM_FAIL if the specified resource does not exist.
#     returns USM_FAIL if en error is encountered.
#
sub usm_resource_exists
{
  my ($resource) = @_;
  my ($which);
  my ($opt) = "";
  my ($ret) = USM_SUCCESS;

  if ($resource eq "registry")
  {
    $which = "ora.registry.acfs";
  }
  elsif ($resource eq "drivers")
  {
    $which = "ora.drivers.acfs";
    $opt = "-init";
  }
  else
  {
    lib_error_print(532, "invalid option: %s", $resource);
    $ret = USM_FAIL;
  }
    
  if ($ret == USM_SUCCESS)
  {
    open CRSCTL, "$ORACLE_HOME/bin/crsctl stat res $which $opt |";
    if ($?)
    {
      $ret = USM_FAIL;
    }
    else
    {
      while (<CRSCTL>)
      {
        if (/CRS-2613/)
        {
          # "Could not find resource '%s'."
          $ret = USM_FAIL;
          last;
        }
      }
      close CRSCTL;
    }
  }

  return $ret;
}

sub modify_usm_drivers_resource
{
  my $crsctl   = File::Spec->catfile($ORACLE_HOME, "bin", "crsctl");
  my $asmgrp   = getParam("ORA_ASM_GROUP");
  my $owner    = "root";
  my $CRSDUSER = getParam("ORACLE_OWNER");
  chomp $CRSDUSER;
  chomp $asmgrp;
  my @cmd;
  my $ret;
  my $ret1 = 0;

  if (($CRSDUSER eq "") || ($asmgrp eq ""))
  {
    # getParam failed.
    lib_error_print(9381,
                      "Modification of ADVM/ACFS drivers resource failed.");
    return USM_FAIL;
  }

  if ($OSNAME eq "Windows_NT" || $OSNAME eq "MSWin32")
  {
    $owner = "NT AUTHORITY\\SYSTEM";
  }

  @cmd = ($crsctl, "modify", "resource", "ora.drivers.acfs", "-attr",
       "ACL='owner:$owner:r-x,pgrp:$asmgrp:r-x,user:$CRSDUSER:r-x,other::r--'",
       "-init");
  $ret = system(@cmd);
  $ret1 = ($ret) ? 1 : $ret1;

  if ($ret1 != 0)
  {
    lib_error_print(9381,
                      "Modification of ADVM/ACFS drivers resource failed.");
    return USM_FAIL;
  }

  lib_inform_print(9382,
                      "Modification of ADVM/ACFS drivers resource succeeded.");
  return USM_SUCCESS;
}

sub modify_usm_registry_resource
{
  my $owner  = "root";
  my $crsctl = File::Spec->catfile($ORACLE_HOME, "bin", "crsctl");
  my $asmgrp = getParam("ORA_ASM_GROUP");
  chomp $asmgrp;
  my @cmd;
  my $ret;
  my $ret1 = 0;

  if ($asmgrp eq "")
  {
    # getParam failed.
    lib_error_print(9397,
                     "Modification of ADVM/ACFS registry resource failed.");
    return USM_FAIL;
  }

  if ($OSNAME eq "Windows_NT" || $OSNAME eq "MSWin32")
  {
    $owner = "NT AUTHORITY\\SYSTEM";
  }

  @cmd = ($crsctl, "modify", "resource", "ora.registry.acfs", "-attr",
          "ACL='owner:$owner:rwx,pgrp:$asmgrp:rwx,other::r--'");
  $ret = system(@cmd);
  $ret1 = ($ret) ? 1 : $ret1;

  # Modify the ora.registry.acfs resource
  @cmd = ($crsctl, "modify", "resource", "ora.registry.acfs", "-attr",
          "TYPE=ora.registry.acfs.type");
  $ret = system(@cmd);
  $ret1 = ($ret) ? 1 : $ret1;

  @cmd = ($crsctl, 'modify', 'resource', 'ora.registry.acfs',
             '-attr', 'SCRIPT_TIMEOUT=120');
  $ret = system(@cmd);
  $ret1 = ($ret) ? 1 : $ret1;

  @cmd = ($crsctl, 'modify', 'resource', 'ora.registry.acfs',
             '-attr', 'START_TIMEOUT=180');
  $ret = system(@cmd);
  $ret1 = ($ret) ? 1 : $ret1;

  @cmd = ($crsctl, 'modify', 'resource', 'ora.registry.acfs',
             '-attr', 'STOP_TIMEOUT=900');
  $ret = system(@cmd);
  $ret1 = ($ret) ? 1 : $ret1;

  @cmd = ($crsctl, 'modify', 'resource', 'ora.registry.acfs',
             '-attr', 'CHECK_TIMEOUT=600');
  $ret = system(@cmd);
  $ret1 = ($ret) ? 1 : $ret1;

  if ($ret1 != 0)
  {
    lib_error_print(9397,
                     "Modification of ADVM/ACFS registry resource failed.");
    return USM_FAIL;
  }

  lib_inform_print(9398,
                     "Modification of ADVM/ACFS registry resource succeeded.");
  return USM_SUCCESS;
}

sub getParam
{
  my $var = $_[0];
  my $paramFhdl;
  my ($paramfile) = File::Spec->catfile($ORACLE_HOME, "crs", "install", "crsconfig_params");

  if (! -e $paramfile)
  {
    lib_error_print(10285, "Pathname '%s' does not exist.", $paramfile);
    return "";
  }

  open ( $paramFhdl, "<$paramfile" );
  while ( my $line = <$paramFhdl> )
  {
    chomp $line;
    if ( $line =~ /^\s*$var/i )
    {
      my $param = $line;
      $param =~ s/^\s*($var)\s*=.*/$1/i;
      my $val = $line;
      $val =~ s/.*=\s*(.*)\s*/$1/i;
      close ( $paramFhdl );
      return $val;
    }
  }
  close ( $paramFhdl );
  return "";
}

sub lib_is_abs_path
{
  return lib_osds_is_abs_path(@_);
}

#
# Subroutine to print a warning header for a command
#
sub lib_print_cmd_header
{
  my ($cmd) = @_;
  $cmd=~ s/"/'/g;
  lib_inform_print (9390, 
                 "The command '%s' returned unexpected output that" .
                 " may be important for system configuration:", $cmd);
} # end lib_print_cmd_header

#
#  lib_run_func - Run specified library function
#
#  This function exposes library functions to the command line so that
#  they may be called by programs outside of the usm/src/cmds framework.
#  This function itself is currently exposed from acfsroot.pl and is
#  accessed as follows.
#
#    acfsroot lib_run_func <acfslib::function()> [args ...]
#
#  For example
#
#    acfsroot lib_run_func lib_is_mounted /my_mount
#
#  This function is useful for things like the USM CRS agents which are
#  written in C++ but can benefit from the functions in this Perl
#  library.  When used with UsmUtils::execCmd, execCmdRead, and
#  execCmdClose, the agents have a reasonably seamless interface into
#  the functions in this library.  The first use of this interface (and
#  hence the only current example) is UsmUtils::CheckLoadedDriversMismatch.
#
#  Note that lib_run_func can be made to expose functions in the command
#  libraries (e.e. acfsload.pm) by putting a call out to it (see
#  the call to this function in acfsroot.pl for an example) and
#  prefixing the function name with the library name.  E.g.
#  osds_acfslog::osds_verify_correct_driver_version().
#

sub lib_run_func
{
    my $fName = shift;      # name of library function to execute
    my $fP;                 # pointer to function

    # Make sure a function name is specified
    if ( ! $fName )
    {
        lib_error_print(9999,
          "ERROR: Internal error: lib_run_func function name not specified");
        return -1;
    }

    # Get a pointer to the function
    $fP = \&$fName;

    # Make sure the function is defined
    if ( ! defined ( &$fP ) )
    {
        lib_error_print(9999,
          "ERROR: Internal error: " .
          "Unknown lib_run_func function: $fName ");
        return -1;
    }

    # Call the function specifying the remaining arguments and returning
    # its return value
    return $fP->( @_ );

}

1;
