MINI MINI MANI MO
# 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