# Copyright (c) 2004, 2014, 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)
#    manuegar   08/19/13 - Backport manuegar_bug-13951456 from main
#    moreddy    04/12/11 - Backport moreddy_bug-12312348 from main
#    moreddy    01/13/11 - Backport moreddy_bug-8667038 from main
#    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     04/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_init);


use strict;
use Getopt::Long qw(:config no_ignore_case bundling);
use asmcmdglobal;
use asmcmdshare;

####################### ASMCMDVOL Global Variables ########################
our (%asmcmdvol_cmds) = ( volcreate   => {no_instance => 'True',
                                          flags       => {'G=s'=>'diskGroup',
                                                           'column=s'=>
                                                           'noOfColumns',
                                                           'primary=s'=>
                                                           'primaryRegion',
                                                           'secondary=s'=>
                                                           'secondaryRegion',
                                                           'redundancy=s'=>
                                                           'redundancyType',
                                                           's=s'=>'size',
                                                           'width=s'=>
                                                           'stripeWidth'}
                                                         },
                          voldelete   => {no_instance => 'True',
                                          flags       =>  {'G=s'=>'diskGroup'}
                                                         },
                          voldisable  => {no_instance => 'True',
                                          flags       =>  {'G=s'=>'diskGroup',
                                                           'a'=>'all'}
                                                         },
                          volenable   => {no_instance => 'True',
                                          flags       =>  {'G=s'=>'diskGroup',
                                                           'a'=>'all'}
                                                         },
                          volinfo     => {no_instance => 'True',
                                          flags       => {'G=s'=>'diskGroup',
                                                          'a'=>'all',
                                                          'show_diskgroup=s'=>
                                                          'displayDiskGroupName',
                                                          'show_volume=s'=>
                                                          'displayVolumeName'}
                                                         },
                          volresize   => {no_instance => 'True',
                                          flags       =>  {'G=s'=>'diskGroup',
                                                           'f'=>'force',
                                                           's=s'=>'size'}
                                                         },
                          volset      => {flags       =>  {'G=s'=>'diskGroup',
                                                           'primary=s'=>
                                                           'primaryRegion',
                                                           'secondary=s'=>
                                                           'secondaryRegion',
                                                           'usagestring=s'=>
                                                           'usageString2TagVol',
                                                           'mountpath=s'=>
                                                           'mountPath'}
                                                         },
                          volstat     => {flags       =>  {'G=s'=>'diskGroup',
                                                           'show_volume'=>
                                                           'displayVolumeName'}
                                                         }
                        );


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)
    {
      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) #
  my (@eargs);

  # Check if minimum number of required parameters are correct.
  # For example "foo bar -s 100M".
  if (@ARGV < 5) 
  {
    asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

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

  # get and validate diskgroup name (-G required)
  if (!defined($args{'G'}))
  {
    asmcmdshare_error_msg(8551, undef);
    return;
  }
  $dgname = $args{'G'};
  if ($dgname =~ /\W/)
  {
    @eargs = ($dgname);
    asmcmdshare_error_msg(8552, \@eargs);
    return;
  }

  # get and validate volume name (last arg - required)
  $volname = pop @ARGV;
  if ( ! $volname )
  {
    asmcmdshare_error_msg(8553, undef);
    return;
  }
  if ($volname =~ /\W/)
  {
    @eargs = ($volname);
    asmcmdshare_error_msg(8554, \@eargs);
    return;
  }
 
  # validate option parameters - "-s" is required .

  if (!defined($args{'s'}))
  {
    asmcmdshare_error_msg(8555, undef);
    return;
  }
  else
  {
    $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;

    # normally I would let SQL field this error but the SQL error message
    # is just awful.
    if (($redundancy ne 'UNPROTECTED') &&
        ($redundancy ne 'MIRROR') &&
        ($redundancy ne 'HIGH'))
    {
      @eargs = ($redundancy);
      asmcmdshare_error_msg(8556, \@eargs);
      return;
    }
  }
 
  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 #
  {
    #Run GetOpts on the value to support first unique chars for option
    push(@ARGV,"--".$args{'primary'});
    GetOptions (\%args, 'hot','cold');
    pop(@ARGV);

    if(defined($args{'hot'}) ^ defined($args{'cold'}))
    {
      if(defined($args{'hot'}))
      {
        $primary_zone = 'HOT';
        delete($args{'hot'});
      }
      elsif(defined($args{'cold'}))
      {
        $primary_zone = 'COLD';
        delete($args{'cold'});
      }
    }
    else
    {
      # part of the user input could not be parsed
      asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }
  }

  if (defined($args{'secondary'}))    # Get secondary zone #
  {
    #Run GetOpts on the value to support first unique chars for option
    push(@ARGV,"--".$args{'secondary'});
    GetOptions (\%args, 'hot','cold');
    pop(@ARGV);

    if(defined($args{'hot'}) ^ defined($args{'cold'}))
    {
      if(defined($args{'hot'}))
      {
        $mirror_zone = 'MIRRORHOT';
        delete($args{'hot'});
      }
      elsif(defined($args{'cold'}))
      {
        $mirror_zone = 'MIRRORCOLD';
        delete($args{'cold'});
      }
    }
    else
    {
      # part of the user input could not be parsed
      asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }
  }

  if (@ARGV != 0)
  {
    # part of the user input could not be parsed
    asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

  # 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 #
  my (@eargs);

  #  the diskgroup name option and volume name must be specified
  if (@ARGV != 3) 
  {
    asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

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

  # get and validate diskgroup name (-d required)
  if (!defined($args{'G'}))
  {
    asmcmdshare_error_msg(8551, undef);
    return;
  }
  $dgname = $args{'G'};
  if ($dgname =~ /\W/)
  {
    @eargs = ($dgname);
    asmcmdshare_error_msg(8552, \@eargs);
    return;
  }

  # Get the volume name.
  $volname = pop(@ARGV);
  if ( ! $volname )
  {
    asmcmdshare_error_msg(8553, undef);
    return;
  }
  if ($volname =~ /\W/)
  {
    @eargs = ($volname);
    asmcmdshare_error_msg(8554, \@eargs);
    return;
  }

  # 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 ($dgname);                                  # name of mounted diskgroup #
  my ($validargs);                                 # Is argument list valid? #
  # "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);

  # Verify valid set of arguments.
  $validargs = ( (!defined($args{'a'}) && defined($args{'G'}) && @ARGV == 1) ||
                 (defined($args{'a'}) && @ARGV == 0) );

  if (!$validargs)
  {
    asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

  if (defined($args{'G'}))    # Get diskgroup name. #
  {
    $dgname = $args{'G'};
  }
  else
  {
    $dgname = 'all';
  }

  if (defined($args{'a'}))    # Get volume name. #
  {
    $volname = 'all';
  }
  else
  {
    $volname = shift(@ARGV);
    # Put single quotes around the volume name for SQL 
    $volname = "'$volname'";
  }

  # Run SQL to enable/disable the volume.
  asmcmdvol_volenable_disable_sql ($dbh, $cmd_name, $dgname, $volname);
}

########
# 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 ($validargs);                                 # Is argument list valid? #
  my (@eargs);

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

  # Verify valid set of arguments.
  $validargs = ( (!defined($args{'a'}) && defined($args{'G'}) && @ARGV == 1) ||
                 (defined($args{'show_diskgroup'}) && @ARGV == 0) || 
                 (defined($args{'show_volume'}) && @ARGV == 0) || 
                 (defined($args{'a'}) && @ARGV == 0) );

  if (!$validargs)
  {
    asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

  if (defined($args{'G'}))    # Get diskgroup name. #
  {
    $dgname = $args{'G'};
  }
  else
  {
    $dgname = 'all';
  }

  if (defined($args{'a'}))    # Get volume name. #
  {
    $volname = 'all';
  }
  else
  {
    $volname = shift(@ARGV);
  }

  # The --show_diskgroup and --show_volume options are for internal use only 
  # and are not documented in "usage" or "help".
  if (defined($args{'show_diskgroup'}))    # output ONLY the diskgroup name
  {
    $device_name = $args{'show_diskgroup'};
  }

  if (defined($args{'show_volume'}))    # output ONLY the volume name
  {
    $device_name = $args{'show_volume'};
  }


  # 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 = '$dg_uc'";
    # perform the SQL select
    $sth = asmcmdshare_do_select($dbh, $sql);
    return undef if (! defined ($sth));
    $row = asmcmdshare_fetch($sth);
    asmcmdshare_finish($sth);
    if (!defined($row))
    {
      @eargs = ($dgname);
      asmcmdshare_error_msg(8001, \@eargs);
      return;
    }
  }

  # 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);
  return undef 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{'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
        printf("%s\n", lc($row->{'NAME'}));
      }
      elsif (defined($args{'show_volume'}))
      {
        # output only the volume name
        printf("%s\n", lc($row->{'VOLUME_NAME'}));
      }
      asmcmdshare_finish ($sth);
      return;
    }

    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'};

      printf "Diskgroup Name: $dgnam\n\n";

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

    }

    $report = 1;    # we found information to print

    # Format and print the volume information.
    printf "\t Volume Name: $row->{'VOLUME_NAME'}\n";
    printf "\t Volume Device: $row->{'VOLUME_DEVICE'}\n";
    printf "\t State: $row->{'STATE'}\n";
    printf "\t Size (MB): $row->{'SIZE_MB'}\n";
    printf "\t Resize Unit (MB): $row->{'RESIZE_UNIT_MB'}\n";
    printf "\t Redundancy: $row->{'REDUNDANCY'}\n";
    printf "\t Stripe Columns: $row->{'STRIPE_COLUMNS'}\n";
    printf "\t Stripe Width (K): $row->{'STRIPE_WIDTH_K'}\n";

    printf "\t Usage: ";
    if (defined($row->{'USAGE'}))
    {
      printf "$row->{'USAGE'}";
    }
    printf "\n";

    printf "\t Mountpath: ";
    if (defined($row->{'MOUNTPATH'}))
    {
      printf ("%s ", $row->{'MOUNTPATH'});
    }
    print "\n";

    print "\n";
  }

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

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

  if (!((@ARGV == 5) || (@ARGV == 6))) 
  {
    # must have dgname, volname, and -s newsize
    asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

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

  # get and validate diskgroup name (-G required)
  if (!defined($args{'G'}))
  {
    asmcmdshare_error_msg(8551, undef);
    return;
  }
  $dgname = $args{'G'};
  if ($dgname =~ /\W/)
  {
    @eargs = ($dgname);
    asmcmdshare_error_msg(8552, \@eargs);
    return;
  }

  # get and validate volume name (last arg - required)
  $volname = pop(@ARGV);
  if ( ! $volname )
  {
    asmcmdshare_error_msg(8553, undef);
    return;
  }
  if ($volname =~ /\W/)
  {
    @eargs = ($volname);
    asmcmdshare_error_msg(8554, \@eargs);
    return;
  }

  # get required -s parameter.
  $ret = asmcmdvol_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  # we will not have gotten here if -s was not specified
  $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);
  return undef if (! defined ($sth));

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

  # newsize must be in MB for the size test
  if (($newsize =~ m/k/) || ($newsize =~ m/K/))
  {
    $newsize =~ s/k//;
    $newsize =~ s/K//;
    if($newsize != 0)
    {
      $newsize = int (($newsize+1023) / 1024);
    }

  }
  elsif (($newsize =~ m/m/) || ($newsize =~ m/M/))
  {
    $newsize =~ s/m//;
    $newsize =~ s/M//;
  }
  elsif (($newsize =~ m/g/) || ($newsize =~ m/G/))
  {
    $newsize =~ s/g//;
    $newsize =~ s/G//;
    $newsize *= 1024;
  }
  elsif (($newsize =~ m/t/) || ($newsize =~ m/T/))
  {
    $newsize =~ s/t//;
    $newsize =~ s/T//;
    $newsize *= (1024 * 1024);
  }
  else
  {
   print "invalid size multiplier specified\n";
   return;
  }

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

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

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

  # tell SQL that the size is in MB
  $newsize = $newsize . "m";

  # 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) #
  my (@eargs);

  # Check if number of non-option parameters are correct.
  if (@ARGV < 3)
  {
    asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

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

  # get and validate diskgroup name (-G required)
  if (!defined($args{'G'}))
  {
    asmcmdshare_error_msg(8551, undef);
    return;
  }
  $dgname = $args{'G'};
  if ($dgname =~ /\W/)
  {
    @eargs = ($dgname);
    asmcmdshare_error_msg(8552, \@eargs);
    return;
  }

  # get and validate volume name (last arg - required)
  $volname = pop(@ARGV);
  if ( ! $volname )
  {
    asmcmdshare_error_msg(8553, undef);
    return;
  }
  if ($volname =~ /\W/)
  {
    @eargs = ($volname);
    asmcmdshare_error_msg(8554, \@eargs);
    return;
  }
 
  if (defined($args{'mountpath'}))
  {
    $mountpath = $args{'mountpath'};
  }

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

  if (defined($args{'primary'}))    # Get primary zone #
  {
    #Run GetOpts on the value to support first unique chars for option
    push(@ARGV,"--".$args{'primary'});
    GetOptions (\%args, 'hot','cold');
    pop(@ARGV);
 
    if(defined($args{'hot'}) ^ defined($args{'cold'}))
    {
      if(defined($args{'hot'}))
      {
        $primary_zone = 'HOT';
        delete($args{'hot'});
      }
      elsif(defined($args{'cold'}))
      {
        $primary_zone = 'COLD';
        delete($args{'cold'});
      }
    }
    else
    {
      # part of the user input could not be parsed
      asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }
  }

  if (defined($args{'secondary'}))    # Get secondary zone #
  {
    #Run GetOpts on the value to support first unique chars for option
    push(@ARGV,"--".$args{'secondary'});
    GetOptions (\%args, 'hot','cold');
    pop(@ARGV);

    if(defined($args{'hot'}) ^ defined($args{'cold'}))
    {
      if(defined($args{'hot'}))
      {
        $mirror_zone = 'MIRRORHOT';
        delete($args{'hot'});
      }
      elsif(defined($args{'cold'}))
      {
        $mirror_zone = 'MIRRORCOLD';
        delete($args{'cold'});
      }
    }
    else
    {
      # part of the user input could not be parsed
      asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }
  }

  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 #
 
  # 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 (@ARGV != 0)
  {
      $volname = pop(@ARGV);
      tr/a-z/A-Z/ for $volname; # must be upper case #
  }
 
  if (@ARGV != 0)
  {
    # part of the user input could not be parsed
    asmcmdvol_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

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

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

  # 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'};
      printf ("\nDISKGROUP NUMBER / NAME:  %d / %s\n", $dgnum, $dgnam);
      print "---------------------------------------\n";

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

      # Now print the volume header - one per diskgroup.
      printf "  VOLUME_NAME\n";
      printf "     %-16s%-16s%-16s%-16s\n",
                          "READS","BYTES_READ","READ_TIME","READ_ERRS"; 
      printf "     %-16s%-16s%-16s%-16s\n",
                          "WRITES","BYTES_WRITTEN","WRITE_TIME","WRITE_ERRS"; 
      print "  -------------------------------------------------------------\n";
    }

    $report = 1;    # we found information to print

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

    print "     ";
    printf ("%-16s", $row->{'READS'});
    printf ("%-16s", $row->{'BYTES_READ'});
    printf ("%-16s", $row->{'READ_TIME'});
    printf ("%-16s", $row->{'READ_ERRS'});
    print "\n";
    print "     ";
    printf ("%-16s", $row->{'WRITES'});
    printf ("%-16s", $row->{'BYTES_WRITTEN'});
    printf ("%-16s", $row->{'WRITE_TIME'});
    printf ("%-16s", $row->{'WRITE_ERRS'});
    print "\n";
  }

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

  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) = @_;
  my ($ret, $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 . ")";
  }

  # execute the SQL statement
  $ret = asmcmdshare_do_stmt($dbh, $qry);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}

########
# 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, $qry);

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

  # execute the SQL statement
  $ret = asmcmdshare_do_stmt($dbh, $qry);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}

########
# 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, $qry);

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

  # execute the SQL statement
  $ret = asmcmdshare_do_stmt($dbh, $qry);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}

########
# 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, $qry);

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

  # execute the SQL statement
  $ret = asmcmdshare_do_stmt($dbh, $qry);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}

########
# 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, $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);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}


########
# 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))
    {
      print STDERR 'usage: ' . $cmd_syntax . "\n";
      print STDERR '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
#   True if $arg is a command that can run without an ASM instance 
#   or does not exist, false otherwise.
#
# 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;

  return !defined ($asmcmdvol_cmds{ $arg }) ||
         !defined ($asmcmdvol_cmds{ $arg }{ no_instance });
}


########
# 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_print_cmds(sort(keys %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);

  #build the list of options to parse using GetOptions
  if($asmcmdvol_cmds{ $cmd }{ flags })
  {
    foreach $key(keys %{$asmcmdvol_cmds{ $cmd }{ flags }})
    {
      push(@string, $key);
    }
  }

  # Use GetOptions() from the Getopt::Long package to parse arguments for
  # internal commands.  These arguments are stored in @ARGV.
  if (!GetOptions($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;
}
1;
