MINI MINI MANI MO
# Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
#
# NAME
# asmcmdbase - ASM CoMmanD line interface (Base Module)
#
# DESCRIPTION
# ASMCMD is a Perl utility that provides easy nagivation of files within
# ASM diskgroups. This module contains the functionality of all
# the commands that have been supported since Oracle 10g. Namely,
# they are the ASM file system related commands. This module is also
# responsible for the CONNECT and DISCONNECT functionality of ASMCMD.
#
# NOTES
# usage: asmcmdcore [-v] [-a <sysasm|sysdba>] [-p] [command]
#
# MODIFIED (MM/DD/YY)
# anovelo 11/02/17 - 26389461: Allow sys directories to be deleted
# emsu 09/26/17 - Add -l to showpatches, --active to showversion
# anovelo 07/21/17 - 26389461: Ignore dir permissions with sysasm conn
# anovelo 03/10/17 - 25514391: Use sysdba privileges to connect in MC
# wanhlee 02/09/17 - 22895947: Handle new kfod getclstype return value
# emsu 11/17/16 - 24760407: Factor out cluster state query
# anovelo 11/16/16 - 12640351: Add --dest_dbname option to 'cp'
# diguzman 10/20/16 - 24923984: lsct shows wrong IOS info
# anovelo 10/18/16 - 24706236: Add option --time_style to 'ls'
# diguzman 10/13/16 - 12825292: add ls -h option
# diguzman 10/10/16 - 10219399: add wildcard support to cp command
# prabbala 09/10/16 - bug19777340: add dbuniquename for dbms_diskgroup.copy
# abhmanju 07/11/16 - add sparse_merge_begin and sparse_merge_end to cp
# diguzman 06/30/16 - 23721855: lsct connects using $OSID when target
# option is set
# diguzman 05/30/16 - 19654070: Little change at _no_instance_cmd routine
# diguzman 05/24/16 - 23073738: asmcmdshare_set_instance signature change
# dacavazo 05/04/16 - Export asmcmdbase_find_sql()
# diguzman 03/08/16 - 21456380: add --target option to lsct, rm and cp
# abhmanju 01/15/16 - Bug 21275171 and 21172775 add sparse_option to cp
# cmd and add setsparseparent cmd
# saklaksh 01/05/16 - 22447438: lsof -G to return err on undefined dg
# dacavazo 12/07/15 - 22223809: undef function before dl_find_symbol
# dacavazo 08/06/15 - 21453939: skip removed files on process_ls
# dacavazo 07/07/15 - 21385763: made group name optional on lsct
# aramacha 07/08/15 - 20402145: parse rhost/rport/rsid for Flex-ASM
# remote copy
# sanselva 05/12/15 - 20721939: quote passwd to prevent SQL injection risk
# diguzman 04/20/15 - 20134010: Check if cd argument isn't empty or undef
# jesugonz 01/12/15 - 19494254: filter out invalid file descriptors in lsof
# ykatada 10/03/14 - #19617921: use bind variables to SELECTs
# cgraybl 09/05/14 - bug19065962: add logical sector size to lsdg
# prabbala 09/25/14 - 19468869:show du output as 0 on dir only with aliases
# shlian 09/09/14 - change showpatches/showversion to work w/o ASM inst
# shlian 07/10/14 - changed lsof command to be case insensitive
# hppancha 03/28/14 - change spawnutil to utilsucc
# siyarlag 05/12/14 - fix connection error on Flex ASM
# pvenkatr 11/22/13 - #17824302 - replace DOS style \ to Unix style / on
# DG file names.
# pvenkatr 11/04/13 - #17395893 - Using PERL2C interface to pickup
# connection string - connection only to ASM instance
# pvenkatr 09/11/13 - #17432676 - Fixed spelling mistake.
# pvenkatr 08/08/13 - 17254040 - Fixed splitting string based on ':'
# properly.
# pvenkatr 08/05/13 - 17267662 - fixed spelling mistake.
# pvenkatr 06/30/13 - Added error string while PERL2C API fails.
# manuegar 05/02/13 - Bug13951456 Support bind parameters
# pvenkatr 03/31/13 - LRG# 8842034 - replaced DOS '\' to Unix '/'.
# pvenkatr 02/01/13 - using asmcmdshare_execute_tool
# sanselva 12/27/12 - XbranchMerge sanselva_lrg-8658536 from
# st_rdbms_12.1.0.1
# mchimang 12/10/12 - XbranchMerge mchimang_lrg-8504705 from main
# sanselva 12/10/12 - XbranchMerge sanselva_lrg-8543480 from main
# sanselva 12/05/12 - Dont show hidden cmds in asmcmdbase_get_asmcmd_cmds
# moreddy 11/26/12 - 14142633 reconnect on demand for remote asm
# pvenkatr 11/10/12 - #15848691 - compute kfod path correctly.
# pvenkatr 09/27/12 - #14676720 deleting sys/usr alias on same dir
# pvenkatr 09/26/12 - Added createclientcluster command
# pvenkatr 08/20/12 - #14493584 - pwcopy/pwmove/cp on target dir/file with
# RO permission.
# pvenkatr 07/04/12 - 13989320 Wrong '/' in Copying message.
# pvenkatr 03/16/12 - Bug 13837760 - permissions
# adileepk 11/29/11 - Fix for bug-12661648. Correcting the exit status for
# rm command when it executes successfully.
# mchimang 11/18/11 - pass flag in dbms_diskgroup.getfilephyblksize
# adileepk 09/27/11 - Bifurcating asmcmdbase_process_cp so that it can be
# reused by pwcopy command. Fix for bug 12984604.
# moreddy 09/07/11 - Correct error message in cp to 8009
# pvenkatr 08/24/11 - Removed flags hash table - using from XML
# adileepk 06/19/11 - Connection Pooling.
# mchimang 05/17/10 - Changes for password file copy
# shmubeen 05/23/11 - bug fix# 12538042
# adileepk 11/01/10 - Changes made to integrate the parser module with
# asmcmd.
# moreddy 08/23/10 - bug 6969662 improve rm command performance
# pvenkatr 07/27/10 - Bug 9778018 Added check for windows syntax \\
# 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; REPLACE -a
# WITh --absolutepath FOR ls
# mchimang 04/29/10 - Added check to see if files are deleted during du cmd
# pvenkatr 03/31/10 - Syntax, descripiton, example - all from XML
# mchimang 03/26/10 - Rectified the DU calculation for the bug 9311197.
# moreddy 03/22/10 - Adding more tracing
# sanselva 03/10/10 - fix parsing remote_conn_str for remote cp
# amitroy 12/30/09 - support for copying a file from local OS to local
# ASM when a file with same name exists
# moreddy 01/19/10 - Adding tracing messages
# pvenkatr 12/03/09 - Bug # 9072284 Added service as optional param for cp
# pvenkatr 11/04/09 - Bug # 8788329:support to cp from/to ASM/remote-os
# pvenkatr 09/03/09 - Help message from xml file.
# sanselva 06/25/09 - remove -r options from cp
# sanselva 05/12/09 - remove type checking for cp since done on server side
# sanselva 04/06/09 - ASMCMD long options and consistency
# heyuen 03/23/09 - update docs
# heyuen 12/03/08 - export rm_sql
# heyuen 10/14/08 - use dynamic modules
# heyuen 10/03/08 - stop cp when a file is invalid
# heyuen 09/10/08 - make lsof deterministic
# heyuen 08/26/08 - add voting file
# heyuen 08/02/08 - add -V
# heyuen 08/01/08 - fix windows \
# heyuen 07/28/08 - use command properties array
# heyuen 06/16/08 - update help
# heyuen 04/15/08 - bug 6957288, reorder help messages
# heyuen 03/31/08 - add -p to ls
# heyuen 02/20/08 - increase cp performance
# heyuen 12/17/07 - change error number ranges
# heyuen 08/02/07 - add lsof
# siyarlag 09/17/07 - bug/5903321 no quotes for diskgroup name
# heyuen 08/02/07 - refresh
# heyuen 06/26/07 - add support for multiple source files in cp
# hqian 06/07/07 - #5131203: add disk group name to lsct results
# heyuen 05/25/07 - add return codes for errors
# dfriedma 05/24/07 - Remove unbalanced column
# hqian 05/24/07 - Use SYSASM as connection default, since bug 5873184
# is fixed
# heyuen 05/09/07 - add support for cp with relative paths
# pbagal 04/13/07 - Add ASMCMD comment in all SQL
# heyuen 04/02/07 - changed help message for cp, fixed remote db
# connection
# hqian 03/09/07 - Improve error msg: give msgid
# hqian 03/02/07 - -c and -g for ls and lsdg
# jilim 11/16/06 - asmcmd cp feature, asmcmdbase_process_cp and
# its sub-modules
# hqian 02/08/07 - lrg-2839533: temporary revert default connection
# type back to sysdba
# hqian 08/17/06 - remove SQL dependency on init_global
# hqian 07/20/06 - #5397026: new asmcmdglobal_no_instance_callbacks
# averhuls 07/06/06 - prevent ls from displaying volume info.
# hqian 06/15/06 - move asmcmdbase_ls_calc_min_col_wid to shared
# module
# hqian 02/02/06 - Bug-5007830: signal out of asmcmd if ORA-03114
# hqian 02/01/06 - Fix process_mkdir(): mkdir +dg with uninit $cre
# hqian 01/27/06 - merge two error schemes into one:
# asmcmdbase_display_msg
# hqian 01/25/06 - Split off asmcmdshare.pm from this module
# hqian 01/19/06 - More modularization
# hqian 01/18/06 - #4939032: remove the main() and shell() commands
# hqian 01/18/06 - #4939032: format asmcmdbase.pm into a module
# hqian 01/18/06 - Rename asmcmdcore to asmcmdbase.pm, inherit history
# hqian 01/18/06 - #4939032: split up asmcmdcore into modules
# hqian 07/19/05 - Remove RCS header
# hqian 06/23/05 - #4450221: support wildcards for CD
# hqian 05/18/05 - Mention 'missing view attributes' in help ls
# hqian 05/03/05 - #4329688: improve SQL efficiency
# hqian 04/13/05 - ls_get_file_info() -> ls_process_file()
# hqian 04/08/05 - Improve implementation of ls
# hqian 04/08/05 - Improve help documentation
# hqian 04/07/05 - LRG 1843355: include seconds in mod-time
# hqian 04/01/05 - #4261342: use asmcmd messages for certain errors
# hqian 02/28/05 - #4204122: change NLS date format for minute to 'MI'
# hqian 10/27/04 - hqian_asmcmd_13306_linux_3
# hqian 10/19/04 - Rename asmcmd0 to asmcmdcore
# hqian 08/03/04 - hqian_asmcmd_13306_linux_2
# hqian 07/28/04 - Add % as wildcard char in addition to *.
# hqian 07/13/04 - Add implementation of find [-t <type>].
# hqian 06/30/04 - Make code that uses BigInt work for both Perl 5.6.1
# and Perl 5.8.3; take out -c <connect_string>
# functionality.
# hqian 06/29/04 - Fix 10gR1 compatibility issues; fix alias name
# case-sensitive bug, should be case insensitive
# but case retentive.
# hqian 06/25/04 - Rename the main program from asmcmd to asmcmd0, so
# that we can name the wrapper script asmcmd; rename
# global constants, global variables, and function
# names so that they are prefixed by 'asmcmd0_',
# as per coding style standards; fix ORA-15128 bug.
# hqian 06/23/04 - Inaccurate error message bug: add error message
# 8004, do not print error when '*' matches nothing;
# fix rm -rf * double error message bug; fix find
# diskgroup bug; fix print header in empty directory
# bug; fix space in alias name bug.
# hqian 06/22/04 - Give the option to turn off the functionality of
# the -c flag; add constants for better code design.
# hqian 06/09/04 - Fix bugs; improve comments.
# hqian 06/07/04 - Organize code for better maintenance; code review
# changes (suggested by Dave Friedman).
# hqian 06/01/04 - Fix some bugs.
# hqian 05/24/04 - Implement rm [-rf] and rm *; some code review fixes.
# hqian 05/20/04 - Implement help, instance_type security check,
# - connection error checks, and debug mode.
# hqian 05/18/04 - Implement ls flags and lsct.
# hqian 05/14/04 - hqian_asmcmd_13306
# hqian 04/21/04 - Creation
#
#
#
#############################################################################
#
############################ Functions List #################################
#
# Top Level Command Processing Routines
# asmcmdbase_init
# asmcmdbase_process_cmd
# asmcmdbase_process_mkdir
# asmcmdbase_process_rm
# asmcmdbase_process_mkalias
# asmcmdbase_process_rmalias
# asmcmdbase_process_pwd
# asmcmdbase_process_cd
# asmcmdbase_process_ls
# asmcmdbase_process_find
# asmcmdbase_process_du
# asmcmdbase_process_lsdg
# asmcmdbase_process_lsct
# asmcmdbase_process_help
# asmcmdbase_process_cp
# asmcmdbase_process_setsparseparent
# asmcmdbase_process_lsof
# asmcmdbase_process_showclustermode
# asmcmdbase_process_showpatches
# asmcmdbase_process_showversion
# asmcmdbase_process_showclusterstate
#
# Internal Command Processing Routines
# asmcmdbase_ls_process_file
# asmcmdbase_ls_get_subdirs
# asmcmdbase_ls_subdir_print
# asmcmdbase_ls_print
# asmcmdbase_ls_print_hdr
# asmcmdbase_ls_init_col_wid
# asmcmdbase_lsdg_init_col_wid
# asmcmdbase_lsdg_print
# asmcmdbase_find_one
# asmcmdbase_find_match
# asmcmdbase_rm_prompt_conf
# asmcmdbase_do_file_copy
#
# Sort Order Routines
# asmcmdbase_name_forward
# asmcmdbase_name_backward
# asmcmdbase_time_forward
# asmcmdbase_time_backward
# asmcmdbase_rm_recur_order
# asmcmdbase_levels
#
# Parameter Parsing Routines
# asmcmdbase_parse_remote_conn_str
# asmcmdbase_getchr_noecho
# asmcmdbase_is_cmd
# asmcmdbase_is_wildcard_cmd
# asmcmdbase_is_no_instance_cmd
# asmcmdbase_parse_int_args
# asmcmdbase_parse_int_cmd_line
#
# Error Routines
# asmcmdbase_syntax_error
#
# Initialization Routines
# asmcmdbase_check_insttype
# asmcmdbase_init_global
#
# SQL Routines
# asmcmdbase_mkdir_sql
# asmcmdbase_rm_sql
# asmcmdbase_mkalias_sql
# asmcmdbase_rmalias_sql
# asmcmdbase_is_rbal
# asmcmdbase_get_alias_path
# asmcmdbase_get_ct
# asmcmdbase_connect
# asmcmdbase_disconnect
# asmcmdbase_find_sql
# asmcmdbase_get_cluster_state
#
# Help Routines
# asmcmdbase_get_asmcmd_cmds
#
#############################################################################
package asmcmdbase;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(asmcmdbase_init
asmcmdbase_process_cmd
asmcmdbase_process_help
asmcmdbase_is_cmd
asmcmdbase_parse_int_args
asmcmdbase_parse_int_cmd_line
asmcmdbase_check_insttype
asmcmdbase_init_global
asmcmdbase_syntax_error
asmcmdbase_connect
asmcmdbase_disconnect
asmcmdbase_get_asmcmd_cmds
asmcmdbase_rm_sql
asmcmdbase_get_alias_path
asmcmdbase_cp_core
asmcmdbase_set_sparse_parent
asmcmdbase_find_sql
asmcmdbase_get_cluster_state
);
use strict;
use DBI qw(:sql_types);
use Getopt::Long qw(:config no_ignore_case bundling no_getopt_compat);
use Math::BigInt;
use File::Glob qw(:bsd_glob);
use asmcmdglobal;
use asmcmdshare;
use asmcmdparser;
#use Term::ReadKey; # Currently not bundled with Oracle.#
use List::Util qw[min max];
use POSIX qw(:termios_h);
require DynaLoader;
############################ Global Constants ###############################
#
# The following list is used primarily for is_cmd. All other data from XML.
#
my (%asmcmdbase_cmds) = (cd => {},
cp => {},
du => {},
find => {},
help => {},
ls => {},
lsct => {},
lsdg => {},
mkalias => {},
mkdir => {},
pwd => {},
rmalias => {},
rm => {},
lsof => {},
showclustermode => {},
showpatches => {},
showversion => {},
showclusterstate => {},
setsparseparent => {}
);
my $ASMCMDBASE_SPACE = ' '; # Constant string for a space. #
my $ASMCMDBASE_SIXMONTH = 183; # Number of days in six months. #
my $ASMCMDBASE_DATELEN = 4; # Length of the date string. #
my $ASMCMDBASE_MAXPASSWD = 256; # Max length of user passwd input. #
# ASMCMD Column Header Names:
# Below are the names of the column headers for ls and lsdg. These headers
# are the ASMCMD equivalent of the columns of v$asm_alias, v$asm_file,
# v$asm_diskgroup, and v$asm_operation. In the comment to the right of each
# constant is the fixed view column name that this column name corresponds to.
my $ASMCMDBASE_LS_HDR_TYPE = 'Type'; # TYPE in v$asm_file. #
my $ASMCMDBASE_LS_HDR_REDUND = 'Redund'; # REDUNDANCY in v$asm_file. #
my $ASMCMDBASE_LS_HDR_STRIPED = 'Striped'; # STRIPED in v$asm_file. #
my $ASMCMDBASE_LS_HDR_TIME = 'Time'; # MODIFICATION_TIME in v$asm_file. #
my $ASMCMDBASE_LS_HDR_SYSCRE = 'Sys'; # SYSTEM_CREATED in v$asm_alias. #
my $ASMCMDBASE_LS_HDR_BSIZE = 'Block_Size'; # BLOCK_SIZE in v$asm_file. #
my $ASMCMDBASE_LS_HDR_BLOCKS = 'Blocks'; # BLOCKS in v$asm_file. #
my $ASMCMDBASE_LS_HDR_BYTES = 'Bytes'; # BYTES in v$asm_file. #
my $ASMCMDBASE_LS_HDR_SPACE = 'Space'; # SPACE in v$asm_file. #
my $ASMCMDBASE_LS_HDR_USER = 'User'; # USER in v$asm_file. #
my $ASMCMDBASE_LS_HDR_GROUP = 'Group'; # GROUP in v$asm_file. #
my $ASMCMDBASE_LS_HDR_PERM = 'Permission'; # PERMISSION in v$asm_file. #
# These declarations have to be declared with the 'our' keyword
# in order for eval() to work correctly in asmcmdbase_lsdg_print().
our $ASMCMDBASE_LSDG_HDR_INSTID = 'Inst_ID'; # Instance ID for
# gv$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_SECTOR = 'Sector'; # SECTOR_SIZE in v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_LSECTOR = 'Logical_Sector'; # LOGICAL_SECTOR_SIZE in #
# v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_REBAL = 'Rebal'; # OPERATION from v$asm_operation. #
our $ASMCMDBASE_LSDG_HDR_BLOCK = 'Block'; # BLOCK_SIZE in v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_AU = 'AU'; # ALLOCATION_UNIT_SIZE in #
# v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_STATE = 'State'; # STATE in v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_TYPE = 'Type'; # TYPE in v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_TMB = 'Total_MB'; # TOTAL_MB in v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_FMB = 'Free_MB'; # FREE_MB in v$asm_diskgroup. #
# REQUIRED_MIRROR_FREE_MB from v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_RMFMB = 'Req_mir_free_MB';
# USABLE_FILE_MB from v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_UFMB = 'Usable_file_MB';
# OFFLINE_DISKS from v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_ODISK = 'Offline_disks';
# VOTING_FILE from v$asm_diskgroup. #
our $ASMCMDBASE_LSDG_HDR_VOTING_FILE = 'Voting_files';
our $ASMCMDBASE_LSDG_HDR_NAME = 'Name'; # NAME in v$asm_diskgroup. #
our (%asmcmdbase_lsof_header) = ('path_kffof', 'Path',
'dbname_kffof', 'DB_Name',
'instancename_kffof', 'Instance_Name'
);
# for remote connection
our ($rusr, $rpswd, $rident, $rhost, $rsid, $rport);
# PLSQL constants
my $PLSQL_DATAFILE = 2;
my $PLSQL_DATAFILE_CP = 12;
my $PLSQL_NUMBER = 22;
my $PLSQL_VARCHAR = 1024;
my $PLSQL_PWFILE = 28;
sub is_asmcmd
{
return 1;
}
################# Top Level Command Processing Routines ######################
########
# NAME
# asmcmdbase_init
#
# DESCRIPTION
# This function initializes the asmcmdbase 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, \&asmcmdbase_process_cmd);
push (@asmcmdglobal_help_callbacks, \&asmcmdbase_process_help);
push (@asmcmdglobal_command_list_callbacks, \&asmcmdbase_get_asmcmd_cmds);
push (@asmcmdglobal_is_command_callbacks, \&asmcmdbase_is_cmd);
push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmdbase_is_wildcard_cmd);
push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdbase_syntax_error);
push (@asmcmdglobal_no_instance_callbacks, \&asmcmdbase_is_no_instance_cmd);
%asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdbase_cmds);
#Perform ASMCMD consistency check if enabled
if($asmcmdglobal_hash{'consistchk'} eq 'y')
{
if(!asmcmdshare_check_option_consistency(%asmcmdbase_cmds))
{
exit 1;
}
}
}
########
# NAME
# asmcmdbase_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; 0 if not.
#
# NOTES
# Only asmcmdcore_shell() calls this routine.
########
sub asmcmdbase_process_cmd
{
my ($dbh) = @_;
my ($succ) = 0;
# Get current command from global value, which is set by
# asmcmdbase_parse_asmcmd_args()and by asmcmdcore_shell().
my($cmd) = $asmcmdglobal_hash{'cmd'};
# Declare and initialize hash of function pointers, each designating a
# routine that processes an ASMCMD command. Now that ASMCMD is divided
# into modules, the help command needs to be removed from this list,
# because it's a global command, not a module specific command.
my (%cmdhash) = ( cd => \&asmcmdbase_process_cd ,
du => \&asmcmdbase_process_du ,
find => \&asmcmdbase_process_find,
ls => \&asmcmdbase_process_ls,
lsct => \&asmcmdbase_process_lsct,
lsdg => \&asmcmdbase_process_lsdg,
mkalias => \&asmcmdbase_process_mkalias,
mkdir => \&asmcmdbase_process_mkdir,
pwd => \&asmcmdbase_process_pwd,
rm => \&asmcmdbase_process_rm,
rmalias => \&asmcmdbase_process_rmalias,
cp => \&asmcmdbase_process_cp,
lsof => \&asmcmdbase_process_lsof,
showclustermode => \&asmcmdbase_process_showclustermode,
showpatches => \&asmcmdbase_process_showpatches,
showversion => \&asmcmdbase_process_showversion,
showclusterstate =>
\&asmcmdbase_process_showclusterstate,
setsparseparent => \&asmcmdbase_process_setsparseparent
);
if (defined ( $cmdhash{ $cmd } ))
{ # If user specifies a known command, then call routine to process it. #
$cmdhash{ $cmd }->($dbh);
$succ = 1;
}
return $succ;
}
########
# NAME
# asmcmdbase_process_lsof
#
# DESCRIPTION
# This top-level routine processes the lsof command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() calls this function.
########
sub asmcmdbase_process_lsof
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my (%norm); # See asmcmdshare_normalize_path() return value comments. #
my ($ret); # asmcmdbase_parse_int_args() return value. #
my ($dgname, $dbname, $instname, $gnum);
my ($sth, $row);
my (@what, @from, @where, @order, @binds);
my (@lsof_list);
my ($k, $v, $h);
my (%min_col_wid, $print_format, $print_string, @what_print);
my ($kffilf_invalid) = 0x00004000;
# get option parameters #
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
$dgname = $args{'G'} if (defined($args{'G'}));
$dbname = $args{'dbname'} if (defined($args{'dbname'}));
$instname = $args{'C'} if (defined($args{'C'}));
push (@what, 'dbname_kffof');
push (@what, 'instancename_kffof');
push (@what, 'path_kffof');
# 'flags_kffof' should be the last element of @what, it gets popped later
push (@what, 'flags_kffof');
push (@from, 'x$kffof');
# Bug19494254: Filter out invalid file descriptors
# $kffilf_invalid represents the same flag as in kff.h:KFFILF_INVALID
push (@where, "bitand(flags_kffof, $kffilf_invalid) = 0");
#filter disk group
if (defined($args{'G'}))
{
#get disk group name
$gnum = asmcmdshare_get_gnum_from_gname($dbh, $args{'G'});
if (!defined($gnum))
{
my (@eargs) = ($args{'G'});
asmcmdshare_error_msg(8001, \@eargs);
return;
}
push (@where, "group_kffof = ?");
push (@binds, [$gnum, SQL_INTEGER]);
}
#filter database
if (defined($args{'dbname'}))
{
push (@where, "upper(dbname_kffof) = ?");
push (@binds, [uc($args{'dbname'}), SQL_VARCHAR]);
}
#filter instance
if (defined($args{'C'}))
{
push (@where, "upper(instancename_kffof) = ?");
push (@binds, [uc($args{'C'}), SQL_VARCHAR]);
}
push (@order, 'dbname_kffof');
push (@order, 'instancename_kffof');
push (@order, 'path_kffof');
$sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where,
\@order, \@binds);
if (!defined ($sth))
{
asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
}
#initialize the min_col_wid array
foreach(@what)
{
$min_col_wid{$_} = length($asmcmdbase_lsof_header{$_});
}
#get the rows
while (defined($row = asmcmdshare_fetch($sth)))
{
my(%file_info) = ();
while (($k,$v) = each(%{$row}))
{
$k =~ tr/[A-Z]/[a-z]/;
# skip 'flags_kffof' column
next if ($k eq 'flags_kffof');
$file_info{$k} = $v;
$min_col_wid{$k} = max($min_col_wid{$k}, length($v));
}
push (@lsof_list, \%file_info);
}
asmcmdshare_finish($sth);
# don't print 'flags_kffof'
pop (@what);
#create print format
$print_format = '';
foreach (@what)
{
$print_format .= "%-" . $min_col_wid{$_} . "s ";
}
$print_format .= "\n";
#print header
if (!defined ($args{'suppressheader'}) )
{
@what_print = ();
foreach (@what)
{
push (@what_print, $asmcmdbase_lsof_header{$_});
}
$print_string = sprintf($print_format, @what_print);
asmcmdshare_print($print_string);
}
#print rows
foreach $h (@lsof_list)
{
@what_print = ();
foreach (@what)
{
push (@what_print, $h->{$_});
}
$print_string = sprintf($print_format, @what_print);
asmcmdshare_print($print_string);
}
}
########
# NAME
# asmcmdbase_process_mkdir
#
# DESCRIPTION
# This top-level routine processes the mkdir command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() calls this function.
########
sub asmcmdbase_process_mkdir
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my (%norm); # See asmcmdshare_normalize_path() return value comments. #
my ($ret); # asmcmdbase_parse_int_args() return value. #
my ($dir); # Create directory with this path name. #
my ($cre); # Last level of in the path $dir; create directory #
# with this name. #
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# Process creation one path at a time.
while (defined ($dir = shift (@{$args{'mkdir'}})))
{
my $backup1 = $1;
$dir = asmcmdshare_make_absolute($dir);
$dir =~ s,/+$,,; # Remove all trailing '/'. #
$dir =~ s,/([^/]+)$,,; # Do not normalize the creation level, remove. #
$cre = $1; # Save creation level of path $dir here. #
undef $cre if(defined $backup1 && $backup1 eq $1);
# If the user entered only '+dg' as an argument, then we have to
# parse that as creating 'dg' in directory '+'. This will result
# in an error, which is the correct behavior.
if (!defined ($cre))
{
$cre = $dir; # Set $cre to e.g. '+dg'. #
$cre =~ s,^\+,,; # Get rid of the '+' from '+dg'. #
$dir = '+'; # Set parent dir to '+'. #
}
%norm = asmcmdshare_normalize_path($dbh, $dir, 0, \$ret);
next if ($ret != 0); # Skip creation if normalization failed. #
# Fix bug that allowed creation under '+'. Setting group name to '+'
# guarantees failure if trying to create under '+', which is the desired
# outcome. Oracle will provide the error code.
if ($dir eq '+')
{
$asmcmdglobal_hash{'gname'} = '+';
}
$dir = $norm{'path'}->[0];
$dir .= '/' . $cre; # Reattach creation name to path. #
# Run creation SQL. #
asmcmdbase_mkdir_sql($dbh, $asmcmdglobal_hash{'gname'}, $dir);
}
return;
}
########
# NAME
# asmcmdbase_process_rm
#
# DESCRIPTION
# This top-level routine processes the rm command.
#
# PARAMETERS
# dbh (IN) - initialize database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() calls this routine.
# Note also that removing a user alias removes the respective system alias
# as well, and vice versa. Only one of the user/system pair needs to match
# a wildcard or be under a directory recursively (if -r) for the other to
# be deleted as well. So please be careful when using rm!
########
sub asmcmdbase_process_rm
{
my $dbh = shift;
my $sid = undef;
my $osid = undef;
my %args; # Argument hash used by getopts(). #
my %norm; # See asmcmdshare_normalize_path() return value comments. #
my @recur_list; # List of hashes of fields of all entries #
# recursively under a directory. #
my %deleted_hash; # Hash of deleted entries. #
my @entries; # List of hashes of fields of matching entries under #
# a directory. #
my $ret; # asmcmdbase_parse_int_args() return value. #
my $alias_path; # User-entered raw path for deletion. #
my $alias_name; # Alias name of a path. #
my $is_dir; # Flag: 'Y' if alias is a directory; 'N' otherwise. #
my $gnum; # Group number. #
my $gname; # Group name. #
my $par_id; # Parent ID of an alias. #
my $hash_str; # Hash string for %deleted_hash. #
my $iter; # Iteration variable for foreach() loops. #
my $spprserr = 0; # Whether to suppress errors on normalization. #
my $i; # Iteration variable for for() loops. #
my ($sth, $dir);
my @eargs;
my @entries2;
my $stmt;
my $tgt;
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
if ($args{'target'})
{
$tgt = $args{'target'};
$sid = asmcmdshare_set_instance($tgt);
$asmcmdglobal_hash{'target'} = $tgt;
if (!defined($sid))
{
my @eargs = ("target", $tgt);
# ASMCMD-8608 "invalid value for '%s' option: %s"
asmcmdshare_error_msg(8608, \@eargs);
return 0;
}
return 0 if ($sid eq '');
asmcmdbase_disconnect($dbh);
if ($ENV{'ORACLE_SID'} ne $sid)
{
$osid = $ENV{'ORACLE_SID'};
$ENV{'ORACLE_SID'} = $sid;
}
$dbh = asmcmdbase_connect(undef);
if (!defined($dbh))
{
asmcmdshare_trace(1, "failed to connect to target instance " .
"[$ENV{'ORACLE_SID'}]", 'y', 'n');
}
}
# First check flags and args to see if we should ask user for confirmation
# before proceeding. We prompt the user when the -f flag is not set, when
# the mode is interactive, and when at least one of these conditions
# is true:
# 1) -r is set,
# 2) at least one argument of 'rm' contains a wildcard.
# First reset $ret
$ret = 0;
if (! defined ($args{'f'}) && ($asmcmdglobal_hash{'mode'} eq 'i'))
{
if (defined ($args{'r'}))
{
$ret = asmcmdbase_rm_prompt_conf(); # Prompt for confirmation. #
return unless ($ret == 1);
}
else
{
for ($i = 0; $i < @{$args{'rm'}}; $i++)
{
if (${$args{'rm'}}[$i] =~ m,$ASMCMDGLOBAL_WCARD_CHARS,)
{
$ret = asmcmdbase_rm_prompt_conf();
return unless ($ret == 1);
last;
}
}
}
}
# Process each alias entry one at a time. (rm can take multiple alias
# arguments.
$spprserr = 1 if (defined ($args{'r'}));
while (defined ($alias_path = shift (@{$args{'rm'}})))
{
# A) First process all entries under $alias_path, if -r.
if (defined ($args{'r'}))
{
# See if path is valid. Get all paths if $path contains a wildcard.
%norm = asmcmdshare_normalize_path($dbh, $alias_path, '0', \$ret);
next if ($ret != 0);
for ($i = 0; $i < @{ $norm{'path'} }; $i++)
{
$alias_name = $norm{'path'}->[$i];
$alias_name =~ s,.*/([^/]+)$,$1,; # Get last level of path for name. #
$gnum = $norm{'gnum'}->[$i];
$par_id = $norm{'par_id'}->[$i];
asmcmdshare_get_subdirs($dbh, \@entries, undef, $gnum, undef, $par_id,
$alias_name, undef, 0, 0);
$is_dir = $entries[$i]->{'alias_directory'};
if (defined($is_dir) && ($is_dir eq 'Y'))
{
$gname = asmcmdshare_get_gname_from_gnum($dbh, $gnum);
$dir=$norm{'path'}->[$i];
# call fixed package
$stmt = <<STMT;
begin
dbms_diskgroup.dropdir('$dir');
exception when others then
raise;
end;
STMT
$sth = $dbh->prepare($stmt);
$ret = $sth->execute();
if (!defined($ret))
{
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
return;
}
# reconnect
asmcmdbase_disconnect($dbh) if defined ($dbh);
#undef since we connect to local asm instance
$dbh = asmcmdbase_connect(undef);
}
}
}
# B) Last process $alias_path itself.
%norm = asmcmdshare_normalize_path($dbh, $alias_path, $spprserr, \$ret);
next if ($ret != 0);
# Remove one entry at a time, if $alias_path contains '*' and has multiple
# matches.
for ($i = 0; $i < @{ $norm{'path'} }; $i++)
{
$alias_name = $alias_path = $norm{'path'}->[$i];
$alias_name =~ s,.*/([^/]+)$,$1,; # Get last level of path for name. #
$par_id = $norm{'par_id'}->[$i];
$gnum = $norm{'gnum'}->[$i];
# If parent index is -1, then the directory must be a diskgroup or '+';
# thus we cannot remove it.
if ($par_id != -1)
{
asmcmdshare_get_subdirs($dbh, \@entries2, undef, $gnum, undef, $par_id,
$alias_name, undef, 0, 0);
$is_dir = $entries2[$i]->{'alias_directory'};
# Bug 14676720: if system & user alias are in same directory,
# file gets deleted while processing first alias, skip the second.
#
if (defined($is_dir))
{
$gname = asmcmdshare_get_gname_from_gnum ($dbh, $gnum);
# Run SQL to remove an entry.
asmcmdbase_rm_sql($dbh, $gname, $alias_path, $is_dir);
}
}
}
}
if (defined($osid))
{
asmcmdbase_disconnect($dbh);
$ENV{'ORACLE_SID'} = $osid;
$dbh = asmcmdbase_connect(undef);
}
return;
}
########
# NAME
# asmcmdbase_process_mkalias
#
# DESCRIPTION
# This top-level routine processes the mkalias command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() can call this routine.
########
sub asmcmdbase_process_mkalias
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my (%norm); # See asmcmdshare_normalize_path() return value comments. #
my ($ret); # asmcmdbase_parse_int_args() return value. #
my ($sys_a); # User-specified existing system alias. #
my ($usr_a); # User-specified new user alias for creation. #
my ($cre); # Last level of in the path $dir; create directory #
# with this name. #
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
($sys_a, $usr_a) = @{$args{'mkalias'}};
$usr_a = asmcmdshare_make_absolute($usr_a);
$usr_a =~ s,/+$,,; # Remove all trailing '/'. #
$usr_a =~ s,/([^/]+)$,,; # Remove last level, which is to be created. #
$cre = $1; # Save creation name. #
# If the user entered only '+dg' as an argument, then we have to
# parse that as creating the alias 'dg' in directory '+'. This
# will result in an error, which is the correct behavior.
if (!defined ($cre))
{
$cre = $usr_a; # Set $cre to e.g. '+dg'. #
$cre =~ s,^\+,,; # Get rid of the '+' from '+dg'. #
$usr_a = '+'; # Set parent dir to '+'. #
}
# Check if system alias exists.
%norm = asmcmdshare_normalize_path($dbh, $sys_a, 0, \$ret);
return if ($ret != 0);
$sys_a = $norm{'path'}->[0];
# Check if directory to contain new user alias exists.
%norm = asmcmdshare_normalize_path($dbh, $usr_a, 0, \$ret);
return if ($ret != 0);
# Fix bug that allowed creation under '+'. Setting group name to '+'
# guarantees failure if trying to create under '+', which is the desired
# outcome. Oracle will provide the error code.
$asmcmdglobal_hash{'gname'} = '+' if ($usr_a eq '+');
$usr_a = $norm{'path'}->[0];
$usr_a .= '/' . $cre; # Reattach creation name to path. #
# Run SQL to create user alias.
asmcmdbase_mkalias_sql($dbh, $asmcmdglobal_hash{'gname'}, $sys_a, $usr_a);
return;
}
########
# NAME
# asmcmdbase_process_rmalias
#
# DESCRIPTION
# This top-level routine processes the rmalias command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() can call this routine.
########
sub asmcmdbase_process_rmalias
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my (%norm); # See asmcmdshare_normalize_path() return value comments. #
my ($ret); # asmcmdbase_parse_int_args() return value. #
my ($alias_path); # User-entered raw path for deletion. #
my ($recurse) = 0; # Boolean: 1 if -r; 0 otherwise. #
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
$recurse = 1 if (defined ($args{'r'}));
# Process each alias entry one at a time. (rmalias can take multiple alias
# arguments.
while (defined ($alias_path = shift (@{$args{'rmalias'}})))
{
# Make sure alias exists.
%norm = asmcmdshare_normalize_path($dbh, $alias_path, 0, \$ret);
next if ($ret != 0);
$alias_path = $norm{'path'}->[0];
# Run SQL to delete user alias.
asmcmdbase_rmalias_sql ($dbh, $asmcmdglobal_hash{'gname'},
$alias_path, $recurse);
}
}
########
# NAME
# asmcmdbase_process_pwd
#
# DESCRIPTION
# This top-level routine processes the pwd command.
#
# PARAMETERS
# None.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() can call this routine.
########
sub asmcmdbase_process_pwd {
my (%args);
my ($ret);
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
asmcmdshare_print("$asmcmdglobal_hash{'cwdnm'}\n");
return;
}
########
# NAME
# asmcmdbase_process_cd
#
# DESCRIPTION
# This top-level routine processes the cd command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() can call this routine.
########
sub asmcmdbase_process_cd
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my (%norm); # See asmcmdshare_normalize_path() return value comments. #
my ($ret); # asmcmdbase_parse_int_args() return value. #
my ($dir); # Change current directory to this directory path. #
my ($maxval); # Reference index value for non-directory aliases in 10gR2. #
my ($ref_id); # Reference index value for an alias entry. #
my ($gnum); # Group number. #
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
($dir) = @{$args{'cd'}};
# 20134010: Check $dir is defined and not empty.
if (!defined($dir) || $dir eq '')
{
asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'});
return;
}
# Check if path is valid.
%norm = asmcmdshare_normalize_path($dbh, $dir, 0, \$ret);
return if ($ret != 0); # Error should already be printed. #
# Since we support wildcards for CD now, make sure there is only one
# match. Otherwise, report error.
if (@{ $norm{'path'} } > 1)
{
# asmcmd: $dir: ambiguous
my (@eargs) = ($dir);
asmcmdshare_error_msg(8005, \@eargs);
return;
}
$dir = $norm{'path'}->[0];
$ref_id = $norm{'ref_id'}->[0];
$gnum = $norm{'gnum'}->[0];
# Calculate maxval:
# $maxval is (group_number + 1) * 2^24 - 1.
$maxval = (($gnum + 1) << 24) - 1;
$maxval = -2 if ($gnum == -1); # We're in '+', no need to calc maxval. #
# Reference index value for non-directory aliases is 0 in 10gR1 and $maxval
# in 10gR2. You can't cd to a file, so display error.
if (($ref_id == $maxval) || ($ref_id == 0))
{
# asmcmd: "entry '%s' does not refer to a valid directory"
my (@eargs) = ($dir);
asmcmdshare_error_msg(8006, \@eargs);
return;
}
# Update global values for new current directory.
$asmcmdglobal_hash{'cwdnm'} = $dir;
$asmcmdglobal_hash{'cwdref'} = $ref_id;
return;
}
########
# NAME
# asmcmdbase_process_ls
#
# DESCRIPTION
# This top-level routine processes the ls command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() can call this routine.
########
sub asmcmdbase_process_ls
{
my $dbh = shift;
my %args; # Argument hash used by getopts(). #
my %min_col_wid; # Hash of mininum column widths for each ls column. #
my %subdirs; # Hash of array pointers, each array containing #
# entries under a directory. #
my %norm; # See asmcmdshare_normalize_path() return value #
# comments. One entry returned. #
my @paths; # Array of normalized paths; $norm{'path'} #
# dereferenced. #
my @ref_ids; # Reference indexes for @paths; $norm{'ref_id'} #
# dereferenced. #
my @par_ids; # Parent indexes for @paths; $norm{'par_id'} #
# dereferenced. #
my @dg_nums; # Diskgroup numbers for @paths; $norm{'gnum'} #
# dereferenced. #
my @entry_list; # List of entries (hashes) that $alias matches after #
# normalization; includes full column values for #
# every file and directory. #
my $cur_date; # Current date in Julian Date. #
my $time_format; # Time format used to print dates. #
my $get_file_info = 0; # boolean: true if we want file info as well. #
my $alias; # User-entered alias to be listed. #
my $ret; # asmcmdbase_parse_int_args() return value. #
my $i; # Iteration variable for for() loops. #
$cur_date = asmcmdshare_cur_julian_date ($dbh);
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
($alias) = @{$args{'ls'}} if (defined $args{'ls'});
$alias = '' unless defined ($alias); # Defaults to '', or current dir. #
# If user specified time format, use that. Otherwise, use MON DD HH24:MI:SS #
$time_format = $args{'time_style'} if defined $args{'time_style'};
$time_format = 'MON DD HH24:MI:SS' unless defined $time_format;
# 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'} };
# Obtain all fields for each match; store in @entry_list, an array of
# hashes, each containing one entry with all fields.
for ($i = 0; $i < @paths; $i++)
{
my %entry_info;
$entry_info{'path'} = $paths[$i];
$entry_info{'name'} = $paths[$i];
$entry_info{'name'} =~ s,.*/(.*)$,$1,;
$entry_info{'name_print'} = $entry_info{'name'};
$entry_info{'reference_index'} = $ref_ids[$i];
$entry_info{'parent_index'} = $par_ids[$i];
$entry_info{'group_number'} = $dg_nums[$i];
# 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 (($entry_info{'reference_index'} == -1) ||
($entry_info{'reference_index'} == $entry_info{'group_number'} << 24))
{
# If the entry is a diskgroup of '+', we treat it as a directory.
$entry_info{'alias_directory'} = 'Y';
push (@entry_list, \%entry_info); # Save hash entry fieldss in list. #
}
else
{
# Get file info only if we need it.
$get_file_info = 1 if (defined($args{'l'}) || defined($args{'s'}) ||
defined($args{'permission'}));
# asmcmdshare_normalize_path currently does not return all the
# column values in v$asm_alias and v$asm_file, so get the remaining
# column values.
my $prev_size = scalar @entry_list;
asmcmdshare_get_subdirs($dbh, \@entry_list, $time_format,
$entry_info{'group_number'},
$entry_info{'reference_index'},
$entry_info{'parent_index'},
$entry_info{'name'}, undef, 0, $get_file_info);
# Continue processing the row only if a new row was added (the file has
# not been removed since the first query)
if (scalar @entry_list > $prev_size)
{
$entry_list[-1]->{'path'} = $entry_info{'path'};
$entry_list[-1]->{'name_print'} = $entry_info{'name'};
# If entry is a file, process the file column values from v$asm_file.
if ($entry_list[-1]->{'alias_directory'} eq 'N')
{
asmcmdbase_ls_process_file ($dbh, $entry_list[-1], \%args, $cur_date,
$get_file_info);
}
}
}
}
# Sort @entry_list based on combinations of the -r and -t flags. See sort
# routines on sort orderings and priorities.
if (defined ($args{'t'}) && defined ($args{'reverse'}))
{
@entry_list = sort asmcmdbase_time_backward @entry_list;
}
elsif (defined ($args{'t'}) && !defined ($args{'reverse'}))
{
@entry_list = sort asmcmdbase_time_forward @entry_list;
}
elsif (!defined ($args{'t'}) && defined ($args{'reverse'}))
{
@entry_list = sort asmcmdbase_name_backward @entry_list;
}
else
{
@entry_list = sort asmcmdbase_name_forward @entry_list;
}
# Diskgroups get processed separately.
if ($entry_list[0]->{'reference_index'} == -1)
{ # We're doing an 'ls +'. #
if (defined ($args{'d'}))
{
# 'ls -d +' should list the information for '+', but it has none, so just
# print '+'.
asmcmdshare_print("+\n");
}
else
{
# Without -d, we list all directories under '+', which are all the
# diskgroups.
asmcmdbase_lsdg_print ($dbh, undef, \%args);
}
return;
}
# We take care of the case of 'ls -d <dg_name>'.
if (($entry_list[0]->{'parent_index'} == -1) &&
(defined ($args{'d'})))
{
# In case pwd is '+dg_name' and we do 'ls -d' with no argument, assign
# $alias to current directory, which is a diskgroup name.
$alias = $entry_list[0]->{'name'} if ($alias eq '');
# Trim the leading '+', as asmcmdbase_lsdg_print doesn't need it. #
$alias =~ s,^\+,,;
$alias =~ s,/+$,,; # Trim ending '+', in cases of 'ls /+'. #
# Print only diskgroups specified by $alias.
asmcmdbase_lsdg_print ($dbh, $alias, \%args);
return;
}
# Prepare column widths for printing.
asmcmdbase_ls_init_col_wid(\%min_col_wid); # Min width of headers. #
asmcmdshare_ls_calc_min_col_wid(\@entry_list, \%min_col_wid);
# Min width of all values. #
# Get list of subdirs for each directory entry. Note that we loop through
# @entry_list twice here; first time to get all the sub-entries so that the
# minimum column width can be calculated. We then save the sub-entries in
# a hash so that the second loop and print them without having to run the
# SQL again.
for ($i = 0; $i < @entry_list; $i++)
{
if (($entry_list[$i]->{'alias_directory'} eq 'Y') &&
(!defined ($args{'d'})))
{
my @list;
@list = asmcmdbase_ls_get_subdirs($dbh, $entry_list[$i]->{'group_number'},
$entry_list[$i]->{'reference_index'},
$time_format, \%args, \%min_col_wid,
$cur_date);
# Save the list of sub-entries in a hash so that next for loop we don't
# have to call asmcmdbase_ls_get_subdirs again.
$subdirs{ $entry_list[$i]->{'reference_index'} } = \@list;
}
}
# Print column headers, now that we have the correct minimum width.
# Print iff all of the following numbers are true:
# 1) either -l or -s are set;
# 2) --suppressheader is not set;
# 3) any one or more of the following letters is true:
# a) -d is set;
# b) the first path matched points to a file;
# c) asmcmdshare_normalize_path() returns more than one path;
# d) the only path returned by asmcmdshare_normalize_path() is a directory,
# it has at least one entry in it.
if ((defined ($args{'l'}) || defined ($args{'s'}) ||
defined($args{'permission'})) &&
(!defined ($args{'suppressheader'})) &&
((defined ($args{'d'})) ||
($entry_list[0]->{'alias_directory'} eq 'N') ||
(@entry_list > 1) ||
(@{ $subdirs{ $entry_list[0]->{'reference_index'} } } != 0)))
{
asmcmdbase_ls_print_hdr (\%args, \%min_col_wid);
}
# Print one row at a time.
for ($i = 0; $i < @entry_list; $i++)
{
if (($entry_list[$i]->{'alias_directory'} eq 'Y') &&
(!defined ($args{'d'})))
{
# Print the directory and its sub-entries if it's a dir and no -d. #
if (@entry_list > 1)
{
asmcmdshare_print("\n" . $entry_list[$i]->{'path'} . "/:\n");
}
asmcmdbase_ls_subdir_print($dbh,
$subdirs{ $entry_list[$i]->{'reference_index'} },
\%args, \%min_col_wid);
}
else
{ # Otherwise print the entry itself. #
asmcmdbase_ls_print($entry_list[$i], \%args, \%min_col_wid);
}
}
return;
}
########
# NAME
# asmcmdbase_process_find
#
# DESCRIPTION
# This top-level routine processes the find command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() can call this routine.
########
sub asmcmdbase_process_find
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my (@match); # Array of hashes of all entry matches. #
my ($ret); # asmcmdshare_parse_int_args() return value. #
my ($path); # Conduct search under this path, wildcard allowed. #
my ($search); # Search string, wildcard allowed. #
my ($type); # File type to search for, if -t is set. #
my ($iter); # Iteration variable for foreach() loops. #
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
# Get type if --type is set. #
$type = $args{'type'} if (defined($args{'type'}));
($path, $search) = @{$args{'find'}};
# Run the internal find routine.
@match = asmcmdbase_find_sql($dbh, $path, $search, $type, undef, 0, 1);
return if (@match == 0);
# Sort entries
@match = sort { $a->{'full_path'} cmp $b->{'full_path'} } @match;
# Print each entry.
foreach $iter (@match)
{
$iter->{'full_path'} .= '/' if ($iter->{'alias_directory'} eq 'Y');
asmcmdshare_print("$iter->{'full_path'}\n");
}
return;
}
########
# NAME
# asmcmdbase_process_du
#
# DESCRIPTION
# This top-level routine processes the du command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() can call this routine.
########
sub asmcmdbase_process_du
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my ($ret); # temporary return value from a subroutine. #
my (@match); # Array of hashes of all entry matches. #
my (%file_info); # Hash of v$asm_file column values for file entries. #
my ($dir); # User-entered directory path, under which du looks for files. #
my ($i); # Iteration variable for for() loops. #
my ($uMB); # Total amount of space used in MB, not counting redund copies. #
my ($mirUMB); # Same as $uMB, but accouting for redundant copies. #
my ($uMBBInt) = Math::BigInt->new('0'); # BigInt copy of $uMB. #
my ($mirUMBBInt) = Math::BigInt->new('0'); # BigInt copy of $mirUMB. #
my ($redund) = ' '; # Redundancy value for files; UNPROT, MIRROR, HIGH. #
my ($redundBInt); # BigInt copy of redundancy value. #
my ($spaceBInt); # Column 'space' from v$asm_file, in BigInt. #
my ($oneMBBInt) = Math::BigInt->new('1048576'); # BigInt for 1MB. #
my ($resBInt); # Result BigInt for BigInt operations. #
my ($perlv); # Current Perl Version Number. #
my ($print_string);
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
if(defined $args{'du'})
{
($dir) = @{$args{'du'}};
}
else
{
$dir = '';
}
# See if path is valid. asmcmdbase_find_sql returns empty array even in
# failure. This check helps to differentiate valid empty dir against failure.
asmcmdshare_normalize_path ($dbh, $dir, 0, \$ret);
return if ($ret == -1);
# Find all system alias only under $dir.
@match = asmcmdbase_find_sql($dbh, $dir, '*', undef, undef, 1, 0);
# bug19468869: If no system alias found, print du output as 0.
if (@match == 0)
{
goto print_op;
}
# Calculate space used one file at at time and tally up the total.
for ($i = 0; $i < @match; $i++)
{
# Get file information from v$asm_file.
asmcmdshare_get_file($dbh, $match[$i]->{'group_number'},
$match[$i]->{'file_number'}, undef, \%file_info);
# The files could be deleted after their name is collected in
# the array @match but before information could be collected from
# from the function asmcmdshare_get_file.
if (!defined ($file_info{'space'}) ||
!defined ($file_info{'redundancy'})) {
asmcmdshare_trace(2, "NOTE: Could not get information about the file"
." $match[$i]->{'full_path'}. It could be"
." deleted.", 'y', 'n');
next;
}
# Must use Math::BigInt to preserve accuracy.
$spaceBInt = Math::BigInt->new( $file_info{'space'} );
# Divide by 1MB to get units in MB.
$resBInt = $spaceBInt / $oneMBBInt;
$spaceBInt = $resBInt;
# Add to mirrored total
$resBInt = $mirUMBBInt + $spaceBInt;
$mirUMBBInt = $resBInt;
# Calculate space used when not counting mirrored copies.
$redund = $file_info{'redundancy'};
if ('UNPROT' eq $redund) {
$redundBInt = Math::BigInt->new("1");
} elsif ('MIRROR' eq $redund ) {
$redundBInt = Math::BigInt->new("2");
} else {
$redundBInt = Math::BigInt->new("3");
}
$resBInt = $spaceBInt / $redundBInt;
$spaceBInt = $resBInt;
# Add to unmirrored total.
$resBInt = $uMBBInt + $spaceBInt;
$uMBBInt = $resBInt;
}
print_op:
# Get Perl version number. The syntax for the members of Math::BigInt
# between versions is neither backward nor forward compatible. Certain
# routines exist in one version but not another, and vice versa. Other
# routines changed names over different version number. Some routines'
# definition even changed. All of this inconsistency is a source of major
# headache.
#
# The ideal is to check the version number of Math::BigInt; however, the
# older version of BigInt, which is bundled with Perl 5.6.1 and 10gR1, does
# not offer any capability to retrieve the version number.
#
# Although checking the Perl version number is not foolproof,
# it's the best we can do in order to decide which routines to call.
$perlv = $];
if ($perlv <= 5.006001)
{ # Perl 5.6.1 (or earlier) as in 10gR1. #
$uMB = $uMBBInt->stringify(); # Covert back to a string. #
$mirUMB = $mirUMBBInt->stringify();
}
else
{ # Any Perl version later than 5.6.1. #
$uMB = $uMBBInt->bstr(); # Covert back to a string. #
$mirUMB = $mirUMBBInt->bstr();
}
$uMB =~ s,^\+,,; # Trim off the leading '+' (positive sign). #
$mirUMB =~ s,^\+,,;
if (! defined ($args{'suppressheader'}))
{
$print_string = sprintf ("%7s%20s\n", 'Used_MB', 'Mirror_used_MB');
asmcmdshare_print($print_string);
}
$print_string = sprintf ("%7s%20s\n", $uMB, $mirUMB);
asmcmdshare_print($print_string);
return;
}
########
# NAME
# asmcmdbase_process_lsdg
#
# DESCRIPTION
# This top-level routine processes the lsdg command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() can call this routine. This routine does the
# equivalent of 'ls -ls +'.
########
sub asmcmdbase_process_lsdg
{
my ($dbh) = shift;
my (%args); # Argument hash used by getopts(). #
my ($ret); # asmcmdbase_parse_int_args() return value. #
my ($gname); # User-entered group name. #
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
$gname = undef;
($gname) = @{$args{'lsdg'}} if(defined($args{'lsdg'}));
# lsdg always returns all columns, so both -l and -s flags are needed.
$args{'l'} = 'l';
$args{'s'} = 's';
asmcmdbase_lsdg_print ($dbh, $gname, \%args);
return;
}
########
# NAME
# asmcmdbase_process_lsct
#
# DESCRIPTION
# This top-level routine processes the lsct command.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() can call this routine.
########
sub asmcmdbase_process_lsct
{
my $dbh = shift;
my $global = 0; # True iff query global view. #
my $inst = undef;
my $osid = undef;
my $row = ''; # One row of one client, for printing. #
my $sid = undef;
my $gname; # Group name, as entered by user. #
my $iter; # Iteration variable for foreach() loops. #
my $print_string;
my $ret; # asmcmdbase_parse_int_args() return value. #
my $tgt;
my %args; # Argument hash used by getopts(). #
my %min_col_wid; # Minimum column width to display. #
my @client; # Array of hashes of clients in v$asm_client. #
my @header;
my @rowval;
# Get option parameters, if any.
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
if ($args{'target'})
{
$tgt = $args{'target'};
$asmcmdglobal_hash{'target'} = $tgt;
$inst = 'ios' if ($tgt =~ m/IOS/i);
$sid = asmcmdshare_set_instance($tgt);
if (!defined($sid))
{
my @eargs = ("target", $tgt);
# ASMCMD-8608 "invalid value for '%s' option: %s"
asmcmdshare_error_msg(8608, \@eargs);
return 0;
}
return 0 if ($sid eq '');
asmcmdbase_disconnect($dbh);
if ($ENV{'ORACLE_SID'} ne $sid)
{
$osid = $ENV{'ORACLE_SID'};
$ENV{'ORACLE_SID'} = $sid;
}
$dbh = asmcmdbase_connect(undef);
if (!defined($dbh))
{
asmcmdshare_trace(1, "failed to connect to target instance " .
"[$ENV{'ORACLE_SID'}]", 'y', 'n');
}
}
# Check for the -g flag.
if (defined($args{'g'}))
{
$global = 1;
}
($gname) = @{$args{'lsct'}} if (defined($args{'lsct'}));
@client = asmcmdbase_get_ct($dbh, $gname, $global, $inst);
return if (@client == 0);
@client = sort { $a->{'instance_name'} cmp $b->{'instance_name'} } @client;
$min_col_wid{'db_name'} = length('DB_Name');
$min_col_wid{'status'} = length('Status');
$min_col_wid{'software_version'} = length('Software_Version');
$min_col_wid{'compatible_version'} = length('Compatible_version');
$min_col_wid{'instance_name'} = length('Instance_Name');
$min_col_wid{'group_name'} = length('Disk_Group');
asmcmdshare_ls_calc_min_col_wid (\@client, \%min_col_wid);
if ($global)
{
$row = "%11s ";
push @header, ('Instance_ID');
}
# First print header. These new columns are 10gR2 or later only.
if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_10gR2) >= 0 )
{ # For 10gR2 compatibility, we have some new columns in v$asm_client. #
$row .= "%-" . $min_col_wid{'db_name'} . "s " .
"%-" . $min_col_wid{'status'} . "s " .
"%" . $min_col_wid{'software_version'} . "s " .
"%" . $min_col_wid{'compatible_version'} . "s " .
"%-" . $min_col_wid{'instance_name'} . "s " .
"%-" . $min_col_wid{'group_name'} . "s\n";
push @header, ('DB_Name', 'Status', 'Software_Version',
'Compatible_version', 'Instance_Name', 'Disk_Group');
if (!defined ($args{'suppressheader'}))
{
$print_string = sprintf($row, @header);
asmcmdshare_print($print_string);
}
}
else
{ # For 10gR1 backward-compatibility, no new columns in v$asm_client. #
$row .= "%-" . $min_col_wid{'db_name'} . "s " .
"%-" . $min_col_wid{'status'} . "s " .
"%-" . $min_col_wid{'instance_name'} . "s " .
"%-" . $min_col_wid{'group_name'} . "s\n";
$row .= "%-8s %-12s %-20s %-s\n";
push @header, ('DB_Name', 'Status', 'Instance_Name', 'Disk_Group');
if (!defined ($args{'suppressheader'}))
{
$print_string = sprintf ($row, @header);
asmcmdshare_print($print_string);
}
}
# Print one row at a time.
foreach $iter (@client)
{
@rowval = ();
if ($global)
{
push @rowval, ($iter->{'inst_id'});
}
if (asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_10gR2) >= 0)
{ # >= 10gR2 version. #
push @rowval, ($iter->{'db_name'}, $iter->{'status'},
$iter->{'software_version'},
$iter->{'compatible_version'},
$iter->{'instance_name'},
$iter->{'group_name'});
$print_string = sprintf $row, @rowval;
asmcmdshare_print($print_string);
}
else
{ # 10gR1 version. #
push @rowval, ($iter->{'db_name'}, $iter->{'status'},
$iter->{'instance_name'},
$iter->{'group_name'});
$print_string = sprintf $row, @rowval;
asmcmdshare_print($print_string);
}
}
if (defined($osid))
{
asmcmdbase_disconnect($dbh);
$ENV{'ORACLE_SID'} = $osid;
$asmcmdglobal_hash{'target'} = 'ASM';
$dbh = asmcmdbase_connect(undef);
if (!defined($dbh))
{
asmcmdshare_trace(1, "failed to connect to original target " .
"[$ENV{'ORACLE_SID'}]", 'y', 'n');
}
}
return;
}
########
# NAME
# asmcmdbase_process_help
#
# DESCRIPTION
# This function is the help function for the asmcmdbase module.
#
# PARAMETERS
# command (IN) - display the help message for this command.
#
# RETURNS
# 1 if command found; 0 otherwise.
########
sub asmcmdbase_process_help
{
my ($command) = shift; # User-specified argument; show help on $cmd. #
my ($desc); # Command description for $cmd. #
my ($succ) = 0; # 1 if command found, 0 otherwise. #
if (asmcmdbase_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
# asmcmdbase_is_remote_syntax
#
# DESCRIPTION
# To check whether given string is of remote-file syntax
#
# PARAMETERS
# file (IN) - name of the file
#
# RETURNS
# 1 if remote syntax and 0 if not
#
# NOTES: Checks with Windows file name syntax also.
###########
sub asmcmdbase_is_remote_syntax
{
my ($file) = shift ; # file name to check for.
my ($remote) = 0 ;
if ( $^O =~ /win/i )
{
#windows OS (both 32 & 64 bit OS).
# the valid syntax are x:\dir\file & \\server\share\dir\file
if ((($file !~ /^[a-z]:/i) && ($file !~ /^\\\\/)) && ($file =~ m':'))
{
# not starting with x: or \\ and found a ':' ->
# this is of format usr@server.port.inst:file used for remote syntax
$remote = 1 ;
}
}
else
{
$remote = 1 if ($file =~ m':');
}
return $remote ;
}
########
# NAME
# asmcmdbase_process_cp
#
# DESCRIPTION
# This top-level routine processes the cp command.
#
# PARAMETERS
# dbh (IN) - initialize database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() calls this routine.
########
sub asmcmdbase_process_cp
{
my $dbh = shift; # local auto-variables #
my $osid = undef;
my $sid = undef;
my $dst;
my $ret;
my $src;
my $tgt;
my %args;
# Get option parameters
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
# Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
if ($args{'target'})
{
$tgt = $args{'target'};
$sid = asmcmdshare_set_instance($tgt);
if (!defined($sid))
{
my @eargs = ("target", $tgt);
# ASMCMD-8608: "invalid value for '%s' option: %s"
asmcmdshare_error_msg(8608, \@eargs);
return 0;
}
return 0 if ($sid eq '');
if ($ENV{'ORACLE_SID'} ne $sid)
{
asmcmdbase_disconnect($dbh);
$osid = $ENV{'ORACLE_SID'};
$ENV{'ORACLE_SID'} = $sid;
$dbh = asmcmdbase_connect(undef);
}
}
asmcmdbase_cp_core($dbh, %args);
if (defined($osid))
{
asmcmdbase_disconnect($dbh);
$ENV{'ORACLE_SID'} = $osid;
$dbh = asmcmdbase_connect(undef);
}
}
########
# NAME
# asmcmdbase_process_setsparseparent
#
# DESCRIPTION
# This top-level routine processes the setsparseparent command.
#
# PARAMETERS
# dbh (IN) - initialize database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_cmd() calls this routine.
########
sub asmcmdbase_process_setsparseparent
{
my ($dbh) = shift; # local auto-variables #
my (%args);
my ($ret);
# get option parameters
$ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
#Set the correct options if deprecated options were used and print WARNING.
asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
asmcmdbase_set_sparse_parent($dbh, %args);
return;
}
#########
# NAME
# asmcmdbase_process_showclustermode
#
# DESCRIPTION
# To get the cluster mode
#
# PARAMETERS
# -NONE-
#
# RETURNS
# Cluster mode
###########
sub asmcmdbase_process_showclustermode
{
my ($op) = "op=getclstype";
my ($mode);
# Environment variable 'ORACLE_HOME' will be set, otherwise
# ASMCMD init code will bail out.
# directories in which kfod can be found.
my (@patharr) =("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
my (@result) = asmcmdshare_execute_tool ( "kfod", ".exe", $op, \@patharr);
$mode = $result[0]; # expecting the first line output only.
chomp($mode); # Remove the newline character at the end of the string
asmcmdshare_print("$mode\n");
}
#######
# NAME
# asmcmdbase_process_showpatches
#
# DESCRIPTION
# This routine displayes a list of patches applied
#
# PARAMETERS
# dbh (IN) - initialized database handle, can be null.
#
# RETURNS
# NONE
#######
sub asmcmdbase_process_showpatches
{
my $dbh = shift;
my %args;
my @result;
my $line;
# Environment variable 'ORACLE_HOME' will be set, otherwise
# ASMCMD init code will bail out.
# directories in which kfod and crsctl can be found.
my @patharr =("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
# get option parameters
my $ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($ret);
if (defined($args{'l'}))
{
# get detailed output (includes release patch level, list of patches, and
# release patch string [i.e. vsnnum_full])
@result = asmcmdshare_execute_tool("crsctl",
".exe",
"query crs releasepatch",
\@patharr);
foreach $line (@result)
{
# replace "Oracle Clusterware" with "Oracle ASM" if we're not displaying
# an error
if ($asmcmdglobal_hash{'utilsucc'} eq "true")
{
$line =~ s/Oracle Clusterware/Oracle ASM/g;
}
asmcmdshare_print($line);
}
}
else
{
@result = asmcmdshare_execute_tool("kfod",
".exe",
"op=patches",
\@patharr);
if ($asmcmdglobal_hash{'utilsucc'} eq "true")
{
foreach $line (@result)
{
asmcmdshare_print($line);
}
}
}
}
######
# NAME
# asmcmdbase_process_showversion
#
# DESCRIPTION
# This routine displays the Rolling migration version
#
# PARAMETERS
# $dbh(IN) - initialized database handle, can be null.
#
# RETURNS
# NONE
#
# NOTE
# If there is no ASM instance (dbh is Null), then the query for
# releasepatch will not be run and only "ASM version" &
# softwarepatch will be printed.
#######
sub asmcmdbase_process_showversion
{
my $dbh = shift;
my $result = "";
my %args;
my $qry;
my ($sth,$row);
my @lines;
my $line;
my @patharr;
# get option parameters
$result = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
return unless defined ($result);
# Environment variable 'ORACLE_HOME' will be set, otherwise ASMCMD init code
# will bail out.
# directories in which kfod, crsctl, ocrconfig, etc. can be found
@patharr = ("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
if (defined($args{'active'}))
{
# the cluster has to be configured in order to retrieve active version, so
# bail out if it isn't
@lines = asmcmdshare_execute_tool("ocrcheck",
".exe",
"-config",
\@patharr);
if ($asmcmdglobal_hash{'utilsucc'} eq "true")
{
@lines = asmcmdshare_execute_tool("crsctl",
".exe",
"query crs activeversion -f",
\@patharr);
foreach $line (@lines)
{
# replace "Oracle Clusterware" with "Oracle ASM" if we're not
# displaying an error
if ($asmcmdglobal_hash{'utilsucc'} eq "true")
{
$line =~ s/Oracle Clusterware/Oracle ASM/g;
}
asmcmdshare_print($line);
}
}
else
{
# display the error from 'ocrcheck -config'
foreach $line (@lines)
{
asmcmdshare_print($line);
}
}
}
else
{
# display vsnnum
asmcmdshare_print ("ASM version : $asmcmdglobal_hash{'acver'}\n");
# releasepatch here is equivalent to the active patch level in crsctl
if (defined($args{'releasepatch'}))
{
# Only run the query when there is an ASM instance.
if (defined($dbh))
{
$qry = "SELECT SYS_CONTEXT";
$qry .= "('sys_cluster_properties','current_patchlvl')";
$qry .= " as PATCHLVL from dual";
$sth = asmcmdshare_do_select($dbh, $qry);
$row = asmcmdshare_fetch($sth);
asmcmdshare_finish($sth);
asmcmdshare_print("Release patchlevel : $row->{'PATCHLVL'}\n");
}
else
{
asmcmdshare_print("Information about release patchlevel is "
."unavailable since no ASM instance connected\n");
}
}
# note that softwarepatch here is equivalent to the release patch level in
# crsctl
if (defined($args{'softwarepatch'}))
{
@lines = asmcmdshare_execute_tool("kfod",
".exe",
"op=patchlvl nohdr=TRUE",
\@patharr);
if ($asmcmdglobal_hash{'utilsucc'} eq "true")
{
foreach $line (@lines)
{
chomp($line);
asmcmdshare_print("Software patchlevel : $line\n");
}
}
}
}
}
#######
# NAME
# asmcmdbase_get_cluster_state
#
# DESCRIPTION
# Queries ASM instance for cluster state
#
# PARAMETERS
# $dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Cluster state (string)
#######
sub asmcmdbase_get_cluster_state
{
my $dbh = shift;
my $qry;
my $sth;
my $row;
$qry = "SELECT SYS_CONTEXT('sys_cluster_properties','cluster_state') ";
$qry .= "as STATE from dual";
$sth = asmcmdshare_do_select($dbh, $qry);
$row = asmcmdshare_fetch($sth);
asmcmdshare_finish($sth);
return $row->{'STATE'};
}
#######
# NAME
# asmcmdbase_process_showclusterstate
#
# DESCRIPTION
# To display rolling migration state
#
# PARAMETERS
# $dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# NONE
#######
sub asmcmdbase_process_showclusterstate
{
my $dbh = shift;
my $state = asmcmdbase_get_cluster_state($dbh);
asmcmdshare_print("$state\n");
return;
}
########
# NAME
# asmcmdbase_cp_core
#
# DESCRIPTION
# This routine processes the cp command and the pwcopy command.
#
# PARAMETERS
# dbh (IN) - initialize database handle, must be non-null.
#
# RETURNS
# Null.
#
########
sub asmcmdbase_cp_core
{
use Text::ParseWords;
my $dbh = shift; # local auto-variables #
my $ret;
my $qry;
my $i;
my (%args) = @_; # cmd args #
my @arg_tokens;
my @src_strs = (); # src file args #
my @src_paths; # src file paths #
my @src_fdata; # src file data #
my @src_norm_paths;
my $src_path;
my $tgt; # instance target #
my $tgt_str; # target file/dir args #
my $tgt_path;
my $dest_dbname; # Root directory for sys alias #
my $sp_merge_begin = 0;
my $sp_merge_end = '';
my $src_remote_inst = 0;
my $tgt_remote_inst = 0;
my $remote_conn_str = '';
my $src_dbh = $dbh;
my $tgt_dbh = $dbh;
my $pos;
my $src_rem_ct = 0; # number of source remote connection parameters #
my $driver;
my $success_files = {}; # reference to a hash storing the list of src and #
# dest files that were successfully copied. #
if (defined($args{'target'}))
{
$tgt = ls($args{'target'});
}
# if optional --service parameter is provided, set it in global for later use
if (defined($args{'service'}))
{
$asmcmdglobal_hash{'service'} = $args{'service'};
}
else
{
# use +ASM as default if no service was provided. Otherwise the
# service name from previous CP will be used, and could be wrong.
$asmcmdglobal_hash{'service'} = "+ASM" ;
}
if (defined($args{'port'}))
{
$asmcmdglobal_hash{'port'} = $args{'port'};
}
else
{
$asmcmdglobal_hash{'port'} = "1521" ;
}
if (defined($args{'dest_dbname'}))
{
$dest_dbname = $args{'dest_dbname'};
}
if (defined($args{'sparse'}))
{
$asmcmdglobal_hash{'sparse'} = "1";
}
else
{
$asmcmdglobal_hash{'sparse'} = "0";
}
if (defined($args{'sparse_merge_begin'}) &&
defined($args{'sparse_merge_end'}))
{
$asmcmdglobal_hash{'sparse_merge_begin'} = "1";
$asmcmdglobal_hash{'sparse_merge_end'} = $args{'sparse_merge_end'};
$sp_merge_begin = 1;
$sp_merge_end = $args{'sparse_merge_end'};
}
else
{
if (defined($args{'sparse_merge_begin'}) ||
defined($args{'sparse_merge_end'}))
{
asmcmdshare_error_msg(8034, undef);
return $success_files;
}
$asmcmdglobal_hash{'sparse_merge_begin'} = "0";
$asmcmdglobal_hash{'sparse_merge_end'} = '';
}
asmcmdshare_trace(3, "NOTE: The service used is "
."$asmcmdglobal_hash{'service'}", 'y', 'n');
asmcmdshare_trace(3, "NOTE: The port used is "
."$asmcmdglobal_hash{'port'}", 'y', 'n');
asmcmdshare_trace(3, "NOTE: sparse option is set to "
."$asmcmdglobal_hash{'sparse'}", 'y', 'n');
if ($sp_merge_begin == 1)
{
asmcmdshare_trace(3, "NOTE: sparse_merge_begin option is set to "
."$asmcmdglobal_hash{'sparse_merge_begin'}", 'y', 'n');
asmcmdshare_trace(3, "NOTE: sparse_merge_end option is set to "
."$asmcmdglobal_hash{'sparse_merge_end'}", 'y', 'n');
}
if (defined($args{'dbuniquename'}))
{
$asmcmdglobal_hash{'dbuniquename'} = $args{'dbuniquename'};
asmcmdshare_trace(3, "NOTE: dbuniquename is set to " .
$asmcmdglobal_hash{'dbuniquename'}, 'y', 'n');
}
# last element is the target
$tgt_str = pop(@{$args{'cp'}});
# remaining is the source
@src_strs = @{$args{'cp'}};
# sparse merge: we do not support multiple source files in the argument
if ($sp_merge_begin == 1 && @src_strs > 1)
{
my (@eargs) = ($src_rem_ct, @src_strs);
asmcmdshare_error_msg(8031, \@eargs);
return $success_files;
}
# we are not supporting multi-src files from a remote instance yet #
foreach (@src_strs)
{
$src_rem_ct ++ if ( 1 == asmcmdbase_is_remote_syntax ($_)) ;
}
# more than one source, and one of them is remote
if ($src_rem_ct > 0 && @src_strs > 1)
{
my (@eargs) = ($src_rem_ct, @src_strs);
asmcmdshare_error_msg(8009, \@eargs);
}
if ($src_rem_ct == 1)
{
# sparse_merge: source file must be on local ASM instance
if ($sp_merge_begin == 1)
{
my (@eargs) = (@src_strs);
asmcmdshare_error_msg(8036, \@eargs);
return $success_files;
}
$src_remote_inst = 1;
$pos = index($src_strs[0], ':');
if (-1 != $pos )
{
$remote_conn_str = substr ($src_strs[0], 0, $pos);
push (@src_paths, substr ($src_strs[0], $pos + 1,
length($src_strs[0]) - $pos - 1));
}
else
{
$remote_conn_str = $src_strs[0];
push (@src_paths, "");
}
#get and parse connection data
return $success_files
unless defined (asmcmdbase_parse_remote_conn_str($remote_conn_str));
asmcmdshare_trace(3, "NOTE: Completed parsing source remote connection".
" string", 'n', 'n');
}
else
{
@src_paths = @src_strs;
}
# Check whether the target is of remote syntax
$tgt_remote_inst = 1 if ( asmcmdbase_is_remote_syntax ($tgt_str) == 1 ) ;
# sparse_merge: target must be on local ASM instance
if ($sp_merge_begin == 1)
{
if ($tgt_remote_inst == 1)
{
my (@eargs) = ($tgt_str);
asmcmdshare_error_msg(8032, \@eargs);
return $success_files;
}
else
{
# check if file associated with sparse_merge_end is of remote syntax
if (asmcmdbase_is_remote_syntax($sp_merge_end) == 1)
{
my (@eargs) = ($sp_merge_end);
asmcmdshare_error_msg(8030, \@eargs);
return $success_files;
}
}
}
# both src & target can't be of remote instance(s) #
if ($src_remote_inst == 1 && $tgt_remote_inst == 1)
{
my (@eargs) = (@src_strs, $tgt_str);
asmcmdshare_error_msg(8008, \@eargs);
return $success_files;
}
if ( $tgt_remote_inst == 1 )
{
$pos = index($tgt_str, ':');
if(-1 != $pos )
{
$remote_conn_str = substr($tgt_str, 0, $pos);
$tgt_path = substr ($tgt_str, $pos + 1, length($tgt_str) - $pos - 1 );
}
else
{
$remote_conn_str = $tgt_str;
$tgt_path = "";
}
# get and parse connection data
return $success_files
unless defined (asmcmdbase_parse_remote_conn_str($remote_conn_str));
asmcmdshare_trace(3, "NOTE: Completed parsing target remote connection"
." string", 'n', 'n');
}
else
{
$tgt_path = $tgt_str;
}
# assert we don't have src and tgt remote_inst
{
my @eargs=();
asmcmdshare_assert(!($src_remote_inst && $tgt_remote_inst), \@eargs);
}
# get remote handler if needed
if ($src_remote_inst || $tgt_remote_inst)
{
# ### remote copy ### #
my ($remote_dbh, $pswd);
asmcmdshare_trace(3, "NOTE: Connecting to remote instance.. ", 'n', 'n');
# connect to remote instance first #
asmcmdshare_trace(3, "Remote identifier $rident", 'y', 'n');
$remote_dbh = asmcmdbase_connect($rident, \$driver);
# added contyp, bug-5402303
if (!defined ($remote_dbh))
{
# Connection failed; record error. #
my (@eargs) = $rident if(defined($rident));
my ($msg) = "ERROR:asmcmdbase_process_cp01:Connection Failed ";
$msg .= "with $driver" if defined($driver);
asmcmdshare_trace(1, $msg, 'y', 'n');
asmcmdshare_error_msg(8201, \@eargs);
return $success_files;
}
asmcmdbase_check_insttype($remote_dbh);
$src_dbh = $remote_dbh if ($src_remote_inst);
$tgt_dbh = $remote_dbh if ($tgt_remote_inst);
}
# If dest_dbname was specified, modify the value of the root directory
if (defined ($dest_dbname))
{
# Only allow alphanumeric characters for the creation of the root
# directory of the system alias.
if ($dest_dbname !~ m/^[a-zA-Z0-9]+$/ )
{
my (@eargs) = ($dest_dbname);
asmcmdshare_error_msg(8037, \@eargs);
return;
}
# If no target has been specified, or is different from ASM, IOS and APX,
# use ASM as the default.
if (!defined($tgt) || $tgt !~ m/^(ASM|IOS|APX)$/)
{
$tgt = "asm";
}
# Set the root directory of the specified target.
$qry = "alter session set \"_" . $tgt . "_root_directory\"=\""
. $dest_dbname . "\"";
$ret = asmcmdshare_do_stmt($dbh, $qry);
}
# perform file copy and path completion
$success_files = asmcmdbase_do_file_copy($src_remote_inst, $tgt_remote_inst,
$src_dbh, $tgt_dbh, $remote_conn_str,
\@src_paths, $tgt_path);
return $success_files;
}
########
# NAME
# asmcmdbase_set_sparse_parent
#
# DESCRIPTION
# This routine processes the setsparseparent command.
#
# PARAMETERS
# dbh (IN) - initialize database handle, must be non-null.
#
# RETURNS
# Null.
#
########
sub asmcmdbase_set_sparse_parent
{
use Text::ParseWords;
my ($dbh) = shift; # local auto-variables
my (%args) = @_; # cmd args
my (@arg_tokens);
my (@child_strs) = (); # child file args
my (@child_paths); # child file paths
my ($parent_str); # parent file args
my ($parent_path); # parent file path
my ($parent_rem_inst) = 0;
my ($child_rem_ct) = 0; #number of child remote connection parameters
my ($child_dbh) = $dbh;
my ($parent_dbh) = $dbh;
my ($success_files) = 0; # count of child files that were successfully linked
# last element is the sparse parent
$parent_str = pop(@{$args{'setsparseparent'}});
# remaining is the child/children
@child_strs = @{$args{'setsparseparent'}};
# multi-child files from a remote instance is not supported
foreach (@child_strs)
{
$child_rem_ct ++ if (asmcmdbase_is_remote_syntax ($_) == 1) ;
}
# if a child is remote
if ($child_rem_ct > 0)
{
# more than one child, and one of them is remote
if (@child_strs > 1)
{
my (@eargs) = ($child_rem_ct, @child_strs);
asmcmdshare_error_msg(8018, \@eargs);
return $success_files;
}
else
{
# if a child is remote
my (@eargs) = (@child_strs);
asmcmdshare_error_msg(8015, \@eargs);
return $success_files;
}
}
else
{
@child_paths = @child_strs;
}
# Check whether the parent is of remote syntax
$parent_rem_inst = 1 if (asmcmdbase_is_remote_syntax ($parent_str) == 1);
# parent can't be of remote instance(s)
if ($parent_rem_inst == 1)
{
my (@eargs) = ($parent_str);
asmcmdshare_error_msg(8019, \@eargs);
return $success_files;
}
else
{
$parent_path = $parent_str;
}
# perform setsparseparent operation and path completion
$success_files = asmcmdbase_set_sparse_parent_child
($child_dbh, $parent_dbh, \@child_paths, $parent_path);
return $success_files;
}
###############################################################################
#################### Internal Command Processing Routines #####################
########
# NAME
# asmcmdbase_ls_process_file
#
# DESCRIPTION
# This routine determines the date format for files. Dates older than six
# months are displayed in MON DD YYYY format, while others are displayed in
# MON DD HH24:MI:SS unless the user specified a different format by using the
# --time_style option. The routine also finds the corresponding system
# filename for an alias and the corresponding alias for a system filename.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# entry_info_ref (IN/OUT) - reference to hash of column values for a file or
# directory entry.
# args_ref (IN) - reference to hash of user-entered command opions.
# cur_date (IN) - current julian date from dual.
# file_info (IN) - boolean: whether we have file info.
#
# RETURNS
# Null.
#
# NOTES
# Does not overwrite existing hashed values in %entry_info.
########
sub asmcmdbase_ls_process_file
{
my ($dbh, $entry_info_ref, $args_ref, $cur_date, $file_info) = @_;
my ($related_alias); # The user or system alias for its corresponding #
# system or user alias, respectively. #
# Calculate dates only if we have file info from v$asm_file.
if ($file_info)
{
# Use separate date format if date older than half a year.
if (($cur_date - $entry_info_ref->{'julian_date'}) >= $ASMCMDBASE_SIXMONTH)
{ # If older than six months, use 'MON DD YYYY' format. #
$entry_info_ref->{'date_print'} = $entry_info_ref->{'mod_date'};
}
else
{ # Otherwise, use 'MON DD HH24:MI:SS' format. #
$entry_info_ref->{'date_print'} = $entry_info_ref->{'mod_time'};
}
}
# Find system alias only if we have info from v$asm_file, i.e., if we're
# doing ls -l or ls -s.
if ($file_info && $entry_info_ref->{'system_created'} eq 'N')
{ # If user-alias for file, than obtain system-alias. #
$related_alias = asmcmdbase_get_alias_path ($dbh,
$entry_info_ref->{'group_number'},
$entry_info_ref->{'file_number'},
'Y');
# Always display in form 'user alias => system alias' for user aliases.
$entry_info_ref->{'name_print'} .= " => $related_alias";
}
elsif (defined ($args_ref->{'absolutepath'}) &&
($entry_info_ref->{'system_created'} eq 'Y'))
{
# If system-alias and -a, then find corresponding user-alias, if any. #
# In the (near) future, v$file joined with v$alias will not return volume
# information. Until then, we'll need the code below to supress volume
# info.
my($name, $not_used) = split /\./, $entry_info_ref->{'name_print'}, 2;
if (($name eq 'volume') || ($name eq 'DRL'))
{
# do not print volume information
return;
}
$related_alias = asmcmdbase_get_alias_path ($dbh,
$entry_info_ref->{'group_number'},
$entry_info_ref->{'file_number'},
'N');
$entry_info_ref->{'name_print'} = "$related_alias => " .
$entry_info_ref->{'name_print'};
}
return;
}
########
# NAME
# asmcmdbase_ls_get_subdirs
#
# DESCRIPTION
# This routine is a wrapper function for asmcmdshare_get_subdirs() for ls. It
# retrieves all entries under a directory by calling
# asmcmdshare_get_subdir(), and sorts the entries based on the -t and/or
# -r flags. It also calculates the minimum column width of each column value
# based on the length of each of the results returned by
# asmcmdbase_get_subdirs().
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gnum (IN) - group number of the directory.
# par_id (IN) - parent index of the directory.
# time_format (IN) - format for the datetime.
# args_ref (IN) - reference to hash of ls options entered by user.
# min_col_wid_ref (OUT) - reference to hash of minimum column width.
# cur_date (IN) - current julian date from dual.
#
# RETURNS
# An array of hashes, each hash containing column values of a file or
# directory from v$asm_alias and v$asm_file.
#
# ls is tricky because it can list entries in two levels:
# 1) 'ls <file>' or 'ls -d <dir>'
# 2) 'ls <dir>'
#
# In case 1), ls lists the specified alias entry itself, whereas in 2), ls
# lists the contents of <dir>. For 1), asmcmdbase_process_ls() calls
# asmcmdbase_ls_print() directly, whereas in 2), asmcmdbase_process_ls()
# calls asmcmdbase_ls_get_subdirs() and asmcmdbase_ls_subdir_print() for
# each <dir>, and asmcmdbase_ls_subdir_print() calls asmcmdbase_ls_print().
########
sub asmcmdbase_ls_get_subdirs
{
my ($dbh, $gnum, $par_id, $time_format, $args_ref, $min_col_wid_ref,
$cur_date) = @_;
my (@entries);# Entries under a dir returned by asmcmdshare_get_subdirs(). #
my ($get_file_info) = 0; # boolean: true if we want file info as well. #
my ($i); # Iteration variable for for() loops. #
# Get file info only if we need it.
$get_file_info = 1 if (defined($args_ref->{'l'}) ||
defined($args_ref->{'s'}) ||
defined($args_ref->{'permission'}));
# Get all entries under a directory in a diskgroup.
asmcmdshare_get_subdirs ($dbh, \@entries, $time_format, $gnum, undef,
$par_id, undef, undef, 0, $get_file_info);
# Obtain more information on each entry, if available.
for ($i = 0; $i < @entries; $i++)
{
# By default, the name to be print by ls is the "name" column from
# v$asm_alias. However, asmcmdbase_ls_process_file() can modify this
# print value if the entry is a file and not a directory. For instance,
# user aliases for files are printed in the form
# 'user_alias => system_alias'.
$entries[$i]->{'name_print'} = $entries[$i]->{'name'};
# If it's a file, then process v$asm_file info.
if ($entries[$i]->{'alias_directory'} eq 'N')
{
asmcmdbase_ls_process_file ($dbh, $entries[$i], $args_ref, $cur_date,
$get_file_info);
}
}
# Sort @entries according to -t and -r flags (4 combinations).
if (defined ($args_ref->{'t'}) && defined ($args_ref->{'reverse'}))
{
@entries = sort asmcmdbase_time_backward @entries;
}
elsif (defined ($args_ref->{'t'}) && !defined ($args_ref->{'reverse'}))
{
@entries = sort asmcmdbase_time_forward @entries;
}
elsif (!defined ($args_ref->{'t'}) && defined ($args_ref->{'reverse'}))
{
@entries = sort asmcmdbase_name_backward @entries;
}
else
{
@entries = sort asmcmdbase_name_forward @entries;
}
# Prepare column widths for printing.
asmcmdshare_ls_calc_min_col_wid(\@entries, $min_col_wid_ref);
return @entries;
}
########
# NAME
# asmcmdbase_ls_subdir_print
#
# DESCRIPTION
# This routine prints each entry in @list by calling asmcmdbase_ls_print()
# on each entry.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# list_ref (IN) - reference to array of entries to be printed.
# args_ref (IN) - reference to hash of ls options entered by user.
# min_col_wid_ref (IN) - reference to hash of minimum column width.
#
# RETURNS
# Null.
#
# NOTES
# Only asmcmdbase_process_ls() calls this routine, after first calling
# asmcmdbase_ls_get_subdirs() to retrieve all entries under a directory.
#
# ls is tricky because it can list entries in two levels:
# 1) 'ls <file>' or 'ls -d <dir>'
# 2) 'ls <dir>'
#
# In case 1), ls lists the specified alias entry itself, whereas in 2), ls
# lists the contents of <dir>. For 1), asmcmdbase_process_ls() calls
# asmcmdbase_ls_print() directly, whereas in 2), asmcmdbase_process_ls()
# calls asmcmdbase_ls_get_subdirs() and asmcmdbase_ls_subdir_print() for each
# <dir>, and asmcmdbase_ls_subdir_print() calls asmcmdbase_ls_print().
########
sub asmcmdbase_ls_subdir_print
{
my ($dbh, $list_ref, $args_ref, $min_col_wid_ref) = @_;
my ($i); # Iteration variable for for() loops. #
# Print one line at a time.
for ($i = 0; $i < @{$list_ref}; $i++)
{
asmcmdbase_ls_print($list_ref->[$i], $args_ref, $min_col_wid_ref);
}
return;
}
########
# NAME
# asmcmdbase_ls_print
#
# DESCRIPTION
# This routine prints one single alias entry, whether a directory or a file.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# entry_ref (IN) - reference to hash of column values of an entry.
# args_ref (IN) - reference to hash of ls options entered by user.
# min_col_wid_ref (IN) - reference to hash of minimum column width.
#
# RETURNS
# Null.
#
# NOTES
# asmcmdbase_process_ls() can call this routine directly or indirectly
# through asmcmdbase_ls_subdir_print().
########
sub asmcmdbase_ls_print
{
my ($entry_ref, $args_ref, $min_col_wid_ref) = @_;
my $row_l = ''; # printf() format string for ls -l. #
my $row_s = ''; # printf() format string for ls -s. #
my $row_p = ''; # printf() format string for ls -p. #
my $row_n = ''; # printf() format string for alias name. #
my $row = ''; # Concatenated printf() format string with all options. #
my @print_args;
my $print_string;
# In the (near) future, v$file joined with v$alias will not return volume
# information. Until then, we'll need the code belowe to supress volume info.
my($name, $not_used) = split /\./, $entry_ref->{'name_print'}, 2;
if (($name eq 'volume') || ($name eq 'DRL'))
{
# do not print volume information
return;
}
# Set formats based on minimum column widths in %min_col_wid.
$row_l = "%-" . $min_col_wid_ref->{'type'} . "s " .
"%-" . $min_col_wid_ref->{'redundancy'} . "s " .
"%-" . $min_col_wid_ref->{'striped'} . "s " ;
$row_l .= "%-" . $min_col_wid_ref->{'date_print'} . "s ";
$row_l .= "%-" . $min_col_wid_ref->{'system_created'} . "s ";
$row_s = "%" . $min_col_wid_ref->{'block_size'} . "s " .
"%" . $min_col_wid_ref->{'blocks'} . "s " .
"%" . $min_col_wid_ref->{'bytes'} . "s " .
"%" . $min_col_wid_ref->{'space'} . "s ";
$row_p = "%" . $min_col_wid_ref->{'user'} . "s " .
"%" . $min_col_wid_ref->{'group'} . "s " .
"%" . $min_col_wid_ref->{'perm'} . "s ";
$row_n = "%-s\n";
# Now concatenate the format strings based on whether -l and/or -s flags.
$row .= $row_l if (defined ($args_ref->{'l'})); # ls -l. #
$row .= $row_s if (defined ($args_ref->{'s'})); # ls -s. #
$row .= $row_p if (defined ($args_ref->{'permission'}));# ls --permission. #
$row .= $row_n; # Always display alias name; concatenate! #
if ($entry_ref->{'alias_directory'} eq 'Y')
{ # Directries have the suffix '/'. #
$entry_ref->{'name_print'} .= '/';
}
#
# some values may be undefined, if so, init with space
#
$entry_ref->{'type'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'type'}));
$entry_ref->{'redundancy'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'redundancy'}));
$entry_ref->{'striped'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'striped'}));
$entry_ref->{'date_print'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'date_print'}));
$entry_ref->{'space'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'space'}));
$entry_ref->{'block_size'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'block_size'}));
$entry_ref->{'blocks'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'blocks'}));
$entry_ref->{'bytes'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'bytes'}));
$entry_ref->{'user'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'user'}));
$entry_ref->{'group'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'group'}));
$entry_ref->{'perm'} = $ASMCMDBASE_SPACE if
(!defined($entry_ref->{'perm'}));
@print_args = ();
if (defined($args_ref->{'l'})) # ls -l. #
{
push (@print_args, $entry_ref->{'type'});
push (@print_args, $entry_ref->{'redundancy'});
push (@print_args, $entry_ref->{'striped'});
push (@print_args, $entry_ref->{'date_print'});
push (@print_args, $entry_ref->{'system_created'});
}
if (defined($args_ref->{'s'})) # ls -s. #
{
my $bsize;
my $bytes;
my $space;
# If the current entry belongs to a directory, the size fields are empty.
# This function decides to call asmcmdshare_humanBytes if the -h option is
# set and the entry is not a directory.
my $humanize = sub { $_[0] =~ /\d/ && defined($args_ref->{'h'}) };
$bsize = $humanize->($entry_ref->{'block_size'})
? asmcmdshare_humanBytes($entry_ref->{'block_size'})
: $entry_ref->{'block_size'};
$bytes = $humanize->($entry_ref->{'bytes'})
? asmcmdshare_humanBytes($entry_ref->{'bytes'})
: $entry_ref->{'bytes'};
$space = $humanize->($entry_ref->{'space'})
? asmcmdshare_humanBytes($entry_ref->{'space'})
: $entry_ref->{'space'};
push (@print_args, $bsize);
push (@print_args, $entry_ref->{'blocks'});
push (@print_args, $bytes);
push (@print_args, $space);
}
if (defined($args_ref->{'permission'})) # ls --permission -l. #
{
push (@print_args, $entry_ref->{'user'});
push (@print_args, $entry_ref->{'group'});
push (@print_args, $entry_ref->{'perm'});
}
push(@print_args, $entry_ref->{'name_print'});
$print_string = sprintf ($row, @print_args);
asmcmdshare_print($print_string);
return;
}
########
# NAME
# asmcmdbase_ls_print_hdr
#
# DESCRIPTION
# This routine prints the column headers for ls.
#
# PARAMETERS
# args_ref (IN) - reference to hash of ls options entered by user.
# min_col_wid_ref (IN) - reference to hash of minimum column width.
#
# RETURNS
# Null.
########
sub asmcmdbase_ls_print_hdr
{
my ($args_ref, $min_col_wid_ref) = @_;
my ($hdr_l); # printf() format string for ls -l. #
my ($hdr_s); # printf() format string for ls -s. #
my ($hdr_p); # printf() format string for ls -p. #
my ($hdr_n); # printf() format string for alias name. #
my ($hdr) = ''; # Concatenated printf() format string with all options. #
my (@print_args);
my ($print_string);
# Set formats based on minimum column widths in %min_col_wid.
$hdr_l = "%-" . $min_col_wid_ref->{'type'} . "s " .
"%-" . $min_col_wid_ref->{'redundancy'} . "s " .
"%-" . $min_col_wid_ref->{'striped'} . "s " .
"%-" . $min_col_wid_ref->{'date_print'} . "s " .
"%-" . $min_col_wid_ref->{'system_created'}. "s ";
$hdr_s = "%" . $min_col_wid_ref->{'block_size'} . "s " .
"%" . $min_col_wid_ref->{'blocks'} . "s " .
"%" . $min_col_wid_ref->{'bytes'} . "s " .
"%" . $min_col_wid_ref->{'space'} . "s ";
$hdr_p = "%-" . $min_col_wid_ref->{'user'} . "s " .
"%-" . $min_col_wid_ref->{'group'} . "s " .
"%-" . $min_col_wid_ref->{'perm'} . "s ";
$hdr_n = "%-s\n";
# Now concatenate the format strings based on whether -l and/or -s flags.
$hdr .= $hdr_l if (defined ($args_ref->{'l'})); # ls -l. #
$hdr .= $hdr_s if (defined ($args_ref->{'s'})); # ls -s. #
$hdr .= $hdr_p if (defined ($args_ref->{'permission'})); # ls --perm. #
$hdr .= $hdr_n;
@print_args = ();
if (defined($args_ref->{'l'})) # ls -l. #
{
push (@print_args, $ASMCMDBASE_LS_HDR_TYPE);
push (@print_args, $ASMCMDBASE_LS_HDR_REDUND);
push (@print_args, $ASMCMDBASE_LS_HDR_STRIPED);
push (@print_args, $ASMCMDBASE_LS_HDR_TIME);
push (@print_args, $ASMCMDBASE_LS_HDR_SYSCRE);
}
if (defined($args_ref->{'s'})) # ls -s. #
{
push (@print_args, $ASMCMDBASE_LS_HDR_BSIZE);
push (@print_args, $ASMCMDBASE_LS_HDR_BLOCKS);
push (@print_args, $ASMCMDBASE_LS_HDR_BYTES);
push (@print_args, $ASMCMDBASE_LS_HDR_SPACE);
}
if (defined($args_ref->{'permission'})) # ls pl. #
{
push (@print_args, $ASMCMDBASE_LS_HDR_USER);
push (@print_args, $ASMCMDBASE_LS_HDR_GROUP);
push (@print_args, $ASMCMDBASE_LS_HDR_PERM);
}
push (@print_args, 'Name');
$print_string = sprintf $hdr, @print_args;
asmcmdshare_print($print_string);
# Do not print header for ls with neither -l nor -s.
return;
}
########
# NAME
# asmcmdbase_ls_init_col_wid
#
# DESCRIPTION
# This routine initializes the minimum column width hash with default values
# for ls. These default values are determined by the length of the values
# of the printed column headers. These header values are not necessarily
# the same as the column names in v$asm_alias or v$asm_file but look
# similar.
#
# PARAMETERS
# min_col_wid_ref (OUT) - reference to hash of minimum column width.
#
# RETURNS
# Null.
#
# NOTES
# Must call this routine or asmcmdbase_lsdg_init_col_wid() before calling
# asmcmdshare_ls_calc_min_col_wid().
########
sub asmcmdbase_ls_init_col_wid
{
my $min_col_wid_ref = shift;
my $min_time_len;
# Default time format is either 'MON DD YYYY' or 'MON DD HH:MI:SS', which
# are at most $ASMCMDBASE_DATELEN characters long. However, if the length
# of $ASMCMDBASE_LS_HDR_TIME is larger, then that becomes the minimum
# length.
$min_time_len = $ASMCMDBASE_DATELEN;
$min_time_len = length($ASMCMDBASE_LS_HDR_TIME)
if (length($ASMCMDBASE_LS_HDR_TIME) > $ASMCMDBASE_DATELEN);
$min_col_wid_ref->{'type'} = length($ASMCMDBASE_LS_HDR_TYPE);
$min_col_wid_ref->{'redundancy'} = length($ASMCMDBASE_LS_HDR_REDUND);
$min_col_wid_ref->{'striped'} = length($ASMCMDBASE_LS_HDR_STRIPED);
$min_col_wid_ref->{'date_print'} = $min_time_len;
$min_col_wid_ref->{'system_created'} = length($ASMCMDBASE_LS_HDR_SYSCRE);
$min_col_wid_ref->{'block_size'} = length($ASMCMDBASE_LS_HDR_BSIZE);
$min_col_wid_ref->{'blocks'} = length($ASMCMDBASE_LS_HDR_BLOCKS);
$min_col_wid_ref->{'bytes'} = length($ASMCMDBASE_LS_HDR_BYTES);
$min_col_wid_ref->{'space'} = length($ASMCMDBASE_LS_HDR_SPACE);
$min_col_wid_ref->{'user'} = length($ASMCMDBASE_LS_HDR_USER);
$min_col_wid_ref->{'group'} = length($ASMCMDBASE_LS_HDR_GROUP);
$min_col_wid_ref->{'perm'} = length($ASMCMDBASE_LS_HDR_PERM);
return;
}
########
# NAME
# asmcmdbase_lsdg_init_col_wid
#
# DESCRIPTION
# This routine initializes the minimum column width hash with default values
# for lsdg. These default values are determined by the length of the values
# of the printed column headers. These header values are not necessarily
# the same as the column names in v$asm_diskgroup but look similar.
#
# PARAMETERS
# min_col_wid_ref (OUT) - reference to hash of minimum column width.
#
# RETURNS
# Null.
#
# NOTES
# Must call this routine or asmcmdbase_ls_init_col_wid() before calling
# asmcmdshare_ls_calc_min_col_wid().
########
sub asmcmdbase_lsdg_init_col_wid
{
my ($min_col_wid_ref) = shift;
$min_col_wid_ref->{'inst_id'} = length($ASMCMDBASE_LSDG_HDR_INSTID);
$min_col_wid_ref->{'sector_size'} = length($ASMCMDBASE_LSDG_HDR_SECTOR);
$min_col_wid_ref->{'logical_sector_size'}
= length($ASMCMDBASE_LSDG_HDR_LSECTOR);
$min_col_wid_ref->{'rebalance'} = length($ASMCMDBASE_LSDG_HDR_REBAL);
$min_col_wid_ref->{'block_size'} = length($ASMCMDBASE_LSDG_HDR_BLOCK);
$min_col_wid_ref->{'allocation_unit_size'} = length($ASMCMDBASE_LSDG_HDR_AU);
$min_col_wid_ref->{'state'} = length($ASMCMDBASE_LSDG_HDR_STATE);
$min_col_wid_ref->{'type'} = length($ASMCMDBASE_LSDG_HDR_TYPE);
$min_col_wid_ref->{'total_mb'} = length($ASMCMDBASE_LSDG_HDR_TMB);
$min_col_wid_ref->{'free_mb'} = length($ASMCMDBASE_LSDG_HDR_FMB);
$min_col_wid_ref->{'required_mirror_free_mb'}
= length($ASMCMDBASE_LSDG_HDR_RMFMB);
$min_col_wid_ref->{'usable_file_mb'} = length($ASMCMDBASE_LSDG_HDR_UFMB);
$min_col_wid_ref->{'offline_disks'} = length($ASMCMDBASE_LSDG_HDR_ODISK);
$min_col_wid_ref->{'voting_files'} = length($ASMCMDBASE_LSDG_HDR_VOTING_FILE);
return;
}
########
# NAME
# asmcmdbase_lsdg_print
#
# DESCRIPTION
# This routine is responsible for printing diskgroups and their column
# values. Both the command 'ls +' and the command 'lsdg' use this routine
# to print their results.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gname (IN) - group name, as entered by user.
# args_ref (IN) - reference to hash of lsdg options entered by user.
#
# RETURNS
# Null.
#
# NOTES
# Both asmcmdbase_process_ls() and asmcmdbase_process_lsdg() can call this
# routine.
########
sub asmcmdbase_lsdg_print
{
my ($dbh, $gname, $args_ref) = @_;
my (@dgroups); # Array of hashes of diskgroup column values, one hash #
# for each diskgroup. #
my (%min_col_wid); # hash of minimum column width. #
my ($row_g); # printf() format string for ls -g. #
my ($row_l); # printf() format string for ls -l. #
my ($row_s); # printf() format string for ls -s. #
my ($row_n); # printf() format string for ls -ls. #
my ($row) = ''; # Concatenated printf() format string with all options. #
my ($rebal); # 'Y' if diskgroup currently rebalancing; 'N' otherwise. #
my ($i); # Iteration variable for for() loops. #
my ($discovery) = 0; # True iff do discovery when querying view. #
my ($global) = 0; # True iff query global view. #
my ($print_string); # generate the string to be printed using sprintf. #
my (@print_what); # The fields to be printed. #
# Check for the -c flag.
if (defined($args_ref->{'discovery'}))
{
$discovery = 1;
}
# Check for the -g flag.
if (defined($args_ref->{'g'}))
{
$global = 1;
}
# Obtain the list of diskgroups. If $gname is not undefined, then only
# information on that diskgroup is obtained.
@dgroups = asmcmdshare_get_dg($dbh, $gname, $discovery, $global);
# If non returned, then diskgroup is not found.
if (@dgroups == 0)
{
my (@eargs) = ($gname);
asmcmdshare_error_msg(8001, \@eargs) if (defined ($gname));
return;
}
# Sort results
if (defined ($args_ref->{'reverse'}))
{ # Reverse alphabetical order by group name. #
@dgroups = sort asmcmdbase_name_backward @dgroups;
}
else
{ # Alphabetical order by group name. #
@dgroups = sort asmcmdbase_name_forward @dgroups;
}
# Calculate column width.
asmcmdbase_lsdg_init_col_wid (\%min_col_wid);
asmcmdshare_ls_calc_min_col_wid (\@dgroups, \%min_col_wid);
# Set up width values for instance ID for gv$asm_diskgroup.
$row_g = "%" . $min_col_wid{'inst_id'} . "s ";
# Set up width values for printf().
$row_l = "%-" . $min_col_wid{'state'} . "s " .
"%-" . $min_col_wid{'type'} . "s " .
"%-" . $min_col_wid{'rebalance'} . "s ";
$row_s = "%" . $min_col_wid{'sector_size'} . "s " .
"%" . $min_col_wid{'logical_sector_size'} . "s " .
"%" . $min_col_wid{'block_size'} . "s " .
"%" . $min_col_wid{'allocation_unit_size'} . "s " .
"%" . $min_col_wid{'total_mb'} . "s " .
"%" . $min_col_wid{'free_mb'} . "s ";
# Column in 10gR2 or later.
if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_10gR2) >= 0 )
{
$row_s .= "%" . $min_col_wid{'required_mirror_free_mb'} . "s ";
$row_s .= "%" . $min_col_wid{'usable_file_mb'} . "s ";
$row_s .= "%" . $min_col_wid{'offline_disks'} . "s ";
}
# Column in 11gR2 or later.
if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_11gR2) >= 0 )
{
$row_s .= "%" . $min_col_wid{'voting_files'} . "s ";
}
$row_n = "%-s\n";
$row .= $row_g if (defined ($args_ref->{'g'})); # ls -g. #
$row .= $row_l if (defined ($args_ref->{'l'})); # ls -l. #
$row .= $row_s if (defined ($args_ref->{'s'})); # ls -s. #
$row .= $row_n; # Always display group name. #
# Now generate the string to be printed based on user-specified flags.
@print_what = ();
if (defined ($args_ref->{'g'}))
{
push (@print_what, "$ASMCMDBASE_LSDG_HDR_INSTID");
}
if (defined ($args_ref->{'l'}))
{
push (@print_what, "$ASMCMDBASE_LSDG_HDR_STATE");
push (@print_what, "$ASMCMDBASE_LSDG_HDR_TYPE");
push (@print_what, "$ASMCMDBASE_LSDG_HDR_REBAL");
}
if (defined ($args_ref->{'s'}))
{
push (@print_what, "$ASMCMDBASE_LSDG_HDR_SECTOR");
push (@print_what, "$ASMCMDBASE_LSDG_HDR_LSECTOR");
push (@print_what, "$ASMCMDBASE_LSDG_HDR_BLOCK");
push (@print_what, "$ASMCMDBASE_LSDG_HDR_AU");
push (@print_what, "$ASMCMDBASE_LSDG_HDR_TMB");
push (@print_what, "$ASMCMDBASE_LSDG_HDR_FMB");
if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_10gR2) >= 0 )
{ # Version is 10gR2 or later. #
push (@print_what, "$ASMCMDBASE_LSDG_HDR_RMFMB");
push (@print_what, "$ASMCMDBASE_LSDG_HDR_UFMB");
push (@print_what, "$ASMCMDBASE_LSDG_HDR_ODISK");
}
# Column in 11gR2 or later.
if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_11gR2) >= 0 )
{
push (@print_what, "$ASMCMDBASE_LSDG_HDR_VOTING_FILE");
}
}
# Always include the name.
push (@print_what, "$ASMCMDBASE_LSDG_HDR_NAME");
# Now print the header if all these conditions are met:
# 1) at least one of these flags are set: -g, -l, or -s;
# 2) the --suppressheader flag is not set; and
# 3) we have at least one row of results.
if ((defined ($args_ref->{'g'}) ||
defined ($args_ref->{'l'}) ||
defined ($args_ref->{'s'})) &&
!defined ($args_ref->{'suppressheader'}) &&
(@dgroups > 0))
{
$print_string = sprintf($row, @print_what);
asmcmdshare_print($print_string);
}
# Print one row at a time.
for ($i = 0; $i < @dgroups; $i++)
{
$dgroups[$i]->{'name'} .= '/';
# Generate the string to be printed.
@print_what = ();
if (defined ($args_ref->{'g'}))
{
push(@print_what, $dgroups[$i]->{'inst_id'});
}
if (defined ($args_ref->{'l'}))
{
# Find out whether this diskgroup is currently rebalacing.
$rebal = asmcmdbase_is_rbal ($dbh, $dgroups[$i]->{'group_number'});
push(@print_what, $dgroups[$i]->{'state'});
push(@print_what, $dgroups[$i]->{'type'});
push(@print_what, $rebal);
}
if (defined ($args_ref->{'s'}))
{
if (!defined($dgroups[$i]->{'total_mb'}))
{
$dgroups[$i]->{'total_mb'}=0;
}
if (!defined($dgroups[$i]->{'free_mb'}))
{
$dgroups[$i]->{'free_mb'}=0;
}
push(@print_what, $dgroups[$i]->{'sector_size'});
push(@print_what, $dgroups[$i]->{'logical_sector_size'});
push(@print_what, $dgroups[$i]->{'block_size'});
push(@print_what, $dgroups[$i]->{'allocation_unit_size'});
push(@print_what, $dgroups[$i]->{'total_mb'});
push(@print_what, $dgroups[$i]->{'free_mb'});
if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_10gR2) >= 0 )
{ # Version is 10gR2 or later. #
push(@print_what, $dgroups[$i]->{'required_mirror_free_mb'});
push(@print_what, $dgroups[$i]->{'usable_file_mb'});
push(@print_what, $dgroups[$i]->{'offline_disks'});
}
if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_11gR2) >= 0 )
{
push(@print_what, $dgroups[$i]->{'voting_files'});
}
}
# Always include the disk group name.
push(@print_what, $dgroups[$i]->{'name'});
# Now evaluate the printf() expression.
$print_string = sprintf($row, @print_what);
asmcmdshare_print($print_string);
}
return;
}
########
# NAME
# asmcmdbase_find_sql
#
# DESCRIPTION
# This routine uses a search string to search under paths for aliases with
# names that match the search string.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# path (IN) - path under which to search, can be full or
# relative; can contain the wildcard '*'.
# search (IN) - the search string; can contain the wildcard '*'.
# type (IN) - optional; the file type to search for if -t is set
# par_id (IN) - optional; if defined, then return only matches
# that have this parent index.
# sys_non_dir_only (IN) - boolean; return only system alias names of
# files if true.
# with_diskgroup (IN) - boolean; search v$asm_diskgroup in addition to
# v$asm_alias if true; search only v$asm_alias
# otherwise.
#
# RETURNS
# An array of hashes of search result, each hash with v$asm_alias column
# values. The hash also contains the full path to that file or directory.
# If the search yields no matches, then return an empty array.
#
# NOTES
# By specifying a parent index, asmcmdbase_find_sql() returns at most one
# match. Use this feature when you have only an alias name by want the
# full path to it.
#
########
sub asmcmdbase_find_sql
{
my ($dbh, $path, $search, $type, $par_id, $sys_non_dir_only,
$with_diskgroup) = @_;
my (%path_rslt); # See asmcmdshare_normalize_path() return value comments. #
my (@srch_rslt); # Array of hashes, one for each entry that has an alias #
# name that matches $search. #
my (@results); # Array of hashes, one for each entry that is found in #
# this search. #
my (@dgroup);# Array of hashes of all diskgroups whose name match $search. #
my ($fnd_str); # The full path to an alias that is found under $path. #
my ($gname); # Group name for an entry entry. #
my ($iter); # Iteration variable for foreach() loops. #
my ($ret);#Is -1 iff asmcmdshare_normalize_path() fails to normalize path. #
# First search under v$asm_alias for alias names that match $search.
# See if path is valid. Get all paths if $path contains a wildcard.
%path_rslt = asmcmdshare_normalize_path ($dbh, $path, 0, \$ret);
return @results if ($ret == -1); # return empty array if bad path. #
# Obtain a list of all aliases in v$asm_alias that 1) matches the search
# string $search, 2) has the parent index $par_id (if defined), and 3)
# is a system alias for a file (if $sys_non_dir_only is true).
asmcmdshare_run_find_sql ($dbh, \@srch_rslt, $par_id, $search,
$type, $sys_non_dir_only);
# Now we need to see if the entries returned from
# asmcmdshare_run_find_sql() fall under one of the paths returned
# from asmcmdshare_normalize_path().
foreach $iter (@srch_rslt)
{
$gname = asmcmdshare_get_gname_from_gnum($dbh, $iter->{'group_number'});
# See if the alias falls under the list of paths in %path_rslt.
$fnd_str = asmcmdbase_find_one($dbh, $path_rslt{'path'},
$path_rslt{'ref_id'},
$iter->{'name'}, $gname,
$iter->{'group_number'},
$iter->{'parent_index'});
# If defined, then $fnd_str contains the full path to matching alias.
if (defined ($fnd_str))
{
$iter->{'full_path'} = $fnd_str; # Hash the path into the entry alias. #
push (@results, $iter); # Add the hash to the array of matches. #
}
}
# Now search under v$asm_diskgroup for diskgroup names that match $search.
# Search for diskgroups only of all of these conditions are met:
# 1) $sys_non_dir_only is false;
# 2) $with_diskgroup is true;
# 3) $type is undefined;
# 4) The path we're searching under normalizes to '+'.
# 5) Either $par_id is undefined or is -1.
if ( (! $sys_non_dir_only) &&
($with_diskgroup) &&
(! defined ($type)) &&
($path_rslt{'path'}->[0] eq '+') &&
( (! defined ($par_id)) || ($par_id == -1) ) )
{
# Get list of all diskgroups that match $search.
@dgroup = asmcmdshare_get_dg ($dbh, $search, 0, 0);
foreach $iter (@dgroup)
{
# Construct full path for asmcmdbase_process_find().
$iter->{'full_path'} = '+' . $iter->{'name'};
# In ASMCMD, a diskgroup is considered a pseudo-directory. We need to
# set this to 'Y' so that asmcmdbase_process_find() knows to append the
# '/' suffix to the path to denote that this path points to a directory.
$iter->{'alias_directory'} = 'Y';
# Save in return array.
push (@results, $iter);
}
}
return @results;
}
########
# NAME
# asmcmdbase_find_one
#
# DESCRIPTION
# This routine checks if the alias with the name $name and parent index
# $index resides under any of the paths in the array referenced by
# $paths_ref. For instance, '+dgroup/ORCL/DATAFILE/SYSTEM.256.1' resides
# under '+dgroup/ORCL', but '+dgroup/ZRCL/DATAFILE/SYSTEM.284.4' does not.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# paths_ref (IN) - reference to an array of paths under which to look for
# the alias name $name.
# par_ids_ref (IN) - reference to an array parallel to the array $paths_ref
# references; this array contains the corresponding
# reference indices for deepest directory in each path;
# these reference indices are used as possible parent
# indices for $name; if $index matches one of the indices,
# in @{$par_ids_ref}, then $name must be under the
# corresponding path in @{paths_ref}.
# name (IN) - name of the alias that we're searching.
# gname (IN) - group name of the group in which the search is done.
# gnum (IN) - corresponding group number of $gname.
# index (IN) - compare this index against all indices in @{$par_ids_ref}
# to determine if $name is under a path in @{$paths_ref};
# $index can be the parent index, grandparent index, great
# grandparent index, etc., of $name, depending on how
# deep $name resides in the directory tree.
#
# RETURNS
# Full path to $name if $name is found under any of the paths in
# @{paths_ref}; undefined if not found.
#
# NOTES
# The alias $name is already known to exist in v$asm_alias at this point, as
# asmcmdbase_find_sql() has already SQL-searched the fixed view for the
# search string, and $name is one of the matches that's returned. So here
# the question is not whether $name is present in v$asm_alias but whether
# $name resides under one of the paths in @{$paths_ref}.
#
# The algorithm here is to see if any of the 'ancestor' indices of $name
# matches any one of the indices in @{$par_ids_ref}. If yes, then $name
# is found under the desired location.
#
# Note that $index can match at most one index in @{$par_ids_ref}, because
# all the paths we're searching are derived from one path string that can
# have wildcards. Thus, all paths are unique.
########
sub asmcmdbase_find_one
{
my ($dbh, $paths_ref, $par_ids_ref, $name, $gname, $gnum, $index) = @_;
my ($sql); # SQL query string for getting the parent index. #
my ($sth); # SQL statement handle. #
my ($row); # One row results returned from the SQL execution. #
my ($prefix); # One path in @{$par_ids_ref}; returned by #
# asmcmdbase_find_match() only if $index matches the index of #
# $prefix; $prefix concatenated to $name forms the full #
# path to $name. #
# This condition is true if $index matches *none* of the indices in
# @{$par_ids_ref}, in which case $prefix would be undefined. Otherwise,
# $prefix contains the corresponding path of the matching index, and
# the loop terminates because a match is found.
while (! defined($prefix = asmcmdbase_find_match($paths_ref, $par_ids_ref,
$index)))
{
# If we're in this loop, then we know $index did not match, in which case
# we need to update $index to the value of the parent index that
# corresponds to the reference index $index.
if ($index == -1)
{ # Special case: we're already at '+', and still no match, so return. #
return undef;
}
elsif ($index == ($gnum << 24))
{
# If we're at diskgroup level, assign index to -1, which is defined
# to be the reference index for '+' in ASMCMD.
$index = -1;
$name = $gname . '/' . $name; # Continue building the full path. #
}
else
{
# Otherwise, we update $index to the index of the parent using this
# SQL statement.
$sql = "select name, parent_index from v\$asm_alias
where reference_index=?";
$sth = asmcmdshare_do_prepare($dbh, $sql);
$sth->bind_param(1,$index);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
asmcmdshare_finish($sth);
return undef if (! defined ($row));
$index = $row->{'PARENT_INDEX'};
$name = $row->{'NAME'} . '/' . $name; # Build the full path. #
}
}
# We get here only if there is a match.
$name = $prefix . '/' . $name; # Append prefix to get full path. #
$name =~ s,^\+/,\+,; # Change '+/' to '+', removing the possible '/'. #
return $name;
}
########
# NAME
# asmcmdbase_find_match
#
# DESCRIPTION
# This routine checks to see if $index matches any of the indices in
# @{$par_ids_ref}.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# paths_ref (IN) - reference to an array of paths under which to look for
# the alias name $name.
# par_ids_ref (IN) - reference to an array parallel to the array $paths_ref
# references; this array contains the corresponding
# reference indices for deepest directory in each path;
# these reference indices are used as possible parent
# indices for $name; if $index matches one of the indices,
# in @{$par_ids_ref}, then $name must be under the
# corresponding path in @{paths_ref}.
# index (IN) - compare this index against all indices in @{$par_ids_ref}
# to determine if $name is under a path in @{$paths_ref};
# $index can be the parent index, grandparent index, great
# grandparent index, etc., of $name, depending on how
# deep $name resides in the directory tree.
#
# RETURNS
# Corresponding path in @{$paths_ref} if there is a match; undefined
# otherwise.
########
sub asmcmdbase_find_match
{
my ($paths_ref, $par_ids_ref, $index) = @_;
my ($i);
for ($i = 0; $i < @{$par_ids_ref}; $i++)
{
return $paths_ref->[$i] if ($par_ids_ref->[$i] == $index);
}
return undef;
}
########
# NAME
# asmcmdbase_rm_prompt_conf
#
# DESCRIPTION
# This routine prompts the user to confirm a delete for the command 'rm'.
#
# PARAMETERS
# None.
#
# RETURNS
# 1 if user response is 'y'; 0 otherwise.
#
# NOTES
# Only asmcmdbase_process_rm() calls this routine.
########
sub asmcmdbase_rm_prompt_conf
{
my ($response); # String to store the user response. #
asmcmdshare_print "You may delete multiple files and/or directories. \n";
asmcmdshare_printprompt 'Are you sure? (y/n) ';
$response = asmcmdshare_readstdin();
chomp ($response);
return 1 if ($response eq 'y');
return 0;
}
#######
# NAME
# asmcmdbase_print_action
#
# DESCRIPTION
# This routine prints the following message for the command used
# Command Message
# cp a b "copying <a> -> <b>"
# pwcopy a b "copying <a> -> <b>"
# mv a b "moving <a> -> <b>"
# cp --sparse_merge_begin a b --sparse_merge_end c "merging <a> through <c>"
# "to <b>"
#
# PARAMETERS
# fromfile (IN) - source file name
# tofile (IN) - target file name
# throughfile (IN) - name of sparse file indicating the ending
# depth in sparse merge operation
#
# RETURNS
# NONE
#
# NOTES
# This function handles both OS File names & ASM file names
#######
sub asmcmdbase_print_action
{
my ($fromfile, $tofile, $throughfile) = @_;
# only on Windows
if ($^O =~ /win/i)
{
#
# DOS style \\ is replaced for reg exp search earlier to UNIX style /.
# To print correctly, replace it back.
# Only if the files are OS files.
#
if ( $fromfile !~ /^\+/ )
{
$fromfile =~ s/\//\\/g;
}
if ( $tofile !~ /^\+/ )
{
$tofile =~ s/\//\\/g;
}
if ( $throughfile !~ /^\+/)
{
$throughfile =~ s/\//\\/g;
}
}
if ($asmcmdglobal_hash{'cmd'} eq "cp" ||
$asmcmdglobal_hash{'cmd'} eq "pwcopy")
{
if ($asmcmdglobal_hash{'sparse_merge_begin'} eq "1")
{
asmcmdshare_print "merging $fromfile through $throughfile to $tofile\n";
}
else
{
asmcmdshare_print "copying $fromfile -> $tofile\n";
}
}
if ($asmcmdglobal_hash{'cmd'} eq "pwmove")
{
asmcmdshare_print "moving $fromfile -> $tofile\n";
}
if ($asmcmdglobal_hash{'cmd'} eq "setsparseparent")
{
asmcmdshare_print "setting parent of $fromfile to $tofile\n";
}
}
########
# NAME
# asmcmdbase_do_file_copy
# DESCRIPTION
# This routine copy specified src file(s) to target file(dir) using
# dbms_diskgroup PL/SQL pkgs. Source cannot be a directory.
#
# PARAMETERS
# src_fname (IN) - the array of src file names.
# tgt_fdname (IN) - target file name or directory.
# remote_dbh (IN) - remote ASM instance handle given by DBI->connect()
# NULL(undef) for local copy mode
#
# RETURNS
# Count of files copied successfully; 0 on error.
#
# NOTES
########
sub asmcmdbase_do_file_copy
{
my ($src_remote_inst, $tgt_remote_inst,
$src_dbh, $tgt_dbh, $remote_conn_str,
$src_paths, $tgt_path) = @_;
my @norm_src_fname;
my @norm_tgt_fname;
my @src_finfo;
my @tgt_paths;
my $src_fil;
my $tgt_fil;
my $src_sth;
my $tgt_sth;
my $ret;
my %norm;
my @eargs;
my @paths;
my @ref_ids;
my @par_ids;
my @dg_nums;
my @entry_list;
my $name;
my $i;
my $tgt_name;
my %invalid_files;
# to print with correct '/' on windows
my $fromfile; # normalized source file name
my $tofile; # normalized target file name
my $remotefname; # host name + file name.
# $asmcmdglobal_hash{'service'} is always set in asmcmdbase_process_cp. If no
# value specified, +ASM is assumed.
my $servicename = $asmcmdglobal_hash{'service'}; # opt param for cp
my $sparse = $asmcmdglobal_hash{'sparse'};
my $copy_success_files = {};
my $success_count = 0;
# sparse merge operation
my $sparse_merge_begin = $asmcmdglobal_hash{'sparse_merge_begin'};
my $sparse_merge_end = $asmcmdglobal_hash{'sparse_merge_end'};
# dbuniquename.
my ($dbuniquename) = $asmcmdglobal_hash{'dbuniquename'};
$dbuniquename = "" if (!defined($dbuniquename));
asmcmdshare_trace(3, "dbuniquename: $dbuniquename", 'y', 'n');
# normalize and validate paths first iterate through each source file
foreach my $src_path (@{$src_paths})
{
my @src_paths;
my $src_name;
my @src_names;
my $tgt_fname = $tgt_path;
my @tgt_names;
my @tmp;
my $src_hdl;
my $tgt_hdl;
my $plkSz;
my $fileSz;
my $openMode;
my $fileType;
my $blkSz;
my $fileName;
my $fileGenName;
my $dbname;
my $dbid;
my $tsname;
my $fno;
my $plid;
my $sameen;
# size of pl/sql NUMBER type
my $pl_sql_number = 22;
# size of pl/sql VARCHAR type
my $pl_sql_varchar = 1024;
# 1Kbytes
my $Kbytes = 1024;
# normalize file paths
# criteria: if it is an OS file, has to be full path, otherwise it will be
# treated as a relative path in the ASM context
$src_path =~ s,\\,/,g; # replace DOS style \\ to UNIX style /
$src_path =~ s,/+,/,g; # multiple slashes together become a single slash
# if src_path doesn't start with '/', then it is an ASM relative path
# every OS path must be absolute.
if (($src_path !~ /^[a-z]:/i) && ($src_path !~ m'^[/\\\\]'))
{
# ASM file case
# if source file name is not an an absolute OS path nor ASM path,
# assume it is ASM and complete it
%norm = asmcmdshare_normalize_path($src_dbh, $src_path, 0, \$ret);
if ($ret != 0)
{
@eargs = ($src_path);
asmcmdshare_error_msg(8014, \@eargs);
next;
}
@src_paths = @{$norm{'path'}};
}
else
{
# OS file(s) case
$src_path =~ s/ /\\ /g; # escape blank spaces
@src_paths = glob($src_path);
if ($src_remote_inst != 1 )
{
# on remote host we cannot check for file existence
foreach $src_path (@src_paths)
{
if ( !(-r $src_path) )
{
@eargs = ($src_path);
asmcmdshare_error_msg(8014, \@eargs);
next;
}
}
}
}
# add the normalized file name(s) to the array
push(@norm_src_fname, @src_paths);
# Save a list containing only the file names
@src_names = map { /.*\/(.*)/ ; $1 } @src_paths;
# if target doesn't start with '/', then it is an ASM relative path
# every OS path must be absolute
if (($tgt_path !~ /^[a-z]:/i) && ($tgt_path !~ m'^[/\\\\]'))
{
$tgt_path = asmcmdshare_make_absolute($tgt_path);
# target is ASM
# if target is a relative path, complete it to be a valid ASM path
%norm = asmcmdshare_normalize_path($tgt_dbh, $tgt_path, 1, \$ret);
# path exists, must be a directory, and need to complete it with
# the source name
if ($ret == 0)
{
# Since we support wildcards now, make sure there is only one match for
# target path. Otherwise, report error.
if (@{$norm{'path'}} != 1)
{
@eargs = ($tgt_path);
# ASMCMD-8005: "directory '%s' is ambiguous"
asmcmdshare_error_msg(8005, \@eargs);
return;
}
# complete the path
$tgt_path = $norm{'path'}->[0];
$name = $tgt_path;
$name =~ s,.*/(.*)$,$1,;
@paths = @{ $norm{'path'} };
@ref_ids = @{ $norm{'ref_id'} };
@par_ids = @{ $norm{'par_id'} };
@dg_nums = @{ $norm{'gnum'} };
asmcmdshare_get_subdirs($tgt_dbh, \@entry_list, undef, $dg_nums[0],
$ref_ids[0], $par_ids[0], $name, undef, 0, 1);
# if target path is a disk group or a directory
if (($ref_ids[0] == $dg_nums[0] << 24 ) ||
$entry_list[0]->{'alias_directory'} eq 'Y')
{
@tgt_names = map { "$tgt_path/$_" } @src_names;
}
elsif ( $entry_list[0]->{'alias_directory'} eq 'N' )
{
asmcmdshare_assert(@src_names == 1, \@src_names);
@tgt_names = ($tgt_path);
}
else
{
@eargs = ($tgt_path);
asmcmdshare_error_msg(8014, \@eargs);
next;
}
}
else
{
@tgt_names = ($tgt_path);
}
}
else
{
$tgt_path =~ s,\\,/,g; # replace DOS style \\ to UNIX style /
$tgt_path =~ s,/+,/,g; # multiple / together become a single /
$tgt_path =~ s/ /\\ /g; # escape blank spaces
@tgt_paths = glob($tgt_path);
# Since we support wildcards now, make sure there is only one match for
# target path. Otherwise, report error.
unless (@tgt_paths == 1)
{
@eargs = ($tgt_path);
# ASMCMD-8005: "directory '%s' is ambiguous"
asmcmdshare_error_msg(8005, \@eargs);
return;
}
$tgt_path = shift @tgt_paths;
# target is OS path, if it exists check if it is a directory
if (-d $tgt_path)
{
# if target OS directory does not have write permission, bail out.
if (!-w $tgt_path)
{
asmcmdshare_error_msg(9463, undef);
return undef;
}
@tgt_names = map { "$tgt_path/$_" } @src_names;
}
else
{
@tgt_names = ($tgt_path);
}
# check for target OS file existence, if exist check for write permission
foreach $tgt_name (@tgt_names)
{
if ( -e $tgt_name )
{
if ( !-w $tgt_name )
{
asmcmdshare_error_msg (9463, undef);
return undef;
}
}
else
{
# target file does not exist, check if the target dir exist
my $filename ;
my @eargs;
# split path to check for existence of dir too.
($tgt_path, $filename) = $tgt_name =~ m|^(.*[/\\])([^/\\]+?)$|;
# check if path is a directory and has write permission
if (-d $tgt_path && !-w $tgt_path)
{
asmcmdshare_error_msg (9463, undef);
return undef;
}
}
}
}
# replace DOS style '\\' to Unix style '\/'
@tgt_names = map { $_ =~ s,\\,/,g ; $_ } @tgt_names;
push(@norm_tgt_fname, @tgt_names);
}
if ($sparse_merge_begin != 0)
{
# normalize file paths
# criteria: if it is an OS file, has to be full path,
# otherwise it will be treated as a relative path
# in the ASM context
# replace DOS style \\ to UNIX style /
$sparse_merge_end =~ s/\\/\//g;
# if sparse_merge_end doesn't start with '/', it is an ASM relative path
# every OS path must be absolute.
if (($sparse_merge_end !~ /^[a-z]:/i) && ($sparse_merge_end !~ m'^[/\\\\]'))
{
# ASM file case
# if sparse_merge_end file name is not an an absolute OS path nor ASM
# path, assume it is ASM and complete it
%norm = asmcmdshare_normalize_path($src_dbh, $sparse_merge_end, 0, \$ret);
if ( $ret != 0 )
{
@eargs = ($sparse_merge_end);
asmcmdshare_error_msg(8014, \@eargs);
return undef;
}
$sparse_merge_end = $norm{'path'}->[0];
}
else
{
# OS file case
if ( !(-r $sparse_merge_end) )
{
@eargs = ($sparse_merge_end);
asmcmdshare_error_msg(8014, \@eargs);
return undef;
}
}
}
# Get file information. Very important to do dbms_diskgroup stuff in the end,
# since the connection becomes useless after calling it.
foreach my $src_path (@norm_src_fname)
{
my $fileType;
my $fileSz;
my $blkSz;
my %info;
# Get file information, block size, file size, file type
if (asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_11gR1) >= 0)
{
$src_sth = $src_dbh->prepare(q{
begin
dbms_diskgroup.getfileattr(:fileName, :fileType, :fileSz, :blkSz);
end;
});
# bind input parameters #
$src_sth->bind_param( ":fileName", $src_path );
# bind ouput params #
$src_sth->bind_param_inout( ":fileType", \$fileType, $PLSQL_NUMBER);
$src_sth->bind_param_inout( ":fileSz", \$fileSz, $PLSQL_NUMBER);
$src_sth->bind_param_inout( ":blkSz", \$blkSz, $PLSQL_NUMBER);
# $sth->execute() || die $dbh->errstr;
$ret = $src_sth->execute();
if (!defined($ret) && ($asmcmdglobal_hash{'mode'} eq 'n'))
{
$asmcmdglobal_hash{'e'} = -1;
}
if (!defined $fileType || $fileType < 1 )
{
my (@eargs) = ($src_path);
asmcmdshare_error_msg(8012, \@eargs);
asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
$invalid_files{$src_path} = 1;
next;
}
if (!defined $blkSz || $blkSz < 1)
{
my (@eargs) = ($src_path);
asmcmdshare_error_msg(8013, \@eargs);
asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
$invalid_files{$src_path} = 1;
next;
}
}
else
{
my $sameen = 1;
my $dbname;
my $tsname;
my $dbid;
my $fno;
my $plid;
# if file path starts with '/' then it is an OS file #
# find file attributes, type, blkSz, fileSz(numberOfblks) #
# use dbms_back_restore.readFileHeader package
# for any instance release older than 11.xx
$src_sth = $src_dbh->prepare(q{
begin
dbms_backup_restore.readFileHeader(:fileName, :dbname,
:dbid, :tsname, :fno, :fileSz, :blkSz, :plid, :sameen);
end;
});
# bind input params first #
$src_sth->bind_param( ":fileName", $src_path );
$src_sth->bind_param( ":sameen", $sameen );
# bind ouput params #
$src_sth->bind_param_inout( ":dbname", \$dbname, $PLSQL_VARCHAR );
$src_sth->bind_param_inout( ":tsname", \$tsname, $PLSQL_VARCHAR );
$src_sth->bind_param_inout( ":dbid", \$dbid, $PLSQL_NUMBER );
$src_sth->bind_param_inout( ":fno", \$fno, $PLSQL_NUMBER );
# fileSZ: total_number_lbk - 1, which is 1 lbk(logical blk)
# less of v$asm_files.blocks
$src_sth->bind_param_inout( ":fileSz", \$fileSz, $PLSQL_NUMBER );
$src_sth->bind_param_inout( ":blkSz", \$blkSz, $PLSQL_NUMBER );
$src_sth->bind_param_inout( ":plid", \$plid, $PLSQL_NUMBER );
$ret = $src_sth->execute();
if (!defined ($ret) && ($asmcmdglobal_hash{'mode'} eq 'n'))
{
asmcmdglobal_hash{'e'} = -1;
}
if ( !defined $fno || $fno <= 0 )
{
my @eargs = ($src_path);
asmcmdshare_error_msg(8012, \@eargs);
asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
$invalid_files{$src_path} = 1;
next;
}
# for values for file type, please refer ASM PL/SQL
# Fixed Package Quickstart Guide
$fileType = $PLSQL_DATAFILE;
#figure out file type from path
}
# if file type is == 2, change it to 12 (datafile copy)
if ($fileType == $PLSQL_DATAFILE)
{
$fileType = $PLSQL_DATAFILE_CP;
}
$info{'fsize'} = $fileSz;
$info{'blksz'} = $blkSz;
$info{'ftyp'} = $fileType;
push (@src_finfo, \%info);
}
# remove unexisting files
# remove from @norm_src_fname and also from @norm_tgt_fname
foreach (keys(%invalid_files))
{
for ($i = 0; $i < @norm_src_fname; $i++)
{
if ($norm_src_fname[$i] eq $_)
{
splice(@norm_src_fname, $i, 1);
splice(@norm_tgt_fname, $i, 1);
next;
}
}
}
if (!$src_remote_inst && !$tgt_remote_inst) # local to local
{
for ($i = 0; $i < @norm_src_fname; $i++)
{
if ($sparse_merge_begin != 1)
{
# If the src and target files are the same then error out
if ($norm_src_fname[$i] eq $norm_tgt_fname[$i])
{
my (@eargs) = ($norm_src_fname[$i]);
asmcmdshare_error_msg(9358, \@eargs);
next;
}
$src_sth = $src_dbh->prepare(q{
begin
dbms_diskgroup.copy('', '', '', :src_path, :src_ftyp, :src_blksz,
:src_fsiz, '','','', :dst_path, 1, 0,
:sparse_option, :sparse_begin, '',
:dbuniquename);
end;
});
}
else
{
# If src (sparse_merge_begin) and sparse_merge_end files are
# the same then error out
if ($norm_src_fname[$i] eq $sparse_merge_end)
{
my (@eargs) = ($norm_src_fname[$i]);
asmcmdshare_error_msg(8035, \@eargs);
return undef;
}
$src_sth = $src_dbh->prepare(q{
begin
dbms_diskgroup.copy('', '', '', :src_path, :src_ftyp, :src_blksz,
:src_fsiz, '','','', :dst_path, 1, 0,
:sparse_option, :sparse_begin, :sparse_end,
:dbuniquename);
end;
});
$src_sth->bind_param(":sparse_end", $sparse_merge_end);
}
$src_sth->bind_param(":src_path", $norm_src_fname[$i]);
$src_sth->bind_param(":src_ftyp", $src_finfo[$i]->{'ftyp'});
$src_sth->bind_param(":src_blksz", $src_finfo[$i]->{'blksz'});
$src_sth->bind_param(":src_fsiz", $src_finfo[$i]->{'fsize'});
$src_sth->bind_param(":dst_path", $norm_tgt_fname[$i]);
$src_sth->bind_param(":sparse_option", $sparse);
$src_sth->bind_param(":sparse_begin", $sparse_merge_begin);
$src_sth->bind_param(":dbuniquename", $dbuniquename);
$fromfile = $norm_src_fname[$i];
$tofile = $norm_tgt_fname[$i];
asmcmdbase_print_action($fromfile, $tofile, $sparse_merge_end);
$ret = $src_sth->execute();
if (!defined($ret))
{
if ($sparse_merge_begin != 1)
{
my (@eargs) = ($fromfile, $tofile);
asmcmdshare_error_msg(8016, \@eargs);
}
else
{
# failure case for sparse_merge
my (@eargs) = ($fromfile, $sparse_merge_end, $tofile);
asmcmdshare_error_msg(8033, \@eargs);
}
asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
next;
}
else
{
$copy_success_files->{$norm_src_fname[$i]} = $norm_tgt_fname[$i];
$success_count++;
}
}
asmcmdshare_trace(3, "SUCCESS: Completed copying from local to local.",
'y', 'n');
}
elsif ($src_remote_inst) # remote to local
{
my $host;
my $port;
my $sid;
my $src_str;
$port = 1521;
$sid = '+ASM';
$port = $rport if(defined $rport);
$sid = $rsid if(defined $rsid);
$host = $rhost if(defined $rhost);
for ($i = 0; $i < @norm_src_fname; $i++)
{
$src_sth = $tgt_dbh->prepare(q{
begin
dbms_diskgroup.copy(:connect_iden, :usrname, :passwd,
:src_path, :src_ftyp, :src_blksz,
:src_fsiz, '','','', :dst_path, 1, 0,
:sparse_option, 0, '', :dbuniquename);
end;
});
$src_str = "(description=(address_list=(ADDRESS=(PROTOCOL=tcp)";
$src_str .= "(HOST=".$host.")" if defined($host);
$src_str .= "(privilege=sysdba)(internal_logon=sysdba)";
$src_str .= "(PORT=".$port.")))"if defined($port);
$src_str .= "(connect_data=(service_name=".$servicename.")"
if defined($servicename);
$src_str .= " (instance_name=".$sid.")))" if defined($sid);
$src_sth->bind_param(":connect_iden", $src_str);
$src_sth->bind_param(":usrname", $rusr);
$src_sth->bind_param(":passwd", $rpswd);
$src_sth->bind_param(":src_path", $norm_src_fname[$i]);
$src_sth->bind_param(":src_ftyp", $src_finfo[$i]->{'ftyp'});
$src_sth->bind_param(":src_blksz", $src_finfo[$i]->{'blksz'});
$src_sth->bind_param(":src_fsiz", $src_finfo[$i]->{'fsize'});
$src_sth->bind_param(":dst_path", $norm_tgt_fname[$i]);
$src_sth->bind_param(":sparse_option", $sparse);
$src_sth->bind_param(":dbuniquename", $dbuniquename);
$fromfile = $host.":".$norm_src_fname[$i];
$tofile = $norm_tgt_fname[$i];
asmcmdbase_print_action($fromfile, $tofile, '');
$ret = $src_sth->execute();
if (!defined($ret))
{
my @eargs = ($fromfile, $tofile);
my $msg = "ERROR:asmcmdbase_do_file_copy01:dbms_diskgroup".
".copy failed with ";
$msg .= "ServiceName=$servicename" if defined($servicename);
$msg .= " connect_iden: $src_str" if defined($src_str);
asmcmdshare_trace(1, $msg , 'y', 'n');
asmcmdshare_error_msg(8016, \@eargs);
asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
next;
}
else
{
$copy_success_files->{$norm_src_fname[$i]} = $norm_tgt_fname[$i];
$success_count++;
}
}
asmcmdshare_trace(3, "SUCCESS: Completed copying from remote to local.",
'y', 'n');
}
elsif ($tgt_remote_inst) # local to remote
{
my $host;
my $port;
my $sid;
my $dst_str;
my $pblkSz;
my $comp;
my $version;
my $src_pwfile;
my $pblksize;
$pblkSz = 0;
$src_pwfile = 0;
for ($i = 0; $i <= $#norm_src_fname; $i++)
{
if ($src_finfo[$i]->{'ftyp'} == $PLSQL_PWFILE)
{
$src_pwfile = 1;
}
}
# We have to obtain physical block size of the destination
# which is required for password file copy.
if ($src_pwfile && $tgt_path !~ /^\+/)
{
# Get the remote asm version
$version = asmcmdshare_get_asm_version($tgt_dbh);
# Copy for password files is supported for versions greater
# than 12.1.0.0.0
$comp = asmcmdshare_version_cmp($version, $ASMCMDGLOBAL_VER_12gR1);
if ($comp >= 0)
{
$tgt_sth = $tgt_dbh->prepare(q{
begin
dbms_diskgroup.getfilephyblksize(:fileName, :flag, :pblksize);
end;
});
# bind input parameters #
$tgt_sth->bind_param( ":fileName", $tgt_path);
# set the value to 1 to obtain the values for pwfile
$tgt_sth->bind_param( ":flag", 1);
$tgt_sth->bind_param_inout( ":pblksize", \$pblksize, $PLSQL_NUMBER);
$ret = $tgt_sth->execute();
if (!defined($ret))
{
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
}
if (!defined $pblksize || $pblksize < 1)
{
my (@eargs) = ($tgt_path);
asmcmdshare_error_msg(8013, \@eargs);
}
$pblkSz = $pblksize;
}
else
{
my (@eargs) = ($ASMCMDGLOBAL_VER_12gR1);
my (@hostname) = ($rhost);
asmcmdshare_error_msg(8023, \@eargs, \@hostname);
}
}
$port = 1521; #default port
$sid = '+ASM'; #default SID
$port = $rport if(defined $rport);
$sid = $rsid if(defined $rsid);
$host = $rhost if(defined $rhost);
for ($i = 0; $i <= $#norm_src_fname; $i++)
{
$src_sth = $src_dbh->prepare(q{
begin
dbms_diskgroup.copy('', '', '', :src_path, :src_ftyp, :src_blksz,
:src_fsiz, :connect_iden, :usrname, :passwd,
:dst_path, 1, :dst_pblksize, :sparse_option, 0, '',
:dbuniquename);
end;
});
$dst_str = "(description=(address_list=(ADDRESS=(PROTOCOL=tcp)";
$dst_str .= "(HOST=".$host.")" if defined($host);
$dst_str .= "(privilege=sysdba)(internal_logon=sysdba)";
$dst_str .= "(PORT=".$port.")))"if defined($port);
$dst_str .= "(connect_data=(service_name=".$servicename.")"
if defined($servicename);
$dst_str .= " (instance_name=".$sid.")))" if defined($sid);
$src_sth->bind_param(":src_path", $norm_src_fname[$i]);
$src_sth->bind_param(":src_ftyp", $src_finfo[$i]->{'ftyp'});
$src_sth->bind_param(":src_blksz", $src_finfo[$i]->{'blksz'});
$src_sth->bind_param(":src_fsiz", $src_finfo[$i]->{'fsize'});
$src_sth->bind_param(":connect_iden", $dst_str);
$src_sth->bind_param(":usrname", $rusr);
$src_sth->bind_param(":passwd", $rpswd);
$src_sth->bind_param(":dst_path", $norm_tgt_fname[$i]);
$src_sth->bind_param(":dst_pblksize", $pblkSz);
$src_sth->bind_param(":sparse_option", $sparse);
$src_sth->bind_param(":dbuniquename", $dbuniquename);
$fromfile = $norm_src_fname[$i];
$tofile = $rhost.":".$norm_tgt_fname[$i];
asmcmdbase_print_action($fromfile, $tofile, '');
$ret = $src_sth->execute();
if ( !defined($ret) )
{
my (@eargs) = ($norm_src_fname[$i], $norm_tgt_fname[$i]);
my ($msg) = "ERROR:asmcmdbase_do_file_copy02:dbms_diskgroup".
".copy failed with ";
$msg .= "ServiceName=$servicename" if defined($servicename);
$msg .= " connect_iden=$dst_str" if defined($dst_str);
asmcmdshare_trace(1, $msg , 'y', 'n');
asmcmdshare_error_msg(8016, \@eargs);
asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
next;
}
else
{
$copy_success_files->{$norm_src_fname[$i]} = $norm_tgt_fname[$i];
$success_count++;
}
}
asmcmdshare_trace(3, "SUCCESS: Completed copying from local to remote.",
'y', 'n');
}
return $copy_success_files;
}
##############################################################################
########
## NAME
## asmcmdbase_set_sparse_parent_child
## DESCRIPTION
## This routine sets parent of the specified child file(s) to a new parent
## file using dbms_diskgroup PL/SQL pkgs. Child cannot be a directory.
##
## PARAMETERS
## child_paths (IN) - the array of child file names.
## parent_path (IN) - parent file name or directory.
##
## RETURNS
## Count of child file(s) whose parent were successfully set; 0 on error.
##
## NOTES
#########
sub asmcmdbase_set_sparse_parent_child
{
my ($child_dbh, $parent_dbh, $child_paths, $parent_path) = @_;
my (@norm_child_fname, @norm_parent_fname);
my ($child_sth);
my ($ret);
my (%norm);
my (@eargs);
my ($i);
my ($success_count) = 0;
# to print with correct '/' on windows
my ($childfile); # normalized sparse child file name
my ($parentfile); # normalized sparse parent file name
# normalize and validate paths first
# iterate through each sparse child file
foreach (@{$child_paths})
{
my $child_path = $_;
# normalize file paths
# criteria: if it is an OS file, has to be full path,
# otherwise it will be treated as a relative path
# in the ASM context
# replace DOS style \\ to UNIX style /
$child_path =~ s/\\/\//g;
# if src_path doesn't start with '/', then it is an ASM relative path
# every OS path must be absolute.
if (($child_path !~ /^[a-z]:/i) && ($child_path !~ m'^[/\\\\]'))
{
# ASM file case
# if sparse child file name is not an an absolute OS path nor ASM path,
# assume it is ASM and complete it
%norm = asmcmdshare_normalize_path($child_dbh, $child_path, 0, \$ret);
if ($ret != 0)
{
@eargs = ($child_path);
asmcmdshare_error_msg(8014, \@eargs);
next;
}
$child_path = $norm{'path'}->[0];
}
else
{
# OS file case
if (!(-r $child_path))
{
@eargs = ($child_path);
asmcmdshare_error_msg(8014, \@eargs);
next;
}
}
# add the normalized file name to the array
push(@norm_child_fname, $child_path);
#replace DOS style '\\' to Unix style '\/'
$parent_path =~ s/\\/\//g;
# if parent doesn't start with '/', then it is an ASM relative path
# every OS path must be absolute
if (($parent_path !~ /^[a-z]:/i) && ($parent_path !~ m'^[/\\\\]'))
{
$parent_path = asmcmdshare_make_absolute($parent_path);
# parent is in ASM
# if parent is a relative path, complete it to be a valid ASM path
%norm = asmcmdshare_normalize_path($parent_dbh, $parent_path, 1, \$ret);
if ($ret != 0)
{
@eargs = ($parent_path);
asmcmdshare_error_msg(8014, \@eargs);
return $success_count;
}
$parent_path = $norm{'path'}->[0];
}
else
{
# OS file case
if (!(-r $parent_path))
{
@eargs = ($parent_path);
asmcmdshare_error_msg(8014, \@eargs);
return $success_count;
}
}
push(@norm_parent_fname, $parent_path);
}
# set child's new parent
for ($i = 0; $i < @norm_child_fname; $i++)
{
# If both child and parent files are the same then error out
if ($norm_child_fname[$i] eq $norm_parent_fname[$i])
{
my (@eargs) = ($norm_child_fname[$i]);
asmcmdshare_error_msg(9358, \@eargs);
next;
}
$child_sth = $child_dbh->prepare(q{
begin
dbms_diskgroup.setsparseparent(:child_path, :parent_path);
end;
});
$child_sth->bind_param(":child_path", $norm_child_fname[$i]);
$child_sth->bind_param(":parent_path", $norm_parent_fname[$i]);
$childfile = $norm_child_fname[$i];
$parentfile = $norm_parent_fname[$i];
asmcmdbase_print_action($childfile, $parentfile, '');
$ret = $child_sth->execute();
if (!defined($ret))
{
my (@eargs) = ($childfile, $parentfile);
asmcmdshare_error_msg(8017, \@eargs);
asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
next;
}
else
{
$success_count++;
}
}
asmcmdshare_trace(3, "Completed setsparseparent from local to local.",
'y', 'n');
return $success_count;
}
############################# Sort Order Routines ############################
########
# NAME
# asmcmdbase_name_forward
#
# DESCRIPTION
# Routine for ordering a sort, used by Perl's built-in function sort().
# Order alphabetically by 1) name from v$asm_alias and 2) the full path to
# that name.
#
# PARAMETERS
# None.
#
# RETURNS
# N/A.
########
sub asmcmdbase_name_forward
{
$a->{'name'} cmp $b->{'name'}
or
$a->{'path'} cmp $b->{'path'};
}
########
# NAME
# asmcmdbase_name_backward
#
# DESCRIPTION
# Routine for ordering a sort, used by Perl's built-in function sort().
# Order reverse-alphabetically by 1) name from v$asm_alias and 2) the full
# path to that name.
#
# PARAMETERS
# None.
#
# RETURNS
# N/A.
########
sub asmcmdbase_name_backward
{
$b->{'name'} cmp $a->{'name'}
or
$b->{'path'} cmp $a->{'path'};
}
########
# NAME
# asmcmdbase_time_forward
#
# DESCRIPTION
# Routine for ordering a sort, used by Perl's built-in function sort().
# Order in this way:
#
# 1) Directory aliases always comes first, then file aliases.
# 2a) If directory, sort in reverse-alphabetical order by name and then
# by path, just like in asmcmdbase_name_backward().
# 2b) If file, sort by Julian date with most recent first, then reverse-
# alphabetically by name and then by path.
#
# PARAMETERS
# None.
#
# RETURNS
# N/A.
#
# NOTES
# We sort by Julian date in absolute number of days. As in Unix versions of
# ls, time forward puts the most recently updated files first, as in ls -r.
########
sub asmcmdbase_time_forward
{
# If one alias is a file and another is a directory, then the directory
# comes first.
if ($a->{'alias_directory'} ne $b->{'alias_directory'})
{
$b->{'alias_directory'} cmp $a->{'alias_directory'};
}
# If both are directories, sort in same way as in asmcmdbase_name_backward().
elsif ($a->{'alias_directory'} eq 'Y')
{
$b->{'name'} cmp $a->{'name'}
or
$b->{'path'} cmp $a->{'path'};
}
# If both are files, sort first by Julian date, most recent first; then
# reverse alphabetically as in asmcmdbase_name_backward().
else
{
# In the (near) future), v$file joined with v$alias will not return volume
# information. When that happens, we can get rid of the 'volume' code
# below - leaving only the 'else' clause.
my($a_name, $a_unused) = split /\./, $a->{'name'}, 2;
my($b_name, $b_unused) = split /\./, $b->{'name'}, 2;
# volumes don't have 'julian_time' or 'path' and so would generate an error
if (($a_name eq 'volume') || ($b_name eq 'volume') ||
($a_name eq 'DRL') || ($b_name eq 'DRL'))
{
$b->{'name'} cmp $a->{'name'};
}
else
{
if (defined($a->{'julian_time'}) && defined($b->{'julian_time'}))
{
$b->{'julian_time'} <=> $a->{'julian_time'}
or
$b->{'name'} cmp $a->{'name'}
or
$b->{'path'} cmp $a->{'path'};
}
else
{
$b->{'name'} cmp $a->{'name'}
or
$b->{'path'} cmp $a->{'path'};
}
}
}
}
########
# NAME
# asmcmdbase_time_backward
#
# DESCRIPTION
# Routine for ordering a sort, used by Perl's built-in function sort().
# Order in this way:
#
# 1) Directory aliases always comes first, then file aliases.
# 2a) If directory, sort in alphabetical order by name and then
# by path, just like in asmcmdbase_name_forward().
# 2b) If file, sort by Julian date with most recent last, then
# alphabetically by name and then by path.
#
# PARAMETERS
# None.
#
# RETURNS
# N/A.
#
# NOTES
# We sort by Julian date in absolute number of days. As in Unix versions of
# ls, time backward puts the most recently updated files last, as in ls -rt.
########
sub asmcmdbase_time_backward
{
# If one alias is a file and another is a directory, then the directory
# comes first.
if ($a->{'alias_directory'} ne $b->{'alias_directory'})
{
$b->{'alias_directory'} cmp $a->{'alias_directory'};
}
# If both are directories, sort in same way as in asmcmdbase_name_forward().
elsif ($a->{'alias_directory'} eq 'Y')
{
$a->{'name'} cmp $b->{'name'}
or
$a->{'path'} cmp $b->{'path'};
}
# If both are files, sort first by Julian date, most recent last; then
# reverse alphabetically as in asmcmdbase_name_forward().
else
{
if (defined($a->{'julian_time'}) && defined($b->{'julian_time'}))
{
$a->{'julian_time'} <=> $b->{'julian_time'}
or
$a->{'name'} cmp $b->{'name'}
or
$a->{'path'} cmp $b->{'path'};
}
else
{
$a->{'name'} cmp $b->{'name'}
or
$a->{'path'} cmp $b->{'path'};
}
}
}
########
# NAME
# asmcmdbase_rm_recur_order
#
# DESCRIPTION
# Routine for ordering a sort, used by Perl's built-in function sort().
# Delete in this order: 1) files, 2) dir with deepest number of levels first.
#
# PARAMETERS
# None.
#
# RETURNS
# N/A.
#
# NOTES
# Only asmcmdbase_process_rm() uses this routine.
########
sub asmcmdbase_rm_recur_order
{
# Delete in this order: 1) files, 2) dir with deepest number of levels first.
$a->{'alias_directory'} cmp $b->{'alias_directory'}
or
asmcmdbase_levels($b->{'full_path'}) <=> asmcmdbase_levels($a->{'full_path'});
}
########
# NAME
# asmcmdbase_levels
#
# DESCRIPTION
# This routine returns the number of levels of subdirectries in a path.
# For instance, +dgroup/ORCL/DATAFILE has three levels.
#
# PARAMETERS
# path (IN) - a full path to an alias.
#
# RETURNS
# The number of levels of subdirectories in a path.
#
# NOTES
# The number of levels is always one more than the number of forward slashes
# in the path. The assumption is true because $path must be normalized
# before calling this routine. Normalized paths cannot have duplicate
# slashes, as in '+dgroup//ORCL'.
########
sub asmcmdbase_levels
{
my ($path) = shift;
my (@levels); # The number of levels of subdirectories in a path. #
@levels = split (/\//, $path);
return scalar @levels;
}
##############################################################################
######################### Parameter Parsing Routines #########################
########
# NAME
# asmcmdbase_parse_remote_conn_str
#
# DESCRIPTION
# This routine parses the remote connect string into two components &
# inquire user-input passwd:
# 1) user
# 2) identifier string
#
# PARAMETERS
# cstr (IN) - the connect string to be parsed.
#
# RETURNS
# Zero on success; undefined on error.
#
# NOTES
# The connect string should be in the format user/password@identifier.
########
sub asmcmdbase_parse_remote_conn_str
{
my ($cstr) = shift;
my ($ident); # The identifier portion of the connect string. #
my ($usr); # The username portion of $usrpswd. #
my ($pswd);
my (@eargs);
if ($cstr !~ m'@') # usr name must be specified followed by '@' #
{
@eargs = ($cstr);
asmcmdshare_error_msg(8010, \@eargs);
return undef;
}
($usr, $ident) = split (/\@/, $cstr); # Split by '@'. #
if ($usr eq '')
{
@eargs = ($cstr);
asmcmdshare_error_msg(8010, \@eargs);
return undef;
}
if ($ident eq '')
{
@eargs = ($cstr);
asmcmdshare_error_msg(8011, \@eargs);
return undef;
}
# store parsed usr name & identifier into gobal variables #
$rusr = $usr;
($rusr, $rpswd) = split(/\//, $rusr);
$rident = $ident;
# Prompt user for password if not already provided#
$rpswd = asmcmdshare_getpswd() unless defined($rpswd);
# 20721939: quote password to prevent SQL injection risks
$rpswd = qq("$rpswd");
return 0;
}
########
# NAME
# asmcmdbase_is_cmd
#
# DESCRIPTION
# This routine checks if a user-entered command is one of the known ASMCMD
# internal commands that belong to the base module.
#
# PARAMETERS
# arg (IN) - user-entered command name string.
#
# RETURNS
# True if $arg is one of the known commands, false otherwise.
########
sub asmcmdbase_is_cmd
{
my ($arg) = shift;
return defined ( $asmcmdbase_cmds{ $arg } );
}
########
# NAME
# asmcmdbase_is_wildcard_cmd
#
# DESCRIPTION
# This routine determines if a 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.
#
# NOTES
# Currently, only cd, du, find, ls, and rm can take wildcard as part of
# their arguments.
########
sub asmcmdbase_is_wildcard_cmd
{
my ($arg) = shift;
return defined ($asmcmdbase_cmds{ $arg }) &&
(asmcmdshare_get_cmd_wildcard($arg) eq "true") ;
}
########
# NAME
# asmcmdbase_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 asmcmdbase module currently supports only the help as a command
# that does not require an ASM instance.
########
sub asmcmdbase_is_no_instance_cmd
{
my ($arg) = shift;
my ($rc);
return 1 unless defined($asmcmdbase_cmds{$arg});
$rc = asmcmdshare_get_cmd_noinst($arg);
if ($rc eq "true")
{
return 1;
}
elsif ($rc eq "undef")
{
return -1;
}
return 0;
}
########
# NAME
# asmcmdbase_parse_int_args
#
# DESCRIPTION
# This routine parses the arguments for flag options for ASMCMD internal
# commands.
#
# PARAMETERS
# cmd (IN) - user-entered command name string.
# args_ref (OUT) - hash of user-specified flag options for a command,
# populated by getopts().
#
# RETURNS
# Zero on success; undefined on error.
#
# NOTES
# $cmd must already be verified as a valid ASMCMD internal command.
########
sub asmcmdbase_parse_int_args
{
my ($cmd, $args_ref) = @_;
my (@string);
my ($key);
#include deprecated options if any
if($asmcmdglobal_deprecated_options{ $cmd })
{
foreach my $key(keys %{$asmcmdglobal_deprecated_options{ $cmd }})
{
#include only if the option is changed and not discontinued
push(@string, $asmcmdglobal_deprecated_options{$cmd}{$key}[0]);
}
}
# 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. #
asmcmdbase_syntax_error($cmd);
return undef;
}
return 0;
}
########
# NAME
# asmcmdbase_parse_int_cmd_line
#
# DESCRIPTION
# This routine parses a line of command and divides it up into tokens of
# arguments, delimited by spaces.
#
# PARAMETERS
# cmd_line (IN) - user-entered line of command, including the command
# name and its arguments.
# argv_ref (OUT) - Reference to an array of arguments to return, with the
# command name stored as element zero of the array, and
# its arguments stored as the subsequent elements; much
# like the array 'argv' in C. Should be passed in as
# an empty array.
#
# RETURNS
# 0 on success, -1 on error.
#
# NOTES
# Arguments are delimited by whitespace, unless that whitespace is enclosed
# within single quotes, in which case they are considered as part of one
# argument.
#
# Valid states for the state transition:
# NO QUOTE - parsing a portion of $cmd_line that's *not* in quotes.
# IN QUOTE - parsing a portion of $cmd_line that's in quotes.
# SPACES - same condition for NO QUOTE is true; also true: currently
# parsing the delimiter $ASMCMDBASE_SPACE before tokens, or
# arguments.
#
# State transition diagram:
#
# Input ->
# ----------------------------------------------------
# |State | quote | space | other | NULL |
# |---------+----------+----------+----------+-------|
# |NO QUOTE | IN QUOTE | SPACES* | NO QUOTE | DONE* |
# |---------+---------------------+----------+-------|
# |IN QUOTE | NO QUOTE | IN QUOTE | IN QUOTE | ERR |
# |---------+----------+----------+----------+-------|
# |SPACES | IN QUOTE | SPACES | NO QUOTE | DONE* |
# |--------------------------------------------------|
#
# * In these cases, $token must have one complete argument, so add $token
# to the output parameter array.
########
sub asmcmdbase_parse_int_cmd_line
{
my ($cmd_line, $argv_ref) = @_;
my ($char); # One character from $cmd_line. #
my ($state) = 'NO QUOTE';
my ($token) = ''; # One argument from $cmd_line. #
my ($offset); # Offset to interate through $cmd_line using substr(). #
my (@eargs); # Array of error arguments. #
# Iterate through $cmd_line character by character using substr().
for ($offset = 0; $offset < length($cmd_line); $offset++)
{
$char = substr ($cmd_line, $offset, 1);
if ($state eq 'NO QUOTE')
{
if ($char eq "'")
{
$state = 'IN QUOTE';
}
elsif ($char eq $ASMCMDBASE_SPACE)
{
$state = 'SPACES';
push (@{ $argv_ref }, $token);
$token = '';
}
else
{ # $char is any non-space, non-single quote character. #
$token .= $char;
}
}
elsif ($state eq 'IN QUOTE')
{
if ($char eq "'")
{
$state = 'NO QUOTE';
}
else
{ # $char is any non-single quote character. #
$token .= $char;
}
}
elsif ($state eq 'SPACES')
{
if ($char eq "'")
{
$state = 'IN QUOTE';
}
elsif ($char ne $ASMCMDBASE_SPACE)
{ # $char is any non-space character. #
$token .= $char;
$state = 'NO QUOTE';
}
else
{ # $char must be a space. #
# Multiplie consecutive spaces encountered; do nothing.
}
}
else
{
# Should never get here. Signal internal error.
@eargs = ("asmcmdbase_parse_int_cmd_line_05");
asmcmdshare_signal_exception (8202, \@eargs);
}
}
push (@{ $argv_ref }, $token);
if ($state eq 'IN QUOTE')
{ # Error: somebody forgot to close the quote; parsing failed. #
return -1;
}
return 0;
}
##############################################################################
############################# Error Routines #################################
########
# NAME
# asmcmdbase_syntax_error
#
# DESCRIPTION
# This routine prints the correct syntax for a command to STDERR, used
# when there is a syntax error. If the command with bad syntax is asmcmd
# itself, then asmcmdbase_syntax_error lso calls exit() to quit out.
#
# 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. Thus, even if exit() is called, the exit value is
# zero.
########
sub asmcmdbase_syntax_error
{
my ($cmd) = shift;
my ($cmd_syntax); # Correct syntax for $cmd. #
my ($cmd_print_name) = '';
my ($succ) = 0;
$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))
{
$cmd_print_name = $cmd if ($cmd ne 'asmcmd');
asmcmdshare_printstderr 'usage: ' . $cmd_syntax . "\n";
asmcmdshare_printstderr 'help: help ' . $cmd_print_name . "\n";
# Can't proceed if asmcmd has bad syntax. #
exit -1 if (($cmd eq 'asmcmd'));
$succ = 1;
}
if ($asmcmdglobal_hash{'mode'} eq 'n')
{
$asmcmdglobal_hash{'e'} = -1;
}
return $succ;
}
##############################################################################
########################## Initialization Routines ###########################
########
# NAME
# asmcmdbase_check_insttype
#
# DESCRIPTION
# This routine checks the instance type of the connected instance to see
# if it is an ASM instance. If not, it prints error and exits 0.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null; may not return if exit 0.
#
# NOTES
# This routine should be called before executing any asmcmd commands.
########
sub asmcmdbase_check_insttype
{
my ($dbh) = shift;
my ($insttype); # Instance type of the connected instance. #
$insttype = asmcmdshare_get_insttype ($dbh);# Get instance type using SQL. #
# It's 'ASM' in 10gR2 and 'asm' in 10gR1.
if ( ($insttype ne 'ASM') && ($insttype ne 'asm') )
{
asmcmdshare_error_msg(8003, undef);
exit 0;
}
asmcmdshare_trace(3, "NOTE: Instance type: $insttype", 'n', 'n');
return;
}
########
# NAME
# asmcmdbase_init_global
#
# DESCRIPTION
# This routine initializes the global variables in the hash
# %asmcmdglobal_hash.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
#
# RETURNS
# Null.
#
# NOTES
#
########
sub asmcmdbase_init_global
{
my ($dbh) = shift;
# Default initial directory is '+'.
$asmcmdglobal_hash{'cwdnm'} = '+';
# Default group is first the first group.
$asmcmdglobal_hash{'cwdref'} = 1 << 24;
$asmcmdglobal_hash{'gnum'} = 1;
}
##############################################################################
############################## SQL Routines ##################################
########
# NAME
# asmcmdbase_mkdir_sql
#
# DESCRIPTION
# This routine constructs the SQL used to create a new directory and calls
# asmcmdshare_do_stmt() to execute it.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gname (IN) - name for diskgroup in which to create the directory.
# dir (IN) - full path to the directory to be created.
#
# RETURNS
# Null.
########
sub asmcmdbase_mkdir_sql
{
my ($dbh, $gname, $dir) = @_;
my ($ret, $qry);
$qry = "alter diskgroup /*ASMCMD*/ \"$gname\" add directory '$dir'";
$ret = asmcmdshare_do_stmt($dbh, $qry);
return;
}
########
# NAME
# asmcmdbase_rm_sql
#
# DESCRIPTION
# This routine constructs the SQL used to drop a file or directory from
# a diskgroup. It calls asmcmdshare_do_stmt() to execute it.
#
# PARAMETERS
# dbh (IN) - database handle.
# gname (IN) - name of diskgroup where to drop the file or directory.
# alias (IN) - full path to the alias to be dropped.
# is_dir (IN) - 'Y' if $alias is a directory, 'N' otherwise.
#
# RETURNS
# Null.
########
sub asmcmdbase_rm_sql
{
my ($dbh, $gname, $alias, $is_dir) = @_;
my $ret;
my $qry;
# Construct the correct SQL for either a directory or a file.
if ($is_dir eq 'Y')
{ # Drop directory! #
$qry = "alter diskgroup /*ASMCMD*/ \"$gname\" drop directory '$alias'";
}
else
{ # Drop file! #
$qry = "alter diskgroup /*ASMCMD*/ \"$gname\" drop file '$alias'";
}
$ret = asmcmdshare_do_stmt($dbh, $qry);
return;
}
########
# NAME
# asmcmdbase_mkalias_sql
#
# DESCRIPTION
# This routine constructs the SQL used to create a user alias. It calls
# asmcmdshare_do_stmt() to execute it.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gname (IN) - name of diskgroup where to create the user alias.
# sys_a (IN) - full path to the system alias to which the new user alias
# will point.
# usr_a (IN) - full path specifying the location for the new user alias.
#
# RETURNS
# Null.
########
sub asmcmdbase_mkalias_sql
{
my ($dbh, $gname, $sys_a, $usr_a) = @_;
my ($ret, $qry);
$qry = "alter diskgroup /*ASMCMD*/ \"$gname\" add alias '$usr_a' for '$sys_a'";
$ret = asmcmdshare_do_stmt($dbh, $qry);
return;
}
########
# NAME
# asmcmdbase_rmalias_sql
#
# DESCRIPTION
# This routine constructs the SQL used to drop a user alias or to drop
# recursively a directory tree that contains only user aliases. It
# calls asmcmdshare_do_stmt() to execute it.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gname (IN) - name of diskgroup where to drop $alias.
# alias (IN) - path specifying the location of the alias to be dropped.
# recurse (IN) - 1 iff $alias points to a directory to be dropped
# recursively.
#
# RETURNS
# Null.
########
sub asmcmdbase_rmalias_sql
{
my ($dbh, $gname, $alias, $recurse) = @_;
my ($ret, $qry);
if ($recurse == 1)
{ # Drop directory force! #
$qry = "alter diskgroup /*ASMCMD*/ \"$gname\" drop directory '$alias' force";
}
else
{ # Drop user alias! #
$qry = "alter diskgroup /*ASMCMD*/ \"$gname\" drop alias '$alias'";
}
$ret = asmcmdshare_do_stmt($dbh, $qry);
}
########
# NAME
# asmcmdbase_is_bal
#
# DESCRIPTION
# This routine constructs the SQL used to determine whether diskgroup number
# $gnum is currently rebalancing. It calls asmcmdshare_do_select() to
# execute it.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gnum (IN) - group number of the diskgroup in question.
#
# RETURNS
# String: 'Y' if diskgroup $gnum is currently rebalancing; 'N' otherwise.
########
sub asmcmdbase_is_rbal
{
my ($dbh, $gnum) = @_;
my ($sth); # SQL statement handler. #
my ($qry); # SQL query string. #
my ($row); # One row of query results. #
my ($rebal) = 'N'; # Return string; see RETURN above. #
$qry = 'select operation from v$asm_operation where group_number=?';
$sth = asmcmdshare_do_prepare($dbh, $qry);
$sth->bind_param(1,$gnum);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
$rebal = 'Y' if (defined ($row) && ($row->{'OPERATION'} eq 'REBAL'));
asmcmdshare_finish ($sth);
return $rebal;
}
########
# NAME
# asmcmdbase_get_alias_path
#
# DESCRIPTION
# This routine constructs the SQL used to retrieve either a system or a
# user alias name when given a group number and a file number. It calls
# asmcmdshare_do_select() to execute it. Finally, it calls
# asmcmdbase_find_sql() to retrieve the full path to the user or system
# alias in question.
#
# PARAMETERS
# dbh (IN) - initialized database handle, must be non-null.
# gnum (IN) - group number for the file in question.
# fnum (IN) - file number for the file in question.
# sys_cre (IN) - 'Y' if we query for system alias; 'N' if we query for user
# alias.
#
# RETURNS
# Full path to the request alias; 'none' if not found (only when sys_cre
# is 'N'.
#
# NOTES
#
########
sub asmcmdbase_get_alias_path
{
my ($dbh, $gnum, $fnum, $sys_cre) = @_;
my ($sth, $qry, $row);
my ($name); # Name of the retrieved alias. #
my ($par_id); # Parent index of the retrieved alias. #
my ($path); # Full path to the retrieved alias. #
my (@results); # Array of hashes: return-format of asmcmdshare_find_int(). #
$qry = 'select name, parent_index from v$asm_alias' .
' where group_number = ?' .
' and file_number = ? and system_created = ?';
$sth = asmcmdshare_do_prepare($dbh, $qry);
$sth->bind_param(1,$gnum);
$sth->bind_param(2,$fnum);
$sth->bind_param(3,$sys_cre,SQL_VARCHAR);
asmcmdshare_do_execute($sth);
$row = asmcmdshare_fetch($sth);
$name = $row->{'NAME'};
$par_id = $row->{'PARENT_INDEX'};
asmcmdshare_finish($sth);
# If sys_cre is 'N', then the alias may not exist. Return 'none'.
return 'none' unless (defined ($name));
# Retrieve the full path to the alias.
@results = asmcmdbase_find_sql($dbh, '+', $name, undef, $par_id, 0, 0);
$path = $results[0]->{'full_path'};
return defined $path ? $path : 'none';
}
########
# NAME
# asmcmdbase_get_ct
#
# DESCRIPTION
# This routine constructs the SQL used to fetch a list of row(s) from
# [g]v$[asm|ios]_client
#
# PARAMETERS
# dbh (IN) - database handle.
# gname (IN) - optional: limit select by this group number.
# global (IN) - flag to query global view
# inst (IN) - type of instance where the data is taken from.
#
# RETURNS
# An array containing zero or one or more rows from v$asm_client. The column
# values for each row are stored in a hash, the reference to which
# is indexed in the array.
# If inst is undefined, then asm instance is assumed.
########
sub asmcmdbase_get_ct
{
my ($dbh, $gname, $global, $inst) = @_;
my $gnum; # The group number for the diskgroup specified by $gname. #
my $qry;
my $row;
my $sth;
my @ct_list; # The return array of hashes; see RETURNS above. #
$inst = 'asm' unless(defined($inst));
if ($global)
{
$qry = 'select * from gv$'.$inst.'_client';
}
else
{
$qry = 'select * from v$'.$inst.'_client';
}
# Narrow select if $gname is specified.
if (defined ($gname))
{
$gnum = asmcmdshare_get_gnum_from_gname($dbh, $gname); # Get group num. #
if (!defined($gnum))
{
my (@eargs) = ($gname);
# ASMCMD-8001 "diskgroup '%s' does not exist or is not mounted"
asmcmdshare_error_msg(8001, \@eargs);
return @ct_list;
}
$qry = $qry . ' where group_number = ?';
}
$sth = asmcmdshare_do_prepare($dbh, $qry);
if (defined ($gnum))
{
$sth->bind_param(1,$gnum,SQL_INTEGER);
}
asmcmdshare_do_execute($sth);
# Fetch results row by row and storeeach row in %ct_info, and reference
# each %ct_info in @ct_list.
while (defined ($row = asmcmdshare_fetch($sth)))
{
my (%ct_info); # Allocate fresh hash for next row. #
if ($inst eq 'ios')
{
$ct_info{'group_number'} = '';
$ct_info{'status'} = '';
}
else
{
$ct_info{'group_number'} = $row->{'GROUP_NUMBER'};
$ct_info{'status'} = $row->{'STATUS'};
$ct_info{'group_name'} =
asmcmdshare_get_gname_from_gnum($dbh, $ct_info{'group_number'});
}
$ct_info{'instance_name'} = $row->{'INSTANCE_NAME'};
$ct_info{'db_name'} = $row->{'DB_NAME'};
if (!defined($ct_info{'group_name'}))
{
$ct_info{'group_name'} = '';
}
# New columns for 10gR2, so check for forward compatiblity.
if (asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'},
$ASMCMDGLOBAL_VER_10gR2) >= 0)
{
if ($inst eq 'ios')
{
$ct_info{'software_version'} = '';
$ct_info{'compatible_version'} = '';
}
else
{
$ct_info{'software_version'} = $row->{'SOFTWARE_VERSION'};
$ct_info{'compatible_version'} = $row->{'COMPATIBLE_VERSION'};
}
}
# If we want to see global information
if ($global)
{
$ct_info{'inst_id'} = $row->{'INST_ID'};
}
push (@ct_list, \%ct_info);
}
asmcmdshare_finish($sth);
return (@ct_list);
}
########
# NAME
# asmcmdbase_connect
#
# DESCRIPTION
# This routine initializes the database handle by establishing a connection
# to an ASM instance, either with a bequeath connection or by the listener.
#
# PARAMETERS
# usr (IN) - username of the connect string.
# pswd (IN) - password for the username.
# ident (IN) - the identifier part of the connect string.
#
# RETURNS
# An initialized database handle; undefined if connect fails.
#
# NOTES
# The connect string is in the form of <user>[/password][@connect_identifier].
# The identifier is optional. If no identifier is in the connect string,
# then we use the value of the ORACLE_SID environment variable as the
# identifier.
#
# In Flex ASM, ASMCMD will not necessarily connect to the local ASM instance
# if there is one running on the node nor does it depend on the value of
# ORACLE_SID environment variable. If Flex ASM is enabled, the connect string
# and credentials are queried using ASMCMDGetConnDetails and these are used
# to establish connection to any one of the ASM instances running in the
# cluster.
#
########
sub asmcmdbase_connect
{
my ($ident, $constr) = @_;
my ($usr, $pswd, $contype);
my ($dbh); # Database handle, to be initialized and returned. #
my ($driver); # Driver parameter for the DBI->connect() call. #
my ($host); # The hostname of the host where listener is run. #
my ($port); # The port where the listener is run. #
my ($sid); # The SID of the ASM instance we're connecting to. #
my (%session_mode); # bug-5402303, session attr for DBI->connect(). #
my (@eargs); # Error arguments for assert. #
my ($clus_mode); # The Cluster mode. #
my ($stor_access); # Storage Access Mode. #
my ($rc, $str); # Return value and Connect string. #
my ($creds); # Credentials which is an array of array's. #
my ($instance_name); # ASM instance to which ASMCMD is connected to. #
my (@result);
my ($discover) = $asmcmdglobal_hash{'discover'};
# Environment variable 'ORACLE_HOME' will be set, otherwise
# ASMCMD init code will bail out.
# directories in which kfod can be found.
my (@patharr) =("$ENV{'ORACLE_HOME'}/bin/",
"$ENV{'ORACLE_HOME'}/rdbms/bin/");
@result = asmcmdshare_execute_tool ("kfod",
".exe",
"op=getclstype",
\@patharr);
chomp($result[0]); # Remove the newline character at the end of the string #
# split the "[cluster mode] - [storage access mode]" information
($clus_mode, $stor_access) = split / - /, $result[0];
chomp($clus_mode);
asmcmdshare_trace(3, "cluster mode - $clus_mode", 'y', 'n');
my $logstr = "Printing the connection string \n";
# 25514391: If connecting via a member cluster, use sysdba privileges #
# instead of defaulting to sysasm. #
if ($clus_mode eq 'Client cluster')
{
$contype = 'sysdba';
}
else
{
$contype = $asmcmdglobal_hash{'contyp'};
}
# Verify connection admin type, sysdba or sysasm, and assign proper ora #
# session mode. (sysasm = 32768 or sysdba = 2) #
if (($contype =~ /^sysdba$/i))
{
$session_mode{'ora_session_mode'} = 2;
}
else
{
$session_mode{'ora_session_mode'} = 32768;
}
$session_mode{'PrintError'} = 0;
$driver = 'dbi:Oracle:';
# Use PERL2C interface to obtain connection string to guarantee we connect
# only to ASM instance. ORACLE_SID set to DBSID was allowing connection to
# DB Instance and operations were failing non-user-friendly.
# clus_mod - Flex mode disabled(or legacy), Flex mod enabled, Client
# cluster - in all these cases if --discover option specified
# obtain connection string using PERL2C.
# 23721855: When global_hash{target} is set to other value than 'ASM' means
# that the caller wants to retrieve information for the instance of the
# specified type. It is the caller's responsibility to set back this value
# to 'ASM' when it reconnects to the ASM instance.
if ($asmcmdglobal_hash{'target'} ne 'ASM' ||
$clus_mode eq 'ASM cluster : Flex mode disabled' && ( $discover == 0 ))
{
asmcmdshare_trace('3', "in flex mode disabled", 'y', 'n');
$logstr .="ENV{ORACLE_HOME} = <" . $ENV{'ORACLE_HOME'} . ">\n"
if defined($ENV{'ORACLE_HOME'});
$logstr .= "ENV{ORACLE_SID} = <" . "$ENV{'ORACLE_SID'}" . ">\n"
if defined($ENV{'ORACLE_SID'});
# ident will be valid only in case of remote connect
if (defined($ident) && $ident ne '')
{
my $i = rindex($ident , '.');
if($i ge 0)
{
$sid = substr($ident, $i+1);
$ident =~ s/\Q.$sid\E//g;
$host = $ident;
}
else
{
$sid = $ident;
}
$port = $asmcmdglobal_hash{'port'}
if (defined ($asmcmdglobal_hash{'port'}));
# Now we have the values for $host, $port, and $sid, finish constructing
# the $driver string.
if (defined ($host))
{
$driver .= 'host=' . $host;
$rhost = $host;
}
$rsid = $sid;
$rport = $port if (defined ($port)) ;
if (defined ($rport))
{
$driver .= ';port=' . $rport;
}
# No port specification needed by default; defaults to 1521. #
$driver .= ';sid=' . $rsid; # SID always required. #
$usr = $rusr;
$pswd = $rpswd;
}
}
elsif ((($clus_mode eq 'ASM cluster : Flex mode disabled') &&
($discover == 1)) ||
($clus_mode eq 'ASM cluster : Flex mode enabled') ||
($clus_mode eq 'Client cluster'))
{
undef &ASMCMDGetConnDetails;
my ($conn) = DynaLoader::dl_find_symbol($asmcmdglobal_hash{'asmperl'},
"XS_ASMCMDCLNT_kgfnGetConnDetails");
DynaLoader::dl_install_xsub("ASMCMDGetConnDetails", $conn);
# Get connection string and credentials from kgfnGetConnDetails using
# PerlToC API
($rc, $str, $creds) = ASMCMDGetConnDetails(0,
$asmcmdglobal_hash{'service'},
$asmcmdglobal_hash{'inst'},
'_asmcmd');
# incase of error, record the $rc & $str.
if ($rc != 0 )
{
asmcmdshare_trace (1,
"Error while fetching connection string for FlexASM".
" Error :$rc Errstr : $str\n",
'y',
'n');
return;
}
# KGFNGETCONNDETAILS_OVERFLOW - overflow of connection strings.
if ($rc == 1)
{
asmcmdshare_trace_msg(3, 9480);
return;
}
# KGFNGETCONNDETAILS_NONE - no connection string
if ($rc == 2)
{
asmcmdshare_trace_msg (3, 9481);
return;
}
# KGFNGETCONNDETAILS_OTHER - unknown issue.
if ($rc == -1)
{
asmcmdshare_trace_msg (3, 9482);
}
# 20402145: Parse the rhost, rport & rsid for remote copy
# operation - local->remote, remote->local.
if (defined($ident) && $ident ne '')
{
my $i = rindex($ident , '.');
asmcmdshare_trace(3, "rindex - $i", 'y', 'n');
if($i ge 0)
{
$rsid = substr($ident, $i+1);
$ident =~ s/\Q.$rsid\E//g;
$rhost = $ident;
asmcmdshare_trace(3, "rsid $rsid", 'y', 'n');
asmcmdshare_trace(3, "rhost $rhost", 'y', 'n');
}
else
{
$rsid = $ident;
asmcmdshare_trace(3, "rsid $rsid", 'y', 'n');
}
$rport = $asmcmdglobal_hash{'port'}
if (defined ($asmcmdglobal_hash{'port'}));
asmcmdshare_trace(3, "rport $rport", 'y', 'n');
}
$driver .= $str;
$usr = @{$$creds[0]}[1];
$pswd = @{$$creds[0]}[0];
asmcmdshare_trace(3, "driver $driver", 'y', 'n');
asmcmdshare_trace(3, "user $usr ", 'y', 'n') if defined ($usr);
}
elsif($clus_mode eq 'Unknown')
{
@eargs = ("Cluster Type is Unknown");
asmcmdshare_assert(0, \@eargs);
}
# Now try to connect! ora_session_mode => 2 means to connect as sysdba
$dbh = DBI->connect($driver, $usr, $pswd, \%session_mode);
$logstr .= "contype = <" . $contype . ">\n"
if defined($contype);
$logstr .= "driver = <" . $driver . ">\n" if defined $driver;
$logstr .= "instanceName = <" . $asmcmdglobal_hash{'inst'} . ">\n"
if defined $asmcmdglobal_hash{'inst'};
$logstr .= "usr = <" . $usr . ">\n" if defined $usr;
$logstr .= "port = <" . $port . ">\n" if defined $port;
$logstr .= "ServiceName = <" . $asmcmdglobal_hash{'service'} . ">\n"
if defined $asmcmdglobal_hash{'service'};
chomp $logstr;
asmcmdshare_trace(3, "$logstr", 'y', 'n');
if(!defined($dbh))
{
$$constr = " Driver="."$driver";
$$constr .=" User=".$usr if defined($usr);
$$constr .= " Session_mode=".$contype if defined($contype);
$$constr .= " ServiceName=" .$asmcmdglobal_hash{'service'}
if defined($asmcmdglobal_hash{'service'});
$$constr .= " port=$port" if defined($port);
asmcmdshare_trace(3, "Failed to connect to ASM instance", 'y', 'n');
asmcmdshare_trace(3, $DBI::errstr, 'y', 'n');
# If Flex ASM, try connecting using another set of credentials.
if (($clus_mode eq 'ASM cluster : Flex mode enabled') ||
($clus_mode eq 'Client cluster'))
{
my $i;
for ( $i = 1; (!defined($dbh) && ($i < @{$creds})); $i++)
{
$usr = @{$$creds[$i]}[1];
$pswd = @{$$creds[$i]}[0];
asmcmdshare_trace('3', "Connecting to ASM instance as User:"
." $usr", 'y', 'n');
$dbh = DBI->connect($driver, $usr, $pswd, \%session_mode);
asmcmdshare_trace(3, $DBI::errstr, 'y', 'n') if (!defined($dbh));
}
if(defined($dbh))
{
$instance_name = asmcmdshare_get_instance_name($dbh);
asmcmdshare_trace(3, "Successfully connected to Flex-ASM instance "
."$instance_name", 'y', 'n');
}
}
}
else
{
$instance_name = asmcmdshare_get_instance_name($dbh);
asmcmdshare_trace(3, "Successfully connected to " .
"$asmcmdglobal_hash{'target'} instance $instance_name",
'y', 'n');
}
return $dbh;
}
########
# NAME
# asmcmdbase_disconnect
#
# DESCRIPTION
# This routine disconnects from the ASM instance, given an initialized
# database handle.
#
# PARAMETERS
# dbh (IN) - initialized database handle.
#
# RETURNS
# Undefined if $dbh is undefined; the return value of $dbh->disconnect()
# upon success.
########
sub asmcmdbase_disconnect
{
my $dbh = shift;
return undef unless(defined $dbh);
return $dbh->disconnect;
}
##############################################################################
############################## Help Routines #################################
########
# NAME
# asmcmdbase_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.
########
sub asmcmdbase_get_asmcmd_cmds
{
return asmcmdshare_filter_invisible_cmds(%asmcmdbase_cmds);
}
##############################################################################
1;
OHA YOOOO