MINI MINI MANI MO

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

# Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      asmcmddiag - ASM CoMmanD line interface (diagnostics)
#
#    DESCRIPTION
#      This module is contains the code for ASM Diagnostics
#
#
#    NOTES
#
#    MODIFIED  (MM/DD/YY)
#    anovelo    11/22/16 - RTI19515970: Fix uninitialized value
#    anovelo    10/24/16 - 24706236: Update call to asmcmdshare_get_subdirs
#    diguzman   05/30/16 - 19654070: Little change at _no_instance_cmd routine
#    ykatada    04/21/16 - 23200534: mapblk mapextent support > 3 mirrors
#    kmatkar    04/20/16 - Add fields from x$kfbf to lsblk
#    kmatkar    02/02/16 - Add asmcmddiag_process_chblk/lsblk
#    kmatkar    09/17/15 - Add asmcmddiag_process_mrkrcvd
#    ykatada    12/04/14 - 15949549: Add asmcmddiag_process_mapblk
#    pbagal     08/06/13 - Add file incarn to date command
#    pvenkatr   02/01/13 - using asmcmdshare_filter_invisible_cmds.
#    sanselva   12/05/12 - Dont show hidden cmds in asmcmddiag_get_asmcmd_cmds
#    pvenkatr   11/10/12 - #15854775 - using asmcmdshare_get_gnum_from_gname
#                          instead of asmcmddiag_xxx.
#    pvenkatr   08/24/11 - Removed flags hash table - using from XML
#    adileepk   06/20/11 - Connection Pooling.
#    adileepk   04/06/11 - Incorporating the asmcmd parser changes.
#    moreddy    05/06/10 - bug 8667038 NLS for error messages
#    amitroy   04/21/10 - BUG 8933243 - USE DIFFERENT CHARACTER IN ASMCMD TO
#                         "HIDE COLUMN DETAILS" INSTEAD OF -H
#    moreddy   03/22/10 - Adding more tracing
#    moreddy   01/18/10 - Adding trace messages
#    sanselva  06/24/09 - format help message
#    soye      06/10/09 - move mapau and mapextent to diagnostic package
#    sanselva  04/12/09 - ASMCMD long options and consistency
#    heyuen    04/06/09 - fix error msgs
#    sanselva  02/23/09 - creation
#############################################################################
#
############################ Functions List #################################
#
# asmcmddiag_init
# asmcmddiag_process_cmd
# asmcmddiag_process_help
# asmcmddiag_is_cmd
# asmcmddiag_is_wildcard_cmd
# asmcmddiag_is_no_instance_cmd
# asmcmddiag_parse_int_args
# asmcmddiag_syntax_error
# asmcmddiag_get_cmd_desc
# asmcmddiag_get_cmd_syntax
# asmcmddiag_get_asmcmd_cmds
# asmcmddiag_process_reloc
# asmcmddiag_process_mapextent
# asmcmddiag_process_mapau
# asmcmddiag_process_mapblk
#
#############################################################################

package asmcmddiag;
require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(asmcmddiag_init
                 );
use strict;
use Getopt::Long qw(:config no_ignore_case bundling);
use asmcmdglobal;
use asmcmdshare;
use asmcmdparser;
use asmcmdbase;

use List::Util qw[min max];
####################### ASMCMDQG Global Constants ########################
# ASMCMD Column Header Names:
# Below are the names of the column headers for lsblk. These headers are 
# the ASMCMD equivalent of the columns of x$kfbf
our (%asmcmddiag_lsblk_header) = ('group_kfbf'      , 'Group_Num',
                                  'filenum_kfbf'    , 'File_Num',
                                  'fileincarn_kfbf' , 'Incarnation',
                                  'xtnt_kfbf'       , 'Extent_Num',
                                  'offset_kfbf'     , 'Offset',
                                  'len_kfbf'        , 'Length_blks',
                                  'ablkno_kfbf'     , 'Block_Num',
                                  'ts_kfbf'         , 'Timestamp',
                                  'reason_kfbf'     , 'Reason',
                                  'disks_kfbf'      , 'Disks'
                                );
                                 
####################### ASMCMDDIAG Global Variables ######################
#
# The following list is used primarily for is_cmd and other data from XML.
our (%asmcmddiag_cmds) = (reloc     => {},
                          convert   => {},
                          mapextent => {},
                          mapau     => {},
                          mapblk    => {},
                          chblk     => {},
                          lsblk     => {}
			  );

our ($ASMCMDSYS_SQLPLUS) = "$ENV{'ORACLE_HOME'}/bin/sqlplus -S / as sysasm";



sub is_asmcmd
{
  return 1;
}

########
# NAME
#   asmcmddiag_init
#
# DESCRIPTION
#   This function initializes the asmcmddiag 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, \&asmcmddiag_process_cmd);
  push (@asmcmdglobal_help_callbacks, \&asmcmddiag_process_help);
  push (@asmcmdglobal_command_list_callbacks,
        \&asmcmddiag_get_asmcmd_cmds);
  push (@asmcmdglobal_is_command_callbacks, \&asmcmddiag_is_cmd);
  push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmddiag_is_wildcard_cmd);
  push (@asmcmdglobal_syntax_error_callbacks, \&asmcmddiag_syntax_error);
  push (@asmcmdglobal_no_instance_callbacks, \&asmcmddiag_is_no_instance_cmd);
  %asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmddiag_cmds);

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

########
# NAME
#   asmcmddiag_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 asmcmddiag module; 0 if not.
#
# NOTES
#   Only asmcmdcore_shell() calls this routine.
########
sub asmcmddiag_process_cmd 
{
  my ($dbh) = @_;
  my ($succ) = 0;

  # Get current command from global value, which is set by 
  # asmcmddiag_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 ASMCMDDIAG command.
  my (%cmdhash) = ( reloc         => \&asmcmddiag_process_reloc,
                    convert       => \&asmcmddiag_process_convert,
                    mapextent     => \&asmcmddiag_process_mapextent,
                    mapau         => \&asmcmddiag_process_mapau,
                    mapblk        => \&asmcmddiag_process_mapblk,
                    chblk         => \&asmcmddiag_process_chblk,
                    lsblk         => \&asmcmddiag_process_lsblk
		);
  if (defined ( $cmdhash{ $cmd } ))
  {    # If user specifies a known command, then call routine to process it. #
    $cmdhash{ $cmd }->($dbh);
    $succ = 1;
  }
  return $succ;
}

########
# NAME
#   asmcmddiag_process_help
#
# DESCRIPTION
#   This function is the help function for the ASMCMDDIAG module.
#
# PARAMETERS
#   command     (IN) - display the help message for this command.
#
# RETURNS
#   1 if command found; 0 otherwise.
########
sub asmcmddiag_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 (asmcmddiag_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
#   asmcmddiag_is_cmd
#
# DESCRIPTION
#   This routine checks if a user-entered command is one of the known
#   ASMCMD internal commands that belong to the ASMCMDDIAG module.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is one of the known commands, false otherwise.
########
sub asmcmddiag_is_cmd 
{
  my ($arg) = shift;

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

########
# NAME
#   asmcmddiag_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 asmcmddiag_get_asmcmd_cmds 
{
  return asmcmdshare_filter_invisible_cmds (%asmcmddiag_cmds);
}

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

  return defined ($asmcmddiag_cmds{ $arg }) &&
         (asmcmdshare_get_cmd_wildcard($arg) eq "true");
}

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

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

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

  return 0;
}

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

  # 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. #
    asmcmddiag_syntax_error($cmd);
    return undef;
  }
  return 0;
}


#asmcmddiag_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 ASMCMDDIAG 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 asmcmddiag_syntax_error
{
  my ($cmd) = shift;
  my ($cmd_syntax);                               # Correct syntax for $cmd. #
  my ($succ) = 0;

  #display syntax only for commands in this module.
  if (asmcmddiag_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;
    }
  }

  return $succ;
}


########
# NAME
#   asmcmddiag_process_convert
#
# DESCRIPTION
#    Function to converte the file incarnation number to a timestamp.
#
# PARAMETERS
#   finc   (IN)  - File incarnation number
#
# RETURNS
#   Date
#
# NOTES
#  Only asmcmddiag_process_cmd calls this function
#########
sub asmcmddiag_process_convert
{

  my ($ret);
  my (%args);
  my ($finc, $myinc);
  my ($date_str);
  my ($sec, $min, $hour, $day, $mon, $year);

  $ret =  asmcmddiag_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);


  $finc = $args{'finc'};
  $myinc = $finc;

  $sec = $finc %60;
  $finc /= 60;
  $min = $finc % 60;
  $finc /= 60;
  $hour = $finc % 24;
  $finc /= 24;
  $day = 1 + $finc % 31;
  $finc /= 31;
  $mon = 1 + $finc % 12;
  $finc /= 12;
  $year = $finc + 1988;

  $date_str = sprintf("%02d:%02d:%02d %02d/%02d/%04d\n", 
                      $hour, $min, $sec, $mon, $day, $year);
  
  # Print the date onto the screen
  asmcmdshare_print "Timestamp:".$myinc." ==> ".$date_str;

}


########
# NAME
#   asmcmddiag_process_reloc
#
# DESCRIPTION
#   This function processes the asmcmd command reloc.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmddiag_process_cmd calls this function.
########
sub asmcmddiag_process_reloc
{
  my ($dbh) = shift;
  my ($alias, $dgname, $extent, $disk1, $disk2, $disk3, $name); 
  my ($qry, $fnum, $row);
  my ($sql,$pre_sql,$ret);
  my (%args);
  my (%norm);
  my (@info);
  my ($sqlplus_stmt);

  $ret =  asmcmddiag_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  $extent = $args{'extent'};
  $alias = $args{'alias'};
  $disk1 = $args{'disk1'};
   
  if (defined $args{'disk2'})
  {
    $disk2 = $args{'disk2'};
  }

  if (defined $args{'disk3'})
  {
    $disk3 = $args{'disk3'};
  }

  #extract the filename which is the last word in the alias
  $name =(split(/\//, $alias))[-1];

  #Normalize path and fetch the grp and file_number
  %norm = asmcmdshare_normalize_path($dbh, $alias, 0, \$ret);
  if($ret != 0)
  {
	  asmcmdshare_error_msg(8001, undef);
	  return;
  }
  $dgname = asmcmdshare_get_gname_from_gnum ($dbh, $norm{'gnum'}->[0]);

  #retreive the unique file number given the parent_id,reference_id,
  #group_number and filename
  asmcmdshare_get_subdirs($dbh, \@info, undef, $norm{'gnum'}->[0], 
                          $norm{'ref_id'}->[0], $norm{'par_id'}->[0], $name, 
                          undef, 0, 0);
 
  $fnum =  $info[0]->{'file_number'};

  $pre_sql = "alter system set events '15193 trace name context forever, ".
              "level 1'";
  $ret = asmcmdshare_do_stmt($dbh, $pre_sql);

  $sqlplus_stmt = "$ASMCMDSYS_SQLPLUS";

  #Construct the Oradebug Command
  # Syntax : "oradebug unit_test asm_test kfda execute <gname> <file_no>
  #           <extent_no> <reason=rebal(6)> <disk0> <disk0_flagsi=0>..."

  $qry ="oradebug unit_test asm_test kfda execute $dgname $fnum $extent ".
         "6 $disk1 0"; 

  if(defined $disk2) {$qry .= " ".$disk2." 0";}
  if(defined $disk3) {$qry .= " ".$disk3." 0";}

  # untaint sqlplus
  $sqlplus_stmt =~ m/(.*)/;
  $sqlplus_stmt = $1;

  open SQLPLUS, "| $sqlplus_stmt";
  print SQLPLUS $qry;
  close SQLPLUS;
  asmcmdshare_trace(3, "$qry", 'y', 'n');

}

########
# NAME
#   asmcmddiag_process_mapextent
#
# DESCRIPTION
#   This function processes the asmcmd command mapextent.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmddiag_process_cmd() calls this function.
########
sub asmcmddiag_process_mapextent
{
  my ($src_dbh) = shift;

  my (%args);
  my ($plsql_stmt, $ret, $src_sth);
  my ($fname, $extnum);
  my (@error);
  my ($mapcount, $extsize);
  my (@dnums, @aus);                            # Disk number and AU number #
  my $pl_sql_number = 22;
  my $i;

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

  # The mapextent command requires at least ASM version 11gR2.
  if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
                               $ASMCMDGLOBAL_VER_11gR2) <= 0 )
  {
    # ASM file mapping support will be available in 11gR2 and later
    asmcmdshare_error_msg(9383, undef);
    return;
  }

  ($fname, $extnum) = @{$args{'mapextent'}};
  
  if (substr($fname, 0, 1) ne "+")
  {
    if ($asmcmdglobal_hash{'cwdnm'} ne "+")
    {
       $fname =  $asmcmdglobal_hash{'cwdnm'} . "/" .  $fname;
    }
    else
    {
       $fname =  "+". $fname;
    }
  }
  
  # print $fname;

  # Construct the PL/SQL statement to map from <file, extent> to <disk, au>
  # Call dbms_diskgroup.mapextent if ASM instance is pre-12.2.
  if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
                               $ASMCMDGLOBAL_VER_12cR2) >= 0 )
  {
    $src_sth = $src_dbh->prepare(q{
      begin
        dbms_diskgroup.mapextent2(:fname, :extnum, :mapcount, :extsize,
                                  :disk1, :au1, :disk2, :au2, :disk3, :au3,
                                  :disk4, :au4, :disk5, :au5, :disk6, :au6,
                                  :disk7, :au7, :disk8, :au8, :disk9, :au9,
                                  :disk10, :au10, :disk11, :au11,
                                  :disk12, :au12, :disk13, :au13,
                                  :disk14, :au14, :disk15, :au15,
                                  :disk16, :au16, :disk17, :au17,
                                  :disk18, :au18);
      end;
      });
  }
  else
  {
    $src_sth = $src_dbh->prepare(q{
      begin
        dbms_diskgroup.mapextent(:fname, :extnum, :mapcount, :extsize,
                                 :disk1, :au1, :disk2, :au2, :disk3, :au3);
      end;
      });
  }

  # bind input params first #
  $src_sth->bind_param( ":fname", $fname );
  $src_sth->bind_param( ":extnum", $extnum );

  # bind ouput params #
  $src_sth->bind_param_inout( ":mapcount", \$mapcount, $pl_sql_number );
  $src_sth->bind_param_inout( ":extsize", \$extsize, $pl_sql_number );
  $src_sth->bind_param_inout( ":disk1", \$dnums[0], $pl_sql_number );
  $src_sth->bind_param_inout( ":au1", \$aus[0], $pl_sql_number );
  $src_sth->bind_param_inout( ":disk2", \$dnums[1], $pl_sql_number );
  $src_sth->bind_param_inout( ":au2", \$aus[1], $pl_sql_number );
  $src_sth->bind_param_inout( ":disk3", \$dnums[2], $pl_sql_number );
  $src_sth->bind_param_inout( ":au3", \$aus[2], $pl_sql_number );

  # More binds for dbms_diskgroup.mapextent2
  if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
                               $ASMCMDGLOBAL_VER_12cR2) >= 0 )
  {
    $src_sth->bind_param_inout( ":disk4", \$dnums[3], $pl_sql_number );
    $src_sth->bind_param_inout( ":au4", \$aus[3], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk5", \$dnums[4], $pl_sql_number );
    $src_sth->bind_param_inout( ":au5", \$aus[4], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk6", \$dnums[5], $pl_sql_number );
    $src_sth->bind_param_inout( ":au6", \$aus[5], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk7", \$dnums[6], $pl_sql_number );
    $src_sth->bind_param_inout( ":au7", \$aus[6], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk8", \$dnums[7], $pl_sql_number );
    $src_sth->bind_param_inout( ":au8", \$aus[7], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk9", \$dnums[8], $pl_sql_number );
    $src_sth->bind_param_inout( ":au9", \$aus[8], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk10", \$dnums[9], $pl_sql_number );
    $src_sth->bind_param_inout( ":au10", \$aus[9], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk11", \$dnums[10], $pl_sql_number );
    $src_sth->bind_param_inout( ":au11", \$aus[10], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk12", \$dnums[11], $pl_sql_number );
    $src_sth->bind_param_inout( ":au12", \$aus[11], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk13", \$dnums[12], $pl_sql_number );
    $src_sth->bind_param_inout( ":au13", \$aus[12], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk14", \$dnums[13], $pl_sql_number );
    $src_sth->bind_param_inout( ":au14", \$aus[13], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk15", \$dnums[14], $pl_sql_number );
    $src_sth->bind_param_inout( ":au15", \$aus[14], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk16", \$dnums[15], $pl_sql_number );
    $src_sth->bind_param_inout( ":au16", \$aus[15], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk17", \$dnums[16], $pl_sql_number );
    $src_sth->bind_param_inout( ":au17", \$aus[16], $pl_sql_number );
    $src_sth->bind_param_inout( ":disk18", \$dnums[17], $pl_sql_number );
    $src_sth->bind_param_inout( ":au18", \$aus[17], $pl_sql_number );
  }

  $ret = $src_sth->execute();
   
  if (!defined ($ret))	
  {	
    asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');	
    if ($asmcmdglobal_hash{'mode'} eq 'n')	
    {	
      $asmcmdglobal_hash{'e'} = -1;	
    }	
  } 

  if ( !defined($mapcount) || $mapcount == 0)
  {
    asmcmdshare_print "No mapping information found\n";
  }
  else
  {
    asmcmdshare_print "Disk_Num \t AU \t Extent_Size\n"
                     if (!defined ($args{'suppressheader'}));

    for ($i = 0; $i < $mapcount; $i++)
    {
      asmcmdshare_print "$dnums[$i]\t\t $aus[$i] \t\t $extsize\n"
    }
  }
}


########
# NAME
#   asmcmddiag_process_mapau
#
# DESCRIPTION
#   This function processes the asmcmd command mapau.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmddiag_process_cmd() calls this function.
########
sub asmcmddiag_process_mapau
{
  my ($src_dbh) = shift;

  my (%args);
  my ($plsql_stmt, $ret, $src_sth);
  my ($gnum, $disk,  $au);
  my (@error);
  my ($file, $extent, $xsn, $filename);
  my $pl_sql_number = 22;

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

  # The mapau command requires at least ASM version 11gR2.
  if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
                               $ASMCMDGLOBAL_VER_11gR2) <= 0 )
  {
    asmcmdshare_error_msg(9384, undef);
    return;
  }

  ($gnum, $disk, $au) = @{$args{'mapau'}};

  $gnum = asmcmdshare_get_gnum_from_gname($src_dbh, $gnum) 
                                         unless ( $gnum =~ /^\d+$/ );

  # Construct the PL/SQL statement to map from <file, extent> to <disk, au>
  $src_sth = $src_dbh->prepare(q{
      begin
         dbms_diskgroup.mapau(:gnum, :disk, :au, :file, :extent, :xsn);
      end;
      });
  # bind input params first #
  $src_sth->bind_param( ":gnum", $gnum );
  $src_sth->bind_param( ":disk", $disk );
  $src_sth->bind_param( ":au", $au);

  # bind ouput params #
  $src_sth->bind_param_inout( ":file", \$file, $pl_sql_number );
  $src_sth->bind_param_inout( ":extent", \$extent, $pl_sql_number );
  $src_sth->bind_param_inout( ":xsn", \$xsn, $pl_sql_number );

  $ret = $src_sth->execute();
  if (!defined ($ret))	
  {	
    asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');	
    if ($asmcmdglobal_hash{'mode'} eq 'n')	
    {	
      $asmcmdglobal_hash{'e'} = -1;	
    }	
  }
  if ( !defined ($file) || $file == 0 )
  {
    asmcmdshare_print "No mapping information found\n";
  }
  else
  {

#   $filename = asmcmddiag_get_fname_from_fnum($src_dbh, $gnum, $file);
#   Don't try to get the filename for now
    asmcmdshare_print "File_Num\t Extent \t Extent_Set\n"
                          if (!defined ($args{'suppressheader'}));
    asmcmdshare_print "$file \t\t $extent \t\t $xsn\n";
  }
}


########
# NAME
#   asmcmddiag_process_mapblk
#
# DESCRIPTION
#   This function processes the asmcmd command mapblk
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmddiag_process_cmd() calls this function.
########
sub asmcmddiag_process_mapblk
{
  my ($dbh) = shift;
  my (%args);
  my $ret;
  my @eargs;
  my $alias   = undef;                                  # User-entered alias #
  my $blkn    = undef;                        # User-entered DB block number #
  my $longdesc = 0;                                 # User-entered -l option #
  my $gnum    = undef;                                # ASM diskgroup number #
  my $fnum    = undef;                                     # ASM file number #
  my $vxn     = undef;                           # ASM virtual extent number #
  my ($mapcount, $lblksz);                # Map count and logical block size #
  my (@dnums, @offsets);               # Disk number and offset withing disk #
  my ($sth);
  my $pl_sql_number = 22;
  my (%norm);       # See asmcmdshare_normalize_path() return value comments #
  my (@paths);                                   # Array of normalized paths #
  my (@ref_ids);                              # Reference indexes for @paths #
  my (@par_ids);                                 # Parent indexes for @paths #
  my (@dg_nums);                              # Diskgroup numbers for @paths #
  my ($ref_id, $par_id);
  my ($gname, $ausz);                  # Diskgroup name and AU size in bytes #
  my ($ausz_in_blk, $t_aun);          # AU size in blocks and temp AU number #
  my (@aus);                                                     # AU number #
  my (@g_info);      # Array of hashes of diskgroup column values, one hash  #
  my (@d_list, @dnams, @devs);               # ASM disk name and device path #
  my (%min_col_wid);                        # Minimum column width to disply #
  my (@result_list);                                      # List of the hash #
  my ($r_format);
  my ($print_string);
  my (@header);
  my (@rowval);
  my ($iter);
  my $i;

  # Get alias and block number from user specified parameters
  $ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);
  ($alias, $blkn) = @{$args{'mapblk'}};

  # check if -l option is specified
  if (defined ($args{'l'}))
  {
    $longdesc = 1;
  }

  asmcmdshare_trace(3, "ASMCMD (PID = $$) - MAPBLK Command", 'y', 'n');

  # The mapblock command requires at least ASM version 12cR2.
  if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
                               $ASMCMDGLOBAL_VER_12cR2) < 0 )
  {
    # ASM file mapping support will be available in 12cR2 and later
    asmcmdshare_error_msg(9383, undef);
    return;
  }

  # For relative file path
  if (substr($alias, 0, 1) ne "+")
  {
    if ($asmcmdglobal_hash{'cwdnm'} ne "+")
    {
      $alias =  $asmcmdglobal_hash{'cwdnm'} . "/" .  $alias;
    }
    else
    {
      $alias =  "+". $alias;
    }
  }

  ###
  # Get group number from the given alias
  #

  # Replace windows style '\\' to Unix style '\/'
  $alias =~ s/\\/\//g;

  # See if any entries exist; if so, find all matches for $alias.
  %norm = asmcmdshare_normalize_path($dbh, $alias, 0, \$ret);
  return unless ($ret == 0);

  @paths   = @{ $norm{'path'} };
  @ref_ids = @{ $norm{'ref_id'} };
  @par_ids = @{ $norm{'par_id'} };
  @dg_nums = @{ $norm{'gnum'} };

  # Should be only one row
  if (@paths != 1)
  {
    asmcmdshare_trace(1, "ERROR: Invalid number of paths : @paths", 'y', 'y');
    return;
  }

  # For diagnostic
  asmcmdshare_trace(5, "asmcmddiag_process_mapblk: " .
                    "alias=$alias blkn=$blkn paths=$paths[0] " .
                    "ref_ids=$ref_ids[0] par_ids=$par_ids[0] " .
                    "dg_nums=$dg_nums[0]", 'n', 'n');

  # Set the returns of asmcmdshare_normalize_path()
  $gnum   = $dg_nums[0];                          # Set the diskgroup number #
  $ref_id = $ref_ids[0];
  $par_id = $par_ids[0];

  # In ASMCMD diskgroups and '+' are both treated as virtual directories.
  # Ref index for diskgroup is defined to be (group_number * 2^24).  Ref
  # index for '+' is define to be -1.  
  if (($ref_id == -1) || ($ref_id == $gnum << 24))
  {
    asmcmdshare_trace(1, "ERROR: Alias has only diskgroup name", 'y', 'y');
    return;
  }

  ###
  # Get diskgroup name and AU size
  #

  $gname  = asmcmdshare_get_gname_from_gnum($dbh, $gnum);   # Set group name #
  @g_info = asmcmdshare_get_dg($dbh, $gname, 0, 0);

  # Only one row should be returned. Diskgroup name should be unique.
  if (@g_info != 1) 
  {
    my (@eargs) = ($gname);
    asmcmdshare_error_msg(8001, \@eargs) if (defined ($gname));
    return;
  }

  $ausz = $g_info[0]->{'allocation_unit_size'};                # Set AU size #

  # For diagnostic
  asmcmdshare_trace(5, "asmcmddiag_process_mapblk: gname=$gname ausz=$ausz",
                    'n', 'n');

  ###
  # Execute the PL/SQL statement to map from <file, block> to <disk, offset>
  #
  $sth = $dbh->prepare(q{
    begin
      dbms_diskgroup.mapblock(:fname, :blkn, :fnum, :blksz, :vxnum, :mapcount,
                              :disk1, :offset1, :disk2, :offset2,
                              :disk3, :offset3, :disk4, :offset4,
                              :disk5, :offset5, :disk6, :offset6,
                              :disk7, :offset7, :disk8, :offset8,
                              :disk9, :offset9, :disk10, :offset10,
                              :disk11, :offset11, :disk12, :offset12,
                              :disk13, :offset13, :disk14, :offset14,
                              :disk15, :offset15, :disk16, :offset16,
                              :disk17, :offset17, :disk18, :offset18);
    end;
    });

  # bind input params first #
  $sth->bind_param( ":fname", $alias);
  $sth->bind_param( ":blkn", $blkn);

  # bind ouput params #
  $sth->bind_param_inout( ":fnum", \$fnum, $pl_sql_number );
  $sth->bind_param_inout( ":blksz", \$lblksz, $pl_sql_number );
  $sth->bind_param_inout( ":vxnum", \$vxn, $pl_sql_number );
  $sth->bind_param_inout( ":mapcount", \$mapcount, $pl_sql_number );
  $sth->bind_param_inout( ":disk1", \$dnums[0], $pl_sql_number );
  $sth->bind_param_inout( ":offset1", \$offsets[0], $pl_sql_number );
  $sth->bind_param_inout( ":disk2", \$dnums[1], $pl_sql_number );
  $sth->bind_param_inout( ":offset2", \$offsets[1], $pl_sql_number );
  $sth->bind_param_inout( ":disk3", \$dnums[2], $pl_sql_number );
  $sth->bind_param_inout( ":offset3", \$offsets[2], $pl_sql_number );
  $sth->bind_param_inout( ":disk4", \$dnums[3], $pl_sql_number );
  $sth->bind_param_inout( ":offset4", \$offsets[3], $pl_sql_number );
  $sth->bind_param_inout( ":disk5", \$dnums[4], $pl_sql_number );
  $sth->bind_param_inout( ":offset5", \$offsets[4], $pl_sql_number );
  $sth->bind_param_inout( ":disk6", \$dnums[5], $pl_sql_number );
  $sth->bind_param_inout( ":offset6", \$offsets[5], $pl_sql_number );
  $sth->bind_param_inout( ":disk7", \$dnums[6], $pl_sql_number );
  $sth->bind_param_inout( ":offset7", \$offsets[6], $pl_sql_number );
  $sth->bind_param_inout( ":disk8", \$dnums[7], $pl_sql_number );
  $sth->bind_param_inout( ":offset8", \$offsets[7], $pl_sql_number );
  $sth->bind_param_inout( ":disk9", \$dnums[8], $pl_sql_number );
  $sth->bind_param_inout( ":offset9", \$offsets[8], $pl_sql_number );
  $sth->bind_param_inout( ":disk10", \$dnums[9], $pl_sql_number );
  $sth->bind_param_inout( ":offset10", \$offsets[9], $pl_sql_number );
  $sth->bind_param_inout( ":disk11", \$dnums[10], $pl_sql_number );
  $sth->bind_param_inout( ":offset11", \$offsets[10], $pl_sql_number );
  $sth->bind_param_inout( ":disk12", \$dnums[11], $pl_sql_number );
  $sth->bind_param_inout( ":offset12", \$offsets[11], $pl_sql_number );
  $sth->bind_param_inout( ":disk13", \$dnums[12], $pl_sql_number );
  $sth->bind_param_inout( ":offset13", \$offsets[12], $pl_sql_number );
  $sth->bind_param_inout( ":disk14", \$dnums[13], $pl_sql_number );
  $sth->bind_param_inout( ":offset14", \$offsets[13], $pl_sql_number );
  $sth->bind_param_inout( ":disk15", \$dnums[14], $pl_sql_number );
  $sth->bind_param_inout( ":offset15", \$offsets[14], $pl_sql_number );
  $sth->bind_param_inout( ":disk16", \$dnums[15], $pl_sql_number );
  $sth->bind_param_inout( ":offset16", \$offsets[15], $pl_sql_number );
  $sth->bind_param_inout( ":disk17", \$dnums[16], $pl_sql_number );
  $sth->bind_param_inout( ":offset17", \$offsets[16], $pl_sql_number );
  $sth->bind_param_inout( ":disk18", \$dnums[17], $pl_sql_number );
  $sth->bind_param_inout( ":offset18", \$offsets[17], $pl_sql_number );

  $ret = $sth->execute();
   
  if (!defined ($ret))	
  {	
    asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');	
    if ($asmcmdglobal_hash{'mode'} eq 'n')	
    {	
      $asmcmdglobal_hash{'e'} = -1;	
    }	
  } 

  # For diagnostic
  asmcmdshare_trace(5, "asmcmddiag_process_mapblk: fnum=$fnum " .
                    "lblksz=$lblksz vxn=$vxn mapcount=$mapcount " .
                    "disk1=$dnums[0] offset1=$offsets[0] " .
                    "disk2=$dnums[1] offset2=$offsets[1] " .
                    "disk3=$dnums[2] offset3=$offsets[2] " .
                    "disk4=$dnums[3] offset4=$offsets[3] " .
                    "disk5=$dnums[4] offset5=$offsets[4] " .
                    "disk6=$dnums[5] offset6=$offsets[5] " .
                    "disk7=$dnums[6] offset7=$offsets[6] " .
                    "disk8=$dnums[7] offset8=$offsets[7] " .
                    "disk9=$dnums[8] offset9=$offsets[8] " .
                    "disk10=$dnums[9] offset10=$offsets[9] " .
                    "disk11=$dnums[10] offset11=$offsets[10] " .
                    "disk12=$dnums[11] offset12=$offsets[11] " .
                    "disk13=$dnums[12] offset13=$offsets[12] " .
                    "disk14=$dnums[13] offset14=$offsets[13] " .
                    "disk15=$dnums[14] offset15=$offsets[14] " .
                    "disk16=$dnums[15] offset16=$offsets[15] " .
                    "disk17=$dnums[16] offset17=$offsets[16] " .
                    "disk18=$dnums[17] offset18=$offsets[17]", 'n', 'n');

  ###
  # Get disk name and device path
  #

  for ($i = 0; $i < $mapcount; $i++)
  {
    @d_list = asmcmdshare_get_dsk($dbh, $gnum, undef, $dnums[$i],
                                  0, 0, 0, undef);

    push (@dnams, $d_list[0]->{'name'});
    push (@devs, $d_list[0]->{'path'});
  }

  ###
  # Calculate AU number
  #
  $ausz_in_blk = $ausz / $lblksz;                        # AU size in blocks #
  for ($i = 0; $i < $mapcount; $i++)
  {
    $t_aun = int($offsets[$i] / $ausz_in_blk);
    push (@aus, $t_aun);
  }

  ###
  # Display the result
  #

  $min_col_wid{'logic_ext'}     = length('Logic_Ext');
  $min_col_wid{'block_size'}    = length('Block_Size');
  $min_col_wid{'offset'}        = length('Offset');
  $min_col_wid{'disk_num'}      = length('Disk_Num');
  $min_col_wid{'path'}          = length('Path');
  $min_col_wid{'disk_name'}     = length('Disk_Name');
  $min_col_wid{'au_size'}       = length('AU_Size');
  $min_col_wid{'file_num'}      = length('File_Num');
  $min_col_wid{'ext_num'}       = length('Ext_Num');
  $min_col_wid{'au_num'}        = length('AU_Num');

  # Populate the result into result_list
  for ($i = 0; $i < $mapcount; $i++)
  {
    my (%result_info);                   # Allocate fresh hash for next row. #

    $result_info{'logic_ext'}   = $i;
    $result_info{'file_num'}    = $fnum;
    $result_info{'au_size'}     = $ausz;
    $result_info{'block_size'}  = $lblksz;
    $result_info{'offset'}      = $offsets[$i];
    $result_info{'disk_name'}   = $dnams[$i] ? $dnams[$i] : "";
    $result_info{'disk_num'}    = $dnums[$i];
    $result_info{'path'}        = $devs[$i] ? $devs[$i] : "";
    $result_info{'ext_num'}     = $vxn;
    $result_info{'au_num'}      = $aus[$i];

    push (@result_list, \%result_info);
  }

  asmcmdshare_ls_calc_min_col_wid (\@result_list, \%min_col_wid);

  # Print header
  $r_format = "%"  . $min_col_wid{'logic_ext'} . "s  " .
              "%"  . $min_col_wid{'block_size'} . "s  " .
              "%"  . $min_col_wid{'offset'} . "s  " .
              "%"  . $min_col_wid{'disk_num'} . "s  " .
              "%-" . $min_col_wid{'path'} . "s  ";

  if ($longdesc)
  {
    $r_format .= "%-" . $min_col_wid{'disk_name'} . "s  " .
                 "%"  . $min_col_wid{'au_size'} . "s  " .
                 "%"  . $min_col_wid{'file_num'}  . "s  " .
                 "%"  . $min_col_wid{'ext_num'}  . "s  " .
                 "%"  . $min_col_wid{'au_num'}  . "s  ";
  }

  $r_format .= "\n";

  push @header, ('Logic_Ext', 'Block_Size', 'Offset', 'Disk_Num', 'Path');

  if ($longdesc)
  {
    push @header, ('Disk_Name', 'AU_Size', 'File_Num', 'Ext_Num', 'AU_Num');
  }

  if (!defined ($args{'suppressheader'}))
  {
    $print_string = sprintf($r_format, @header);
    asmcmdshare_print($print_string);
  }

  # Print one row at a time.
  foreach $iter (@result_list)
  {
    @rowval = ();

    push @rowval, ($iter->{'logic_ext'},
                   $iter->{'block_size'},
                   $iter->{'offset'},
                   $iter->{'disk_num'},
                   $iter->{'path'});

    if ($longdesc)
    {
      push @rowval, ($iter->{'disk_name'},
                     $iter->{'au_size'},
                     $iter->{'file_num'},
                     $iter->{'ext_num'},
                     $iter->{'au_num'});
    }

    $print_string = sprintf $r_format, @rowval;
    asmcmdshare_print($print_string);
  }

  asmcmdshare_trace(3, "ASMCMD (PID = $$) - MAPBLK Command Finished", 'y', 'n');
}

########
# NAME
#   asmcmddiag_process_chblk
#
# DESCRIPTION
#   This function processes the asmcmd command chblk (change block).
#   As of now this command is only used to delete entries for blocks that have
#   been recovered.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmddiag_process_cmd() calls this function.
########
sub asmcmddiag_process_chblk
{
  my ($src_dbh) = shift;

  my (%args);
  my ($plsql_stmt, $ret, $src_sth);
  my ($fname, $ablkno, $nblks);
  my (@error);

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

  # The command requires at least ASM version 12.2.
  if (asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
                              $ASMCMDGLOBAL_VER_12cR2) < 0 )
  {
    asmcmdshare_error_msg(9386, undef);
    return;
  }

  if(defined($args{'recovered'}))
  {
    if(defined($args{'all'}))
    { 
      $fname = $args{'file'};
      $ablkno = '0';
      $nblks = '0';
    }
    else
    {
      $fname = $args{'file'};
      $ablkno = $args{'block'};
      $nblks = $args{'len'};
    }

    # Construct the PL/SQL statement to delete entry <fn, ablkno, numblks>
    # from the BAD file directory
    $src_sth = $src_dbh->prepare(q{
        begin
           dbms_diskgroup.deletebadfentry(:fname, :ablkno, :nblks);
        end;
        });

    # bind input params first 
    $src_sth->bind_param(":fname", $fname);
    $src_sth->bind_param(":ablkno", $ablkno);
    $src_sth->bind_param(":nblks", $nblks);

    $ret = $src_sth->execute();

    if (!defined ($ret))	
    {	
      asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');	
      if ($asmcmdglobal_hash{'mode'} eq 'n')	
      {	
        $asmcmdglobal_hash{'e'} = -1;	
      }	
    }
    else
    {
      asmcmdshare_print("Successfully executed\n");
    }
  } 
}

########
# NAME
#   asmcmddiag_process_lsblk
#
# DESCRIPTION
#   This function processes the asmcmd command lsblk to list blocks of a file in
#   a diskgroup.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmddiag_process_cmd() calls this function.
########
sub asmcmddiag_process_lsblk
{
  my ($dbh) = shift;
  my (%args);
  my ($ret);
  my ($fgname);
  my ($gnum);
  my (@what);
  my (@from);
  my ($sth);
  my ($qry);
  my (@where);
  my (@order);
  my (@tmp_cols);
  my (@fglist) = ();
  my (%min_col_wid);
  my ($print_format);
  my ($print_string);
  my (@what_print);
  my ($k, $v, $row, $h);

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

  # The command requires at least ASM version 12.2.
  if (asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
                              $ASMCMDGLOBAL_VER_12cR2) < 0 )
  {
    asmcmdshare_error_msg(9385, undef);
    return;
  }

  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{'lost'}))
  {
    @what_print = ();
    
    push (@what, 'x$kfbf.group_kfbf');
    push (@what, 'x$kfbf.filenum_kfbf');
    push (@what, 'x$kfbf.fileincarn_kfbf');
    push (@what, 'x$kfbf.xtnt_kfbf');
    push (@what, 'x$kfbf.offset_kfbf');
    push (@what, 'x$kfbf.len_kfbf');
    push (@what, 'x$kfbf.ablkno_kfbf');
    push (@what, 'x$kfbf.ts_kfbf');
    push (@what, 'x$kfbf.reason_kfbf');
    push (@what, 'x$kfbf.disks_kfbf');

    push (@what_print, $asmcmddiag_lsblk_header{'group_kfbf'});
    push (@what_print, $asmcmddiag_lsblk_header{'filenum_kfbf'});
    push (@what_print, $asmcmddiag_lsblk_header{'fileincarn_kfbf'});
    push (@what_print, $asmcmddiag_lsblk_header{'xtnt_kfbf'});
    push (@what_print, $asmcmddiag_lsblk_header{'offset_kfbf'});
    push (@what_print, $asmcmddiag_lsblk_header{'len_kfbf'});
    push (@what_print, $asmcmddiag_lsblk_header{'ablkno_kfbf'});
    push (@what_print, $asmcmddiag_lsblk_header{'ts_kfbf'});
    push (@what_print, $asmcmddiag_lsblk_header{'reason_kfbf'});
    push (@what_print, $asmcmddiag_lsblk_header{'disks_kfbf'});

    push (@from,  'x$kfbf');
    push (@order, 'x$kfbf.filenum_kfbf');

    if (defined($gnum))
    {
      push (@where, "x\$kfbf.group_kfbf = '$gnum'");
    }

    if (defined($dbh))
    {
      $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where, 
                                             \@order);
    }

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

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

    %min_col_wid = ();
    # initialize the min_col_wid array
    foreach(@what)
    {
      $min_col_wid{$_} = length($asmcmddiag_lsblk_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)
      {
        push (@what_print, $h->{$_});
      }
      $print_string = sprintf($print_format, @what_print);
      asmcmdshare_print($print_string);
    }
  }
  return;
}

1;

OHA YOOOO