Edit D:\app\Administrator\product\11.2.0\dbhome_1\LIB\asmcmdshare.pm
# Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved. # # NAME # asmcmdshare - ASM CoMmanD line interface (Shared Functionality Module) # # DESCRIPTION # ASMCMD is a Perl utility that provides easy nagivation of files within # ASM diskgroups. This module provides some basic functionalities # that are shared by multiple other modules, such as path, normalization, # SQL access, etc. # # NOTES # usage: asmcmdcore [-p] [command] # # MODIFIED (MM/DD/YY) # sanselva 06/26/09 - 'ls -L --permission' issue, include diskgroup_number # in usergroupnumber2name # sanselva 06/03/09 - add asmcmdglobal_deprecated_options # sanselva 05/13/09 - corrected invalid column in usergroupnumber2name # heyuen 09/19/08 - fix signal handler for infinite commands # heyuen 08/26/08 - add voting file # heyuen 07/28/08 - add commands array, fix time format for debug # heyuen 05/22/08 - add get_ugnum_from_ugname # heyuen 04/15/08 - move unix_os table # heyuen 03/30/08 - enable ls -p # heyuen 11/08/07 - add do_construct_select # heyuen 09/12/07 - flush print line # heyuen 08/10/07 - refresh from main # hqian 06/04/07 - Fix three stragglers that are still using # v$asm_diskgroup (should use _stat) # heyuen 05/25/07 - add return codes for errors # dfriedma 05/24/07 - Remove unbalanced column # heyuen 04/17/07 - untaint the file to log debugging info # hqian 03/05/07 - Add asmcmdshare_version_cmp; update version checks # hqian 03/02/07 - modify asmcmdshare_get_dg to include _stat and gv$ # options # hqian 07/20/06 - #5397026: new asmcmdglobal_no_instance_callbacks # hqian 06/15/06 - Move asmcmdbase_ls_calc_min_col_wid to this module # hqian 01/25/06 - Split off asmcmdshare.pm from asmcmdshare.pm # hqian 01/19/06 - More modularization # hqian 01/18/06 - #4939032: remove the main() and shell() commands # hqian 01/18/06 - #4939032: format asmcmdbase.pm into a module # hqian 01/18/06 - Rename asmcmdcore to asmcmdbase.pm, inherit history # hqian 01/18/06 - #4939032: split up asmcmdcore into modules # hqian 07/19/05 - Remove RCS header # hqian 06/23/05 - #4450221: support wildcards for CD # hqian 05/18/05 - Mention 'missing view attributes' in help ls # hqian 05/03/05 - #4329688: improve SQL efficiency # hqian 04/13/05 - ls_get_file_info() -> ls_process_file() # hqian 04/08/05 - Improve implementation of ls # hqian 04/08/05 - Improve help documentation # hqian 04/07/05 - LRG 1843355: include seconds in mod-time # hqian 04/01/05 - #4261342: use asmcmd messages for certain errors # hqian 02/28/05 - #4204122: change NLS date format for minute to 'MI' # hqian 10/27/04 - hqian_asmcmd_13306_linux_3 # hqian 10/19/04 - Rename asmcmd0 to asmcmdcore # hqian 08/03/04 - hqian_asmcmd_13306_linux_2 # hqian 07/28/04 - Add % as wildcard char in addition to *. # hqian 07/13/04 - Add implementation of find [-t <type>]. # hqian 06/30/04 - Make code that uses BigInt work for both Perl 5.6.1 # and Perl 5.8.3; take out -c <connect_string> # functionality. # hqian 06/29/04 - Fix 10gR1 compatibility issues; fix alias name # case-sensitive bug, should be case insensitive # but case retentive. # hqian 06/25/04 - Rename the main program from asmcmd to asmcmd0, so # that we can name the wrapper script asmcmd; rename # global constants, global variables, and function # names so that they are prefixed by 'asmcmd0_', # as per coding style standards; fix ORA-15128 bug. # hqian 06/23/04 - Inaccurate error message bug: add error message # 8004, do not print error when '*' matches nothing; # fix rm -rf * double error message bug; fix find # diskgroup bug; fix print header in empty directory # bug; fix space in alias name bug. # hqian 06/22/04 - Give the option to turn off the functionality of # the -c flag; add constants for better code design. # hqian 06/09/04 - Fix bugs; improve comments. # hqian 06/07/04 - Organize code for better maintenance; code review # changes (suggested by Dave Friedman). # hqian 06/01/04 - Fix some bugs. # hqian 05/24/04 - Implement rm [-rf] and rm *; some code review fixes. # hqian 05/20/04 - Implement help, instance_type security check, # - connection error checks, and debug mode. # hqian 05/18/04 - Implement ls flags and lsct. # hqian 05/14/04 - hqian_asmcmd_13306 # hqian 04/21/04 - Creation # # # ############################################################################# # ############################ Functions List ################################# # # Parameter Parsing Routines # asmcmdshare_is_wildcard_cmd # # Error Routines # asmcmdshare_error_msg # asmcmdshare_signal_exception # asmcmdshare_assert # asmcmdshare_print_debug_info # asmcmdshare_signal_handler # # Normalization Routines # asmcmdshare_normalize_path # asmcmdshare_validate_path # asmcmdshare_validate_path_recurse # asmcmdshare_validate_dir # asmcmdshare_validate_dg # asmcmdshare_make_absolute # asmcmdshare_cdup_update_ids # # Misc Routines # asmcmdshare_ls_calc_min_col_wid # asmcmdshare_version_cmp # asmcmdshare_get_asm_version # # SQL Routines # asmcmdshare_cur_julian_date # asmcmdshare_get_insttype # asmcmdshare_get_file # asmcmdshare_get_subdirs # asmcmdshare_get_subdir_simple # asmcmdshare_run_find_sql # asmcmdshare_get_par_id # asmcmdshare_get_dg # asmcmdshare_get_redund # asmcmdshare_get_gnum_from_gname # asmcmdshare_get_gname_from_gnum # asmcmdshare_do_select # asmcmdshare_fetch # asmcmdshare_finish # asmcmdshare_do_stmt ############################################################################# package asmcmdshare; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(asmcmdshare_is_wildcard_cmd asmcmdshare_is_no_instance_cmd asmcmdshare_error_msg asmcmdshare_signal_exception asmcmdshare_assert asmcmdshare_print_debug_info asmcmdshare_signal_handler asmcmdshare_normalize_path asmcmdshare_make_absolute asmcmdshare_cur_julian_date asmcmdshare_ls_calc_min_col_wid asmcmdshare_version_cmp asmcmdshare_get_asm_version asmcmdshare_get_insttype asmcmdshare_get_file asmcmdshare_get_subdirs asmcmdshare_get_subdir_simple asmcmdshare_run_find_sql asmcmdshare_get_par_id asmcmdshare_get_dg asmcmdshare_get_redund asmcmdshare_get_gnum_from_gname asmcmdshare_get_gname_from_gnum asmcmdshare_do_construct_select asmcmdshare_do_select asmcmdshare_fetch asmcmdshare_finish asmcmdshare_do_stmt asmcmdshare_getpswd asmcmdshare_getchr_noecho asmcmdshare_usergroupnumber2name asmcmdshare_usernumber2name asmcmdshare_get_ugnum_from_ugname asmcmdshare_print_cmds asmcmdshare_check_option_consistency asmcmdshare_handle_deprecation %asmcmdshare_unix_os ); use strict; use DBI; use Getopt::Long qw(:config no_ignore_case bundling no_getopt_compat); use asmcmdglobal qw(%asmcmdglobal_hash %asmcmdglobal_options %asmcmdglobal_deprecated_options @asmcmdglobal_is_wildcard_callbacks @asmcmdglobal_syntax_error_callbacks @asmcmdglobal_no_instance_callbacks @asmcmdglobal_error_message_callbacks @asmcmdglobal_signal_exception_callbacks $ASMCMDGLOBAL_WCARD_CHARS $ASMCMDGLOBAL_VER_10gR1 $ASMCMDGLOBAL_VER_10gR2 $ASMCMDGLOBAL_VER_11gR1 $ASMCMDGLOBAL_VER_11gR2 ); use POSIX qw(:termios_h); ############################ Global Constants ############################### my ($ASMCMDSHARE_CSET) = '[\w .\-#$]'; # C-set for alphanumeric, '_', ' ', # # '.', '-', '#', '$' . # my ($ASMCMDSHARE_N_CSET) = '[^\w .\-#$]'; # Char-set for all chars except # # those in $ASMCMDSHARE_CSET. # my ($ASMCMDSHARE_CSET_W_WCARD) = '[\w %*.\-#$]'; # Char-set for all # # $ASMCMDSHARE_CSET chars plus $WCARD_CHARS. # my ($ASMCMDSHARE_N_CSET_W_WCARD) = '[^\w %*.\-#$]'; # C-set for all except # # $ASMCMDSHARE_CSET_W_WCARD. # my ($ASMCMDSHARE_MAXPASSWD) = 256; # Max length of user passwd input # # List of possible platforms. our (%asmcmdshare_unix_os) = ( aix => 'aix', bsdos => 'bsdos', dgux => 'dgux', dynixptx => 'dynixptx', freebsd => 'freebsd', linux => 'linux', hpux => 'hpux', irix => 'irix', openbsd => 'openbsd', dec_osf => 'dec_osf', sco_sv => 'sco_sv', svr4 => 'svr4', unicos => 'unicos', unicosmk => 'unicosmk', solaris => 'solaris', sunos => 'sunos', ); ######################### Parameter Parsing Routines ######################### ######## # NAME # asmcmdshare_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 # 1 if $arg is a command that can take wildcards as part of its argument, # 0 otherwise. # # NOTES # This routine calls the callbacks from each module to check if $arg # supports the use of wildcards. It asserts that the command is found # in only one module. ######## sub asmcmdshare_is_wildcard_cmd { my ($arg) = shift; my ($module); my ($use_wildcard) = 0; my ($count) = 0; my (@eargs); foreach $module (@asmcmdglobal_is_wildcard_callbacks) { if ($module->($arg)) { $use_wildcard = 1; $count++; } } # Assert here that 0 <= count <= 1. @eargs = ("asmcmdshare_is_wildcard_cmd_05", $arg, $count); asmcmdshare_assert( (($count >= 0) && ($count <= 1)), \@eargs); return $use_wildcard; } ######## # NAME # asmcmdshare_is_no_instance_cmd # # DESCRIPTION # This routine is shared/general routine that determines if a command # can run without an ASM instance. # # PARAMETERS # arg (IN) - user-entered command name string. # # RETURNS # 1 if $arg is a command that can run without an ASM instance or # does not exist, 0 otherwise. # # NOTES # This routine calls the callbacks from each module to check if $arg # can run without an ASM instance. It asserts that the command is found # in only one module. ######## sub asmcmdshare_is_no_instance_cmd { my ($arg) = shift; my ($module); my ($no_instance_necessary) = 1; my ($count) = 0; my (@eargs); foreach $module (@asmcmdglobal_no_instance_callbacks) { if (!$module->($arg)) { $no_instance_necessary = 0; last; } } return $no_instance_necessary; } ############################# Error Routines ################################# ######## # NAME # asmcmdshare_error_msg # # DESCRIPTION # This function provides the main interface for recorded errors. All # modules must call this function to record an error. # # PARAMETERS # err_num (IN) - ASMCMD internal error number. # args_ref (IN) - (Optional) Reference to array of error arguments # # RETURNS # Null. # # NOTES # This function calls the callbacks for other modules. These callbacks # are referenced in asmcmdglobal::asmcmdglobal_error_message_callbacks. # # The error arguments are defined by each module. Usually, each error # type has a fixed number of error arguments that are displayed. ######## sub asmcmdshare_error_msg { my ($err_num, $args_ref) = @_; my ($module); my (@eargs); # Array of error arguments. # # Assert that $err_num is not within 8000-9400, inclusive. @eargs = ("asmcmdshare_error_msg_05", $err_num); asmcmdshare_assert( (($err_num > 8000) && ($err_num < 9400)) , \@eargs); foreach $module (@asmcmdglobal_error_message_callbacks) { $module->($err_num, $args_ref); $|++; } if ($asmcmdglobal_hash{'mode'} eq 'n') { $asmcmdglobal_hash{'e'} = -1; $|++; } return; } ######## # NAME # asmcmdshare_signal_exception # # DESCRIPTION # This function provides the main interface for signaled exceptions. # All modules must call this function to record an error. # # PARAMETERS # exception_num (IN) - ASMCMD internal error/exception number. # args_ref (IN) - (Optional) Reference to array of error arguments # # RETURNS # Never returns; always exits 1. # # NOTES # Only call this routine for exceptions. This routine always exits 1. # # This function calls the callbacks for other modules. These callbacks # are referenced in asmcmdglobal::asmcmdglobal_signal_exception_callbacks. # # The error arguments are defined by each module. Usually, each error # type has a fixed number of error arguments that are displayed, if the # error is an external error. If the error is internal, then arbitrary # number of arguments can be included. ######## sub asmcmdshare_signal_exception { my ($exception_num, $args_ref) = @_; my ($module); # Assert that $exception_num is within 8200-8299, inclusive. if (($exception_num < 8000) || ($exception_num > 9400)) { die "asmcmd: 8202: [asmcmdshare_signal_exception_05] [$exception_num]\n"; } foreach $module (@asmcmdglobal_signal_exception_callbacks) { $module->($exception_num, $args_ref); } # All exceptions end session. exit 1; } ######## # NAME # asmcmdshare_assert # # DESCRIPTION # This function assert that first argument is true, or signals exception. # # PARAMETERS # is_true (IN) - assert that this argument is TRUE. # args_ref (IN) - (Optional) Reference to array of assert arguments # # RETURNS # Null if is_true is TRUE; signals internal error otherwise. # # NOTES # The assert error arguments get displayed when the error is signaled. The # following is the convention: # argument 0 - the function name plus a number, e.g. asmcmdshare_assert_05 # argument 1 and onward - values of variables that are being evaluated # for truth. ######## sub asmcmdshare_assert { my ($is_true, $args_ref) = @_; # Assert that this is true or signal exception. if (!$is_true) { asmcmdshare_signal_exception(8202, $args_ref); } return; } ######## # NAME # asmcmdshare_print_debug_info # # DESCRIPTION # This routine prints debug information to the file $file. # # PARAMETERS # file (IN) - database handle. # output (IN) - the desired information to be printed. # # RETURNS # Null. # # NOTES # The functionality of this routine is for Oracle internal and support # debugging only. It's not intended for end-users. This functionality # is not part of the ASMCMD Functional Specificationl. # # $output is usually a SQL statement that is run. Recording it helps to # see if there is any errors in the SQL itself, e.g. in the case when # there's SQL error. # # Information is recorded in the format: # [$output]<tab>$time # one entry per line. # # If unable to open file, this routine simply notes to that effect in # STDERR; it does not die. ######## sub asmcmdshare_print_debug_info { my ($file, $output) = @_; my ($time) = scalar localtime(); #untaint the input file name ($file) = $file =~ m/(.*)/; if (! open (FILE, ">>$file")) { print STDERR "asmcmd: can't open $file for debug write.\n"; return; } print FILE "[$time] $output\n"; close (FILE); return; } ######## # NAME # asmcmdshare_signal_handler # # DESCRIPTION # This routine catches and handles OS signals. # # PARAMETERS # sigtype (IN) - string: type of signal caught. # # RETURNS # Null. # # NOTES # Currently, this routine catches only SIGINT. ######## sub asmcmdshare_signal_handler { my ($sigtype) = shift; if ($sigtype eq 'INT') { if ($asmcmdglobal_hash{'running'} == 1) { $asmcmdglobal_hash{'running'}=0; return; } if (defined ($asmcmdglobal_hash{'sth'})) { $asmcmdglobal_hash{'sth'}->cancel; asmcmdshare_finish($asmcmdglobal_hash{'sth'}); $asmcmdglobal_hash{'sth'} = undef; } print STDERR "asmcmd: caught the interrupt signal; exiting\n"; exit 1; } } ############################################################################## ########################### Normalization Routines ########################### # NOTE: # ASMCMD defines the parent and reference indices for the directory # '+' to be both -1. ASMCMD defines the reference index of a diskgroup # to be (group_number * 2^24) and its parent index to be -1. # ######## # NAME # asmcmdshare_normalize_path # # DESCRIPTION # This routine is the top-level normalization routine. It calls # asmcmdshare_make_absolute() to get an absolute path to $path, if it's not # already absolute. It then calls asmcmdshare_validate_path() to check if # every directory level of the path exists. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # path (IN) - the path to be normalized. # spprserr (IN) - SuPPReSs ERRor: 1 shows no errors, 0 shows error. # ret_ref (OUT) - reference to return flag: 0 if path normalized; -1 if # path failed to normalize. # # RETURNS # A hash of references to four parallel arrays: # $ret{'path'} - reference to array of normalized paths. # $ret{'ref_id'} - reference to array of reference indices for the files # or directories specified by the paths. # $ret{'par_id'} - reference to array of parent indices for the files or # directories specified by the paths. # $ret{'gnum'} - reference to array of group numbers for the files or # directories specified by the paths. # # NOTES # ASMCMD defines the reference index, the parent index, and the group # number of '+' to be -1. ASMCMD also defines the reference index of a # diskgroup to be (group_number * 2^24). ######## sub asmcmdshare_normalize_path { my ($dbh, $path, $spprserr, $ret_ref) = @_; my ($full_path); # Absolute path. # my (%ret); # Hash of references to parallel arrays; see RETURN above. # my (@paths); # Array of normalized path(s), plural if using wildcards. # my (@ref_ids); # Parallel array of reference indices. # my (@par_ids); # Parallel array of parent indices, parallel to @paths. # my (@dg_nums); # Parallel array of diskgroup numbers for the paths. # $full_path = asmcmdshare_make_absolute($path); $full_path =~ s/\/{2,}/\//g; # Remove all duplicate forward slashes. # $full_path =~ s,^\+/,\+,; # '+/' should become just '+'. # $$ret_ref = 0; # Zero by default. # # If the full path is '+', then there is no need to call # asmcmdshare_validate_path(). ASMCMD defines the reference and parent # indices of '+' to be -1. if ($full_path eq '+') { push (@paths, '+'); push (@ref_ids, -1); push (@par_ids, -1); push (@dg_nums, -1); $ret{'path'} = \@paths; $ret{'ref_id'} = \@ref_ids; $ret{'par_id'} = \@par_ids; $ret{'gnum'} = \@dg_nums; return %ret; } # Validate the path. %ret = asmcmdshare_validate_path ($dbh, $full_path, $spprserr); # Nothing returned; then set -1. # $$ret_ref = -1 if (! defined ($ret{'ref_id'}) || (@{ $ret{'ref_id'} } == 0)); return %ret; } ######## # NAME # asmcmdshare_validate_path # # DESCRIPTION # This routine is a wrapper routine that calls # asmcmdshare_validate_path_recurse(). It pre-processes the parameters # before calling asmcmdshare_validate_path_recurse(). # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # work_path (IN) - path to be validated; must be absolute path. # spprserr (IN) - SuPPReSs ERRor: 1 shows no errors, 0 shows error. # # RETURNS # A hash of references to four parallel arrays: # $ret{'path'} - reference to array of validated paths. # $ret{'ref_id'} - reference to array of reference indices for the files # or directories specified by the paths. # $ret{'par_id'} - reference to array of parent indices for the files or # directories specified by the paths. # $ret{'gnum'} - reference to array of group numbers for the files or # directories specified by the paths. # # NOTES # ######## sub asmcmdshare_validate_path { my ($dbh, $work_path, $spprserr) = @_; my (%ret); # Hash of references to parallel arrays; see RETURN above. # my ($cont) = 0; # Whether to continue loop after # # recursive call. # my ($wpath_upd) = ''; # Used to update $work_path in a lower # # level from a higher level recursive # # call when the call completes before # # $work_path is fully parsed, i.e. # $cont == 1. # my ($i); $work_path =~ s,^\+,,; # Remove '+' for asmcmdshare_validate_path_recurse. # $asmcmdglobal_hash{'nfnd'} = 0; # Must reset not found flag at start. # %ret = asmcmdshare_validate_path_recurse ($dbh, $work_path, \$cont, \$wpath_upd, -1, -1, 0, $spprserr); # Now we put the '+' back for every path that's returned. if (defined ($ret{'path'})) { for ($i = 0; $i < @{ $ret{'path'} }; $i++) { $ret{'path'}->[$i] = '+' . $ret{'path'}->[$i] unless ($ret{'path'}->[$i] =~ m,^\+,); } } return %ret; } ######## # NAME # asmcmdshare_validate_path_recurse # # DESCRIPTION # This recursive routine parses every level of $work_path to check if it # is a valid entry in v$asm_alias. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # work_path (IN) - abolute path to be validated; no '+' prefix. # cont_ref (IN/OUT) - internal state for recursive function: 1 means # to continue while loop after recursive call # returns; 0 means to terminate loop after its # return. # wpath_upd_ref (OUT) - the unparsed portion of $work_path passed back # to the calling function by the recursively # called function; this scenario can happen when # in the scenario of having '*/..' in a path; # $cont_ref must be set to 1 in conjunction, and # the calling function will finish parsing # $work_path. # cur_ref_id (IN) - the reference index of the level of directory # that is currently processed # cur_par_id (IN) - the parent index of the level of directory # that is currently processed # level (IN) - the level of recursive call; top level starts # at 0; $level increments by 1 for each new level # of recursive call. # spprserr (IN) - SuPPReSs ERRor: 1 shows no errors, 0 shows error. # # RETURNS # A hash of references to four parallel arrays: # $ret{'path'} - reference to array of validated paths. # $ret{'ref_id'} - reference to array of reference indices for the files # or directories specified by the paths. # $ret{'par_id'} - reference to array of parent indices for the files or # directories specified by the paths. # $ret{'gnum'} - reference to array of group numbers for the files or # directories specified by the paths. # # NOTES # This routine does not call itself recursively, but calls # asmcmdshare_validate_dir() and asmcmdshare_validate_dg(), which call # asmcmdshare_validate_path_recurse() recursively. If a level of directory is # a diskgroup, we call asmcmdshare_validate_dg(); if a level of directory is # an alias directory, we call asmcmdshare_validate_dir(). # # This routine handles all special cases that a path can have, including # the pseudo-directories '.' and '..', and the wildcard '*'. An interesting # and special scenario may be '+dgroup/dir/*/../../dir', which should # normalize to '+dgroup/dir' if '+dgroup/dir' exists and if at least one # sub-directory exists under '+dgroup/dir'. ######## sub asmcmdshare_validate_path_recurse { my ($dbh, $work_path, $cont_ref, $wpath_upd_ref, $cur_ref_id, $cur_par_id, $level, $spprserr) = @_; my (@subdirs); # Array of tokenized levels of directories, obtained from # # $work_path by separating by the '/'. # my ($dir); # One level of directory, taken from @subdirs. # my (%ret); # Hash of references to parallel arrays; see RETURN above. # my (%empty); # Empty hash equivalent of %ret, used when validation fails. # my (@vld_path_a); # Array of validated directory tokens from $vld_path. # my ($vld_path) = ''; # String for the subset of $work_path that's # # validated so far. # my ($wpath_cpy) = '+'. $work_path; # Copy of $work_path that has '+' # # prefix added. # my ($wpath_slash); # Copy of $work_path that has '/' # # prefix added. # my ($vld_path_slash); # Copy of $vld_path with '/' suffix added. # my (@eargs); # Array of error arguments. # @subdirs = split (/\//, $work_path); # Tokenize $work_path by '/'. # # We validate the path one directory level at a time, starting from the top # level directory. while (defined ($dir = shift(@subdirs))) { $work_path = join ('/', @subdirs); # $work_path is not stripped of $dir. # next if ($dir eq '.'); if ($dir eq '..') { if (@vld_path_a > 0) { pop (@vld_path_a); $vld_path = join ('/', @vld_path_a); # Call asmcmdshare_cdup_update_ids() to shift the indices up a level. ($cur_ref_id, $cur_par_id) = asmcmdshare_cdup_update_ids($dbh, $cur_par_id); # Update indices here. $ret{'path'}->[0] = $vld_path; $ret{'ref_id'}->[0] = $cur_ref_id; $ret{'par_id'}->[0] = $cur_par_id; $ret{'gnum'}->[0] = $asmcmdglobal_hash{'gnum'}; next; } else { # @vld_path_a <= 0 # @vld_path_a has 0 elements here, so either we're in '+' or we'll # in a recursive call right after parsing a '*/..'. if ($level > 0) { # If we're here that means this scenario: # cdup in case of +dgName/dir/*/../ . . . # Here is the special case where a recursive call can terminate prior # to $work_path being entirely parsed. Thus, the calling function # needs to continue the loop after recursion returns. Set OUT # values below so the caller knows to continue to parse the # remainder of $work_path that's not finished here. $$cont_ref = 1; $$wpath_upd_ref = $work_path; return %empty; } # Already in '+'; cd '..' in '+' is still '+'. next; } } elsif ($dir =~ /$ASMCMDSHARE_N_CSET_W_WCARD/) { # Bad charset, no match. if ($level == 0) { # Top level, if no match, then none found. $asmcmdglobal_hash{'nfnd'} = 1; # Print error only if none found. if (@vld_path_a == 0) { @eargs = ($dir); asmcmdshare_error_msg(8001, \@eargs) unless ($spprserr); } else { # @vld_path_a != 0 # @eargs = ($dir); asmcmdshare_error_msg(8004, \@eargs) unless ($spprserr); } } return %empty; # Non-found. # } elsif ($dir =~ $ASMCMDGLOBAL_WCARD_CHARS) { # It's the $ASMCMDSHARE_CSET_W_WCARD. if (asmcmdshare_is_wildcard_cmd($asmcmdglobal_hash{'cmd'})) { # Wildcard supported; call recursively. if (($level == 0) && (@vld_path_a == 0)) { # $dir must be diskgroup name. %ret = asmcmdshare_validate_dg ($dbh, $dir, $work_path, $cont_ref, $wpath_upd_ref, $level, 1, $spprserr); } else { # $level != 0 || @vld_path_a != 0 # # $dir is a normal directory within a diskgroup. %ret = asmcmdshare_validate_dir ($dbh, $dir, $work_path, $cont_ref, $wpath_upd_ref, $vld_path, $cur_ref_id, $level, 1, $spprserr); } # Normally, the loop should terminate after a return from a recursive # call, as the recursive function should have finished parsing the # rest of $work_path. However, the one exception is when the # recursive call encounters a '..' after a '*', e.g. '*/..', or # even '*/blah/../..', which simplifies to '*/..', assuming 'blah' # exists. In this exception, the $cont reference is set to 1. if ($$cont_ref == 1) { $$cont_ref = 0; # Reset. # @subdirs = split (/\//, $$wpath_upd_ref); # Get updated work_path. # $ret{'path'}->[0] = $vld_path; $ret{'ref_id'}->[0] = $cur_ref_id; $ret{'par_id'}->[0] = $cur_par_id; $ret{'gnum'}->[0] = $asmcmdglobal_hash{'gnum'}; # So do not stop loop, hence no return statement. } else { # $$cont_ref != 1 # if ( ( @{ $ret{'path'} } == 0 ) && ($level == 0) ) { # Since we're on top level, an empty path array means we've # exhausted all possible matches for the wildcard and still no # match. Thus, print 'not found' message. $asmcmdglobal_hash{'nfnd'} = 1; $wpath_slash = ''; $wpath_slash = '/' . $work_path if ($work_path ne ''); $vld_path_slash = ''; $vld_path_slash = $vld_path . '/' if ($vld_path ne ''); if (($vld_path ne '') || ($work_path ne '')) { # Trying to match an alias; use 8002. # @eargs = ('+' . $vld_path_slash, $dir . $wpath_slash); asmcmdshare_error_msg(8002, \@eargs) unless ($spprserr); } else { # Trying to match a diskgroup; use 8001. # @eargs = ($dir . $wpath_slash); asmcmdshare_error_msg(8001, \@eargs) unless ($spprserr); } } # Since the recursive function call has finished parsing $work_path, # the caller can terminte its loop and return. return %ret; } } else { # asmcmdshare_is_wildcard_cmd($asmcmdglobal_hash{'cmd'}) == FALSE # # Wildcard not supported; so the fact we see a wildcard in $work_path # means that the path fails to validate for sure, as there is no # real alias name that contains a '*'. Thus no match. if ($level == 0) { # Top level, if no match, then validate fails. $asmcmdglobal_hash{'nfnd'} = 1; if (@vld_path_a == 0) { # Invalid diskgroup name with a '*'. # @eargs = ($dir); asmcmdshare_error_msg(8001, \@eargs) unless ($spprserr); } else { # Invalid alias name with a '*'. # @eargs = ($dir); asmcmdshare_error_msg(8004, \@eargs) unless ($spprserr); } } return %empty; # No match, so return empty hash. # } } else { # $dir !~ /\*/ # # Normal $ASMCMDSHARE_CSET; process as normal directory. if (($level == 0) && (@vld_path_a == 0)) { # $dir must be diskgroup name. %ret = asmcmdshare_validate_dg ($dbh, $dir, undef, undef, undef, $level, 0, $spprserr); } else { # $level != 0 || @vld_path_a != 0 # # $dir is a normal directory within a diskgroup. %ret = asmcmdshare_validate_dir ($dbh, $dir, undef, undef, undef, $vld_path, $cur_ref_id, $level, 0, $spprserr); } if ( (defined ($ret{'path'})) && ( @{ $ret{'path'} } == 1 ) ) { # Found match. # Update indices. $cur_ref_id = $ret{'ref_id'}->[0]; $cur_par_id = $ret{'par_id'}->[0]; # Update validate path and its tokens array. @vld_path_a = split (/\//, $ret{'path'}->[0]); $vld_path = join ('/', @vld_path_a); } else { # Dir not found, so stop loop. if ($level == 0) { # Since we're on top level, an empty path array means we've # exhausted all possible matches for the wildcard and still no # match. Thus, print 'not found' message. $asmcmdglobal_hash{'nfnd'} = 1; if (@vld_path_a == 0) { @eargs = ($dir); asmcmdshare_error_msg(8001, \@eargs) unless ($spprserr); } else { @eargs = ('+' . $vld_path . '/', $dir); asmcmdshare_error_msg(8002, \@eargs) unless ($spprserr); } } last; # No need to finish parsing $work_path, since no match. # } } } # while() # if (($level == 0) && (! $asmcmdglobal_hash{'nfnd'})) { if (@vld_path_a == 0) { # This area should not be reached unless the path simplifies to # '+' after a series of '..', and no 'not found' occurred at $level # equal to 0. If that's the case, then $work_path has normalized to # simply '+'. So update return hash to hold the values for '+'. $ret{'path'}->[0] = '+'; $ret{'ref_id'}->[0] = -1; $ret{'par_id'}->[0] = -1; $ret{'gnum'}->[0] = -1; } } return %ret; } ######## # NAME # asmcmdshare_validate_dir # # DESCRIPTION # This routine calls the appropriate functions to validate the existence # of $dir in v$asm_alias. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # dir (IN) - the directory alias to be validated. # work_path (IN) - remainder of the path to be validated in case $dir # contains wildcard and need to call # asmcmdshare_validate_path_recurse() recursively. # cont_ref (OUT) - internal state for recursive function: 1 means # to continue while loop after recursive call # returns; 0 means to terminate loop after its # return. # wpath_upd_ref (OUT) - the unparsed portion of $work_path passed back # to the calling function by the recursively # called function; this scenario can happen when # in the scenario of having '*/..' in a path; # $cont_ref must be set to 1 in conjunction, and # the calling function will finish parsing # $work_path. # vld_path (IN) - String for the subset of $work_path that's validated # so far. # old_ref_id (IN) - the parent index of $dir. # level (IN) - the level of recursive call; top level starts # at 0; $level increments by 1 for each new level # of recursive call. # wildcard (IN) - boolean: true iff $dir contains wildcard '*'. # spprserr (IN) - SuPPReSs ERRor: 1 shows no errors, 0 shows error. # # RETURNS # A hash of references to four parallel arrays: # $ret{'path'} - reference to array of validated paths. # $ret{'ref_id'} - reference to array of reference indices for the files # or directories specified by the paths. # $ret{'par_id'} - reference to array of parent indices for the files or # directories specified by the paths. # $ret{'gnum'} - reference to array of group numbers for the files or # directories specified by the paths. # # NOTES # This routine calls asmcmdshare_get_subdir_simple() and get_subdir() to # validate the existence of $dir. If $dir contains a wildcard, then this # routine calls asmcmdshare_validate_path_recurse() recursively on every # match of $dir returned by asmcmdshare_get_subdirs(). ######## sub asmcmdshare_validate_dir { my ($dbh, $dir, $work_path, $cont_ref, $wpath_upd_ref, $vld_path, $old_ref_id, $level, $wildcard, $spprserr) = @_; my (%results); # Hash of refs to parallel arrays returned by # # asmcmdshare_validate_path_recurse(). # my (%ret); # Hash of references to parallel arrays; see RETURN above. # my (@vld_dirs); # Array of validated paths. # my (@vld_ref_ids); # Array of reference indices for the file or directory # # specified by the paths. # my (@vld_par_ids); # Array of parent indices for the file or directory # # specified by the paths. # my (@vld_gnums); # Array of group numbers for the file or directory # # specified by the paths. # my (@subdirs); # Array of alias names that match wildcard expression $dir. # my ($new_ref_id); # Reference index of $dir, $wildcard is FALSE. # my ($tmp); # Temp string to hold a path from @{ $results{'path'} }. # my ($iter); my ($i); if (! $wildcard) { # $dir contains no wildcard; process it as a normal alias. # # Obtain reference index from parent index and alias name. $new_ref_id = asmcmdshare_get_subdir_simple($dbh, $old_ref_id, $dir); # If $dir exists, i.e. reference index is defined, then save them as # a validated entry. if (defined ($new_ref_id)) { push (@vld_dirs, $vld_path . '/' . $dir); push (@vld_ref_ids, $new_ref_id); push (@vld_par_ids, $old_ref_id); push (@vld_gnums, $asmcmdglobal_hash{'gnum'}); } } else { # $dir contains wildcards. # # Obtain all entries with alias names that match the wildcard expression # $dir. asmcmdshare_get_subdirs($dbh, \@subdirs, $asmcmdglobal_hash{'gnum'}, undef, $old_ref_id, $dir, undef, 0, 0); if (@subdirs > 0) { # If there is one or more matches. # foreach $iter (@subdirs) # Process each match. # { if ($work_path ne '') # Call recursively only if more path left to # { # validate. # # Call recursively to parse the remainder of $work_path; e.g. if # $dir matchs, 'a', 'b', and 'c'; and $work_path is 'foo', we # we need to validate 'a/foo', 'b/foo', and 'c/foo'. %results = asmcmdshare_validate_path_recurse ($dbh, $work_path, $cont_ref, $wpath_upd_ref, $iter->{'reference_index'}, $iter->{'parent_index'}, $level + 1, $spprserr); # If it's a '*/..', then there's no need to loop through all # matches of '*' here. last if ($$cont_ref == 1); # Make sure that the hash has defined elements before attempting # to do any dereferencing. if (defined($results{'path'})) { # Add all the results from %results to the @vld* arrays. for ($i = 0; $i < @{ $results{'path'} }; $i++) { $tmp = $results{'path'}->[$i]; # We need to add each path from %results to $vld_path to get # full validated paths. e.g. we have $vld_path as # '+dgroup/new', and $iter->{'name'} as 'a', and %results as # 'foo/bar', 'foo/candy', then we need to add the paths # '+dgroup/new/a/foo/candy' and # '+dgroup/new/a/foo/bar to @vld_dirs. # This if clause is present only to prevent the concatenation # of a possibly null string $tmp, which would result in a # Perl warning if using perl -w. if ($tmp ne '') { $tmp =~ s,^/,,; # Remove leading '/' if any. # $tmp = $vld_path . '/' . $iter->{'name'} . '/' . $tmp; } else { $tmp = $vld_path . '/' . $iter->{'name'}; } # Now save each entry. push (@vld_dirs, $tmp); push (@vld_ref_ids, $results{'ref_id'}->[$i]); push (@vld_par_ids, $results{'par_id'}->[$i]); push (@vld_gnums, $results{'gnum'}->[$i]); } } } else { # $workpath eq '' # # No need to call asmcmdshare_validate_path_recurse(); simply append # each match of $dir to $vld_path and add the resulting string to # @vld_dirs. $tmp = $vld_path . '/' . $iter->{'name'}; push (@vld_dirs, $tmp); push (@vld_ref_ids, $iter->{'reference_index'}); push (@vld_par_ids, $iter->{'parent_index'}); push (@vld_gnums, $asmcmdglobal_hash{'gnum'}); } } } } # Prepare return hash. $ret{'path'} = \@vld_dirs; $ret{'ref_id'} = \@vld_ref_ids; $ret{'par_id'} = \@vld_par_ids; $ret{'gnum'} = \@vld_gnums; return %ret; } ######## # NAME # asmcmdshare_validate_dg # # DESCRIPTION # This routine calls the appropriate functions to validate the existence # of $gname in v$asm_diskgroup. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # work_path (IN) - remainder of the path to be validated in case $gname # contains wildcard and need to call # asmcmdshare_validate_path_recurse() recursively. # cont_ref (OUT) - internal state for recursive function: 1 means # to continue while loop after recursive call # returns; 0 means to terminate loop after its # return. # wpath_upd_ref (OUT) - the unparsed portion of $work_path passed back # to the calling function by the recursively # called function; this scenario can happen when # in the scenario of having '*/..' in a path; # $cont_ref must be set to 1 in conjunction, and # the calling function will finish parsing # $work_path. # level (IN) - the level of recursive call; top level starts # at 0; $level increments by 1 for each new level # of recursive call. # wildcard (IN) - boolean: true iff $gname contains wildcard '*'. # spprserr (IN) - SuPPReSs ERRor: 1 shows no errors, 0 shows error. # # RETURNS # A hash of references to four parallel arrays: # $ret{'path'} - reference to array of validated paths. # $ret{'ref_id'} - reference to array of reference indices for the file # or directory specified by the paths. # $ret{'par_id'} - reference to array of parent indices for the file or # directory specified by the paths. # $ret{'gnum'} - reference to array of group numbers for the file or # directory specified by the paths. # # NOTES # If $gname contains a wildcard, then this routine calls # asmcmdshare_validate_path_recurse() recursively on every match of $gname # returned by asmcmdshare_get_dg(). ######## sub asmcmdshare_validate_dg { my ($dbh, $gname, $work_path, $cont_ref, $wpath_upd_ref, $level, $wildcard, $spprserr) = @_; my (@dg_list); # Array of diskgroup hashes returned by asmcmdshare_get_dg. # my (%results); # Hash of refs to parallel arrays returned by # # asmcmdshare_validate_path_recurse(). # my (%ret); # Hash of references to parallel arrays; see RETURN above. # my (@vld_dirs); # Array of validated paths. # my (@vld_ref_ids); # Array of reference indices for the paths. # my (@vld_par_ids); # Array of parent indices for the paths. # my (@vld_gnums); # Array of group numbers for the paths. # my ($gnum); # The group number for $gname, if $wildcard is FALSE. # my ($tmp); # Temp string to hold a path from @{ $results{'path'} }. # my ($iter); my ($i); if (! $wildcard) { # No wildcard, process normally. # $gnum = asmcmdshare_get_gnum_from_gname($dbh, $gname); # If group number exists for the group name, then the group name must be # valid. if (defined ($gnum)) { # Save validated diskgroup information. push (@vld_dirs, $gname); push (@vld_ref_ids, $gnum << 24); push (@vld_par_ids, -1); push (@vld_gnums, $gnum); # Update globals for default diskgroup name and number. $asmcmdglobal_hash{'gname'} = $gname; $asmcmdglobal_hash{'gnum'} = $gnum; } } else { # Wildcard, call recursively. # @dg_list = asmcmdshare_get_dg($dbh, $gname, 0, 0); # Get matching diskgroups. # # the wildcard expression $gname. # if (@dg_list > 0) { # At least one dg found. # foreach $iter (@dg_list) # Process one match at a time. # { # Update global current diskgroup information. $asmcmdglobal_hash{'gname'} = $iter->{'name'}; $asmcmdglobal_hash{'gnum'} = $iter->{'group_number'}; if ($work_path ne '') # Call recursively only if more path left to # { # validate. # # Call recursively to parse the remainder of $work_path. %results = asmcmdshare_validate_path_recurse ($dbh, $work_path, $cont_ref, $wpath_upd_ref, $iter->{'group_number'} << 24, -1, $level + 1, $spprserr); # If it's a '*/..', then there's no need to loop through all # matches of '*' here. last if ($$cont_ref == 1); # Make sure that the hash has defined elements before attempting # to do any dereferencing. if (defined($results{'path'})) { # Add all the results from %results to the @vld* arrays. for ($i = 0; $i < @{ $results{'path'} }; $i++) { # Build fully validated paths. $tmp = $results{'path'}->[$i]; $tmp =~ s,^/,,; # Remove leading '/' if any. # $tmp = $iter->{'name'} . '/' . $tmp; # Now save each entry. push (@vld_dirs, $tmp); push (@vld_ref_ids, $results{'ref_id'}->[$i]); push (@vld_par_ids, $results{'par_id'}->[$i]); push (@vld_gnums, $results{'gnum'}->[$i]); } } } else # $work_path eq '' # { # No need to call asmcmdshare_validate_path_recurse(); simply add # each matching group name to @vld_dirs. push (@vld_dirs, $iter->{'name'}); push (@vld_ref_ids, $iter->{'group_number'} << 24); push (@vld_par_ids, -1); push (@vld_gnums, $iter->{'group_number'}); } } } } # Prepare return hash. $ret{'path'} = \@vld_dirs; $ret{'ref_id'} = \@vld_ref_ids; $ret{'par_id'} = \@vld_par_ids; $ret{'gnum'} = \@vld_gnums; return %ret; } ######## # NAME # asmcmdshare_make_absolute # # DESCRIPTION # This routine checks to see if $path is an absolute path; if not, it # attaches the current directory to the front of $path and returns the # full path. # # PARAMETERS # path (IN) - user-entered relative or absoluate path. # # RETURNS # $path if it is absolute; $asmcmdglobal_hash{'cwdnm'} . '/'. $path # otherwise. ######## sub asmcmdshare_make_absolute { my ($path) = shift; my ($full_path) = $path; if ($path !~ m,^\+,) # An absolute path always starts with '+'. # { # If not, attach current directory to the front of $path. # $full_path = $asmcmdglobal_hash{'cwdnm'} . '/'. $path; } return $full_path; } ######## # NAME # asmcmdshare_cdup_update_ids # # DESCRIPTION # Given a parent index of an alias, this function returns the reference and # parent indices that belong the parent of that alias. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # par_id (IN) - the parent index of the child alias. # # RETURNS # Two-element array: # Element 1: reference index of parent alias. # Element 2: parent index of parent alias. ######## sub asmcmdshare_cdup_update_ids { my ($dbh, $par_id) = @_; my ($ref_id) = $par_id; # Ref id always becomes par id. # if ($par_id == -1) { # cdup from '+' or '+dgName'. # return ($ref_id, $par_id); } elsif ($par_id == ($asmcmdglobal_hash{'gnum'} << 24)) { # cdup from '+dgName/dir1'. # return ($ref_id, -1); } else { # All other cases, i.e. 2+ levels of subdirs. # $par_id = asmcmdshare_get_par_id($dbh, $par_id); return ($ref_id, $par_id); } } ############################################################################## ############################## MISC Routines ################################# ######## # NAME # asmcmdshare_ls_calc_min_col_wid # # DESCRIPTION # This formatting routine calculates the miminum column width of each # displayed attribute based on the values from v$asm_alias and v$asm_file. # # PARAMETERS # list_ref (IN) - reference to list of hashes, each hash holding # values for attributes of one alias entry. # min_col_wid_ref (IN/OUT) - reference to hash of minimum column width. # # RETURNS # Null. # # NOTES # The hashes in @{$list_ref} must be populated with values from v$asm_alias # and v$asm_file if the entry is a file. Thus, asmcmdshare_get_subdirs() # must be called first. Important: these hashes contains keys that are not # attribute names. These extra keys are internal to ASMCMD and do not # correspond to a column name in v$asm_alias or v$asm_file. One example # is 'name_print', which is used to store the value to be printed under # the name column, which can be the same as the value for the 'name' key, # but it doesn't have to be. # # %{$min_col_wid_ref} must be initialized with the default minimum column # widths before calling this routine. Thus, asmcmdbase_ls_init_col_wid() or # asmcmdbase_lsdg_init_col_wid() must be called first. All keys in this # hash are attribute names from v$asm_alias and/or v$asm_file. # # Important note: the hashes in @{$list_ref} can contain keys that are not # in %{$min_col_wid_ref} and vice versa. Do not assume that if a key exists # in one then it must in the other. Always use defined() to check, or else # perl -w will complain. ######## sub asmcmdshare_ls_calc_min_col_wid { my ($list_ref, $min_col_wid_ref) = @_; my ($entry); # Reference to one hash entry of attribute values in @list. # my ($key); # One hash key of %{$entry}, used for iteration. # # For the values of each attribute in the list of entries, find the one with # the longest length. That length is the minimum column width for that # attribute. foreach $entry (@{$list_ref}) { foreach $key (keys %{$entry}) { if (defined ($min_col_wid_ref->{ $key }) && defined ($entry->{ $key }) && (length($entry->{ $key }) > $min_col_wid_ref->{ $key })) { # Save the longest length so far. $min_col_wid_ref->{ $key } = length($entry->{ $key }); } } } return; } ######## # NAME # asmcmdshare_version_cmp # # DESCRIPTION # This function determines if the Oracle Database version $version1 is # less than, equal to, or greater than Oracle Database version $version2. # # PARAMETERS # version1 (IN) - Oracle Database version number one # version2 (IN) - Oracle Database version number two # # RETURNS # A negative number if $version1 is smaller or earlier than $version2; # zero of $version1 equals $version2; and a positive number if $version1 # is greater or later than $version2. # # NOTES # This function asserts that the version numbers are in the format # a.b.c.d.e . # # We assume this order of significance for the five sub-version numbers: # a is most significant, then b, then c, and then d. e is least # significant. ######## sub asmcmdshare_version_cmp { my ($version1, $version2) = @_; # Major database release number, database maintenance release number, # application server release number, component-specific release number, # platform-specific release number. my ($v1_maj, $v1_maint, $v1_appserv, $v1_comp, $v1_plat); my ($v2_maj, $v2_maint, $v2_appserv, $v2_comp, $v2_plat); # Error array. my (@eargs); # Split $version1 into the five sub-version parts. ($v1_maj, $v1_maint, $v1_appserv, $v1_comp, $v1_plat) = split (/\./, $version1, 5); # Split $version2 into the five sub-version parts. ($v2_maj, $v2_maint, $v2_appserv, $v2_comp, $v2_plat) = split (/\./, $version2, 5); # Assert that we have all five sub-version parts for both versions. @eargs = ('asmcmdshare_version_cmp05'); asmcmdshare_assert(defined($v1_maj), \@eargs); @eargs = ('asmcmdshare_version_cmp07'); asmcmdshare_assert(defined($v1_maint), \@eargs); @eargs = ('asmcmdshare_version_cmp09'); asmcmdshare_assert(defined($v1_appserv), \@eargs); @eargs = ('asmcmdshare_version_cmp11'); asmcmdshare_assert(defined($v1_comp), \@eargs); @eargs = ('asmcmdshare_version_cmp13'); asmcmdshare_assert(defined($v1_plat), \@eargs); @eargs = ('asmcmdshare_version_cmp15'); asmcmdshare_assert(defined($v2_maj), \@eargs); @eargs = ('asmcmdshare_version_cmp17'); asmcmdshare_assert(defined($v2_maint), \@eargs); @eargs = ('asmcmdshare_version_cmp19'); asmcmdshare_assert(defined($v2_appserv), \@eargs); @eargs = ('asmcmdshare_version_cmp21'); asmcmdshare_assert(defined($v2_comp), \@eargs); @eargs = ('asmcmdshare_version_cmp23'); asmcmdshare_assert(defined($v2_plat), \@eargs); # Compare major release number. if ($v1_maj != $v2_maj) { return ($v1_maj - $v2_maj); } # Compare maintenance release number. if ($v1_maint != $v2_maint) { return ($v1_maint - $v2_maint); } # Compare application server release number. if ($v1_appserv != $v2_appserv) { return ($v1_appserv - $v2_appserv); } # Compare component release number. if ($v1_comp != $v2_comp) { return ($v1_comp - $v2_comp); } # Compare platform release number. return ($v1_plat - $v2_plat); } ######## # NAME # asmcmdshare_get_asm_version # # DESCRIPTION # This function retrieves the ASM instance software version number from # the ASM instance. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # # RETURNS # The ASM instance sofware version number. ######## sub asmcmdshare_get_asm_version { my ($dbh) = shift; # Database handle. # my ($sth); # SQL statement handle. # my ($version); # ASM instance version. # my ($query); # SQL query statement. # my ($row); # Row of query results. # # SQL for getting the ASM instance software version. $query = 'select version from v$instance'; # Execute the SQL. $sth = asmcmdshare_do_select($dbh, $query); # Fetch the only row of results. $row = asmcmdshare_fetch($sth); # Get the version number. $version = $row->{'VERSION'}; # Close the statement handle. asmcmdshare_finish($sth); return $version; } ############################################################################## ############################## SQL Routines ################################## ######## # NAME # asmcmdshare_cur_julian_date # # DESCRIPTION # This routine constructs the SQL used to retrieve the current Julian Date. # It calls asmcmdshare_do_select() to execute it. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # # RETURNS # The current Julian Date as a number in string format. ######## sub asmcmdshare_cur_julian_date { my ($dbh) = shift; my ($sth, $qry, $row); my ($jul_date); # Return string for the current Julian Date: an integer. # $qry = 'select to_char(current_date, \'J\') "JULIAN_DATE" from dual'; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $jul_date = $row->{'JULIAN_DATE'}; asmcmdshare_finish($sth); return $jul_date; } ######## # NAME # asmcmdshare_get_insttype # # DESCRIPTION # This routine constructs the SQL used to get the instance type of the # connected Oracle instance. It calls asmcmdshare_do_select() to execute it. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # # RETURNS # The instance type as a string: either 'ASM' or 'RDBMS'. ######## sub asmcmdshare_get_insttype { my ($dbh) = shift; my ($sth, $qry, $row); my ($insttype); # Return string that holds the instance type. # $qry = 'select value from v$parameter where name=\'instance_type\''; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $insttype = $row->{'VALUE'}; asmcmdshare_finish($sth); return $insttype; } ######## # NAME # asmcmdshare_get_file # # DESCRIPTION # This routine constructs the SQL used to retrieve all the attributes # in v$asm_file for a file, given its group number and file number. It # calls asmcmdshare_do_select() to execute it. # # 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. # file_info_ref (IN/OUT) - reference to hash of file informationt to be # returned. # # RETURNS # A hash of all the attribute values, hashed by attribute names. # # NOTES # Not all hash keys in %file_info are attributes from v$asm_file. The keys # MOD_TIME, MOD_DATE, JULIAN_DATE, and JULIAN_TIME are differen time # formats of the attribute 'modification_date'. ######## sub asmcmdshare_get_file { my ($dbh, $gnum, $fnum, $file_info_ref) = @_; my ($sth, $qry, $row, $cmpd_ind); $cmpd_ind = $gnum * (1 << 24) + $fnum; $qry = 'select group_number, file_number, incarnation, block_size, ' . 'blocks, bytes, space, type, redundancy, striped, creation_date, ' . 'user_number, usergroup_number, permissions, ' . 'to_char(modification_date, \'MON DD HH24:MI:SS\') "MOD_TIME", ' . 'to_char(modification_date, \'MON DD YYYY\') "MOD_DATE", ' . 'to_char(modification_date, \'J HH24 MI SS\') "JULIAN_TIME", ' . 'to_char(modification_date, \'J\') "JULIAN_DATE" ' . 'from v$asm_file where compound_index = ' . $cmpd_ind . ' ORDER BY ' . 'GROUP_NUMBER, FILE_NUMBER, USER_NUMBER, USERGROUP_NUMBER ' ; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); # Copy values into hash. $file_info_ref->{'group_number'} = $row->{'GROUP_NUMBER'}; $file_info_ref->{'file_number'} = $row->{'FILE_NUMBER'}; $file_info_ref->{'incarnation'} = $row->{'INCARNATION'}; $file_info_ref->{'block_size'} = $row->{'BLOCK_SIZE'}; $file_info_ref->{'blocks'} = $row->{'BLOCKS'}; $file_info_ref->{'bytes'} = $row->{'BYTES'}; $file_info_ref->{'space'} = $row->{'SPACE'}; $file_info_ref->{'type'} = $row->{'TYPE'}; $file_info_ref->{'redundancy'} = $row->{'REDUNDANCY'}; $file_info_ref->{'striped'} = $row->{'STRIPED'}; $file_info_ref->{'creation_date'} = $row->{'CREATION_DATE'}; $file_info_ref->{'mod_time'} = $row->{'MOD_TIME'}; $file_info_ref->{'mod_date'} = $row->{'MOD_DATE'}; $file_info_ref->{'julian_time'} = $row->{'JULIAN_TIME'}; $file_info_ref->{'julian_date'} = $row->{'JULIAN_DATE'}; $file_info_ref->{'user_number'} = $row->{'USER_NUMBER'}; $file_info_ref->{'usergroup_number'} = $row->{'USERGROUP_NUMBER'}; $file_info_ref->{'user'} = asmcmdshare_usernumber2name($dbh, $row->{'USER_NUMBER'}); $file_info_ref->{'group'} = asmcmdshare_usergroupnumber2name($dbh, $row->{'USERGROUP_NUMBER'}, $row->{'GROUP_NUMBER'}); $file_info_ref->{'perm'} = $row->{'PERMISSIONS'}; # Combine date and time into one sortable number by removing the spaces. $file_info_ref->{'julian_time'} =~ s,\s+,,g; asmcmdshare_finish($sth); return; } ######## # NAME # asmcmdshare_get_subdirs # # DESCRIPTION # This routine constructs the SQL used to select one or more rows from # v$asm_alias. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # list_ref (IN/OUT) - reference to array of hashes, each representing # a single row of results. # gnum (IN) - optional: limit select by this group number. # ref_id (IN) - optional: limit select by this reference index. # par_id (IN) - optional: limit select by this parent index. # name (IN) - optional: limit select by this alias name. # type (IN) - optional: limit select by this file type. # sys_non_dir_only (IN) - boolean: limit select by 1) only system-created # aliases and 2) only non-directories, iff this # boolean is true. # get_file_info (IN) - boolean: query v$asm_file iff true. # # RETURNS # Undefined if the following requirement in NOTES is not met; otherwise # an array of hashes, each containing the values of attributes for one # row of v$asm_alias, returned by the query. # # NOTES # You must specify at least one of $gnum or $name. Note also that we do # not select every element in v$asm_alias. ######## sub asmcmdshare_get_subdirs { my ($dbh, $list_ref, $gnum, $ref_id, $par_id, $name, $type, $sys_non_dir_only, $get_file_info) = @_; my ($sth, $qry_alias, $row, $hash_key); my (%tmphash); # Substitute all '*' with '%', as the latter is the wildcard character in # SQL. $name =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\%,g if (defined ($name)); # Construct query against v$asm_alias. $qry_alias = 'select name, group_number, file_number, reference_index, parent_index, alias_directory, system_created from v$asm_alias'; # Add conditions to the SQL statement, based on which parameter is present. # Note that either group number or the name is required. if (defined ($gnum)) { $qry_alias .= ' where group_number=' . $gnum; $qry_alias .= ' and upper(name) like \'' . uc($name) . '\'' if (defined ($name)); } elsif (defined ($name)) { $qry_alias .= ' where upper(name) like \'' . uc($name) . '\''; $qry_alias .= ' group_number=' . $gnum if (defined ($gnum)); } else { return undef; # Neither $gnum nor $name is present; error. # } $qry_alias .= ' and reference_index=' . $ref_id if (defined ($ref_id)); $qry_alias .= ' and parent_index=' . $par_id if (defined ($par_id)); if ($sys_non_dir_only) { # We want only system-created aliases to only files here. # $qry_alias .= " and alias_directory='N' and system_created='Y'"; } $sth = asmcmdshare_do_select($dbh, $qry_alias); # Fetch results row by row and store each row in %entry_info, and reference # each %entry_info in @{$list_ref}. while (defined ($row = asmcmdshare_fetch($sth))) { my (%entry_info); # Allocate fresh hash for next row. # # v$asm_alias entries $entry_info{'group_number'} = $row->{'GROUP_NUMBER'}; $entry_info{'file_number'} = $row->{'FILE_NUMBER'}; $entry_info{'reference_index'} = $row->{'REFERENCE_INDEX'}; $entry_info{'parent_index'} = $row->{'PARENT_INDEX'}; $entry_info{'alias_directory'} = $row->{'ALIAS_DIRECTORY'}; $entry_info{'system_created'} = $row->{'SYSTEM_CREATED'}; $entry_info{'name'} = $row->{'NAME'}; # Append to v$asm_file query if ($get_file_info && $entry_info{'alias_directory'} eq 'N') { # Hash key consists of group_number, file_number, and # the system_created flag. $hash_key = $entry_info{'group_number'} * (1 << 24) + $entry_info{'file_number'} . $entry_info{'system_created'}; $tmphash{$hash_key} = \%entry_info; } push (@{$list_ref}, \%entry_info); } asmcmdshare_finish($sth); # Here we query v$asm_file once for each file. This method may seem # excessive, but it is actually the fastest way. There are two # alternatives: # A) Build a single query to query all files. # B) Join v$asm_file against a filtered v$asm_alias after a subquery. # # A) is not good because such a query can be too long and may require # a lot of memory on the server side. # B) is not good because it runs in O(N) time if querying a single # file entry, versus O(1) time as implemented here. foreach (keys (%tmphash)) { asmcmdshare_get_file($dbh, $tmphash{$_}->{'group_number'}, $tmphash{$_}->{'file_number'}, $tmphash{$_}); } ############################### return; } ######## # NAME # asmcmdshare_get_subdir_simple # # DESCRIPTION # This routine constructs the SQL used to fetch the reference index for the # alias that has the parent index $par_id, the name $name, and the group # number $asmcmdglobal_hash{'gnum'}. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # par_id (IN) - limit select by this parent index. # name (IN) - limit select by this alias name. # # RETURNS # The reference index as a string. ######## sub asmcmdshare_get_subdir_simple { my ($dbh, $par_id, $name) = @_; my ($sth, $qry, $row); my ($ref_id); # The return string for the reference index. # $qry = 'select reference_index from v$asm_alias where group_number=' . $asmcmdglobal_hash{'gnum'} . ' and parent_index=' . $par_id . ' and upper(name)=\'' . uc($name) . '\''; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $ref_id = $row->{'REFERENCE_INDEX'}; asmcmdshare_finish($sth); return $ref_id; } ######## # NAME # asmcmdshare_run_find_sql # # DESCRIPTION # This routine constructs and executes SQL to find all the aliases # in v$asm_alias that match the pattern given by $name. $par_id # and $type can further limit the results. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # list_ref (IN/OUT) - reference to array of hashes, each representing # a single row of results. # par_id (IN) - optional: limit select by this parent index. # name (IN) - limit select by this alias name. # type (IN) - optional: limit select by this file type. # sys_non_dir_only (IN) - boolean: limit select by 1) only system-created # aliases and 2) only non-directories, iff this # boolean is true. # # NOTES # asmcmdshare_find_int() uses this SQL routine, which optimizes for # searches, while asmcmdshare_get_subdirs() optimizes for ls. ######## sub asmcmdshare_run_find_sql { my ($dbh, $list_ref, $par_id, $name, $type, $sys_non_dir_only) = @_; my ($sth, $qry, $qry2, $row, $cmpd_ind, $entry); my (@tmparray); # Set wildcard as '%' and contruct v$asm_alias query. $name =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\%,g if (defined ($name)); $qry = 'select name, group_number, file_number, reference_index, parent_index, alias_directory, system_created from v$asm_alias where upper(name) like \'' . uc($name) . '\''; if (defined ($type)) { $qry .= ' and alias_directory=\'N\''; } if (defined ($par_id)) { $qry .= ' and parent_index=' . $par_id; } if ($sys_non_dir_only) { # We want only system-created aliases to only files here. # $qry .= " and alias_directory='N' and system_created='Y'"; } $sth = asmcmdshare_do_select($dbh, $qry); while (defined ($row = asmcmdshare_fetch($sth))) { my (%entry_info); # Allocate fresh hash for next row. # # v$asm_alias entries $entry_info{'group_number'} = $row->{'GROUP_NUMBER'}; $entry_info{'file_number'} = $row->{'FILE_NUMBER'}; $entry_info{'reference_index'} = $row->{'REFERENCE_INDEX'}; $entry_info{'parent_index'} = $row->{'PARENT_INDEX'}; $entry_info{'alias_directory'} = $row->{'ALIAS_DIRECTORY'}; $entry_info{'system_created'} = $row->{'SYSTEM_CREATED'}; $entry_info{'name'} = $row->{'NAME'}; push (@tmparray, \%entry_info); } asmcmdshare_finish($sth); foreach $entry (@tmparray) { # If we're qualifying by file type, then we need to query v$asm_file. if (defined ($type)) { $cmpd_ind = $entry->{'group_number'} * (1 << 24) + $entry->{'file_number'}; $qry2 = 'select compound_index from v$asm_file where compound_index=' . $cmpd_ind . ' and upper(type)=\'' . uc($type) . '\''; $sth = asmcmdshare_do_select($dbh, $qry2); $row = asmcmdshare_fetch($sth); # Add entry to results only if there is a match. if (defined ($row)) { push (@{$list_ref}, $entry); } asmcmdshare_finish($sth); } else # If we're not searching by type, then always return the entry # { push (@{$list_ref}, $entry); } } } ######## # NAME # asmcmdshare_get_par_id # # DESCRIPTION # This routine constructs the SQL used to fetch the parent index of the # alias that has the group number $asmcmdglobal_hash{'gnum'} and # reference_index $ref_id. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # ref_id (IN) - limit select by this reference index. # # RETURNS # The parent index as a string. # # NOTES # The reference index specified by $ref_id must be unique; it must not be # the reference index of a file alias, as those are not unique. ######## sub asmcmdshare_get_par_id { my ($dbh, $ref_id) = @_; my ($sth, $qry, $row); my ($par_id); # The return string holding the parent index. # $qry = 'select parent_index from v$asm_alias where group_number=' . $asmcmdglobal_hash{'gnum'} . ' and reference_index=' . $ref_id; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $par_id = $row->{'PARENT_INDEX'}; asmcmdshare_finish($sth); return $par_id; } ######## # NAME # asmcmdshare_get_dg # # DESCRIPTION # This routine constructs the SQL used to fetch a list of row(s) from # v$asm_diskgroup. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # gname (IN) - optional: limit select by this group number, can contain # the wildcard '*'. # # RETURNS # An array containing zero or one or more rows from v$asm_diskgroup. The # values of attributes for each row are stored in a hash, the reference to # which is indexed in the array. ######## sub asmcmdshare_get_dg { my ($dbh, $gname, $discovery, $global) = @_; my ($sth, $qry, $row); my (@dg_list); # The return array of hashes; see RETURNS above. # my ($view); # The ASM view to querry. # # If Oracle Database version is less than 10gR2, then always do # discovery, because the *_stat views are not available in 10gR1. if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_10gR2) < 0 ) { $discovery = 1; } if ($discovery && $global) { $view = 'gv$asm_diskgroup'; } elsif ($discovery && !$global) { $view = 'v$asm_diskgroup'; } elsif (!$discovery && $global) { $view = 'gv$asm_diskgroup_stat'; } else { $view = 'v$asm_diskgroup_stat'; } $qry = 'select * from ' . $view; # Narrow select if $gname is specified. if (defined ($gname)) { $gname =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\%,g; $qry = $qry . ' where name like \'' . uc($gname) . '\''; } $sth = asmcmdshare_do_select($dbh, $qry); # Fetch results row by row and storeeach row in %dg_info, and reference # each %dg_info in @dg_list. while (defined ($row = asmcmdshare_fetch($sth))) { my (%dg_info); # Allocate fresh hash for next row. # $dg_info{'inst_id'} = $row->{'INST_ID'}; $dg_info{'group_number'} = $row->{'GROUP_NUMBER'}; $dg_info{'name'} = $row->{'NAME'}; $dg_info{'sector_size'} = $row->{'SECTOR_SIZE'}; $dg_info{'block_size'} = $row->{'BLOCK_SIZE'}; $dg_info{'allocation_unit_size'} = $row->{'ALLOCATION_UNIT_SIZE'}; $dg_info{'state'} = $row->{'STATE'}; $dg_info{'type'} = $row->{'TYPE'}; $dg_info{'total_mb'} = $row->{'TOTAL_MB'}; $dg_info{'free_mb'} = $row->{'FREE_MB'}; # This attribute is available only in 10gR2 and after. if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_10gR2) >= 0 ) { $dg_info{'required_mirror_free_mb'} = $row->{'REQUIRED_MIRROR_FREE_MB'}; $dg_info{'usable_file_mb'} = $row->{'USABLE_FILE_MB'}; $dg_info{'offline_disks'} = $row->{'OFFLINE_DISKS'}; } # This attribute is available only in 11gR2 and after. if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_11gR2) >= 0 ) { $dg_info{'voting_files'} = $row->{'VOTING_FILES'}; } # It is possible that type is undefined if diskgroup is unmounted. In # that case we assign it an empty still so that we won't run into problems # of trying to concatenate undefined strings later. $dg_info{'type'} = '' unless (defined ($dg_info{'type'})); push (@dg_list, \%dg_info); } asmcmdshare_finish($sth); return (@dg_list); } ######## # NAME # asmcmdshare_get_redund # # DESCRIPTION # This routine constructs the SQL used to the redundancy information for # diskgroup $gnum (type attribute in v$asm_diskgroup). # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # gnum (IN) - group number for the diskgroup in question. # # RETURNS # 1 if diskgroup has external redundancy, 2 if normal redundancy, and 3 # if high redundancy; 1 is default if diskgroup not found. ######## sub asmcmdshare_get_redund { my ($dbh, $gnum) = @_; my ($sth, $qry, $row); my ($redund); # Return string for redundancy; see RETURNS above. # my ($view); # The view to query. # # If Oracle Database version is less than 10gR2, then always do # discovery, because the *_stat views are not available in 10gR1. if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_10gR2) < 0 ) { $view = 'v$asm_diskgroup'; } else { $view = 'v$asm_diskgroup_stat'; } # Get diskgroup redundancy from group number. $qry = 'select type from ' . $view . ' where group_number=' . $gnum; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $redund = $row->{'TYPE'}; asmcmdshare_finish($sth); return 1 if ($redund eq 'EXTERN'); return 2 if ($redund eq 'NORMAL'); return 3 if ($redund eq 'HIGH'); return 1; } ######## # NAME # asmcmdshare_get_gnum_from_gname # # DESCRIPTION # This routine constructs the SQL used to fetch the group number of the # diskgroup that has the name $gname, iff it is mounted. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # gname (IN) - the name for the diskgroup for which we need the group # number. # # RETURNS # The group number as a string if the diskgroup is mounted; undefined # otherwise. ######## sub asmcmdshare_get_gnum_from_gname { my ($dbh, $gname) = @_; my ($sth, $qry, $row); my ($gnum); # Group number return value; see RETURNS above. # my ($state); # The state attribute from v$asm_diskgroup. # my ($view); # The view to query. # # If Oracle Database version is less than 10gR2, then always do # discovery, because the *_stat views are not available in 10gR1. if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_10gR2) < 0 ) { $view = 'v$asm_diskgroup'; } else { $view = 'v$asm_diskgroup_stat'; } # Get diskgroup number from group name. $qry = 'select group_number, state from ' . $view . ' where name=\'' . uc($gname) . '\''; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $gnum = $row->{'GROUP_NUMBER'}; $state = $row->{'STATE'}; asmcmdshare_finish($sth); # Return undefined if dismounted, so that caller knows not to 'cd' to this # diskgroup. return undef if (defined ($state) && ($state eq 'DISMOUNTED')); return $gnum; } ######## # NAME # asmcmdshare_get_gname_from_gnum # # DESCRIPTION # This routine constructs the SQL used to fetch the name of the diskgroup # that has the group number $gnum. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # gnum (IN) - the group number for the diskgroup for which we need the # name. # # RETURNS # The diskgroup name as a string. ######## sub asmcmdshare_get_gname_from_gnum { my ($dbh, $gnum) = @_; my ($sth, $qry, $row); my ($gname); # Group name return value; see RETURNS above. # my ($view); # The view to query. # # If Oracle Database version is less than 10gR2, then always do # discovery, because the *_stat views are not available in 10gR1. if ( asmcmdshare_version_cmp($asmcmdglobal_hash{'ver'}, $ASMCMDGLOBAL_VER_10gR2) < 0 ) { $view = 'v$asm_diskgroup'; } else { $view = 'v$asm_diskgroup_stat'; } # Get diskgroup name from group number. $qry = 'select name from ' . $view . ' where group_number=' . $gnum; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $gname = $row->{'NAME'}; asmcmdshare_finish($sth); return $gname; } ######## # NAME # asmcmdshare_get_ugnum_from_ugname # # DESCRIPTION # This routine gets the usergroup number from the usergroup name, diskgroup number # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # dgnum (IN) - disk group number. # ugname(IN) - user group name. # # RETURNS # The usergroup number. ######## sub asmcmdshare_get_ugnum_from_ugname { my ($dbh, $dgnum, $ugname) = @_; my ($sth, $qry, $row); my ($ugnum); # Group name return value; see RETURNS above. # # Get usergroup name from usergroup number. $qry = 'select usergroup_number from v$asm_usergroup where group_number=' . $dgnum . ' and name = ' . "\'$ugname\'"; $sth = asmcmdshare_do_select($dbh, $qry); $row = asmcmdshare_fetch($sth); $ugnum = $row->{'USERGROUP_NUMBER'}; asmcmdshare_finish($sth); return $ugnum; } ######## # NAME # asmcmdshare_do_construct_select # # DESCRIPTION # This routine executes select SQL queries specified by $qry. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # what (IN) - array of columns to select # from (IN) - array of tables to select from # where (IN) - array of conditions # order (IN) - array of order by # # RETURNS # The initialized SQL statement handle. # # NOTES # This routine constructs the statement and calls asmcmdshare_do_select. ######## sub asmcmdshare_do_construct_select { my ($dbh, $what, $from, $where, $order) = @_; my ($stmt); if (!@$what || !@$from) { asmcmdshare_error_msg(8100, undef); } $stmt = "SELECT " . join(', ', @$what); $stmt .= " FROM " . join(', ', @$from); if (defined($where) && @$where) { $stmt .= " WHERE " . join(' AND ', @$where); } if (defined($order) && @$order) { $stmt .= " ORDER BY " . join(', ', @$order); } return asmcmdshare_do_select($dbh, $stmt); } ######## # NAME # asmcmdshare_do_select # # DESCRIPTION # This routine executes select SQL queries specified by $qry. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # qry (IN) - the SQL query to be executed. # # RETURNS # The initialized SQL statement handle. # # NOTES # This routine call hit two errors: # 1) 8100: bad statement handle, probably bad SQL syntax in $qry. # 2) 8200: lost connection to ASM instance or foreground died. # # This routine can call asmcmdshare_print_debug_info() to save $qry and # $DBI::errstr if the environment variable ASMCMD_DEBUG_MODE is set to # the path of the debug log file. ######## sub asmcmdshare_do_select { my ($dbh, $qry) = @_; my ($sth, $rv); if (defined ($ENV{'ASMCMD_DEBUG_MODE'})) { # Save $qry as debug information if env var set. # asmcmdshare_print_debug_info ($ENV{'ASMCMD_DEBUG_MODE'}, $qry); } $qry = '/* ASMCMD */ ' . $qry; # Add ASMCMD comment. # $sth = $dbh->prepare($qry); if (!defined ($sth)) { # Null $sth, must be an error. # if ($DBI::errstr =~ /ORA-03113/) { # ORA-03113 means connection lost of foreground died, signal # # exception and exit. # asmcmdshare_signal_exception (8200, undef); } else { # Connect not lost but probably SQL syntax error caused this. # asmcmdshare_error_msg(8100, undef); } if (defined ($ENV{'ASMCMD_DEBUG_MODE'}) && defined ($DBI::errstr)) { # Save $DBI::errstr as debug information if env var set. # asmcmdshare_print_debug_info ($ENV{'ASMCMD_DEBUG_MODE'}, $DBI::errstr); } return undef; } $asmcmdglobal_hash{'sth'} = $sth; $rv = $sth->execute; $asmcmdglobal_hash{'sth'} = undef; return undef unless(defined $rv); # Return null if execution fails. # return $sth; } ######## # NAME # asmcmdshare_fetch # # DESCRIPTION # This routine routine returns one row of results from an already executed # SQL query. # # PARAMETERS # sth (IN) - initialized SQL statement handle. # # RETURNS # A hash with attribute values as its values and attribute names as its # keys, for one row of results; undefined if $sth is undefined. ######## sub asmcmdshare_fetch { my $sth = shift; return undef unless(defined $sth); return $sth->fetchrow_hashref; } ######## # NAME # asmcmdshare_finish # # DESCRIPTION # This routine closes the SQL statement handle $sth. # # PARAMETERS # sth (IN) - initialized SQL statement handle. # # RETURNS # Undefined if $sth is undefined; otherwise returns the return value of # $sth->finish(). ######## sub asmcmdshare_finish { my $sth = shift; return undef unless(defined $sth); return $sth->finish; } ######## # NAME # asmcmdshare_do_stmt # # DESCRIPTION # This routine executes non-select SQL statements. # # PARAMETERS # dbh (IN) - initialized database handle, must be non-null. # # RETURNS # The return value of $sth->do($stmt). # # NOTES # ######## sub asmcmdshare_do_stmt { my ($dbh, $stmt) = @_; my ($comment) = '/* ASMCMD */'; if (defined ($ENV{'ASMCMD_DEBUG_MODE'})) { asmcmdshare_print_debug_info ($ENV{'ASMCMD_DEBUG_MODE'}, $stmt); } if (!defined ($dbh)) { asmcmdshare_signal_exception(8200, undef); } return $dbh->do($comment . $stmt); } ############################################################################## ######## # NAME # asmcmdshare_getpswd() # # DESCRIPTION # This routine prompts the user for a password when the user uses a connect # string with the -c option but does not specify the password as part of # the connect string. # # PARAMETERS # None. # # RETURNS # The user entered password in a string. # # NOTES ######## sub asmcmdshare_getpswd { my ($msg) = shift; my ($pswd) = ''; my ($chrgot); my ($maxstringput) = $ASMCMDSHARE_MAXPASSWD; $| = 1; $msg = 'Enter password: ' if (!defined($msg)); print $msg; while ($maxstringput--) { $chrgot = asmcmdshare_getchr_noecho(); if ($chrgot =~ m'\D' && $chrgot =~ m'\W') { last; } else { if ($pswd eq '') { $pswd = $chrgot; } else { $pswd = $pswd . $chrgot; } print "*"; } } print "\n"; return $pswd; } ######## # NAME # asmcmdshare_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 asmcmdshare_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 # asmcmdshare_usergroupnumber2name # DESCRIPTION # This routine gets a usergroup number and returns its name. # PARAMETERS # usergroup number, diskgroup_number # RETURNS # The usergroup name. # NOTES ######## sub asmcmdshare_usergroupnumber2name { my ($dbh, $usergroup_number, $dg_number) = @_; my ($usergroup_name); my (@what, @from, @where); my ($sth, $row); @what = ('name'); @from = ('v$asm_usergroup'); @where = ('usergroup_number = ' . $usergroup_number . ' and group_number = ' . $dg_number ); $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where); warn "$DBI::errstr\n" unless defined ($sth); $row = asmcmdshare_fetch($sth); #bug 7420875 corrected column to fetch user_group $usergroup_name = $row->{'NAME'}; asmcmdshare_finish($sth); $usergroup_name = '' if (!defined($usergroup_name)); return $usergroup_name; } ######## # NAME # asmcmdshare_usernumber2name # DESCRIPTION # This routine gets a user number and returns its name. # PARAMETERS # user number # RETURNS # The user name. # NOTES ######## sub asmcmdshare_usernumber2name { my ($dbh, $user_number) = @_; my ($user_name) = ''; my (@what, @from, @where); my ($sth, $row); @what = ('os_name'); @from = ('v$asm_user'); @where = ('user_number = ' . $user_number); $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where); warn "$DBI::errstr\n" unless defined ($sth); $row = asmcmdshare_fetch($sth); $user_name = $row->{'OS_NAME'}; asmcmdshare_finish($sth); $user_name = '' if (!defined($user_name)); return $user_name; } ######## # NAME # asmcmdshare_print_cmds # DESCRIPTION # This routine prints a list of commands in a formatted way for help. # PARAMETERS # An array with the commands to print. # RETURNS # A string with the formatted commands. # NOTES ######## sub asmcmdshare_print_cmds { my (@cmds) = @_; my ($cmd, $t); my (@line); my ($str) = ''; my ($header) = ' '; foreach $cmd(@cmds) { push (@line, $cmd); $t = $header . join(', ', @line); if (length ($t) >= 60) { @line = (); $str = $str . $t . "\n"; } } $str = $str . $t . "\n" if (@line); return ($str); } ######## # NAME # asmcmdshare_check_option_consistency # DESCRIPTION # # PARAMETERS # cmd(IN) - current command being processed # $args_ref(IN) - reference to GetOptions result hash # # RETURNS # None # # NOTES ######## sub asmcmdshare_check_option_consistency { my(%module_cmds) = @_; my ($opt); my ($cmd); my $return_val = 1; ###################################################### # If option not present in global hash # - Add it to global hash # Else if duplicate (k,V) pair # - ignore and move to next option # Else if the value is differnt for same options (key with diff values) # - Error out and exit(check Failed) ####################################################### foreach $cmd(sort(keys %module_cmds)) { foreach $opt (sort(keys %{$module_cmds{$cmd}{flags}})) { my $k = $opt; #remove the '=' in the options which take values $opt =~ s/=.//; if($asmcmdglobal_options{$opt}) { #handle duplicate options,error out if inconsistent if($asmcmdglobal_options{$opt} ne $module_cmds{ $cmd }{ flags}{$k}) { print STDERR "Option '$opt' inconsistent while processing command:". "$cmd, correct the same to continue\n\n"; $return_val=0; goto done; } next; } else { #if not already present add the current option to global options Hash $asmcmdglobal_options{$opt} = $module_cmds{ $cmd }{ flags}{$k}; } } } done: return $return_val; } ######## # NAME # asmcmdshare_handle_deprecation # DESCRIPTION # This function checks whether the options for a command is deprecated # If yes then print out a warning and set the new option for processing. # # PARAMETERS # cmd(IN) - current command being processed # $args_ref(IN) - reference to GetOptions result hash # # RETURNS # None # # NOTES # This function is alled only for commands which have deprecated # options ie. find,ls,lsdsk,lsdg,md_restore,md_backup ######## sub asmcmdshare_handle_deprecation { my ($cmd,$args_ref) = @_; my $iter; my @string; my (%args_depr); my $depr_opt = \%{$asmcmdglobal_deprecated_options{$cmd}}; my @common_keys = grep { exists $depr_opt->{$_} } keys( %{ $args_ref } ); #If there are deprecated options used. if($#common_keys >= 0) { foreach $_(@common_keys) { #Fetch the new option for current deprecated option my $option = $asmcmdglobal_deprecated_options{$cmd}{$_}[1]; # Set the new option if corresponding deprecated option was set # Special checks for 'lsdsk' and 'md_restore' since options expand if($cmd eq 'lsdsk' && $_ eq 'm') { $$args_ref{'member'} = '1' if($$args_ref{$_} eq 'm'); $$args_ref{'candidate'} ='1' if($$args_ref{$_} eq 'c'); } elsif($cmd eq 'md_restore' && $_ eq 't') { $$args_ref{'full'} ='1' if($$args_ref{$_} eq 'full'); $$args_ref{'nodg'} =1 if($$args_ref{$_} eq 'nodg'); $$args_ref{'newdg'} =1 if($$args_ref{$_} eq 'newdg'); } else { $$args_ref{$option} = $$args_ref{$_}; } # Note: There are corner cases where both new option and the # corresponding deprecated options is used together in a # command the value that appears later in the order takes # precedence. # for eg : ASMCMD> md_backup -G DG1 -g DG2 # ASMCMD> md_backup -G DG1 -G DG2 # ASMCMD> md_backup -g DG1 -G DG2 # In the above examples md_backup will backup DG2 not DG1 print STDERR "WARNING:option '$_' is deprecated for '$cmd'\n"; print STDERR "please use '$option'\n\n" if($option ne 'NULL'); } } return; }
Ms-Dos/Windows
Unix
Write backup
jsp File Browser version 1.2 by
www.vonloesch.de