# Copyright (c) 2004, 2011, 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 ] [-p] [command] # # MODIFIED (MM/DD/YY) # shmubeen 06/12/11 - backport of 12538042 # moreddy 01/13/11 - Backport moreddy_bug-8667038 from main # moreddy 01/12/11 - Backport moreddy_bug-6969662 from main # pvenkatr 11/01/10 - Backport pvenkatr_bug-9778018 from main # 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 ]. # hqian 06/30/04 - Make code that uses BigInt work for both Perl 5.6.1 # and Perl 5.8.3; take out -c # 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_lsof # # 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_int # 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 # # 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 ); use strict; use DBI; use Getopt::Long qw(:config no_ignore_case bundling no_getopt_compat); use Math::BigInt; use asmcmdglobal; use asmcmdshare; #use Term::ReadKey; # Currently not bundled with Oracle.# use List::Util qw[min max]; use POSIX qw(:termios_h); ############################ Global Constants ############################### my (%asmcmdbase_cmds) = (cd => {wildcard => 'True', no_instance => 'True', }, cp => {wildcard => '', no_instance => 'True', flags => { 'r'=>'recursive', 'service=s'=> 'servicename', 'port=s'=>'portnum'} }, du => {wildcard => 'True', no_instance => 'True', flags => {'suppressheader'=> 'suppressHeaders'} }, find => {wildcard => 'True', no_instance => 'True', flags => {'type=s'=>'fileType'} }, help => { }, ls => {wildcard => 'True', no_instance => 'True', flags => {'l'=>'longListing', 'i'=>'interactive', 's'=>'size', 'd'=>'directory', 'reverse'=> 'reverseOrder', 't'=>'timeStamp', 'L'=>'fileReference', 'absolutepath'=> 'absolutepath', 'g'=>'global', 'suppressheader'=> 'suppressHeaders', 'permission'=> 'showPermission'} }, lsct => {no_instance => 'True', flags => {'g'=>'global', 'suppressheader'=> 'suppressHeaders'} }, lsdg => {no_instance => 'True', flags => {'discovery'=>'noCache', 'g'=>'global', 'suppressheader'=> 'suppressHeaders'} }, mkalias => {no_instance => 'True', }, mkdir => {no_instance => 'True', }, pwd => {no_instance => 'True', }, rmalias => {no_instance => 'True', flags => {'r'=>'recursive'} }, rm => {wildcard => 'True', no_instance => 'True', flags => {'r'=>'recursive', 'f'=>'force'} }, lsof => {no_instance => 'True', flags => {'dbname=s'=>'database', 'G=s'=>'diskGroup', 'C=s'=> 'clientInstanceName', 'suppressheader'=> 'suppressHeaders'} } ); my ($ASMCMDBASE_SPACE) = ' '; # Constant string for a space. # my ($ASMCMDBASE_SIXMONTH) = 183; # Number of days in six months. # my ($ASMCMDBASE_DATELEN) = 15; # 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' keyward # 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_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; 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); 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); my (@lsof_list); my ($k, $v, $h); my (%min_col_wid, $print_format, $printf_code, @what_print); # 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'); push (@from, 'x$kffof'); #filter disk group if (defined($args{'G'})) { #get disk group name $gnum = asmcmdshare_get_gnum_from_gname($dbh, $args{'G'}); if (!defined($gnum)) { return; } push (@where, "group_kffof = ". $gnum); } #filter database if (defined($args{'dbname'})) { push (@where, "dbname_kffof = '".$args{'dbname'}."'"); } #filter instance if (defined($args{'C'})) { push (@where, "instancename_kffof = '".$args{'C'}."'"); } push (@order, 'dbname_kffof'); push (@order, 'instancename_kffof'); push (@order, 'path_kffof'); $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where, \@order); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth); #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]/; $file_info{$k} = $v; $min_col_wid{$k} = max($min_col_wid{$k}, length($v)); } push (@lsof_list, \%file_info); } asmcmdshare_finish($sth); #create print format $print_format = ''; foreach (@what) { $print_format .= "%-$min_col_wid{$_}s "; } $print_format .= "\\n"; #print header if (!defined ($args{'suppressheader'}) ) { $printf_code = "printf \"$print_format\", "; @what_print = (); foreach (@what) { push (@what_print, "\'" . $asmcmdbase_lsof_header{$_} . "\'"); } $printf_code .= "(" . join (", ", @what_print) . ")"; eval $printf_code; } #print rows foreach $h (@lsof_list) { $printf_code = "printf \"$print_format\", "; @what_print = (); foreach (@what) { push (@what_print, "\'" . $h->{$_} . "\'"); } $printf_code .= "(" . join (", ", @what_print) . ")"; eval $printf_code; } } ######## # 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); # Check if number of non-option parameters are correct. if (@ARGV < 1) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # Process creation one path at a time. while (defined ($dir = shift (@ARGV))) { $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. # # 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 (%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); # Get option parameters, if any. $ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # Check if number of non-option parameters are correct. if (@ARGV == 0) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # 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 < @ARGV; $i++) { if ($ARGV[$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. while (defined ($alias_path = shift (@ARGV))) { # 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, $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 = <prepare($stmt); $ret = $sth->execute(); if (!defined($ret)) { asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y'); 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. $spprserr = 1 if (defined ($args{'r'})); %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, $gnum, undef, $par_id, $alias_name, undef, 0, 0); $is_dir = $entries2[$i]->{'alias_directory'}; $gname = asmcmdshare_get_gname_from_gnum ($dbh, $gnum); # Run SQL to remove an entry. asmcmdbase_rm_sql($dbh, $gname, $alias_path, $is_dir); } } } 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); # Check if number of non-option parameters are correct. if (@ARGV != 2) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $sys_a = shift (@ARGV); $usr_a = shift (@ARGV); $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'})); # Check if number of non-option parameters are correct. if (@ARGV == 0) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # Process each alias entry one at a time. (rmalias can take multiple alias # arguments. while (defined ($alias_path = shift (@ARGV))) { # 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); # Check if number of non-option parameters are correct. if (@ARGV != 0) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } 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); # Check if number of non-option parameters are correct. if (@ARGV != 1) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $dir = shift (@ARGV); # 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 ($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 = shift(@ARGV) if (@ARGV > 0); $alias = '' unless defined ($alias); # Defaults to '', or current dir. # # 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. asmcmdshare_get_subdirs($dbh, \@entry_list, $entry_info{'group_number'}, $entry_info{'reference_index'}, $entry_info{'parent_index'}, $entry_info{'name'}, undef, 0, $get_file_info); $entry_list[$i]->{'path'} = $entry_info{'path'}; $entry_list[$i]->{'name_print'} = $entry_info{'name'}; # If the entry is a file, process the file column values from v$asm_file. if ($entry_list[$i]->{'alias_directory'} eq 'N') { asmcmdbase_ls_process_file ($dbh, $entry_list[$i], \%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 '+'. 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 '. 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'}, \%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) { 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); # Check if number of non-option parameters are correct. if(@ARGV != 2) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # Get type if --type is set. # $type = $args{'type'} if (defined($args{'type'})); $path = shift(@ARGV); $search = shift (@ARGV); # Run the internal find routine. @match = asmcmdbase_find_int($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'); 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); # asmcmdbase_parse_int_args() return value. # 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. # # 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); # Check if number of non-option parameters are correct. if (@ARGV > 1) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $dir = shift (@ARGV); $dir = '' unless (defined ($dir)); # Find all system alias only under $dir. @match = asmcmdbase_find_int ($dbh, $dir, '*', undef, undef, 1, 0); return if (@match == 0); # 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'}, \%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; } # 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'})) { printf ("%7s%20s\n", 'Used_MB', 'Mirror_used_MB'); } printf ("%7s%20s\n", $uMB, $mirUMB); 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); # Check if number of non-option parameters are correct. if (@ARGV > 1) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } $gname = shift (@ARGV); # 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 (%args); # Argument hash used by getopts(). # my ($ret); # asmcmdbase_parse_int_args() return value. # my (@client); # Array of hashes of clients in v$asm_client. # my ($gname); # Group name, as entered by user. # my ($row) = ''; # One row of one client, for printing. # my (@header); my (@rowval); my ($iter); # Iteration variable for foreach() loops. # my ($global) = 0; # True iff query global view. # my (%min_col_wid); # Minimum column width to disply. # # 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); # Check if number of non-option parameters are correct. if (@ARGV > 1) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # Check for the -g flag. if (defined($args{'g'})) { $global = 1; } $gname = shift (@ARGV); @client = asmcmdbase_get_ct($dbh, $gname, $global); 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'); printf $row, @header if (!defined ($args{'suppressheader'})); } 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'); printf $row, @header if (!defined ($args{'suppressheader'})); } # 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'}); printf $row, @rowval; } else { # 10gR1 version. # push @rowval, ($iter->{'db_name'}, $iter->{'status'}, $iter->{'instance_name'}, $iter->{'group_name'}); printf $row, @rowval; } } 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); 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 { use Text::ParseWords; my ($dbh) = shift; # local auto-variables # my ($ret); 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_str); # target file/dir args # my ($tgt_path); my ($src_remote_inst) = 0; my ($tgt_remote_inst) = 0; my ($remote_conn_str) = ''; my ($src_dbh) = $dbh; my ($tgt_dbh) = $dbh; my ($src_rem_ct) = 0; #number of source remote connection parameters my ($driver); # get option parameters $ret = asmcmdbase_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); return unless defined ($ret); # if optional --serivce parameter is provide, 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" ; } 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'); # check if src & tgt parameters are correct # if (@ARGV < 2) { asmcmdbase_syntax_error($asmcmdglobal_hash{'cmd'}); return; } # last element is the target $tgt_str = pop(@ARGV); # remaining is the source @src_strs = @ARGV; # 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(8008, \@eargs); } if ($src_rem_ct == 1) { $src_remote_inst = 1; @arg_tokens = "ewords(':', 0, $src_strs[0]); $remote_conn_str = $arg_tokens[0]; push (@src_paths, $arg_tokens[1]); #get and parse connection data return 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 ) ; # 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; } if ( $tgt_remote_inst == 1 ) { @arg_tokens = "ewords(':', 0, $tgt_str); $remote_conn_str = $arg_tokens[0]; $tgt_path = $arg_tokens[1]; #get and parse connection data return 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 # $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; } asmcmdbase_check_insttype($remote_dbh); $src_dbh = $remote_dbh if ($src_remote_inst); $tgt_dbh = $remote_dbh if ($tgt_remote_inst); } # perform file copy and path completion asmcmdbase_do_file_copy($src_remote_inst, $tgt_remote_inst, $src_dbh, $tgt_dbh, $remote_conn_str, \@src_paths, $tgt_path); } ############################################################################## ################### 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 format. It 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. # 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 ' or 'ls -d ' # 2) 'ls ' # # In case 1), ls lists the specified alias entry itself, whereas in 2), ls # lists the contents of . 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 , and asmcmdbase_ls_subdir_print() calls asmcmdbase_ls_print(). ######## sub asmcmdbase_ls_get_subdirs { my ($dbh, $gnum, $par_id, $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, $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 ' or 'ls -d ' # 2) 'ls ' # # In case 1), ls lists the specified alias entry itself, whereas in 2), ls # lists the contents of . 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 # , 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 ($mon, $day, $time); my ($diff); # Difference of $min_col_wid_ref->{'date_print'} # # - $ASMCMDBASE_DATELEN # + 1 .# my (@print_args); # 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 " ; # In case the header length is longer than $ASMCMDBASE_DATELEN, # calculate difference and use it as minimum length of time or year. # Add 1 because default is at least one extra space, compensated by # one less space between the fields 'Time' and 'Sys'. $diff = $min_col_wid_ref->{'date_print'} - $ASMCMDBASE_DATELEN + 1; # MON DD HH:MI:SS or # MON DD YYYY $row_l .= "%-3s %2s %8s" . "%$diff" . "s "; # Month, day, and time/year. # $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'} .= '/'; } elsif (defined ($args_ref->{'l'})) { # Date values exists for files only. # ($mon, $day, $time) = split (/\s+/, $entry_ref->{'date_print'}); } if (($entry_ref->{'alias_directory'} eq 'Y') || (($entry_ref->{'system_created'} eq 'N') && (!defined ($args_ref->{'L'})))) { $entry_ref->{'type'} = $ASMCMDBASE_SPACE; $entry_ref->{'redundancy'} = $ASMCMDBASE_SPACE; $entry_ref->{'striped'} = $ASMCMDBASE_SPACE; $mon = $ASMCMDBASE_SPACE; $day = $ASMCMDBASE_SPACE; $time = $ASMCMDBASE_SPACE; $entry_ref->{'space'} = $ASMCMDBASE_SPACE; $entry_ref->{'block_size'} = $ASMCMDBASE_SPACE; $entry_ref->{'blocks'} = $ASMCMDBASE_SPACE; $entry_ref->{'bytes'} = $ASMCMDBASE_SPACE; $entry_ref->{'user'} = $ASMCMDBASE_SPACE; $entry_ref->{'group'} = $ASMCMDBASE_SPACE; $entry_ref->{'perm'} = $ASMCMDBASE_SPACE; } @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, $mon); push (@print_args, $day); push (@print_args, $time); push (@print_args, $ASMCMDBASE_SPACE); push (@print_args, $entry_ref->{'system_created'}); } if (defined($args_ref->{'s'})) # ls -s. # { push (@print_args, $entry_ref->{'block_size'}); push (@print_args, $entry_ref->{'blocks'}); push (@print_args, $entry_ref->{'bytes'}); push (@print_args, $entry_ref->{'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'}); printf ($row, @print_args); 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); # 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'); printf $hdr, @print_args; # 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->{'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 ($printf_code); # dynamically generated printf() code for eval(). # # 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{'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 printf() code based on user-specified flags. $printf_code = 'printf $row, ('; if (defined ($args_ref->{'g'})) { $printf_code .= '$ASMCMDBASE_LSDG_HDR_INSTID, '; } if (defined ($args_ref->{'l'})) { $printf_code .= '$ASMCMDBASE_LSDG_HDR_STATE, $ASMCMDBASE_LSDG_HDR_TYPE, $ASMCMDBASE_LSDG_HDR_REBAL, '; } if (defined ($args_ref->{'s'})) { $printf_code .= '$ASMCMDBASE_LSDG_HDR_SECTOR, $ASMCMDBASE_LSDG_HDR_BLOCK, $ASMCMDBASE_LSDG_HDR_AU, $ASMCMDBASE_LSDG_HDR_TMB, $ASMCMDBASE_LSDG_HDR_FMB, '; if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_10gR2) >= 0 ) { # Version is 10gR2 or later. # $printf_code .= '$ASMCMDBASE_LSDG_HDR_RMFMB, $ASMCMDBASE_LSDG_HDR_UFMB, $ASMCMDBASE_LSDG_HDR_ODISK, '; } # Column in 11gR2 or later. if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_11gR2) >= 0 ) { $printf_code .= '$ASMCMDBASE_LSDG_HDR_VOTING_FILE, '; } } # Always include the name. $printf_code .= '$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)) { eval($printf_code); asmcmdshare_trace(1, $@, 'y', 'y') if $@; } # Print one row at a time. for ($i = 0; $i < @dgroups; $i++) { $dgroups[$i]->{'name'} .= '/'; # Construct the printf() statement to eval() later. $printf_code = 'printf $row, ('; if (defined ($args_ref->{'g'})) { $printf_code .= q|$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'}); $printf_code .= q|$dgroups[$i]->{'state'}, $dgroups[$i]->{'type'}, $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; } $printf_code .= q|$dgroups[$i]->{'sector_size'}, $dgroups[$i]->{'block_size'}, $dgroups[$i]->{'allocation_unit_size'}, $dgroups[$i]->{'total_mb'}, $dgroups[$i]->{'free_mb'}, |; if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_10gR2) >= 0 ) { # Version is 10gR2 or later. # $printf_code .= q|$dgroups[$i]->{'required_mirror_free_mb'}, $dgroups[$i]->{'usable_file_mb'}, $dgroups[$i]->{'offline_disks'}, |; } if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_11gR2) >= 0 ) { $printf_code .= q|$dgroups[$i]->{'voting_files'}, |; } } # Always include the disk group name. $printf_code .= q|$dgroups[$i]->{'name'}) |; # Now evaluate the printf() expression. eval($printf_code); asmcmdshare_trace(1, $@, 'y', 'y') if $@; } return; } ######## # NAME # asmcmdbase_find_int # # 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_int() returns at most one # match. Use this feature when you have only an alias name by want the # full path to it. # # asmcmdbase_process_du() is the only routine that calls asmcmdbase_find_int() # with sys_non_dir_only set to true. ######## sub asmcmdbase_find_int { 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_int() 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=$index"; $sth = asmcmdshare_do_select($dbh, $sql); return undef if (! defined ($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. # print "You may delete multiple files and/or directories. \n" . 'Are you sure? (y/n) '; $response = ; chomp ($response); return 1 if ($response eq 'y'); return 0; } ######## # 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 # Zero on success; undefined 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, @norm_tgt_fname, @src_finfo); my ($src_fil, $tgt_fil); my ($src_sth, $tgt_sth); my ($ret); my (%norm); my (@eargs); my (@paths, @ref_ids, @par_ids, @dg_nums, @entry_list, $name); my ($i); my ($tgt_name); my (%invalid_files); my ($servicename) = $asmcmdglobal_hash {'service'}; #opt param for cp ## $asmcmdglobal_hash{'service'} is always set in ## asmcmdbase_process_cp. If no value speficified, the +ASM ## is assumed. # normalize and validate paths first # iterate through each source file foreach (@{$src_paths}) { my $src_path = $_; my ($src_name); #my $file_to_copy_name; my $tgt_fname = $tgt_path; my @tmp; my ( $src_hdl, $tgt_hdl, $plkSz, $fileSz, $openMode ); my ( $fileType, $blkSz, $fileName, $fileGenName ); my ( $dbname, $dbid, $tsname, $fno, $plid, $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 # if src_path doesn't start with '/', then it is an ASM relative path # every OS path must be absolute. if ( ($src_path !~ m'^[a-z]:') && ($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_path = $norm{'path'}->[0]; } else { # OS file case if ($src_remote_inst != 1 ) { # on remote host we cannot check for file existence if ( !(-r $src_path) ) { @eargs = ($src_path); asmcmdshare_error_msg(8014, \@eargs); next; } } } # add the normalized file name to the array push(@norm_src_fname, $src_path); #my ($fileType, $fileSz, $blkSz); $src_name = $src_path; $src_name =~ s,.*/(.*)$,$1,; # if target doesn't start with '/', then it is an ASM relative path # every OS path must be absolute if (( $tgt_path !~ m'^[a-z]:') && ($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 ) { # complete the path $tgt_path = $norm{'path'}->[0]; @paths = @{ $norm{'path'} }; @ref_ids = @{ $norm{'ref_id'} }; @par_ids = @{ $norm{'par_id'} }; @dg_nums = @{ $norm{'gnum'} }; $name = $paths[0]; $name =~ s,.*/(.*)$,$1,; asmcmdshare_get_subdirs($tgt_dbh, \@entry_list, $dg_nums[0], $ref_ids[0], $par_ids[0], $name, undef, 0, 1); # if the directory is a diskgroup or a directory if ( ($ref_ids[0] == $dg_nums[0] << 24 ) || $entry_list[0]->{'alias_directory'} eq 'Y' ) { $tgt_name = $tgt_path . '/' . $src_name; } elsif ( $entry_list[0]->{'alias_directory'} eq 'N' ) { $tgt_name = $tgt_path; } else { @eargs = ($tgt_name); asmcmdshare_error_msg(8014, \@eargs); next; } } else { $tgt_name = $tgt_path; } } else { # target is OS # if path exists it must be a directory if ( -d $tgt_path ) { $tgt_name = $tgt_path . '/' . $src_name; } else { $tgt_name = $tgt_path; } } push(@norm_tgt_fname, $tgt_name); } #get file information #very important to do dbms_diskgroup stuff in the end, since the connection #becomes useless after calling it foreach (@norm_src_fname) { my ($src_path) = $_; my ($fileType, $fileSz, $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 $fileType || $fileType < 1 ) { 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; } 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, $tsname, $dbid, $fno, $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 $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++) { $src_sth = $src_dbh->prepare(q{ begin dbms_diskgroup.copy('', '', '', :src_path, :src_ftyp, :src_blksz, :src_fsiz, '','','', :dst_path, 1); 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]); print STDOUT "copying $norm_src_fname[$i] -> $norm_tgt_fname[$i]\n"; $ret = $src_sth->execute(); if ( !defined($ret) ) { my (@eargs) = ($norm_src_fname[$i], $norm_tgt_fname[$i]); asmcmdshare_error_msg(8016, \@eargs); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y'); next; } } asmcmdshare_trace(3, "SUCCESS: Completed copying from local to local.", 'y', 'n'); } elsif ($src_remote_inst) # remote to local { my ($host, $port, $sid, $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); 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]); print STDOUT "copying $rhost:$norm_src_fname[$i] -> $norm_tgt_fname[$i]\n"; $ret = $src_sth->execute(); if ( !defined($ret) ) { my (@eargs) = ($norm_src_fname[$i], $norm_tgt_fname[$i]); 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'); next; } } asmcmdshare_trace(3, "SUCCESS: Completed copying from remote to local.", 'y', 'n'); } elsif ($tgt_remote_inst) # local to remote { my ($host, $port, $sid, $dst_str); $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); 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]); print STDOUT "copying $norm_src_fname[$i] -> $rhost:$norm_tgt_fname[$i]\n"; $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', 'n'); next; } } asmcmdshare_trace(3, "SUCCESS: Completed copying from local to remote.", 'y', 'n'); } return 0; } ############################################################################## ############################# 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); return 0; } ######## # NAME # asmcmdbase_getchr_noecho() # DESCRIPTION # This routine reads in usr character input without clear-text echo, thus # provide a simple way of pretecting usr privacy & security. # PARAMETERS # None. # RETURNS # The user entered password in a string. # NOTES ######## sub asmcmdbase_getchr_noecho { my $inputchr; my $term; my $fd_stdin; my $oldterm; my $echo = ECHO | ECHOK | ICANON; my $noecho = $echo & ~$echo; $fd_stdin = fileno(STDIN); $term = POSIX::Termios->new(); $term->getattr($fd_stdin); $oldterm = $term->getlflag(); # disable stdin clear-text echo so we can protect user passwd $term->setlflag($noecho); $term->setcc(VTIME, 1); $term->setattr($fd_stdin, TCSANOW); # read one character at a time for sync. purpose sysread(STDIN, $inputchr, 1); # enable stdin right away so other apps won't be affected $term->setlflag($oldterm); $term->setcc(VTIME, 0); $term->setattr($fd_stdin, TCSANOW); return $inputchr; } ######## # 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 }) && defined ($asmcmdbase_cmds{ $arg }{ wildcard }); } ######## # 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 # True if $arg is a command that can run without an ASM instance # or does not exist, false otherwise. # # 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; return !defined ($asmcmdbase_cmds{ $arg }) || !defined ($asmcmdbase_cmds{ $arg }{ no_instance }); } ######## # 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); #build the list of options to parse using GetOptions if($asmcmdbase_cmds{ $cmd }{ flags }) { foreach $key(keys %{$asmcmdbase_cmds{ $cmd }{ flags }}) { push(@string, $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 GetOptions() from the Getopt::Long package to parse arguments for # internal commands. These arguments are stored in @ARGV. if (!GetOptions($args_ref,@string)) { # Print correct command format if syntax error. # 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 # Exits if cmd is "asmcmd" or "asmcmd_no_conn_str"; 1 if another command # that 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; # Get the syntax for non -c version of asmcmd if -c is deprecated. $cmd = 'asmcmd_no_conn_str' if (! $ASMCMDGLOBAL_USE_CONN_STR && ($cmd eq 'asmcmd')); $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_no_conn_str' && $cmd ne 'asmcmd'); print STDERR 'usage: ' . $cmd_syntax . "\n"; print STDERR 'help: help ' . $cmd_print_name . "\n"; # Can't proceed if asmcmd has bad syntax. # exit -1 if (($cmd eq 'asmcmd') || ($cmd eq 'asmcmd_no_conn_str')); $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); if (!defined ($ret)) { asmcmdshare_trace(1, $DBI::errstr, 'y', 'y'); if ($asmcmdglobal_hash{'mode'} eq 'n') { $asmcmdglobal_hash{'e'} = -1; } } 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, $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); asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); if ($asmcmdglobal_hash{'mode'} eq 'n') { $asmcmdglobal_hash{'e'} = -1; } 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); if (!defined ($ret)) { asmcmdshare_trace(1, $DBI::errstr, 'y', 'y'); if ($asmcmdglobal_hash{'mode'} eq 'n') { $asmcmdglobal_hash{'e'} = -1; } } 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); if (!defined ($ret)) { asmcmdshare_trace(1, $DBI::errstr, 'y', 'y'); if ($asmcmdglobal_hash{'mode'} eq 'n') { $asmcmdglobal_hash{'e'} = -1; } } } ######## # 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=' . $gnum; $sth = asmcmdshare_do_select($dbh, $qry); $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_int() 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=' . $gnum . ' and file_number=' . $fnum . ' and system_created=\'' . $sys_cre . '\''; $sth = asmcmdshare_do_select($dbh, $qry); $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_int ($dbh, '+', $name, undef, $par_id, 0, 0); $path = $results[0]->{'full_path'}; return $path; } ######## # NAME # asmcmdbase_get_ct # # DESCRIPTION # This routine constructs the SQL used to fetch a list of row(s) from # v$asm_client or gv$asm_client. # # PARAMETERS # dbh (IN) - database handle. # gname (IN) - optional: limit select by this group number. # # 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. ######## sub asmcmdbase_get_ct { my ($dbh, $gname, $global) = @_; my ($sth, $qry, $row); my (@ct_list); # The return array of hashes; see RETURNS above. # my ($gnum); # The group number for the diskgroup specified by $gname. # if ($global) { $qry = 'select * from gv$asm_client'; } else { $qry = 'select * from v$asm_client'; } # Narrow select if $gname is specified. if (defined ($gname)) { $gnum = asmcmdshare_get_gnum_from_gname($dbh, $gname); # Get group num. # return @ct_list unless (defined ($gnum)); # Group must exist. # $qry = $qry . ' where group_number=' . $gnum; } $sth = asmcmdshare_do_select($dbh, $qry); # 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. # $ct_info{'group_number'} = $row->{'GROUP_NUMBER'}; $ct_info{'instance_name'} = $row->{'INSTANCE_NAME'}; $ct_info{'db_name'} = $row->{'DB_NAME'}; $ct_info{'status'} = $row->{'STATUS'}; $ct_info{'group_name'} = asmcmdshare_get_gname_from_gnum($dbh, $ct_info{'group_number'}); # New columns for 10gR2, so check for forward compatiblity. if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_10gR2) >= 0 ) { $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 [/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. # ######## 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. # $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:'; #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'}) ); $port = 1521 if( !defined ($port)); $rsid = $sid; $rport = $port if (defined ($port)); # Now we have the values for $host, $port, and $sid, finish contructing # the $driver string. if (defined ($host)) { $driver .= 'host=' . $host; $rhost = $host; } 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; } # Now try to connect! ora_session_mode => 2 means to connect as sysdba $dbh = DBI->connect($driver, $usr, $pswd, \%session_mode); if(!defined($dbh)) { $$constr = " Driver="."$driver"; $$constr .=" User=".$usr if defined($usr); $$constr .= " Password=". $pswd if defined($pswd); $$constr .= " Session_mode=".$contype if defined($contype); $$constr .= " ServiceName=" .$asmcmdglobal_hash{'service'} if defined($asmcmdglobal_hash{'service'}); $$constr .= " port=$port" if defined($port); } 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_print_cmds(sort(keys %asmcmdbase_cmds)); } ############################################################################## 1;