MINI MINI MANI MO

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

# Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      asmcmdvol - ASM CoMmanD line interface for VOLume commands
#
#    DESCRIPTION
#      ASMCMD is a Perl utility that provides easy nagivation of files within
#      ASM diskgroups.  This module contains the functionality for
#      the ASM volume commands.
#
#    NOTES
#      usage: asmcmdcore [-p] [command]
#
#    MODIFIED  (MM/DD/YY)
#    wanhlee    02/09/17 - 22895947: Handle new kfod getclstype return value
#    averhuls   08/27/16 - Code coverage improvement - no functional change.
#    jochoa     08/15/16 - 24344967: Do not proceed further when volume 
#                          resize request is 0 
#    diguzman   05/30/16 - 19654070: Little change at _no_instance_cmd routine
#    mnollen    08/13/15 - #21569440 - export asmcmdvol_volcreate_sql
#    ykatada    10/03/14 - #19617921: use bind_param for strings in WHERE
#    manuegar   07/04/13 - Bug12991117 Remove all duplicate forward slashes
#                          from $device_name in asmcmdvol_process_volinfo().
#    manuegar   05/08/13 - Bug13951456 Support bind parameters
#    averhuls   03/01/13 - Fix volresize to generate error if vol < 1M.
#    averhuls   02/13/13 - volinfo perl fix if modules not loaded (bug
#                          16263194)
#    averhuls   01/04/13 - Tighten size parsing in volresize - bug 16043884.
#                          Fix Windows hostname case issue - bug 16043579.
#    bonelso    01/02/13 - Remobed some erroneous/redundant syntax checks.
#    mchimang   12/01/12 - 14142633: handle proxy connection in remote asm
#    averhuls   11/08/12 - Rename ora,proxy_asm to ora.proxy_advm.
#    pvenkatr   09/08/12 - 14321350: process volenable/disable multiple vols
#    bonelso    07/12/12 - Fix volcreate when using zones.
#    averhuls   05/31/12 - Enable/disable/stat volumes via proxy if remote ASM.
#    pvenkatr   08/24/11 - Removed flags hash table - using from XML
#    adileepk   06/20/11 - Connection Pooling.
#    moreddy    04/01/11 - 12312348 missing msg when resize invalid volume
#    adileepk   02/13/11 - Fix for HAS SRG issue, volinfo bug.
#    adileepk   11/09/10 - Adding changes to integrate the parser module with
#                          asmcmd.
#    moreddy    05/06/10 - bug 8667038 NLS for error messages
#    bonelso    04/17/10 - Fix asmcmdvol_error_msg() and the warning message
#                          in volresize when shrinking a volume.
#                          Bugs 94561723 and 9466091
#    bonelso    03/30/10 - Fix for LRG4522067
#    pvenkatr   03/25/10 - Syntax, command description, example all from XML
#    moreddy    03/22/10 - Adding more tracing
#    bonelso    03/17/10 - Fix asmcmdvol_error_msg() and the warning message
#                          in volresize when shrinking a volume.
#                          Bugs 94561723 and 9466091
#    moreddy    01/18/10 - Adding tracing messages
#    pvenkatr   09/03/09 - Help message from xml file
#    sanselva   06/22/09 - volcreate --secondary option not set correctly
#    sanselva   06/17/09 - correct check for --primary and --secondary
#    sanselva   04/12/09 - ASMCMD long options and consistency
#    heyuen     04/06/09 - fix asmcmd_error_msg
#    bonelso    03/04/09 - OFS -> ACFS and add support for K suffix in resize
#    heyuen     10/14/08 - use dynamic modules
#    bonelso    10/09/08 - Add back help text for volstat
#    gsanders   08/05/08 - fix merge errors from 07/28 version
#    heyuen     07/28/08 - use command properties array
#    gsanders   07/10/08 - fix bug 6963383 require -d dgname option
#    heyuen     03/09/08 - reorder help commands and improve help message.
#    averhuls   04/01/08 - add to volinfo documentation.
#    averhuls   03/05/08 - Add -v and -g undocumented switches to volinfo.
#    josmith    01/05/08 - Put single quotes around list of volumes
#    josmith    12/13/07 - Don't put single quotes around DG names
#    josmith    11/21/07 - Bug 6629935 - Need quotes around identifiers
#    averhuls   11/15/07 - Document volset needing quotes if options contain
#                          spaces
#    josmith    11/14/07 - Add resize_unit_mb column
#    averhuls   11/14/07 - Warn users before shrinking a non-ACFS volume.
#    josmith    11/02/07 - Change volume redundancy
#    josmith    10/17/07 - Support zones
#    josmith    06/20/07 - Make columns consistent with v
#    heyuen     05/25/07 - add return codes for errors
#    averhuls   04/10/07 - Bug 5958908 and 5958923 fixes 
#                          Re-document volset
#    averhuls   03/01/07 - add asmcmdvol_is_no_instance_cmd
#    josmith    09/03/06 - Add wildcard subroutine
#    averhuls   09/01/06 - Add process_help
#    averhuls   07/18/06 - Undocument volset as it intended as an internal 
#                          command. 
#    averhuls   06/23/06 - Created
#
#############################################################################
#
############################ Functions List #################################
#
#   asmcmdvol_process_volcreate
#   asmcmdvol_process_voldelete
#   asmcmdvol_process_voldisable
#   asmcmdvol_process_volenable
#   asmcmdvol_process_volinfo
#   asmcmdvol_process_volresize
#   asmcmdvol_process_volset
#   asmcmdvol_process_volstat
#
# SQL Routines
#   asmcmdvol_volcreate_sql
#   asmcmdvol_voldelete_sql
#   asmcmdvol_volenable_disable_sql
#   asmcmdvol_volresize_sql
#   asmcmdvol_volset_sql
#
# Help Routines
#   asmcmdvol_get_asmcmd_cmds
#
# Parameter Parsing Routines
#   asmcmdvol_is_cmd
#   asmcmdvol_parse_int_args


#############################################################################

package asmcmdvol;
require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(asmcmdvol_volcreate_sql);


use strict;
use DBI qw(:sql_types);
use Getopt::Long qw(:config no_ignore_case bundling);
use Scalar::Util qw(looks_like_number);
use Sys::Hostname;
use asmcmdglobal;
use asmcmdshare;
use asmcmdparser;
use asmcmdbase;

####################### ASMCMDVOL Global Variables ########################
our (%asmcmdvol_cmds) = ( volcreate   => {},
                          voldelete   => {},
                          voldisable  => {},
                          volenable   => {},
                          volinfo     => {},
                          volresize   => {},
                          volset      => {},
                          volstat     => {},
                        );

my $PROXY_RESOURCE = "ora.proxy_advm";
my $CRSCTL = "$ENV{ORACLE_HOME}/bin/crsctl";

sub is_asmcmd
{
  return 1;
}

########
# NAME
#   init
#
# DESCRIPTION
#   This function initializes the asmcmdvol module.  It simply registers
#   its callbacks with the asmcmdglobal module. Note that you must register
#   all 8 callbacks - even if the module has no corresponding function. 
#
# PARAMETERS
#   None
#
# RETURNS
#   Null
#
# NOTES
#   Only asmcmdcore_main() calls this routine.
########
sub init
{
  # All of the arrays defined in the asmcmdglobal module must be 
  # initialized here.  Otherwise, an internal error will result.
  push (@asmcmdglobal_command_callbacks, \&asmcmdvol_process_cmd);
  push (@asmcmdglobal_help_callbacks, \&asmcmdvol_process_help);
  push (@asmcmdglobal_command_list_callbacks, \&asmcmdvol_get_asmcmd_cmds);
  push (@asmcmdglobal_is_command_callbacks, \&asmcmdvol_is_cmd);
  push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmdvol_is_wildcard_cmd);
  push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdvol_syntax_error);
  push (@asmcmdglobal_no_instance_callbacks, \&asmcmdvol_is_no_instance_cmd);
  %asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdvol_cmds);

  #Perform ASMCMD consistency check if enabled
  if($asmcmdglobal_hash{'consistchk'} eq 'y')
  {
     if(!asmcmdshare_check_option_consistency(%asmcmdvol_cmds))
     {
       exit 1;
     }
  }
}

########
# NAME
#   process_cmd
#
# DESCRIPTION
#   This routine calls the appropriate routine to process the command 
#   specified by $asmcmdglobal_hash{'cmd'}.
#
# PARAMETERS
#   dbh       (IN) - initialized datavol handle, must be non-null.
#
# RETURNS
#   1 if command is found in the asmcmdvol module; 0 if not.
#
# NOTES
#   Only asmcmdcore_shell() calls this routine.
########
sub asmcmdvol_process_cmd 
{
  my ($dbh) = @_;
  my $succ = 0;

  # Get current command from global value, which is set by 
  # asmcmdvol_parse_asmcmd_args()and by asmcmdcore_shell().
  my $cmd = $asmcmdglobal_hash{'cmd'};

  # Declare and initialize hash of function pointers, each designating a 
  # routine that processes an ASMCMD volume command.
  my (%cmdhash) = (
                    volcreate  => \&asmcmdvol_process_volcreate,
                    voldelete  => \&asmcmdvol_process_voldelete,
                    voldisable => \&asmcmdvol_process_voldisable,
                    volenable  => \&asmcmdvol_process_volenable,
                    volinfo    => \&asmcmdvol_process_volinfo,
                    volresize  => \&asmcmdvol_process_volresize,
                    volset     => \&asmcmdvol_process_volset,
                    volstat    => \&asmcmdvol_process_volstat,
                  );


  if (defined ( $cmdhash{ $cmd } ))
  {
    # If user specifies a known command, then call routine to process it. #
    $cmdhash{ $cmd }->($dbh);
    $succ = 1;
  }

  return $succ;
}

########
# NAME
#   asmcmdvol_process_help
#
# DESCRIPTION
#   This function is the help function for the asmcmdvol module.
#
# PARAMETERS
#   command     (IN) - display the help message for this command.
#
# RETURNS
#   1 if command found; 0 otherwise.
########
sub asmcmdvol_process_help 
{
  my $command = shift;       # User-specified argument; show help on $cmd. #
  my $desc;                                # Command description for $cmd. #
  my $succ = 0;                         # 1 if command found, 0 otherwise. #

  if (asmcmdvol_is_cmd ($command)) 
  {                              # User specified a command name to look up. #
    $desc = asmcmdshare_get_help_desc($command);

    # The following check prevents an "undefined value" error message for
    # legal commands that have no help information.
    if (defined $desc)
    {
      asmcmdshare_print "$desc\n";
      $succ = 1;
    }
      else
    {
      $succ = 0;
    }
  }
  return $succ;
}

########
# NAME
#   asmcmdvol_process_volcreate
#
# DESCRIPTION
#   This top-level routine processes the volcreate command.
#
# PARAMETERS
#   dbh   (IN) - initialized handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdvol_process_cmd() can call this routine.
########
sub asmcmdvol_process_volcreate 
{
  my $dbh = shift;
  my %args;                             # Argument hash used by getopts(). #
  my $ret;                      # asmcmdvol_parse_int_args() return value. #
  my $volname;           # volume name to create (required user parameter) #
  my $dgname;        # name of mounted diskgroup (required user parameter) #
  my $size;                        # volume size (required user parameter) #
  my $stripe_columns;           # stripe columns (optional user parameter) #
  my $stripe_width;               # stripe width (optional user parameter) #
  my $redundancy;                   # redundancy (optional user parameter) #
  my $primary_zone;             # disk zone for primary extents (optional) #
  my $mirror_zone;             # disk zone for mirrored extents (optional) #

  # parse options
  $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  # get and validate diskgroup name (-G required)
  $dgname = $args{'G'};

  # get and validate volume name (last arg - required)
  $volname = pop @{$args{'volcreate'}};
 
  $size = $args{'s'};

  # initialize optional parameters
  $redundancy = 'default';
  $stripe_columns = 'default';
  $stripe_width = 'default';
 
  if (defined($args{'redundancy'}))    # Get redundancy. #
  {
    # user can specifiy in upper or lower case
    $redundancy = $args{'redundancy'};
    tr/a-z/A-Z/ for $redundancy;
  }
 
  if (defined($args{'column'}))    # Get stripe columns. #
  {
    $stripe_columns = $args{'column'};
  }
 
  if (defined($args{'width'}))    # Get stripe width. #
  {
    $stripe_width = $args{'width'};
  }
 
  if (defined($args{'primary'}))    # Get primary zone #
  {
    $primary_zone = $args{'primary'};
    tr/a-z/A-Z/ for $primary_zone;
  }

  if (defined($args{'secondary'}))    # Get secondary zone #
  {
    $mirror_zone = "MIRROR" . $args{'secondary'};
    tr/a-z/A-Z/ for $mirror_zone;
  }

  # Run SQL to create the volume.
  asmcmdvol_volcreate_sql ($dbh, $dgname, $volname,
                           $size, $redundancy,
                           $stripe_columns, $stripe_width,
                           $primary_zone, $mirror_zone);
}

########
# NAME
#   asmcmdvol_process_voldelete
#
# DESCRIPTION
#   This top-level routine processes the voldelete command.
#
# PARAMETERS
#   dbh   (IN) - initialized handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdvol_process_cmd() can call this routine.
########
sub asmcmdvol_process_voldelete 
{
  my $dbh = shift;
  my %args;                             # Argument hash used by getopts(). #
  my $ret;                      # asmcmdvol_parse_int_args() return value. #
  my $volname;                                     # volume name to delete #
  my $dgname;                                  # name of mounted diskgroup #

  # parse options
  $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  # get and validate diskgroup name (-d required)
  $dgname = $args{'G'};

  # Get the volume name.
  ($volname) = @{$args{'voldelete'}};

  # Run SQL to delete the volume.
  asmcmdvol_voldelete_sql ($dbh, $dgname, $volname);
}

########
# NAME
#   asmcmdvol_process_volenable
#   asmcmdvol_process_voldisable
#
# DESCRIPTION
#   This top-level routine processes the volenable command.
#   This top-level routine processes the voldisable command.
#
# PARAMETERS
#   dbh   (IN) - initialized datavol handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdvol_process_cmd() can call asmcmdvol_process_voldisable
#   and asmcmdvol_process_volenable.
#
#   Since the volenable and voldisable commands have the same arguments,
#   it makes sense to have the commands as a front end to a common function.
#   The only difference is whether "enable" or "disable" is sent to SQL.
########

sub asmcmdvol_process_voldisable 
{
  # add the "disable" command onto the front of the argument stack
  # The common routine will parse this.
  unshift @ARGV, 'disable';
  asmcmdvol_process_volenable_disable(@_);
}

sub asmcmdvol_process_volenable 
{
  # add the "enable" command onto the front of the argument stack
  # The common routine will parse this.
  unshift  @ARGV, 'enable';
  asmcmdvol_process_volenable_disable(@_);
}

# The common function for volenable and voldisable.
sub asmcmdvol_process_volenable_disable 
{
  my $dbh = shift;
  my %args;                             # Argument hash used by getopts(). #
  my $ret;                      # asmcmdvol_parse_int_args() return value. #
  my $cmd_name;                      # SQL "enable" or "disable" statement #
  my $volname;                             # volume name to enable/disable #
  my $vname;                                    # each volume name in loop #
  my $dgname;                                  # name of mounted diskgroup #
  my $have_proxy;                       # Set if node has a proxy instance #
  my $proxy_online;                  # Set if the proxy instance is online #
  my $switched_to_asm_proxy = 0;      # Set if we connect to the ASM proxy #
  my @eargs;                           # asmcmdshare_error_msg() arguments # 

  # "enable" or "disable" - NOT user supplied
  $cmd_name  = shift(@ARGV);

  # initialize required parameters. if the user does not specify,
  # all diskgroups and/or all volumes are enabled/disabled.
  $dgname = 'all';
  $volname = 'all';

  # validate parameters, if any. since this routine is NOT called directly
  # from asmcmdvol_process_cmd(), we have to play some games to get the 
  # user supplied command name.
  $ret = asmcmdvol_parse_int_args('vol' . $cmd_name, \%args);
  return unless defined ($ret);

  if (defined($args{'G'}))    # Get diskgroup name. #
  {
    # multiple diskgroups can be specified.  Make a list - comma separated.
    $dgname = "" ;
    foreach (@{$args{'G'}})
    {
      if ($dgname eq "" )
      {
        $dgname = $_ ;
      }
      else
      {
        $dgname = $dgname . "," . $_ ;
      }
    }
  }

  if (defined($args{'a'}))    # Get volume name. #
  {
    $volname = 'all';
  }
  elsif(defined($args{'vol' . $cmd_name}))
  {
    # multiple volumes can be specified, across diskgroups too.
    # make a comma separated list

    $volname = "";

    foreach ((@{$args{'vol'.$cmd_name}}))
    {
      $vname = $_ ;
      if ( $volname eq "" )
      {
        $volname = $vname ;
      }
      else
      {
        $volname = $volname . "," . $vname;
      }
    }
  }

  ($have_proxy, $proxy_online) = get_asm_proxy_state();

  if ($have_proxy && !$proxy_online)
  {
    @eargs = ('');
    # 9470 - "ASM proxy instance unavailable."
    asmcmdshare_error_msg(9470, \@eargs);
    # 9471 - "Cannot enable/disable volumes."
    asmcmdshare_error_msg(9471, \@eargs);
    return;
  }

  # If there is an online proxy AND our ORACLE_SID is that of the 
  # proxy, we simply use SQL. If our ORACLE_SID is NOT that of the proxy
  # instance, we connect to the proxy and use SQL.
  if ($proxy_online)
  {
    ($switched_to_asm_proxy, $dbh) = connect_to_asm_proxy($dbh);

    if (!defined($dbh))
    {
      @eargs = ('');
      # 9472 - "Failed to connect to the ASM proxy."
      asmcmdshare_error_msg(9472, \@eargs);
      return;
    }
  }

  asmcmdvol_volenable_disable_sql ($dbh, $cmd_name, $dgname, $volname);

  if ($switched_to_asm_proxy)
  {
    # Disconnect from the proxy and restore original state
    asmcmdbase_disconnect($dbh);
  }
}

########
# NAME
#   asmcmdvol_process_volinfo
#
# DESCRIPTION
#   This top-level routine processes the volinfo command.
#
# PARAMETERS
#   dbh   (IN) - initialized handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdvol_process_cmd() can call this routine.
########
sub asmcmdvol_process_volinfo 
{
  my $dbh = shift;
  my %args;                             # Argument hash used by getopts(). #
  my $ret;                      # asmcmdvol_parse_int_args() return value. #
  my $sql;                                             #SQL select command # 
  my $sth;                                         # SQL statement handle. #
  my $row;              # One row results returned from the SQL execution. #
  my $dgname;                                              #diskgroup name #
  my $volname;                                               # volume name #
  my $cur_dgname;                            # determines when to print dg #
  my $report;        # set when data is printed - used for error reporting #
  my $device_name;     # used only to report volume name or diskgroup name #
  my @eargs;                           # asmcmdshare_error_msg() arguments # 
  my $print_string;
  my $have_proxy;                       # Set if node has a proxy instance #
  my $proxy_online;                  # Set if the proxy instance is online #
  my $switched_to_asm_proxy = 0;        # Set if node has a proxy instance #

  # validate option parameters, if any.
  $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  if(defined($args{'all'}))
  {
    $dgname = 'all';
    $volname = 'all';
  }
  elsif (defined($args{'G'}))    # Get diskgroup name. #
  {
    $dgname = $args{'G'};
    if (defined($args{'a'}))    # Get volume name. #
    {
      $volname = 'all';
    }
    elsif(defined($args{'volinfo'}))
    {
      ($volname) = @{$args{'volinfo'}};
    }
  }
  
  if(defined($args{'volinfo'}))
  {
    ($volname) = @{$args{'volinfo'}};
  }

  if (defined($args{'show_diskgroup'}) || defined($args{'show_volume'}))    # output ONLY the diskgroup name
  {
    $device_name = $args{'show_diskgroup'} if defined $args{'show_diskgroup'};
    $device_name = $args{'show_volume'} if defined $args{'show_volume'};
    # Bug12991117 Remove all duplicate forward slashes from $device_name
    $device_name =~ s/\/{2,}/\//g;
  }

  ($have_proxy, $proxy_online) = get_asm_proxy_state();

  if ($have_proxy && !$proxy_online)
  {
    @eargs = ('');
    # 9470 - "ASM proxy instance unavailable."
    asmcmdshare_error_msg(9470, \@eargs);
    # 9473 - "Volume STATE will show as REMOTE."
    asmcmdshare_error_msg(9473, \@eargs);
    # Continue to do what we can on the current instance.
  }

  # If there is an online proxy AND our ORACLE_SID is that of the 
  # proxy, we simply use SQL. If our ORACLE_SID is NOT that of the proxy
  # instance, we connect to the proxy and use SQL.
  if ($proxy_online)
  {
    ($switched_to_asm_proxy, $dbh) = connect_to_asm_proxy($dbh);

    if (!defined($dbh))
    {
      @eargs = ('');
      # 9472 - "Failed to connect to the ASM proxy."
      asmcmdshare_error_msg(9472, \@eargs);
      return;
    }
  }

  # if a diskgroup is specified, does it exist?
  if (defined($args{'G'}))
  {
    my $dg_uc = uc($dgname);

    $sql = "select * from V\$ASM_DISKGROUP_STAT where name = ?";

    # perform the SQL select
    $sth = asmcmdshare_do_prepare($dbh, $sql);
    $sth->bind_param(1,$dg_uc,SQL_VARCHAR);
    asmcmdshare_do_execute($sth);
    goto done if (! defined ($sth));
    $row = asmcmdshare_fetch($sth);
    asmcmdshare_finish($sth);
    if (!defined($row))
    {
      @eargs = ($dgname);
      asmcmdshare_error_msg(8001, \@eargs);
      goto done;
    }
  }

  # Note1: All that we want from V$ASM_DISKGROUP is the diskgroup name.
  # Note2: it looks like diskgroups are sorted in dg number order - which is
  # what we want. But, just to be sure, we enforce sort order here. In 
  # addition, we also sort by volume name within the diskgroup.
  # Note3: The order of the fixed views is important! Both V$ASM_DISKGROUP
  # and V$ASM_VOLUME have a STATE column. The order below ensures that we get
  # the V$ASM_VOLUME state and not the V$ASM_DISKGROUP state.
  $sql = "select * from V\$ASM_DISKGROUP_STAT join V\$ASM_VOLUME
                   on V\$ASM_VOLUME.GROUP_NUMBER =
                      V\$ASM_DISKGROUP_STAT.GROUP_NUMBER
                   order by V\$ASM_DISKGROUP_STAT.GROUP_NUMBER,
                            V\$ASM_VOLUME.VOLUME_NAME"; 

  # perform the SQL select
  $sth = asmcmdshare_do_select($dbh, $sql);
  goto done if (! defined ($sth));

  # process the volumes
  $cur_dgname = 'none';
  $report = 0;
  while (defined ($row = asmcmdshare_fetch($sth)))
  {
    if (defined($args{'G'}) &&
        (uc($dgname) ne $row->{'NAME'}))
    {
      # a dgname was specified on the cmd line and this row isn't it - continue
      next;
    }
    if ((defined($args{'show_diskgroup'})) || (defined($args{'show_volume'})))
    {
      if ($device_name ne $row->{'VOLUME_DEVICE'})
      {
        # We're looking for a specific device and this isn't it - continue.
        next;
      }
    }
    elsif ((!defined($args{'all'})) && (!defined($args{'a'})) &&
        (uc($volname) ne $row->{'VOLUME_NAME'}))
    {
      # a volname was specified on the cmd line and this row isn't it - continue
      next;
    }

    if (defined($device_name))
    {
      # We have already verified that this is the device we are looking for.
      #
      # We are printing either the diskgroup name or the volume name
      # and NOTHING else.
      if (defined($args{'show_diskgroup'}))
      {
        # output only the diskgroup name
        $print_string = sprintf("%s\n", lc($row->{'NAME'}));
        asmcmdshare_print($print_string);
      }
      elsif (defined($args{'show_volume'}))
      {
        # output only the volume name
        $print_string = sprintf("%s\n", lc($row->{'VOLUME_NAME'}));
        asmcmdshare_print($print_string);
      }
      asmcmdshare_finish ($sth);
      goto done;
    }

    if ($cur_dgname ne $row->{'NAME'})
    {
      my($dgnum, $dgnam);

      # This is the first time that we've seen this disk group, so
      # print the dg number and name - remember that this list is sorted.

      # Perl complains if the printf line wraps.
      $dgnum = $row->{'GROUP_NUMBER'};
      $dgnam = $row->{'NAME'};

      $print_string = sprintf "Diskgroup Name: $dgnam\n\n";
      asmcmdshare_print($print_string);

      # Indicate that we've seen this diskgroup.
      $cur_dgname = $dgnam;

    }

    $report = 1;    # we found information to print

    # Format and print the volume information.

    # The 'VOLUME_DEVICE' is the only field that can possibly be NULL. This
    # can happen if the drivers are not loaded or if there is no libskvol
    # or if the ADVM device control files have the wrong permissions.
    my $vol_dev;
    $vol_dev = ($row->{'VOLUME_DEVICE'}) ? $row->{'VOLUME_DEVICE'} : "UNKNOWN";

    asmcmdshare_print "\t Volume Name: " . $row->{'VOLUME_NAME'} . "\n";
    asmcmdshare_print "\t Volume Device: " . $vol_dev . "\n";
    asmcmdshare_print "\t State: " . $row->{'STATE'} . "\n";
    asmcmdshare_print "\t Size (MB): " . $row->{'SIZE_MB'} . "\n";
    asmcmdshare_print "\t Resize Unit (MB): " . $row->{'RESIZE_UNIT_MB'} . "\n";
    asmcmdshare_print "\t Redundancy: " . $row->{'REDUNDANCY'} . "\n";
    asmcmdshare_print "\t Stripe Columns: " . $row->{'STRIPE_COLUMNS'} . "\n";
    asmcmdshare_print "\t Stripe Width (K): " . $row->{'STRIPE_WIDTH_K'} . "\n";

    $print_string = "\t Usage: ";
    if (defined($row->{'USAGE'}))
    {
      $print_string .= $row->{'USAGE'};
    }
    asmcmdshare_print($print_string . "\n");

    $print_string = "\t Mountpath: ";
    if (defined($row->{'MOUNTPATH'}))
    {
      $print_string .= $row->{'MOUNTPATH'} . " ";
    }
    asmcmdshare_print($print_string . "\n");

    asmcmdshare_print " \n";
  }

  if (!$report)
  {
    if (defined($args{'G'}) && !defined($args{'a'}))
    {
      $print_string = sprintf("volume %s not found in diskgroup %s\n", $volname, $dgname);
      asmcmdshare_print($print_string);
    }
    elsif (defined($args{'G'}))
    {
      $print_string = sprintf("diskgroup %s has no volumes or is not mounted\n", $dgname);
      asmcmdshare_print($print_string);
    }
    elsif ((defined($args{'show_diskgroup'})) || (defined($args{'show_volume'})))
    {
      $print_string = sprintf("no device name \"%s\" found\n", $device_name);
      asmcmdshare_print($print_string);
      
    }
    else
    {
      asmcmdshare_print("no volumes found\n");
    }
  }

done:
  if ($switched_to_asm_proxy)
  {
    # Disconnect from the proxy and restore original state
    asmcmdbase_disconnect($dbh);
  }

  asmcmdshare_finish ($sth);
}

########
# NAME
#   asmcmdvol_process_volresize
#
# DESCRIPTION
#   This top-level routine processes the volresize command.
#
# PARAMETERS
#   dbh   (IN) - initialized datavol handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdvol_process_cmd() can call this routine.
########
sub asmcmdvol_process_volresize 
{
  my $dbh = shift;
  my %args;                             # Argument hash used by getopts(). #
  my $ret;                      # asmcmdvol_parse_int_args() return value. #
  my $volname;                                     # volume name to resize #
  my $dgname;                                  # name of mounted diskgroup #
  my $newsize;                                        # new size of volume #
  my $cursize;                                    # current size of volume #
  my $testsize;           # test against cursize to check for reduced size # 
  my $usage;                                      # usage string of volume #
  my $force;                                                # force option #
  my $sql;                                          # SQL select statement #
  my $sth;                                          # SQL statement handle #
  my $row;               # One row results returned from the SQL execution #
  my @eargs;

  # parse options
  $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  # get and validate diskgroup name (-G required)
  $dgname = $args{'G'};

  # get and validate volume name (last arg - required)
  $volname = pop(@{$args{'volresize'}});

  $newsize = $args{'s'};

  # force option
  if (defined($args{'f'}))
  {
    $force = 1;
  }

  # check to see if this is a non ACFS volume.
  # If so and the new size is less than the current size, warn the user.
  $sql = "select * from V\$ASM_VOLUME join V\$ASM_DISKGROUP  
                   on V\$ASM_DISKGROUP.GROUP_NUMBER =
                            V\$ASM_VOLUME.GROUP_NUMBER";

  # perform the SQL select
  $sth = asmcmdshare_do_select($dbh, $sql);

  # process the volumes
  while (defined ($row = asmcmdshare_fetch($sth)))
  {
    tr/a-z/A-Z/ for $dgname;
    tr/a-z/A-Z/ for $volname;
    if (($dgname eq $row->{'NAME'}) && ($volname eq  $row->{'VOLUME_NAME'}))
    {
      $cursize = $row->{'SIZE_MB'};
      if (defined($row->{'USAGE'}))
      {
        $usage = $row->{'USAGE'};
      }
      else
      {
        $usage = "not defined";
      }
      last;
    }
  }
  asmcmdshare_finish($sth);

  # did we find the volume?
  if (!defined($cursize))
  {
    @eargs = ($dgname, $volname);
    asmcmdshare_error_msg(8557, \@eargs);
    return;
  }

  # convert SQL returned cursize from MB to KB.
  # We later compare the current size to the new size and issue a warning
  # if the new size is smaller.
  $cursize *= 1024;

  $newsize = uc $newsize;

  if ($newsize =~ m/K$/)
  {
    $newsize =~ s/K$//;

    if (!looks_like_number($newsize))
    {
      # "valid size modifier but invalid numeric size specified\n"
      asmcmdshare_error_msg(8559, \@eargs);
      return;
    }
    $testsize = $newsize;
    $newsize .= 'K';
  }
  elsif ($newsize =~ m/M$/)
  {
    $newsize =~ s/M$//;

    if (!looks_like_number($newsize))
    {
      # "valid size modifier but invalid numeric size specified\n"
      asmcmdshare_error_msg(8559, \@eargs);
      return;
    }
    $testsize = $newsize * 1024;
    $newsize .= 'M';
  }
  elsif ($newsize =~ m/G$/)
  {
    $newsize =~ s/G$//;

    if (!looks_like_number($newsize))
    {
      # "valid size modifier but invalid numeric size specified\n"
      asmcmdshare_error_msg(8559, \@eargs);
      return;
    }
    $testsize = $newsize * 1024 * 1024;
    $newsize .= 'G';
  }
  elsif ($newsize =~ m/T$/)
  {
    $newsize =~ s/T$//;

    if (!looks_like_number($newsize))
    {
      # "valid size modifier but invalid numeric size specified\n"
      asmcmdshare_error_msg(8559, \@eargs);
      return;
    }
    $testsize = $newsize * 1024 * 1024 * 1024;
    $newsize .= 'T';
  }
  else
  {
    #  "invalid size modifier specified"
    asmcmdshare_error_msg(8558, \@eargs);
    return;
  }

  if ($testsize == 0)
  {
    #  "invalid numeric size specified"
    asmcmdshare_error_msg(8559, \@eargs);
    return;
  }

  if (($testsize < $cursize) && ($usage ne "ACFS"))
  {
    my $response = 'x';

    if (!defined($force))
    {
      while (($response ne 'y') && ($response ne 'n'))
      {
        asmcmdshare_print "The requested size is smaller than the current size.\n";
        asmcmdshare_print "Data corruption may occur.\n";
        asmcmdshare_printprompt "Are you sure? [y/n]: ";
        $response = asmcmdshare_readstdin(); 
        chomp ($response);
      }

      if ($response eq 'n')
      {
        return;
      }
    }
  }

  # Run SQL to resize the volume.
  asmcmdvol_volresize_sql ($dbh, $dgname, $volname, $newsize);
}

########
# NAME
#   asmcmdvol_process_volset
#
# DESCRIPTION
#   This top-level routine processes the volset command.
#
# PARAMETERS
#   dbh   (IN) - initialized datavol handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdvol_process_cmd() can call this routine.
########
sub asmcmdvol_process_volset 
{
  my $dbh = shift;
  my %args;                             # Argument hash used by getopts(). #
  my $ret;                      # asmcmdvol_parse_int_args() return value. #
  my $dgname;                                  # name of mounted diskgroup #
  my $volname;                                        # target volume name #
  my $usage_string;                   # usage string to add to volume name #
  my $mountpath;                 # mount path string to add to volume name #
  my $primary_zone;             # disk zone for primary extents (optional) #
  my $mirror_zone;             # disk zone for mirrored extents (optional) #

  # parse options
  $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  # get and validate diskgroup name (-G required)
  $dgname = $args{'G'};

  # get and validate volume name (last arg - required)
  $volname = pop(@{$args{'volset'}});
 
  if (defined($args{'mountpath'}))
  {
    $mountpath = $args{'mountpath'};
  }

  if (defined($args{'usagestring'}))
  {
    $usage_string = $args{'usagestring'};
  }

  if (defined($args{'primary'}))    # Get primary zone #
  {
    $primary_zone = $args{'primary'};
    tr/a-z/A-Z/ for $primary_zone;
  }

  if (defined($args{'secondary'}))    # Get secondary zone #
  {
    $mirror_zone = "MIRROR" . $args{'secondary'};
    tr/a-z/A-Z/ for $mirror_zone;
  }

  if (!defined($usage_string) && !defined($mountpath) &&
      !defined($mirror_zone) && !defined($primary_zone))
  {
    asmcmdvol_process_help($asmcmdglobal_hash{'cmd'});
    return;
  }

  # Run SQL to add mountpath/usage string to the volume.
  asmcmdvol_volset_sql ($dbh, $dgname, $volname, $usage_string, $mountpath,
                        $primary_zone, $mirror_zone);
}

########
# NAME
#   asmcmdvol_process_volstat
#
# DESCRIPTION
#   This top-level routine processes the volstat command.
#
# PARAMETERS
#   dbh   (IN) - initialized datavol handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdvol_process_cmd() can call this routine.
########
sub asmcmdvol_process_volstat 
{
  my $dbh = shift;
  my %args;                             # Argument hash used by getopts(). #
  my $ret;                      # asmcmdvol_parse_int_args() return value. #
  my $sql;                                             #SQL select command # 
  my $sth;                                         # SQL statement handle. #
  my $row;              # One row results returned from the SQL execution. #
  my $dgname;                                              #diskgroup name #
  my $volname;                                               # volume name #
  my $cur_dgname;                            # determines when to print dg #
  my $report;        # set when data is printed - used for error reporting #
  my $print_string;
  my $have_proxy;                       # Set if node has a proxy instance #
  my $proxy_online;                  # Set if the proxy instance is online #
  my $switched_to_asm_proxy = 0;        # Set if node has a proxy instance #
  my @eargs;                           # asmcmdshare_error_msg() arguments # 

  # validate option parameters, if any.
  $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  $dgname = 'UNSPECIFIED';
  if (defined($args{'G'}))    # Get diskgroup name. #
  {
    $dgname = $args{'G'};
    tr/a-z/A-Z/ for $dgname;  # must be upper case #
  }

  # Get optional volume name
  $volname = 'UNSPECIFIED';
  if (defined($args{'volstat'}))
  {
      $volname = pop(@{$args{'volstat'}});
      tr/a-z/A-Z/ for $volname; # must be upper case #
  }
 
  # Note: it looks like diskgroups are sorted in dg number order - which is
  # what we want. But, just to be sure, we enforce sort order here. In 
  # addition, we also sort by volume name within the diskgroup.
  $sql = "select * from V\$ASM_DISKGROUP_STAT join V\$ASM_VOLUME_STAT
                   on V\$ASM_DISKGROUP_STAT.GROUP_NUMBER =
                            V\$ASM_VOLUME_STAT.GROUP_NUMBER
                   order by V\$ASM_VOLUME_STAT.GROUP_NUMBER,
                            V\$ASM_VOLUME_STAT.VOLUME_NAME"; 

  ($have_proxy, $proxy_online) = get_asm_proxy_state();

  if ($have_proxy && !$proxy_online)
  {
    @eargs = ('');
    # 9470 - "ASM proxy instance unavailable.
    asmcmdshare_error_msg(9470, \@eargs);
    # 9474 - "Volume statistics cannot be queried.
    asmcmdshare_error_print(9474, \@eargs);
    return;
  }

  # If there is an online proxy AND our ORACLE_SID is that of the 
  # proxy, we simply use SQL. If our ORACLE_SID is NOT that of the proxy
  # instance, we connect to the proxy and use SQL.
  if ($proxy_online)
  {
    ($switched_to_asm_proxy, $dbh) = connect_to_asm_proxy($dbh);

    if (!defined($dbh))
    {
      @eargs = ('');
      # 9472 - "Failed to connect to the ASM proxy."
      asmcmdshare_error_msg(9472, \@eargs);
      return;
    }
  }

  # perform the SQL select
  $sth = asmcmdshare_do_select($dbh, $sql);

  # process the volumes
  $cur_dgname = 'none';
  $report = 0;
  while (defined ($row = asmcmdshare_fetch($sth)))
  {
    if (($dgname ne 'UNSPECIFIED') &&
        ($dgname ne $row->{'NAME'}))
    {
      # a dgname was specified on the cmd line and this row isn't it - continue
      next;
    }
    if (($volname ne 'UNSPECIFIED') &&
        ($volname ne $row->{'VOLUME_NAME'}))
    {
      # a volname was specified on the cmd line and this row isn't it - continue
      next;
    }

    if ($cur_dgname ne $row->{'NAME'})
    {
      my($dgnum, $dgnam);

      # This is the first time that we've seen this disk group, so
      # print the dg number and name - remember that this list is sorted.

      # Perl complains if the printf line wraps.
      $dgnum = $row->{'GROUP_NUMBER'};
      $dgnam = $row->{'NAME'};
      $print_string = sprintf ("\nDISKGROUP NUMBER / NAME:  %d / %s\n", $dgnum, $dgnam);
      asmcmdshare_print($print_string);
      asmcmdshare_print("---------------------------------------\n");

      # Indicate that we've seen this diskgroup.
      $cur_dgname = $dgnam;

      # Now print the volume header - one per diskgroup.
      asmcmdshare_print("  VOLUME_NAME\n");
      $print_string = sprintf("     %-16s%-16s%-16s%-16s\n",
                          "READS","BYTES_READ","READ_TIME","READ_ERRS");
      asmcmdshare_print($print_string);
      $print_string = sprintf("     %-16s%-16s%-16s%-16s\n",
                          "WRITES","BYTES_WRITTEN","WRITE_TIME","WRITE_ERRS"); 
      asmcmdshare_print($print_string);
      asmcmdshare_print("  -------------------------------------------------------------\n");
    }

    $report = 1;    # we found information to print

    # Format and print the volume statistics.
    $print_string = sprintf ("  %s\n",   $row->{'VOLUME_NAME'});
    asmcmdshare_print($print_string);

    $print_string  = sprintf ("     %-16s", $row->{'READS'});
    $print_string .= sprintf ("%-16s", $row->{'BYTES_READ'});
    $print_string .= sprintf ("%-16s", $row->{'READ_TIME'});
    $print_string .= sprintf ("%-16s\n", $row->{'READ_ERRS'});
    asmcmdshare_print($print_string);
    $print_string = sprintf ("     %-16s", $row->{'WRITES'});
    $print_string .= sprintf ("%-16s", $row->{'BYTES_WRITTEN'});
    $print_string .= sprintf ("%-16s", $row->{'WRITE_TIME'});
    $print_string .= sprintf ("%-16s\n", $row->{'WRITE_ERRS'});
    asmcmdshare_print($print_string);

  }

  if (!$report)
  {
    if (defined($args{'G'}) && defined($args{'show_volume'}))
    {
      $print_string = sprintf("volume %s not found in diskgroup %s\n", 
         $args{'show_volume'}, $args{'G'});
      asmcmdshare_print($print_string);
    }
    elsif (defined($args{'G'}))
    {
      $print_string = sprintf("diskgroup %s has no volumes or is not mounted\n",
         $args{'G'});
      asmcmdshare_print($print_string);
    }
    elsif (defined($args{'show_volume'}))
    {
      $print_string = sprintf("no volumes \"%s\" found in any mounted diskgroup\n",
         $args{'show_volume'});
      asmcmdshare_print($print_string);
    } 
    else
    {
      asmcmdshare_print("no volumes found\n");
    }
  }

  if ($switched_to_asm_proxy)
  {
    # Disconnect from the proxy and restore original state
    asmcmdbase_disconnect($dbh);
  }

  asmcmdshare_finish ($sth);
}

########
# NAME
#   asmcmdvol_volcreate_sql
#
# DESCRIPTION
#   This routine constructs the SQL used to create an ASM volume.
#   It calls asmcmdshare_do_stmt() to execute it.
#
# PARAMETERS
#   dbh        (IN) - initialized datavol handle, must be non-null.
#   dgname     (IN) - name of diskgroup in which to create the volume.
#   volname    (IN) - name of volume to be created within the diskgroup.
#   size       (IN) - size of the volume.
#   redundancy (IN) - redundancy type of the volume.
#   size       (IN) - size of the stripe width.
#   size       (IN) - number of stripe columns.
#
# RETURNS
#   Null.
########
sub asmcmdvol_volcreate_sql 
{
  my ($dbh, $dgname, $volname, $size,
      $redundancy, $stripe_columns, $stripe_width,
      $primary_zone, $mirror_zone, $retqry) = @_;
  my $ret;
  my $qry;

  # The minimum required of statements to the SQL command
  $qry = "alter diskgroup $dgname add volume '$volname' size $size";

  # Optional SQL statements follow

  # if redundancy is not specified, you get the diskgroup default
  if ($redundancy ne 'default')
  {
    $qry = $qry . " $redundancy";
  }

  # if the stripe width is not specified, you get the AVD default
  if ($stripe_width ne 'default')
  {
    $qry = $qry . " stripe_width $stripe_width";
  }

  # if the stripe columns are not specified, you get the AVD default
  if ($stripe_columns ne 'default')
  {
    $qry = $qry . " stripe_columns $stripe_columns";
  }

  # Add disk zone attributes, if any.
  if (defined($primary_zone) or defined($mirror_zone))
  {
    $qry = $qry . " ATTRIBUTE(";

    if (defined($primary_zone))
    {
      $qry = $qry . " $primary_zone";
    }

    if (defined($mirror_zone))
    {
      $qry = $qry . " $mirror_zone";
    }

    $qry = $qry . ")";
  }
  # Return query if requested
  return $qry if ($retqry);
  # execute the SQL statement
  $ret = asmcmdshare_do_stmt($dbh, $qry);
}

########
# NAME
#   asmcmdvol_volenable_disable_sql
#
# DESCRIPTION
#   This routine constructs the SQL used to enable/disable  an ASM volume.
#   It calls asmcmdshare_do_stmt() to execute it.
#
# PARAMETERS
#   dbh        (IN) - initialized datavol handle, must be non-null.
#   cmd_name   (IN) - "enable" or "disable".
#   dgname     (IN) - name of diskgroup or "all".
#   volname    (IN) - name of volume or "all".
#
# RETURNS
#   Null.
########
sub asmcmdvol_volenable_disable_sql 
{
  my ($dbh, $cmd_name, $dgname, $volname) = @_;
  my $ret;
  my $qry;

  $qry = "alter diskgroup $dgname $cmd_name volume $volname";

  # execute the SQL statement
  $ret = asmcmdshare_do_stmt($dbh, $qry);
}

########
# NAME
#   asmcmdvol_volresize_sql
#
# DESCRIPTION
#   This routine constructs the SQL used to resize an ASM volume.
#   It calls asmcmdshare_do_stmt() to execute it.
#
# PARAMETERS
#   dbh        (IN) - initialized datavol handle, must be non-null.
#   dgname     (IN) - name of diskgroup in which to resize the volume.
#   volname    (IN) - name of volume within the diskgroup to be resized.
#   newsize    (IN) - new size of volume.
#
# RETURNS
#   Null.
########
sub asmcmdvol_volresize_sql 
{
  my ($dbh, $dgname, $volname, $newsize) = @_;
  my $ret;
  my $qry;

  $qry = "alter diskgroup $dgname resize volume '$volname' size $newsize";

  # execute the SQL statement
  $ret = asmcmdshare_do_stmt($dbh, $qry);
}

########
# NAME
#   asmcmdvol_voldelete_sql
#
# DESCRIPTION
#   This routine constructs the SQL used to delete an ASM volume.
#   It calls asmcmdshare_do_stmt() to execute it.
#
# PARAMETERS
#   dbh        (IN) - initialized datavol handle, must be non-null.
#   dgname     (IN) - name of diskgroup containing the volume.
#   volname    (IN) - name of the volume to be deleted.
#
# RETURNS
#   Null.
########
sub asmcmdvol_voldelete_sql 
{
  my ($dbh, $dgname, $volname) = @_;
  my $ret;
  my $qry;

  $qry = "alter diskgroup $dgname drop volume '$volname'";

  # execute the SQL statement
  $ret = asmcmdshare_do_stmt($dbh, $qry);
}

########
# NAME
#   asmcmdvol_volset_sql
#
# DESCRIPTION
#   This routine constructs the SQL used to add a usage string and/or a
#   mountpath to an ASM volume. It calls asmcmdshare_do_stmt() to execute it.
#
# PARAMETERS
#   dbh          (IN) - initialized datavol handle, must be non-null.
#   dgname       (IN) - name of diskgroup containing the volume.
#   volname      (IN) - name of the volume to add the usage string to.
#   usage_string (IN) - usage string to add to the volume.
#   primary_zone (IN) - zone for the primary extents (hot|cold)
#   mirror_zone  (IN) - zone for the mirrored extents (hot|cold)
#
# RETURNS
#   Null.
########
sub asmcmdvol_volset_sql 
{
  my ($dbh, $dgname, $volname, $usage_string, $mountpath, 
      $primary_zone, $mirror_zone) = @_;
  my $ret;
  my $qry;

  $qry = "alter diskgroup $dgname modify volume '$volname'"; 

  # Add disk zone attributes, if any.
  if (defined($primary_zone) or defined($mirror_zone))
  {
    $qry = $qry . " ATTRIBUTE(";

    if (defined($primary_zone))
    {
      $qry = $qry . " $primary_zone";
    }

    if (defined($mirror_zone))
    {
      $qry = $qry . " $mirror_zone";
    }

    $qry = $qry . ")";
  }

  if (defined($mountpath))
  {
    $qry = $qry . " mountpath '$mountpath'";
  }

  if (defined($usage_string))
  {
    $qry = $qry . " usage '$usage_string'";
  }

  # execute the SQL statement
  $ret = asmcmdshare_do_stmt($dbh, $qry);
}


########
# NAME
#   asmcmdvol_syntax_error
#
# DESCRIPTION
#   This function prints the correct syntax for a command to STDERR, used 
#   when there is a syntax error.  This function is responsible for 
#   only ASMCMD volume commands.
#
# PARAMETERS
#   cmd   (IN) - user-entered command name string.
#
# RETURNS
#   1 if the command belongs to this module; 0 if command not found.
#
# NOTES
#   These errors are user-errors and not internal errors.  They are of type
#   record, not signal.  
# 
#   N.B. Functions in this module can call this function directly, without
#   calling the asmcmdshare::asmcmdshare_syntax_error equivalent.  The
#   latter is used only by the asmcmdcore module.
########
sub asmcmdvol_syntax_error 
{
  my $cmd = shift;
  my $cmd_syntax;                               # Correct syntax for $cmd. #
  my $succ = 0;

  #display syntax only for commands from this module
  if(asmcmdvol_is_cmd($cmd))
  {
    $cmd_syntax = asmcmdshare_get_help_syntax($cmd);  # Get syntax for $cmd. #
    $cmd_syntax = asmcmdshare_trim_str ($cmd_syntax);   # Trim blank spaces #

    if (defined ($cmd_syntax))
    {
      asmcmdshare_printstderr 'usage: ' . $cmd_syntax . "\n";
      asmcmdshare_printstderr 'help:  help ' . $cmd . "\n";
      $succ = 1;
    }

    if ($asmcmdglobal_hash{'mode'} eq 'n')
    {
      $asmcmdglobal_hash{'e'} = -1;
    }
  }

  return $succ;
}

########
# NAME
#   asmcmdvol_is_no_instance_cmd
#
# DESCRIPTION
#   This routine determines if a command can run without an ASM instance.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   1 if $arg is a command that can run without an ASM instance or it does not
#   belong to this module
#   0 if $arg is a command that needs to connect to an ASM instance
#   -1 if $arg is a command that may use an ASM instance.
#
# NOTES
#   The hash list contains asmcmd volume functions that* DO* require that 
#   an ASM instance be running - all volume commands.
########
sub asmcmdvol_is_no_instance_cmd 
{
  my $arg = shift;
  my ($rc);

  return 1 unless defined($asmcmdvol_cmds{$arg});

  $rc = asmcmdshare_get_cmd_noinst($arg);
  if ($rc eq "true")
  {
    return 1;
  }
  elsif ($rc eq "undef")
  {
    return -1;
  }

  return 0;
}


########
# NAME
#   asmcmdvol_get_asmcmd_cmds
#
# DESCRIPTION
#   This routine constructs a string that contains a list of the names of all 
#   ASMCMD internal commands and returns this string.
#
# PARAMETERS
#   None.
#
# RETURNS
#   A string contain a list of the names of all ASMCMD internal commands.
#
# NOTES
#   Used by the help command and by the error command when the user enters
#   an invalid internal command.
#
#   IMPORTANT: the commands names must be preceded by eight (8) spaces of
#              indention!  This formatting is mandatory.
########
sub asmcmdvol_get_asmcmd_cmds 
{
  return asmcmdshare_filter_invisible_cmds(%asmcmdvol_cmds);
}

########
# NAME
#   asmcmdvol_is_cmd
#
# DESCRIPTION
#   This routine checks if a user-entered command is one of the known ASMCMD
#   internal commands that belong to the vol module.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is one of the known commands, false otherwise.
########
sub asmcmdvol_is_cmd 
{
  my $arg = shift;

  return defined ( $asmcmdvol_cmds{ $arg } );
}

########
# NAME
#   asmcmdvol_parse_int_args
#
# DESCRIPTION
#   This routine parses the arguments for flag options for ASMCMD internal
#   commands.  
#
# PARAMETERS
#   cmd      (IN)  - user-entered command name string.
#   args_ref (OUT) - hash of user-specified flag options for a command, 
#                    populated by getopts().
#
# RETURNS
#   Zero on success; undefined on error.
#
# NOTES
#   $cmd must already be verified as a valid ASMCMD internal command.
########
sub asmcmdvol_parse_int_args
{
  my ($cmd, $args_ref) = @_;
  my @string;
  my $key;

  # Use asmcmdparser_parse_issued_command() from the asmcmdparser package to parse arguments for
  # internal commands.  These arguments are stored in @ARGV.
  if (!asmcmdparser_parse_issued_command($cmd, $args_ref, \@string))
  {
    # Print correct command format if syntax error. #
    asmcmdvol_syntax_error($cmd);
    return undef;
  }
  return 0;
}

#########
# NAME
#   asmcmdvol_is_wildcard_cmd
#
# DESCRIPTION
#   This routine determines if an ASMCMDVOL command allows the use 
#   of wild cards.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is a command that can take wildcards as part of its argument, 
#   false otherwise.
########
sub asmcmdvol_is_wildcard_cmd 
{
  my $arg = shift;

  return 0;
}

#########
# NAME
#   get_asm_proxy_state
#
# DESCRIPTION
#   Get the state of the ASM proxy resource.
#
# PARAMETERS
#   arg   (IN) - None.
#
# RETURNS
#    2 values - proxy exists / proxy is (or not) online.
########
sub get_asm_proxy_state
{
  my $have_proxy = 0;
  my $proxy_online = 0;
  my $hostname = get_hostname();

  # The CRSCTL line below is functionally equivalent to 
  #      $CRSCTL status resource $PROXY_RESOUCE -n $hostname
  # with the exception that the command is now insensitive to the $hostname
  # case. That '-n' is sensitive to the host name case is bug 16080860.
  open PROXY, "$CRSCTL status resource -w \"(NAME = $PROXY_RESOURCE) AND (LAST_SERVER eqi $hostname)\" |";
  while (<PROXY>)
  {
    if ($_ =~ /CRS-/)
    {
      # Could not find resource (CRS-2613).
      # No Oracle Clusterware components configured (CRS-4047).
      # etc.
      $have_proxy = 0;
      $proxy_online = 0;
      last;
    }
    elsif ($_ =~ /^STATE=ONLINE/)
    {
      # on line.
      $have_proxy = 1;
      $proxy_online = 1;
      last;
    }
    elsif ($_ =~ /^STATE=/)
    {
      # STATE could be OFFLINE, UNKNOWN, etc. 
      # Unless ONLINE, treat all other states as OFFLINE
      $have_proxy = 1;
      $proxy_online = 0;
      last;
    }
  }
  close (PROXY);

  return ($have_proxy, $proxy_online);
}

#########
# NAME
#   get_asm_proxy_sid
#
# DESCRIPTION
#   Get the SID of the ASM proxy instance.
#
# PARAMETERS
#   arg   (IN) - None.
#
# RETURNS
#    proxy SID.
########
sub get_asm_proxy_sid
{
  my $proxy_sid;
  my $hostname = get_hostname();

  # The CRSCTL line below is functionally equivalent to 
  #      $CRSCTL status resource $PROXY_RESOUCE -n $hostname
  # with the exception that the command is now insensitive to the $hostname
  # case. That '-n' is sensitive to the host name case is bug 16080860.
  open SID, "$CRSCTL status resource -p -w \"(NAME = $PROXY_RESOURCE) AND (LAST_SERVER eqi $hostname)\" |";
  while (<SID>)
  {
    if ($_ =~ /^GEN_USR_ORA_INST_NAME/)
    {
      my (@array) = split /=/, $_;
      $proxy_sid = $array[-1];
      chomp $proxy_sid;
      last;
    }
  }
  close SID;

  return $proxy_sid;
}

#########
# NAME
#   get_hostname
#
# DESCRIPTION
#   Get the hostname of the system
#
# PARAMETERS
#   arg   (IN) - None.
#
# RETURNS
#    hostname.
########
sub get_hostname
{
  my $hostname = hostname();

  #remove domain name from hostname (if any).
  my @array = split /\./, $hostname;
  $hostname = $array[0];

  return $hostname;
}

#########
# NAME
#   connect_to_asm_proxy
#
# DESCRIPTION
#   Connect to the ASM proxy (if required).
#
# PARAMETERS
#   arg   (IN) - ASM dbh.
#
# RETURNS
#                                    switched_to_proxy  dbh
#                                    ---------------------------
#    case: switched to the proxy               1        proxy
#    case: failed to connect to the proxy      0        undef
#    case: already connected to proxy          0        original
#    case: failed to detect the proxy SID      0        original
########
sub connect_to_asm_proxy
{
  my ($dbh) = @_;
  my $orig_sid = $ENV{ORACLE_SID};             # SID in effect when called #
  my $proxy_sid;
  my $switched_to_proxy = 0;
  my $flex_cluster = 0;
  my $clus_mode;                                            # Cluster mode #
  my $stor_access;                                   # Storage Access Mode #
  
  # Environment variable 'ORACLE_HOME' will be set, otherwise
  # ASMCMD init code will bail out.

  # directories in which kfod can be found.
  my (@patharr) =("$ENV{'ORACLE_HOME'}/bin/",
                  "$ENV{'ORACLE_HOME'}/rdbms/bin/");

  my (@buf) = asmcmdshare_execute_tool ("kfod",
                                        ".exe",
                                        "op=getclstype",
                                        \@patharr);
  # only one line output.
  chomp($buf[0]); # Remove the newline character at the end of the string #
  # split the "[cluster mode] - [storage access mode]" information
  ($clus_mode, $stor_access) = split / - /, $buf[0];

  if ($clus_mode eq 'ASM cluster : Flex mode enabled')
  {
    $flex_cluster = 1;
  }

  if ($flex_cluster)
  {

    if (defined($asmcmdglobal_hash{'proxy_dbh'}) && 
        asmcmdshare_check_dbh_alive($asmcmdglobal_hash{'proxy_dbh'}))
    {
      # let $switched_to_proxy = 0, since we cache the proxy connection
      # in the flex cluster. This is released during shutdown
      return ($switched_to_proxy, $asmcmdglobal_hash{'proxy_dbh'});
    }
  }

  $proxy_sid = get_asm_proxy_sid();

  if (!defined($proxy_sid))
  {
    my @eargs = ('');
    # 9475 - "ASM proxy SID not found\n");
    asmcmdshare_error_msg (9475, \@eargs);
    return $switched_to_proxy, $dbh;
  }

  # 1. In case of flex cluster regardless of whether the SID is equal to proxy
  # instance SID or not, the connection established in asmcmdbase_connect is
  # always to the ASM instance.
  # Hence in flex cluster, we have to establish connection to the proxy 
  # instance
  # 2. In case of classic clusters, if sid is equal to proxy sid, the earlier
  # connection established in asmcmdbase_connect would be to Proxy instance.
  # Hence no need to connect again.
  if ($flex_cluster)
  {
    $ENV{ORACLE_SID} = $proxy_sid;
    $dbh = asmcmdvol_proxy_connect();
    $ENV{ORACLE_SID} = $orig_sid;
  }
  elsif ($proxy_sid ne $orig_sid)
  {
    $ENV{ORACLE_SID} = $proxy_sid;
    $dbh = asmcmdbase_connect(undef);

    if (defined($dbh))
    {
      $switched_to_proxy = 1;
    }
    $ENV{ORACLE_SID} = $orig_sid;
  }

  return $switched_to_proxy, $dbh;
}

########
# NAME
#   asmcmdvol_proxy_connect
#
# DESCRIPTION
#   The user who wants to connect to local ASM proxy instance should set the
#   value of ORACLE_SID environment variable to the ASM proxy instance SID. The
#   ORACLE_SID value and ORACLE_HOME value would be used to establish the
#   connection to the local ASM proxy instance.
#
# PARAMETERS
#   None.
#
# RETURNS
#   DBI Handle to the local ASM proxy instance
#
# NOTES
#
########
sub asmcmdvol_proxy_connect
{
  my $usr;
  my $pswd;
  my $contype;
  my $driver;
  my %session_mode;

  $contype = $asmcmdglobal_hash{'contyp'};

  # Verify connection admin type, sysdba or sysasm, and assign proper ora #
  # session mode. (sysasm = 32768 or sysdba = 2)                          #
  if (($contype =~ /^sysdba$/i))
  {
     $session_mode{'ora_session_mode'} = 2;
  }
  else
  {
    $session_mode{'ora_session_mode'} = 32768;
  }

  $session_mode{'PrintError'} = 0;
  $driver = 'dbi:Oracle:';

  $asmcmdglobal_hash{'proxy_dbh'} = DBI->connect($driver, $usr, $pswd,
                                                 \%session_mode);

  if(!defined($asmcmdglobal_hash{'proxy_dbh'}))
  {
    # 9470 - failed to connect to the ASM proxy instance
    asmcmdshare_error_msg(9472, undef);
    asmcmdshare_trace(3, $DBI::errstr, 'y', 'n');
    return undef;
  }
  else
  {
    my $instance_name =
                asmcmdshare_get_instance_name($asmcmdglobal_hash{'proxy_dbh'});
    asmcmdshare_trace(3, "Successfully connected to ASM PROXY instance "
                      ."$instance_name", 'y', 'n');
  }

  return $asmcmdglobal_hash{'proxy_dbh'};
}

OHA YOOOO