MINI MINI MANI MO

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

# 
# $Header: rdbms/src/client/tools/asmcmd/modules/asmcmdfgrp.pm /main/8 2016/09/23 20:19:03 diguzman Exp $
#
# asmcmdfgrp.pm
# 
# Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      asmcmdfgrp - ASM CoMmanD line interface for File GRouP operations
#
#    DESCRIPTION
#      This module contains the interface for ASMCMD/ASM filegroup-related and
#      operations, such as creation, removing and listing the contents of
#      V$ASM_FILEGROUP.
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    diguzman    09/20/16 - 21270117: implement move file to file group command
#    diguzman    06/29/16 - 23584826: lsfg display used quota
#    diguzman    05/30/16 - 19654070: Little change at _no_instance_cmd routine
#    diguzman    05/10/16 - 23232867: Convert to string only client type field
#    diguzman    04/12/16 - 22855558: lsfg diplay names instead of numbers
#    diguzman    02/02/16 - 21204059: fix output of lsfg w/ -G & --filegorup
#    bhshanmu    07/27/15 - bhshanmu_perl_522_changes_for_rdbms
#    diguzman    11/12/14 - Creation
# 
############################ Functions List #################################
#
# init
# is_asmcmd
# asmcmdfgrp_chfg_char
# asmcmdfgrp_chfg_end
# asmcmdfgrp_chfg_start
# asmcmdfgrp_get_asmcmd_cmds
# asmcmdfgrp_get_cmd_desc 
# asmcmdfgrp_get_cmd_syntax 
# asmcmdfgrp_is_cmd 
# asmcmdfgrp_is_no_instance_cmd 
# asmcmdfgrp_is_wildcard_cmd 
# asmcmdfgrp_mkfg_char
# asmcmdfgrp_mkfg_end
# asmcmdfgrp_mkfg_start
# asmcmdfgrp_parse_int_args 
# asmcmdfgrp_process_chfg 
# asmcmdfgrp_process_cmd 
# asmcmdfgrp_process_help 
# asmcmdfgrp_process_lsfg 
# asmcmdfgrp_process_mkfg 
# asmcmdfgrp_process_rmfg 
# asmcmdfgrp_process_mvfile
# asmcmdfgrp_syntax_error 
#
#############################################################################

package asmcmdfgrp;
require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(asmcmdfgrp_init
                 %asmcmdfgrp_cmds);

use strict;
use DBI qw(:sql_types);
use Getopt::Long qw(:config no_ignore_case bundling);
use Data::Dumper;
use asmcmdglobal;
use asmcmdshare;
use asmcmdparser;
use XML::Parser;
use asmcmdexceptions;
use asmcmdxmlexceptions;

use List::Util qw[min max];

######################### ASMCMDFGRP Global Constants #########################
# ASMCMD Column Header Names:
# Below are the names of the column headers for lsfg. 
our (%asmcmdfgrp_lsfg_header) = (
                                 'file_group' ,   'File Group' ,
                                 'disk_group' ,   'Disk Group' ,
                                 'quota_group',   'Quota Group',
                                 'used_quota_mb', 'Used Quota MB',
                                 'client_type',   'Client Type',
                                 'client_name',   'Client Name',
                                 'property',      'Property',
                                 'value',         'Value',
                                 'file_type',     'File Type'
                                );

######################### ASMCMDFGRP Global Variables #########################

our (%asmcmdfgrp_cmds) = ('mkfg'   => {},
                          'rmfg'   => {},
                          'chfg'   => {},
                          'lsfg'   => {},
                          'mvfile' => {});

our ($fgstmt)                  =  'dummy';
our ($xml_error)               =  'dummy';
our (@asmcmdfgrp_parser_state) = ('dummy');
our (@asmcmdfgrp_parser_fgrp)  = ('dummy');
our (@asmcmdfgrp_parser_ppty)  = ('dummy');
our ($asmcmdfgrp_parser_text)  = ('dummy') ;      # to parse for invalid text
our (@asmcmdfgrp_parser_quorum);

sub is_asmcmd
{
  return 1;
}

########
# NAME
#   asmcmdfgrp_init
#
# DESCRIPTION
#   This function initializes the asmcmdfgrp module.  For now it simply 
#   registers its callbacks with the asmcmdglobal module.
#
# 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,      \&asmcmdfgrp_process_cmd);
  push (@asmcmdglobal_help_callbacks,         \&asmcmdfgrp_process_help);
  push (@asmcmdglobal_command_list_callbacks, \&asmcmdfgrp_get_asmcmd_cmds);
  push (@asmcmdglobal_is_command_callbacks,   \&asmcmdfgrp_is_cmd);
  push (@asmcmdglobal_is_wildcard_callbacks,  \&asmcmdfgrp_is_wildcard_cmd);
  push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdfgrp_syntax_error);
  push (@asmcmdglobal_no_instance_callbacks,  \&asmcmdfgrp_is_no_instance_cmd);

  %asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdfgrp_cmds);

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

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

  # Get current command from global value, which is set by 
  # asmcmdfgrp_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 ASMCMDfgrp command.
  my (%cmdhash) = (mkfg   => \&asmcmdfgrp_process_mkfg,
                   rmfg   => \&asmcmdfgrp_process_rmfg,
                   chfg   => \&asmcmdfgrp_process_chfg,
                   lsfg   => \&asmcmdfgrp_process_lsfg,
                   mvfile => \&asmcmdfgrp_process_mvfile);

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

  return $succ;
}

########
# NAME
#   asmcmdfgrp_mkfg_start
#
# DESCRIPTION
#   This function processes the start of xml tags in mkfg.
#
# PARAMETERS
#   expat   (IN) - expat parser object
#   element (IN) - tag element name
#   attrs   (IN) - tag element attributes
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_mkfg() calls this function.
########
sub asmcmdfgrp_mkfg_start
{
  my ($expat, $element, %ppty) = @_;
  my (@eargs);
  my ($name);  # file group NAME
  my ($dgnam); # Disk Group NAMe
  my ($cttyp); # ClienT TYPe
  my ($ctnam); # ClienT NAMe
  my ($fltyp); # FiLe TYPe

  if ($element eq 'filegroup')
  {
    if (@asmcmdfgrp_parser_state != 0)
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }
    push (@asmcmdfgrp_parser_state, $element);

    if (defined($ppty{'name'}))
    {
      $name = uc $ppty{'name'};
    }
    else
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }

    if (defined($ppty{'dg'}))
    {
      $dgnam = uc $ppty{'dg'};
    }
    else
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }

    if (defined($ppty{'client_type'}))
    {
      $cttyp = uc $ppty{'client_type'};

      # Only these three client types are supported
      unless ($cttyp eq 'DATABASE' || $cttyp eq 'CLUSTER' || $cttyp eq 'VOLUME')
      {
        asmcmdshare_error_msg(9391, undef);
        $xml_error = 1;
        return;
      }
    }
    else
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }

    if (defined($ppty{'client_name'}))
    {
      $ctnam = uc $ppty{'client_name'};
    }
    else
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }

    $fgstmt = "ALTER DISKGROUP $dgnam ADD FILEGROUP $name $cttyp $ctnam ";

    @asmcmdfgrp_parser_fgrp = ();
    @asmcmdfgrp_parser_ppty = ();
  }
  elsif ($element eq 'p')
  {
    my ($string, $name, $value, $clause);
    if (!defined($asmcmdfgrp_parser_state[$#asmcmdfgrp_parser_state]) ||
        $asmcmdfgrp_parser_state[$#asmcmdfgrp_parser_state] ne 'filegroup')
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }
    push (@asmcmdfgrp_parser_state, $element);
    
    if (defined ($ppty{'name'}))
    {
      $name = $ppty{'name'};
    }
    if (defined ($ppty{'value'}))
    {
      $value = $ppty{'value'};
    }
    if (defined ($ppty{'file_type'}))
    {
      $fltyp = $ppty{'file_type'};
    }

    if (defined ($fltyp))
    {
      $clause = qq('$fltyp.$name' = '$value');
    }
    else
    {
      $clause = qq('$name' = '$value');
    }
    push (@asmcmdfgrp_parser_ppty, $clause);
  }
  else
  {
    @eargs = ($element);
    asmcmdshare_error_msg(9390, \@eargs);
    $xml_error = 1;
    return;
  }
}

########
# NAME
#   asmcmdfgrp_mkfg_end
#
# DESCRIPTION
#   This function processes the end of xml tags in mkfg.
#
# PARAMETERS
#   expat   (IN) - expat parser object
#   element (IN) - tag element name
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_mkfg() calls this function.
########
sub asmcmdfgrp_mkfg_end
{
  my ($expat, $element) = @_;

  pop @asmcmdfgrp_parser_state;
 
  if ($element eq 'filegroup')
  {
    if (@asmcmdfgrp_parser_ppty > 0)
    {
      $fgstmt .= uc "SET " . join(', ', @asmcmdfgrp_parser_ppty);
      @asmcmdfgrp_parser_ppty = ();
    }
  }
}


########
# NAME
#   asmcmdfgrp_mkfg_char
#
# DESCRIPTION
#   This function processes the text value of xml node  in mkfg.
#
# PARAMETERS
#   expat   (IN) - expat parser object
#   string  (IN) - one line of text from text node
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_mkfg() calls this function.
########
sub asmcmdfgrp_mkfg_char
{
  my ($expat, $string) = @_ ;

  # in case of multi-line, this callback is called for each line,
  # append and collect all lines together
  if ( length (chomp($string)) > 0 )
  {
    $asmcmdfgrp_parser_text .= $string ;
  }
}

########
# NAME
#   asmcmdfgrp_process_mkfg
#
# DESCRIPTION
#   This function creates a file group.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_cmd() calls this function.
########
sub asmcmdfgrp_process_mkfg 
{
  my ($dbh) = shift;

  my (%args);
  my ($args_ref);
  my ($qry, $ret);
  my ($parser, $file);
  my ($string_args) = '';
  my ($hdls);
  my (@eargs);

  $xml_error = 0;

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

  $args_ref = $args{'mkfg'};
  # check for a configuration file
  if ($args_ref->[0] =~ m/\.xml$/)
  {
    my ($rc) = asmcmdshare_xml_exists($args_ref->[0]);
    return unless defined($rc);

    $file = $args_ref->[0];
  }
  else
  { 
    # The argument does not look like an XML file name. We'll try to parse it
    # as an XML string.
    $string_args = join(' ', @{$args_ref});
  }

  @asmcmdfgrp_parser_state = ();
  $fgstmt = "";

  # We do not expect any text values for any nodes.
  $asmcmdfgrp_parser_text  = "" ;

  # specify the handler callbacks
  $hdls = { Start => \&asmcmdfgrp_mkfg_start,
            End   => \&asmcmdfgrp_mkfg_end,
            Char  => \&asmcmdfgrp_mkfg_char };
  $parser = XML::Parser->new(Handlers     => $hdls,
                             ErrorContext => 5);

  eval
  {
    if (defined($file))
    {
      $parser->parsefile($file);
    }
    else
    {
      $parser->parse($string_args);
    }
  };
  if (asmcmdxmlexceptions::catch())
  { 
    my (@msg, $err);
    $err = asmcmdxmlexceptions::getErrmsg();
    @msg = split(/\n/, $err);
    if ($msg[$#msg] =~ m/Parser.pm/)
    {
      pop @msg;
    }

    $err = join("\n", @msg) ."\n";
    @eargs = ($err);
    asmcmdshare_error_msg(9395, \@eargs);
    return;
  }

  # error out, if any of the nodes has text values.  simply print the text.
  $asmcmdfgrp_parser_text =~ s/\s+// ;

  if (length($asmcmdfgrp_parser_text) > 0)
  {
    $xml_error = 1 ;
    @eargs = ($asmcmdfgrp_parser_text);

    asmcmdshare_error_msg(9395, \@eargs);
    return ;
  }

  if ($xml_error == 1)
  {
    return;
  }

  # Run SQL. #
  asmcmdshare_trace(3, $fgstmt, 'y', 'n');
  $ret = asmcmdshare_do_stmt($dbh, $fgstmt);
}

########
# NAME
#   asmcmdfgrp_process_rmfg
#
# DESCRIPTION
#   This function deletes a fg.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_cmd() can call this routine.
########
sub asmcmdfgrp_process_rmfg 
{
  my ($dbh) = shift;

  my (%args);                             # Argument hash used by getopts(). #
  my (%norm);      # See asmcmdshare_normalize_path() return value comments. #
  my ($ret);                     # asmcmdfgrp_parse_int_args() return value. #
  my ($path);                          # User-entered raw path for deletion. #
  my ($qry);                                          # SQL query statement. #
  my ($cascade) = '';
  my ($dg);
  my ($name);
  my ($mode);

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

  # TODO: Enable this option once Bug 20951338 is fixed.
  # $cascade = ' CASCADE' if (defined($args{'r'}));
  $dg      = shift @{$args{'rmfg'}};
  $name    = shift @{$args{'rmfg'}};

  $qry = uc "ALTER DISKGROUP $dg DROP FILEGROUP $name" . $cascade;

  $ret = asmcmdshare_do_stmt($dbh, $qry);

  return;
}

########
# NAME
#   asmcmdfgrp_process_mvfile
#
# DESCRIPTION
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_cmd() can call this routine.
########
sub asmcmdfgrp_process_mvfile
{
  my $dbh = shift;
  my $ret;
  my $file;
  my $gnum;
  my $dgname;
  my $fgname;
  my $qry;
  my @eargs;
  my %args;
  my %norm;

  $ret = asmcmdfgrp_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);

  if (defined($args{'G'}))
  {
    $dgname = uc($args{'G'});
    $gnum   = asmcmdshare_get_gnum_from_gname($dbh, $args{'G'});
    if (!defined ($gnum))
    {
      @eargs = ($dgname);
      # ASMCMD-8001: diskgroup '%s' does not exist or is not mounted
      asmcmdshare_error_msg(8001, \@eargs);
      return;
    }
  }

  if (defined($args{'filegroup'}))
  {
    $fgname = uc($args{'filegroup'});
    unless (defined(asmcmdshare_filegroup_exists($dbh, $fgname)))
    {
      my (@eargs) = ($fgname);
      # ASMCMD-8609: file group '%s' does not exist
      asmcmdshare_error_msg(8609, \@eargs);
      return;
    }
  }

  foreach $file (@{$args{'mvfile'}})
  {
    my $i;
    my $alias;
    my @dg_nums;
    my @paths;

    # Replace Windows style '\\' to Unix style '\/'
    $file =~ s/\\/\//g;
    %norm = asmcmdshare_normalize_path($dbh, $file, 0, \$ret);
    next unless ($ret == 0);

    @paths   = @{ $norm{'path'} };

    unless (@paths > 0) # The file name should expand at least to one file
    {
      @eargs = (uc($file));
      # ASMCMD-8014 "file '%s' does not exist"
      asmcmdshare_error_msg(8014, \@eargs);
      next;
    }

    @dg_nums = @{ $norm{'gnum'} };

    for ($i = 0; $i < @paths; $i++) 
    {
      # The specified file must exist in the specified disk group
      next unless ($dg_nums[$i] == $gnum);

      $alias = $paths[$i];

      $qry = "ALTER DISKGROUP $dgname MOVE FILE '$alias' TO FILEGROUP $fgname";
      $ret = asmcmdshare_do_stmt($dbh, $qry);
    }
  }
}

########
# NAME
#   asmcmdfgrp_chfg_start
#
# DESCRIPTION
#   This function processes the start of xml tags in chfg.
#
# PARAMETERS
#   expat   (IN) - expat parser object
#   element (IN) - tag element name
#   attrs   (IN) - tag element attributes
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_chfg() calls this function.
########
sub asmcmdfgrp_chfg_start
{
  my ($expat, $element, %ppty) = @_;
  my (@eargs);
  my ($name);  # file group NAME
  my ($dgnam); # Disk Group NAMe
  my ($cttyp); # ClienT TYPe
  my ($ctnam); # ClienT NAMe
  my ($fltyp); # FiLe TYPe

  if ($element eq 'filegroup')
  {
    if (@asmcmdfgrp_parser_state != 0)
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }
    push (@asmcmdfgrp_parser_state, $element);

    if (defined($ppty{'name'}))
    {
      $name = uc $ppty{'name'};
    }
    else
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }

    if (defined($ppty{'dg'}))
    {
      $dgnam = uc $ppty{'dg'};
    }
    else
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }

    $fgstmt = "ALTER DISKGROUP $dgnam MODIFY FILEGROUP $name ";

    @asmcmdfgrp_parser_fgrp = ();
    @asmcmdfgrp_parser_ppty = ();
  }
  elsif ($element eq 'p')
  {
    my ($string, $name, $value, $clause);
    if (!defined($asmcmdfgrp_parser_state[$#asmcmdfgrp_parser_state]) ||
        $asmcmdfgrp_parser_state[$#asmcmdfgrp_parser_state] ne 'filegroup')
    {
      asmcmdshare_error_msg(9391, undef);
      $xml_error = 1;
      return;
    }
    push (@asmcmdfgrp_parser_state, $element);
    
    if (defined ($ppty{'name'}))
    {
      $name = $ppty{'name'};
    }
    if (defined ($ppty{'value'}))
    {
      $value = $ppty{'value'};
    }
    if (defined ($ppty{'file_type'}))
    {
      $fltyp = $ppty{'file_type'};
    }

    if (defined ($fltyp))
    {
      $clause = qq('$fltyp.$name' = '$value');
    }
    else
    {
      $clause = qq('$name' = '$value');
    }
    push (@asmcmdfgrp_parser_ppty, $clause);
  }
  else
  {
    @eargs = ($element);
    asmcmdshare_error_msg(9390, \@eargs);
    $xml_error = 1;
    return;
  }
}

########
# NAME
#   asmcmdfgrp_chfg_end
#
# DESCRIPTION
#   This function processes the end of xml tags in chfg.
#
# PARAMETERS
#   expat   (IN) - expat parser object
#   element (IN) - tag element name
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_chfg() calls this function.
########
sub asmcmdfgrp_chfg_end
{
  my ($expat, $element) = @_;

  pop @asmcmdfgrp_parser_state;
 
  if ($element eq 'filegroup')
  {
    if (@asmcmdfgrp_parser_ppty > 0)
    {
      $fgstmt .= uc "SET " . join(', ', @asmcmdfgrp_parser_ppty);
      @asmcmdfgrp_parser_ppty = ();
    }
  }
}


########
# NAME
#   asmcmdfgrp_chfg_char
#
# DESCRIPTION
#   This function processes the text value of xml node  in chfg.
#
# PARAMETERS
#   expat   (IN) - expat parser object
#   string  (IN) - one line of text from text node
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_chfg() calls this function.
########
sub asmcmdfgrp_chfg_char
{
  my ($expat, $string) = @_ ;

  # in case of multi-line, this callback is called for each line,
  # append and collect all lines together
  if ( length (chomp($string)) > 0 )
  {
    $asmcmdfgrp_parser_text .= $string ;
  }
}

########
# NAME
#   asmcmdfgrp_process_chfg
#
# DESCRIPTION
#   This function changes the attributes of a file group.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_cmd() can call this routine.
########
sub asmcmdfgrp_process_chfg 
{
  my ($dbh) = shift;

  my (%args);                             # Argument hash used by getopts(). #
  my ($args_ref);
  my (%norm);      # See asmcmdshare_normalize_path() return value comments. #
  my ($ret);                 # asmcmdfgrp_parse_int_args() return value. #
  my ($path);                                        # Look under this path. #
  my ($maxval);  # Reference index value for non-directory aliases in 10gR2. #
  my ($ref_id);                  # Reference index value for an alias entry. #
  my ($gnum);                                           # Disk group number. #
  my (@eargs);                                   # Array of error arguments. #
  my (@list);          # List of returned results from the select statement. #
  my ($row);                                     # One row results in @list. #
  my ($hdls);
  my ($parser);
  my ($file);
  my ($string_args);

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

  $args_ref = $args{'chfg'};
  # check for a configuration file
  if ($args_ref->[0] =~ m/\.xml$/)
  {
    my ($rc) = asmcmdshare_xml_exists($args_ref->[0]);
    return unless defined($rc);

    $file = $args_ref->[0];
  }
  else
  { 
    # The argument does not look like an XML file name. We'll try to parse it
    # as an XML string.
    $string_args = join(' ', @{$args_ref});
  }

  @asmcmdfgrp_parser_state = ();
  $fgstmt = "";

  # We do not expect any text values for any nodes.
  $asmcmdfgrp_parser_text  = "" ;

  # specify the handler callbacks
  $hdls = { Start => \&asmcmdfgrp_chfg_start,
            End   => \&asmcmdfgrp_chfg_end,
            Char  => \&asmcmdfgrp_chfg_char };
  $parser = XML::Parser->new(Handlers     => $hdls,
                             ErrorContext => 5);

  eval
  {
    if (defined($file))
    {
      $parser->parsefile($file);
    }
    else
    {
      $parser->parse($string_args);
    }
  };
  if (asmcmdxmlexceptions::catch())
  { 
    my (@msg, $err);
    $err = asmcmdxmlexceptions::getErrmsg();
    @msg = split(/\n/, $err);
    if ($msg[$#msg] =~ m/Parser.pm/)
    {
      pop @msg;
    }

    $err = join("\n", @msg) ."\n";
    @eargs = ($err);
    asmcmdshare_error_msg(9395, \@eargs);
    return;
  }

  # error out, if any of the nodes has text values.  simply print the text.
#  $asmcmddisk_parser_text = trim($asmcmddisk_parser_text);
  $asmcmdfgrp_parser_text =~ s/\s+//;

  if (length($asmcmdfgrp_parser_text) > 0)
  {
    $xml_error = 1 ;
    @eargs = ($asmcmdfgrp_parser_text);

    asmcmdshare_error_msg(9395, \@eargs);
    return ;
  }

  if ($xml_error == 1)
  {
    return;
  }

  # Run SQL. #
  asmcmdshare_trace(1, $fgstmt, 'y', 'n');
  $ret = asmcmdshare_do_stmt($dbh, $fgstmt);
}

########
# NAME
#   asmcmdfgrp_process_lsfg
#
# DESCRIPTION
#   This function lists the properties of a file group.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdfgrp_process_cmd() can call this routine.
########
sub asmcmdfgrp_process_lsfg 
{
  my ($dbh)    = shift;
  my ($gnum)   = undef;
  my (@ctype)  = (' ', 'DATABASE', 'CLUSTER', 'VOLUME');
  my (@fglist) = ();
  my (@what)   = ();
  my (@from)   = ();
  my (@where)  = ();
  my (@order)  = ();
  my (@what_print);
  my (%args);
  my ($ret);
  my ($fgname);
  my ($sth);
  my ($qry);
  my (@tmp_cols);
  my (%min_col_wid);
  my ($print_format);
  my ($print_string);
  my ($i, $k, $v, $row, $h);

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

  if (defined($args{'G'}))
  {
    $gnum = asmcmdshare_get_gnum_from_gname($dbh, $args{'G'});
    if (!defined ($gnum))
    {
      my (@eargs) = ($args{'G'});
      asmcmdshare_error_msg(8001, \@eargs);
      return;
    }
  }

  if (defined($args{'filegroup'}))
  {
    $fgname = uc $args{'filegroup'};
    unless (defined(asmcmdshare_filegroup_exists($dbh, $fgname)))
    {
      my (@eargs) = ($args{'filegroup'});
      asmcmdshare_error_msg(8609, \@eargs);
      return;
    }
  }

  push (@what, 'v$asm_filegroup.name as file_group');
  push (@what, 'v$asm_diskgroup.name as disk_group');

  @what_print = ();
  push (@what_print, $asmcmdfgrp_lsfg_header{'file_group'});
  push (@what_print, $asmcmdfgrp_lsfg_header{'disk_group'});

  push (@from, 'v$asm_filegroup');
  push (@from, 'v$asm_diskgroup');

  push (@where, 'v$asm_diskgroup.group_number = '.
                'v$asm_filegroup.group_number');
  if (defined($fgname))
  {
    # Fetch data from V$ASM_FILEGROUP_PROPERTY as well
    push (@what, 'v$asm_filegroup_property.name as property');
    push (@what, 'v$asm_filegroup_property.value');
    push (@what, 'v$asm_filegroup_property.file_type');

    push (@what_print, $asmcmdfgrp_lsfg_header{'property'});
    push (@what_print, $asmcmdfgrp_lsfg_header{'value'});
    push (@what_print, $asmcmdfgrp_lsfg_header{'file_type'});

    push (@from,  'v$asm_filegroup_property');
    push (@order, 'v$asm_filegroup_property.group_number');
    push (@order, 'v$asm_filegroup_property.filegroup_number');

    push (@where, "v\$asm_filegroup.name = '$fgname'");
    push (@where, 'v$asm_filegroup.filegroup_number = '.
          'v$asm_filegroup_property.filegroup_number');
    push (@where, 'v$asm_filegroup.group_number = '.
          'v$asm_filegroup_property.group_number');
    push (@where, 'v$asm_filegroup_property.filegroup_number = ' .
                  'v$asm_filegroup.filegroup_number');
    if (defined($gnum))
    {
      push (@where, "v\$asm_filegroup_property.group_number = '$gnum'");
    }
  }
  else
  {
    # Fetch data from V$ASM_QUOTAGROUP as well
    push (@what, 'v$asm_quotagroup.name as quota_group');
    push (@what, 'v$asm_filegroup.used_quota_mb as used_quota_mb');
    push (@what, 'v$asm_filegroup.client_name');
    push (@what, 'v$asm_filegroup.client_type');

    push (@what_print, $asmcmdfgrp_lsfg_header{'quota_group'});
    push (@what_print, $asmcmdfgrp_lsfg_header{'used_quota_mb'});
    push (@what_print, $asmcmdfgrp_lsfg_header{'client_name'});
    push (@what_print, $asmcmdfgrp_lsfg_header{'client_type'});

    push (@order, 'v$asm_filegroup.filegroup_number');
    push (@order, 'v$asm_filegroup.client_type');
    push (@order, 'v$asm_diskgroup.group_number');

    push (@from,  'v$asm_quotagroup');

    push (@where, 'v$asm_diskgroup.group_number = '.
                  'v$asm_quotagroup.group_number');
    push (@where, 'v$asm_quotagroup.quotagroup_number = '.
                  'v$asm_filegroup.quotagroup_number');
  }

  if (defined($gnum))
  {
    push (@where, "v\$asm_filegroup.group_number = '$gnum'");
  }

  $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where, 
                                         \@order);

  @tmp_cols = @{$sth->{NAME}};

  @what = map { lc $_ } @tmp_cols;

  # Initialize the min_col_wid array
  %min_col_wid = ();
  foreach (@what)
  {
    $min_col_wid{$_} = length($asmcmdfgrp_lsfg_header{$_});
  }

  # Get the rows
  while (defined($row = asmcmdshare_fetch($sth)))
  {
    my (%info) = ();

    while (($k,$v) = each(%{$row}))
    {
      $k =~ tr/[A-Z]/[a-z]/;
      $v = '' unless (defined($v));

      $info{$k} = $v;

      if (defined($min_col_wid{$k}))
      {
        $min_col_wid{$k} = max($min_col_wid{$k}, length($v));
      }
      else
      {
        $min_col_wid{$k} = length($v);
      }
    }

    push (@fglist, \%info);
  }
  asmcmdshare_finish($sth);

  #create print format
  $print_format = '';

  foreach (@what)
  {
    $print_format .= "%-" . $min_col_wid{$_} . "s  " if (defined($_));
  }
  $print_format .= "\n";

  #print header if not suppressed and if #rows >= 1
  if (defined ($args{'suppressheader'}) || ($#fglist lt 0))
  {
    @what_print = ();
  }
  else
  {
    $print_string = sprintf($print_format, @what_print);
    asmcmdshare_print($print_string);
  }

  #print rows
  foreach $h (@fglist)
  {
    @what_print = ();
    foreach (@what)
    {
      if (length($h->{$_}) == 0)
      {
        # If the field is empty string, print a blank space instead
        push (@what_print, ' ');
      }
      elsif (/client_type/)
      {
        # 23232867: Only client_type field must be mapped to the strings stored
        # at @ctype array.
        push (@what_print, $ctype[$h->{$_}]);
      }
      else
      {
        push (@what_print, $h->{$_});
      }
    }

    $print_string = sprintf($print_format, @what_print);
    asmcmdshare_print($print_string);
  }

  return;
}

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

  if (asmcmdfgrp_is_cmd ($command)) 
  {                              # User specified a command name to look up. #
    $desc = asmcmdshare_get_help_desc($command);
    asmcmdshare_print "$desc\n" ;#.
    $succ = 1;
  }

  return $succ;
}

########
# NAME
#   asmcmdfgrp_is_cmd
#
# DESCRIPTION
#   This routine checks if a user-entered command is one of the known
#   ASMCMD internal commands that belong to the ASMCMDfgrp module.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is one of the known commands, false otherwise.
########
sub asmcmdfgrp_is_cmd 
{
  my ($arg) = shift;
  my (%cmdhash) = (mkfg   => 'mkfg',
                   rmfg   => 'rmfg',
                   chfg   => 'chfg',
                   lsfg   => 'lsfg',
                   mvfile => 'mvfile');

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

########
# NAME
#   asmcmdfgrp_is_wildcard_cmd
#
# DESCRIPTION
#   This routine determines if an ASMCMDFGRP 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 asmcmdfgrp_is_wildcard_cmd 
{
  my ($arg) = shift;
  my (%cmdhash); # Empty hash; no ASMCMDFGRP command supports wildcards. # 

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

########
# NAME
#   asmcmdfgrp_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 asmcmdfgrp module currently supports no command that can run 
#   without an ASM instance.
########
sub asmcmdfgrp_is_no_instance_cmd 
{
  my ($arg) = shift;
  my ($rc);

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

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

  return 0;
}

########
# NAME
#   asmcmdfgrp_parse_int_args
#
# DESCRIPTION
#   This routine parses the arguments for flag options for ASMCMDfgrp 
#   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 ASMCMDfgrp internal command.
########
sub asmcmdfgrp_parse_int_args 
{
  my ($cmd, $args_ref) = @_;
  my (@string);
  my ($key);

  # Build the list of options to parse using GetOptions
  if ($asmcmdfgrp_cmds{ $cmd }{ flags })
  {
    foreach $key (keys %{$asmcmdfgrp_cmds{ $cmd }{ flags }})
    {
      push(@string, $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. #
    asmcmdfgrp_syntax_error($cmd);
    return undef;
  }
  return 0;
}

########
# NAME
#   asmcmdfgrp_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 ASMCMDfgrp 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 asmcmdfgrp_syntax_error 
{
  my ($cmd) = shift;
  my ($cmd_syntax);                               # Correct syntax for $cmd. #
  my ($succ) = 0;

  $cmd_syntax = asmcmdfgrp_get_cmd_syntax($cmd);  # Get syntax for $cmd. #

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

  return $succ;
}


########
# NAME
#   asmcmdfgrp_get_cmd_desc
#
# DESCRIPTION
#   This routine returns the help description of the command specified by $cmd.
#
# PARAMETERS
#   cmd   (IN) - the name of the command of which we're looking up the 
#                description.
#
# RETURNS
#   The description paragraph(s) for command $cmd; undefined if $cmd does not
#   exist.
#
# NOTES
#   IMPORTANT: the commands descriptions must be preceded by eight (8) spaces
#              of indentation!  This formatting is mandatory.
########
sub asmcmdfgrp_get_cmd_desc 
{
  my ($cmd) = shift;
  my (%cmd_desc);  # Hash storing the description for each internal command. #

  $cmd_desc{'mkfg'} = '
        Adds a filegroup to the specified disk group.';

  $cmd_desc{'rmfg'} = '
        Deletes a file group.';

  $cmd_desc{'chfg'} = '
        Changes property values for the specified file group.';

  $cmd_desc{'lsfg'} = '
        Lists the existing filegroups or the file group properties if a file' . "\n" .'
        group name is specified.';

  return $cmd_desc{$cmd};
}

########
# NAME
#   asmcmdfgrp_get_cmd_syntax
#
# DESCRIPTION
#   This routine returns the help syntax of the command specified by $cmd.
#
# PARAMETERS
#   cmd   (IN) - the name of the command of which we're looking up the 
#                syntax.
#
# RETURNS
#   The syntax for command $cmd; undefined if $cmd does not exist.
########
sub asmcmdfgrp_get_cmd_syntax 
{
  my ($cmd) = shift;
  my (%cmd_syntax);     # Hash storing the syntax for each internal command. #

  $cmd_syntax{'mkfg'} = q(mkfg {<config_file.xml> | <'contents_of_xml_file'>});
  # $cmd_syntax{'rmfg'} = 'rmfg [-r] <diskgroup> <filegroup>';
  $cmd_syntax{'rmfg'} = 'rmfg <diskgroup> <filegroup>';
  $cmd_syntax{'chfg'} = q(chfg {<config_file.xml> | <'contents_of_xml_file'>});
  $cmd_syntax{'lsfg'} = 'lsfg [--suppressheader] [-G <diskgroup>] [--filegroup'.
                        ' <filegroup>]';

  return $cmd_syntax{$cmd};
}

########
# NAME
#   asmcmdfgrp_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 asmcmdfgrp_get_asmcmd_cmds
{
  return asmcmdshare_filter_invisible_cmds(%asmcmdfgrp_cmds);
}
1;

OHA YOOOO