# Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      asmcmdsys - ASM CoMmanD line interface (Sample Module)
#
#    DESCRIPTION
#      This module is a dummy/sample module that provides a working 
#      template for module additions.
#
#    NOTES
#      usage: asmcmdcore [-p] [command]
#
#    MODIFIED  (MM/DD/YY)
#    manuegar   08/19/13 - Backport manuegar_bug-13951456 from main
#    pvenkatr   05/28/13 - Bug# 16859888 - using backtick instead 
#                          "|sqlplus>file" for shutdown
#    apfwkr     01/05/12 - Backport shmubeen_bug-7189804 from main
#    soye       05/11/11 - db spfile ok in spbackup
#    moreddy    01/13/11 - Backport moreddy_bug-8667038 from main
#    amitroy    04/21/10 - BUG 8933243 - USE DIFFERENT CHARACTER IN ASMCMD TO
#                          "HIDE COLUMN DETAILS" INSTEAD OF -H
#    pvenkatr   03/31/10 - Syntax, description, example - all from XML
#    moreddy    03/22/10 - Adding more tracing
#    moreddy    01/19/10 - Adding tracing messages
#    sanselva   12/16/09 - lrg 4067759 fix crsctl/srvctl:for NT
#    sanselva   11/02/09 - NT paths interpreted wrongly for spcopy spmove
#    pvenkatr   09/03/09 - Help message from xml file.
#    amitroy    09/24/09 - adding 'mount' option for STARTUP; bug#8676198
#    amitroy    09/22/09 - modifying the hashkey for 'normal'
#    amitroy    09/15/09 - #8676198 add NORMAL option for SHUTDOWN
#    canuprem   07/29/09 - update dsset and dsget to work in SIHA mode
#    sanselva   07/27/09 - #8683273 asm_diskstring glob string issue
#    lajain     07/23/09 - Display empty string in dsget.
#    sanselva   06/24/09 - correct syntax for command startup
#    canuprem   06/18/09 - options for dsset should be mutually exclusive.
#    gayavenk   06/11/09 - Fix pathname issue in dsset
#    shagarw    06/11/09 - add param to setds to ignore the input ds.
#    canuprem   05/29/09 - modify dsset and dsget
#    sanselva   05/14/09 - spbackup,spmove,spcopy issue when target directory
#    heyuen     04/17/09 - add dsset dsget help
#    heyuen     04/20/09 - add spbackup
#    sanselva   04/12/09 - ASMCMD long options and consistency
#    heyuen     04/06/09 - update spmove to use asmcmdsys_dualget
#    heyuen     03/23/09 - remove iostat
#    heyuen     01/30/09 - update offline commands
#    heyuen     02/09/09 - fix asmcmd_spset
#    heyuen     12/03/08 - add spmove, spset, spget
#    heyuen     10/24/08 - change zones to regions
#    heyuen     10/14/08 - use dynamic modules
#    heyuen     09/19/08 - fix ctrl-c in iostat
#    heyuen     09/17/08 - complete path for spcopy
#    heyuen     08/02/08 - fix startup/shutdown msgs
#    heyuen     07/28/08 - use command properties array
#    heyuen     07/17/08 - fix messages, rewrite iostat
#    heyuen     05/14/08 - add spcopy
#    heyuen     05/19/08 - startup restricted
#    heyuen     05/12/08 - enable iostat continuously
#    heyuen     04/15/08 - bug 6935431, reorder help
#    heyuen     03/26/08 - bug 6753856: fix shutdown hang
#    heyuen     03/17/08 - enable -g for iostat
#    heyuen     02/21/08 - disable iostat for non mounted disks
#    heyuen     09/20/07 - creation
#
############################################################################
#
############################ Functions List #################################
#
#############################################################################

package asmcmdsys;
require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(asmcmdsys_init
                 %asmcmdsys_cmds
                 );

use strict;
use Getopt::Long qw(:config no_ignore_case bundling);
use asmcmdglobal;
use asmcmdshare;
use asmcmdbase;

use List::Util qw[min max];
use File::Find;
use File::Spec;

####################### ASMCMDSYS Global Constants ######################
our ($ASMCMDSYS_SQLPLUS) = "$ENV{'ORACLE_HOME'}/bin/sqlplus";        ## sqlplus
our ($ASMCMDSYS_SQLPLUSW) = "$ENV{'ORACLE_HOME'}\\bin\\sqlplus.exe"; ## windows
our ($ASMCMDSYS_SQLPLUSOPTS) = " -S / as sysasm" ;              ## default opts


####################### ASMCMDSYS Global Variables ######################
our (%asmcmdsys_lsop_header) = ('name'       ,'Group_Name',
                                'operation'  ,'Dsk_Num',
                                'state'      ,'State',
                                'power'      ,'Power',
                                'est_work'   ,'EST_WORK',
                                'est_rate'   ,'EST_RATE',
                                'est_minutes'   ,'EST_TIME',
                                );

our (%asmcmdsys_cmds) = (lsop     => {no_instance => 'True',
                                      
                                      flags       => {'G=s'=>'diskGroup'}    
                                                    },
                         shutdown => {flags       =>  {'abort'=>'abortOperations',
                                                       'immediate'=>'immediate',
                                                       'normal'=>'default'}
                                                    },
                         spcopy   => {no_instance => 'True',

                                      flags       =>  {'u'=>'updateGPnP'}
                                                    },
                         spmove   => {no_instance => 'True'
                                                    },
                         startup  => {flags       =>  {'nomount'=>'noMount',
                                                       'restrict'=>'restrictedMode',
                                                       'pfile=s'=>'parameterFile',
                                                       'mount'=>'default' }
                                                    },
                         spset    => {no_instance => 'True'
                                                    },
                         spget    => {no_instance => 'True'
                                                    },
                         dsset    => {flags       => {'normal'=>'default',
                                                      'profile'=>'scopeGPnP',
                                                      'f' =>'force',
                                                      'parameter'=>'scopeASMInstance'}
                                                     },
			 dsget    => {flags       => {'normal'=>'default',
                                                      'profile'=>'scopeGPnP',
                                                      'f' =>'force',
                                                      'parameter'=>'scopeASMInstance'}
                                                     },
                         spbackup => {no_instance => 'True'
                                                    }
                         );

# PLSQL constants
my ($PLSQL_NUMBER)      = 22;

sub is_asmcmd
{
  return 1;
}

########
# NAME
#   asmcmdsys_init
#
# DESCRIPTION
#   This function initializes the asmcmdsys 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, \&asmcmdsys_process_cmd);
  push (@asmcmdglobal_help_callbacks, \&asmcmdsys_process_help);
  push (@asmcmdglobal_command_list_callbacks, \&asmcmdsys_get_asmcmd_cmds);
  push (@asmcmdglobal_is_command_callbacks, \&asmcmdsys_is_cmd);
  push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmdsys_is_wildcard_cmd);
  push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdsys_syntax_error);
  push (@asmcmdglobal_no_instance_callbacks, \&asmcmdsys_is_no_instance_cmd);
  %asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdsys_cmds);

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


########
# NAME
#   asmcmdsys_process_cmd
#
# DESCRIPTION
#   This routine calls the appropriate routine to process the command 
#   specified by $asmcmdglobal_hash{'cmd'}.
#
# PARAMETERS
#   dbh       (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   1 if command is found in the asmcmdsys module; 0 if not.
#
# NOTES
#   Only asmcmdcore_shell() calls this routine.
########
sub asmcmdsys_process_cmd 
{
  my ($dbh) = @_;
  my ($succ) = 0;

  # Get current command from global value, which is set by 
  # asmcmdsys_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 ASMCMDSYS command.
  my (%cmdhash) = ( startup      => \&asmcmdsys_process_startup ,
                    shutdown     => \&asmcmdsys_process_shutdown ,
                    lsop         => \&asmcmdsys_process_lsop,
                    spcopy       => \&asmcmdsys_process_spcopy,
                    spmove       => \&asmcmdsys_process_spmove,
                    spset        => \&asmcmdsys_process_spset,
                    spget        => \&asmcmdsys_process_spget,
                    dsset        => \&asmcmdsys_process_dsset,
                    dsget        => \&asmcmdsys_process_dsget,
                    spbackup     => \&asmcmdsys_process_spbackup);

  if (defined ( $cmdhash{ $cmd } ))
  {
    # If user specifies a known command, then call routine to process it. #
    $cmdhash{ $cmd }->($dbh);
    $succ = 1;
  }

  return $succ;
}


########
# NAME
#   asmcmdsys_process_startup
#
# DESCRIPTION
#   This function processes the asmcmd command startup.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_startup
{
  my (%args);
  my ($ret);
  my ($qry);
  my ($pfile);
  my ($sqlplus_stmt);

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

  #bug 6994980
  delete $ENV{'ENV'} if (defined($ENV{'ENV'}));

  $sqlplus_stmt = "$ASMCMDSYS_SQLPLUS";
  $sqlplus_stmt = "$ASMCMDSYS_SQLPLUSW" if ($^O =~ /win/i) ; # windows.
  $sqlplus_stmt .= $ASMCMDSYS_SQLPLUSOPTS;

  $qry = "startup";

  #nomount
  if (defined($args{'nomount'}))
  {
    $qry .= " nomount ";
  }

  if (defined($args{'restrict'}))
  {
    $qry .= " restrict ";
  }

  if (defined($args{'pfile'}))
  {
    $qry .= " pfile=" . $args{'pfile'};
  }

  #mount
  if (defined($args{'mount'}))
  {
    $qry .= " mount ";
  }

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

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

  return;
}




########
# NAME
#   asmcmdsys_process_shutdown
#
# DESCRIPTION
#   This function processes the asmcmd command shutdown.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_shutdown
{
  my ($dbh) = shift;
  my (%args);                             # Argument hash used by getopts(). #
  my ($ret);                      # asmcmdsys_parse_int_args() return value. #
  my ($qry);                                          # SQL query statement. #
  my ($sqlplus_stmt);

  my ($tempdir) = File::Spec->tmpdir();
  my ($tempoutputfile) = "$tempdir/shutdowntemp.out";
  my ($tempfilehandle);
  my (@lines) = ();
  
  # Get option parameters, if any.
  $ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  #bug 6994980
  delete $ENV{'ENV'} if (defined($ENV{'ENV'}));

  #sqlplus
  $sqlplus_stmt = "$ASMCMDSYS_SQLPLUS";
  $sqlplus_stmt = "$ASMCMDSYS_SQLPLUSW" if ($^O =~ /win/i) ;  #windows

  # make sure sqlplus binary exists.
  if ( -x $sqlplus_stmt )
  {
    # add standard options.
    $sqlplus_stmt .= $ASMCMDSYS_SQLPLUSOPTS;

    $qry = "SHUTDOWN ";
  
    if (defined($args{'immediate'}))
    {
      $qry .= "IMMEDIATE ";
    }

    if (defined($args{'abort'}))
    {
      $qry .= "ABORT ";
    }

    if (defined($args{'normal'}))
    {
      $qry .= "NORMAL ";
    }

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

    if (defined($dbh))
    {
      asmcmdbase_disconnect($dbh);
    }

    # On Windows opening process using "| process > outfile" with redirecting
    # output does not work.  So, write the query and execute SQLPLUS using
    # backtick.  On all other platforms, continue using the old method.
    if ($^O =~  /win/i)    # windows
    {
      my ($exec);

      # store the query in a temp file
      open ($tempfilehandle, ">$tempoutputfile") or 
                   die "Could not open the temporary file $tempoutputfile";
      print $tempfilehandle  $qry . ";\n" ;
      print $tempfilehandle  "exit\n" ;
      close $tempfilehandle ;

      # complete path
      $exec = $sqlplus_stmt . " \@$tempoutputfile" ;

      eval
      {
        @lines = `$exec`;
      };
      unlink $tempoutputfile;
    }
    else      #non-windows platform
    {
      open SQLPLUS, "| $sqlplus_stmt > $tempoutputfile";
      print SQLPLUS $qry;
      close SQLPLUS;

      eval
      {
        open($tempfilehandle, "<$tempoutputfile") or
                    die "Could not open the temporary file $tempoutputfile";
        @lines = <$tempfilehandle>;
        close $tempfilehandle;
        unlink $tempoutputfile;
      };
    }

    foreach(@lines)
    {
      print $_;
      if($_ =~ m/[A-Z]+-[0-9]+\s*:/)
      {
        $asmcmdglobal_hash{'e'} = -1;
      }
    }
    asmcmdshare_trace (3, "$qry", 'y', 'n');
  }

  return;
}

########
# NAME
#   asmcmdsys_process_lsop
#
# DESCRIPTION
#   This function processes the asmcmd command lsop.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_lsop
{
  my ($dbh) = shift;
  my (@what , @from, $sth, @where, @order);
  my (%min_col_wid, $print_format, $printf_code, @what_print);
  my ($k, $v, @op_list, $h);
  my ($row);
  my (%args);
  my (@tmp_cols);  
  my $gnum; 
  my $ret;
  my @eargs;
  my $dgname;

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

  push (@what, 'v$asm_diskgroup_stat.name as name');
  push (@what, 'v$asm_operation.operation as operation');
  push (@what, 'v$asm_operation.state as state');
  push (@what, 'v$asm_operation.power as power');
  push (@what, 'v$asm_operation.est_work as est_work');
  push (@what, 'v$asm_operation.est_rate as est_rate');
  push (@what, 'v$asm_operation.est_minutes as est_minutes');
 
  push (@from, 'v$asm_operation');
  push (@from, 'v$asm_diskgroup_stat');

  push (@where, 'v$asm_operation.group_number = v$asm_diskgroup_stat.group_number');

  $dgname = $args{'G'};
  if( defined($dgname) )
  {
    $gnum = asmcmdshare_get_gnum_from_gname($dbh, $dgname);
    if( $gnum )
    {
      push (@where, 'v$asm_operation.group_number = '."$gnum");
    }

    else
    {
      @eargs = ($dgname); 
      asmcmdshare_error_msg(8001, \@eargs);
      return;
    }
  }
  $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where, 
                                         \@order);

  asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y') unless defined ($sth);
  
  @tmp_cols = @{$sth->{NAME}};
  @what = ();
  foreach (@tmp_cols)
  {
    push (@what, "\L$_");
  }

  #initialize the min_col_wid array
  foreach(@what)
  {
    $min_col_wid{$_} = 0;
  }

  while (defined($row = asmcmdshare_fetch($sth)))
  {
    my(%op_info) = ();

    while(($k, $v) = each(%{$row}))
    {
      $k =~ tr/[A-Z]/[a-z]/;
      if(!defined($v))
      {
        $v="";
      } 
      $op_info{$k} = $v;

      $min_col_wid{$k} = max($min_col_wid{$k}, length($v));
    }

    push (@op_list, \%op_info);
  }

  #get header length
  foreach (@what)
  {
    $min_col_wid{$_} = max($min_col_wid{$_},
                           length($asmcmdsys_lsop_header{$_}));
  }

  $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, "\'" . $asmcmdsys_lsop_header{$_} . "\'");
    }
    $printf_code .= "(" . join (", ", @what_print) . ")";

    eval $printf_code;
  }

  #print rows
  foreach $h (@op_list)
  {
    $printf_code = "printf \"$print_format\", ";
    @what_print = ();
    foreach (@what)
    {
      push (@what_print, "\'" . $h->{$_} . "\'");
    }
    $printf_code .= "(" . join (", ", @what_print) . ")";
    eval $printf_code;
  }

  return;
}


########
# NAME
#   asmcmdsys_process_spcopy
#
# DESCRIPTION
#   This function processes the asmcmd command spcopy.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_spcopy
{
  my ($dbh) = shift;
  my ($src_path, $dst_path, $src_name, $dst_name);
  my ($sth);
  my ($hdl, $pblk, $fname, $ftyp, $blksz, $openmode);
  my (%norm, $ret);
  my ($update_gpnp) = 0;
  my ($pl_sql_number) = 22;
  my ($spfile_number) = 253;
  my (%args);
  my ($fileType, $blkSz, $fileSz);
  my ($client_mode) = 0;
  my (@eargs);

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

  if (!defined($ARGV[0]) || !defined($ARGV[1]))
  {
    asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

  $src_path = $ARGV[0];
  $src_path =~ /(.*)/;
  $src_path = $1;
 
  $src_name = $src_path;
  #Match the last part in the src_path which is source file name
  #seperator is '/' for linux path and '\' for NT 
  $src_name =~ s,(.*/|.*\\)(.*)$,$2,;  
  
  $dst_path = $ARGV[1];

  # complete relative paths
  # paths begining with "/"(linux) or containing ":\" (NT) are OS paths
  if ($src_path !~ m':\\' and $src_path !~ m'^/')
  {
    $src_path = asmcmdshare_make_absolute($src_path);
  }

  if ($dst_path !~ m':\\' and $dst_path !~ m'^/')
  {
    # Target is ASM
    asmcmdsys_get_target_name($dbh,$src_name,$dst_path,\$dst_name);
  }
  else
  {
    # target is OS
    # if path exists it must be a directory
    if ( -d $dst_path )
    {
      $dst_name = $dst_path . '/' . $src_name;
    }
    else
    {
      $dst_name = $dst_path;
    }
  }
   
  if (!defined $src_path || !defined $dst_name)
  {
    asmcmdshare_trace(3, "NOTE: Either src_path or dst_name is not defined",
                        'n', 'n');
    return;
  }

  $update_gpnp = 1 if defined($args{'u'});
  #do not update gpnp if destination file is not in asm diskgroup
  if ($dst_name !~ m,^\+,) 
  {
    asmcmdshare_trace(3, "NOTE: GPNP is not updated as destination file is "
             ."not in asm diskgroup", 'n', 'n');         
    $update_gpnp = 0;
  }

  $sth = $dbh->prepare(q{
     begin
       dbms_diskgroup.getfileattr(:src_path, :fileType, :fileSz, :blkSz);
     end;
     });

  # bind input params #
  $sth->bind_param( ":src_path", $src_path);

  # bind output params #
  $sth->bind_param_inout( ":fileType", \$fileType, $PLSQL_NUMBER);
  $sth->bind_param_inout( ":fileSz", \$fileSz, $PLSQL_NUMBER);
  $sth->bind_param_inout( ":blkSz", \$blkSz, $PLSQL_NUMBER);

  $ret = $sth->execute();
  if (!defined $fileType || !defined $fileSz || !defined $blkSz)
  {
    @eargs = ( $src_path );
    asmcmdshare_error_msg(8303, \@eargs);
    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);

  $sth = $dbh->prepare(q{
     begin
       dbms_diskgroup.asmcopy(:src_path, :dst_name, :spfile_number, 
                              :fileType, :blkSz, :spfile_number2, 
                              :spfile_type, :client_mode);
     exception when others then
       raise;
     end;
     });

  $sth->bind_param( ":src_path", $src_path);
  $sth->bind_param( ":dst_name", $dst_name);
  $sth->bind_param( ":spfile_number", $spfile_number);
  $sth->bind_param( ":fileType", $fileType);
  $sth->bind_param( ":blkSz", $blkSz);
  $sth->bind_param( ":spfile_number2", $spfile_number);
  $sth->bind_param( ":spfile_type", $fileType);
  $sth->bind_param( ":client_mode", $client_mode);

  $ret = $sth->execute();
  if (!defined($ret))
  {
    asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
    return;
  }

  if ($update_gpnp)
  {

    $ret = asmcmdsys_spset($dbh, $dst_name);

    if (!defined($ret))
    {
      asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
      return;
    }
    asmcmdshare_trace(3, "NOTE: spfile $dst_name location has been set in"
                     ." gpnp profile ", 'n', 'n');
  }

  return;
}


########
# NAME
#   asmcmdsys_process_spmove
#
# DESCRIPTION
#   This function processes the asmcmd command spmove.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_spmove
{
  my ($dbh) = shift;
  my ($attr, $val);
  my ($sth);
  my ($src_path, $dst_path, $src_name, $dst_name,$spfile);
  my (%norm, $ret);
  my ($update_gpnp) = 0;
  my ($pl_sql_number) = 22;
  my ($spfile_number) = 253;
  my (%args);
  my ($fileType, $blkSz, $fileSz);
  my ($client_mode) = 0;
  my ($gname);
  my (@eargs);

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

  if (!defined($ARGV[0]) || !defined($ARGV[1]))
  {
    asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

  $src_path = $ARGV[0];
  $src_path =~ /(.*)/;
  $src_path = $1;

  $src_name = $src_path;
  #Match the last part in the src_path which is source file name
  #seperator is '/' for linux path and '\' for NT
  $src_name =~ s,(.*/|.*\\)(.*)$,$2,;

  $dst_path = $ARGV[1];

  # complete relative paths
  # paths begining with "/"(linux) or containing ":\" (NT) are OS paths
  if($src_path !~ m':\\' and $src_path !~ m'^/')
  {
    $src_path = asmcmdshare_make_absolute($src_path);
  }

  if($dst_path !~ m':\\' and $dst_path !~ m'^/')
  {
    # Target is ASM
    asmcmdsys_get_target_name($dbh,$src_name,$dst_path,\$dst_name);
  }
  else
  {
    # target is OS
    # if path exists it must be a directory
    if ( -d $dst_path )
    {
      $dst_name = $dst_path . '/' . $src_name;
    }
    else
    {
      $dst_name = $dst_path;
    }
  }

  return if (!defined $src_path || !defined $dst_name);

  $sth = $dbh->prepare(q{
     begin
       dbms_diskgroup.getfileattr(:src_path, :fileType, :fileSz, :blkSz);
     end;
     });

  # bind input params #
  $sth->bind_param( ":src_path", $src_path);

  # bind output params #
  $sth->bind_param_inout( ":fileType", \$fileType, $PLSQL_NUMBER);
  $sth->bind_param_inout( ":fileSz", \$fileSz, $PLSQL_NUMBER);
  $sth->bind_param_inout( ":blkSz", \$blkSz, $PLSQL_NUMBER);

  $ret = $sth->execute();
  if (!defined $fileType || !defined $fileSz || !defined $blkSz)
  {
    @eargs = ( $src_path );
    asmcmdshare_error_msg(8303, \@eargs);
    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);

  $sth = $dbh->prepare(q{
     begin
       dbms_diskgroup.asmcopy(:src_path, :dst_name, :spfile_number, 
                              :fileType, :blkSz, :spfile_number2,
			      :spfile_type, :client_mode);
     exception when others then
       raise;
     end;
     });

  $sth->bind_param( ":src_path", $src_path);
  $sth->bind_param( ":dst_name", $dst_name);
  $sth->bind_param( ":spfile_number", $spfile_number);
  $sth->bind_param( ":fileType", $fileType);
  $sth->bind_param( ":blkSz", $blkSz);
  $sth->bind_param( ":spfile_number2", $spfile_number);
  $sth->bind_param( ":spfile_type", $fileType);
  $sth->bind_param( ":client_mode", $client_mode);

  $ret = $sth->execute();
  if (!defined($ret))
  {
    asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
    return;
  }

  # if the current spfile is the active one, update gpnp
  $spfile = asmcmdsys_dualget($dbh, 'asm_spfile');

  if (!defined($spfile) or $spfile ne $dst_name)
  {
    asmcmdshare_trace(3, "NOTE: The current spfile is the active one."
        ." Updating gpnp profile to $dst_name", 'n', 'n');
    $ret = asmcmdsys_spset($dbh, $dst_name);
    if (!defined($ret))
    {
      asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
     
      # UNDO the move if GPnP update is not successful 
      if($dst_name =~ m'^\+')  
      {
        %norm = asmcmdshare_normalize_path($dbh, $src_path, 0, \$ret);
        $gname = asmcmdshare_get_gname_from_ gnum ($dbh, $norm{'gnum'}->[0]);
        asmcmdbase_rm_sql($dbh, $gname, $norm{'path'}->[0], 0);
      }
      else
      {
        #os File
        #Untaint
        $dst_name =~ m/(.*)/;
        $dst_name = $1;
 
        unlink("$dst_name");
      }
      return;
    }
  }
  asmcmdbase_disconnect($dbh) if defined ($dbh);

  #undef since we connect to local asm instance
  $dbh = asmcmdbase_connect(undef);

  #delete the source file
  asmcmdshare_trace(3, "NOTE: Deleting the source file ", 'n', 'n');
  if ($src_path =~ m'^\+')
  {
    # asm spfile
    %norm = asmcmdshare_normalize_path($dbh, $src_path, 0, \$ret);  
    $gname = asmcmdshare_get_gname_from_gnum ($dbh, $norm{'gnum'}->[0]);
    asmcmdbase_rm_sql($dbh, $gname, $norm{'path'}->[0], 0);
  }
  else
  {
    #os spfile
    unlink($src_path);
  }
}


########
# NAME
#   asmcmdsys_process_spbackup
#
# DESCRIPTION
#   This function processes the asmcmd command spbackup.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_spbackup
{
  my ($dbh) = shift;
  my (%args, $ret);
  my ($src_path, $dst_path,$src_name,$dst_name);
  my ($sth, $fileType, $fileSz, $blkSz);
  my ($spfile_type)    = 31;
  my ($db_spfile_type) = 13;
  my ($spbackup_type)  = 33;
  my ($client_mode)    = 1;
  my (@eargs);

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

  if (!defined($ARGV[0]) || !defined($ARGV[1]))
  {
    asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $src_path = $ARGV[0];
  $src_path =~ /(.*)/;
  $src_path = $1;

  $src_name = $src_path;
  #Match the last part in the src_path which is source file name
  #seperator is '/' for linux path and '\' for NT
  $src_name =~ s,(.*/|.*\\)(.*)$,$2,;

  $dst_path = $ARGV[1];
  
  # complete relative paths
  # paths begining with "/"(linux) or containing ":\" (NT) are OS paths
  if($src_path !~ m':\\' and $src_path !~ m'^/')
  {
    $src_path = asmcmdshare_make_absolute($src_path);
  }

  if($dst_path !~ m':\\' and $dst_path !~ m'^/')
  {
    asmcmdsys_get_target_name($dbh,$src_name,$dst_path,\$dst_name);
  }
  else
  {
      # target is OS
      # if path exists it must be a directory
      if ( -d $dst_path )
      {
        $dst_name = $dst_path . '/' . $src_name;
      }
      else
      {
        $dst_name = $dst_path;
      }
  }
 
  if (!defined $src_path || !defined $dst_name)
  {
    asmcmdshare_trace(3, "NOTE: Either src_path or dst_name is not defined",
                                                    'n', 'n');
    return;
  }

  $sth = $dbh->prepare(q{
     begin
       dbms_diskgroup.getfileattr(:src_path, :fileType, :fileSz, :blkSz);
     end;
     });

  # bind input params #
  $sth->bind_param( ":src_path", $src_path);

  # bind output params #
  $sth->bind_param_inout( ":fileType", \$fileType, $PLSQL_NUMBER);
  $sth->bind_param_inout( ":fileSz", \$fileSz, $PLSQL_NUMBER);
  $sth->bind_param_inout( ":blkSz", \$blkSz, $PLSQL_NUMBER);

  $ret = $sth->execute();
  if (!defined $fileType || !defined $fileSz || !defined $blkSz 
      || ($fileType != $spfile_type && $fileType != $db_spfile_type))
  {
    @eargs = ( $src_path );
    asmcmdshare_error_msg(8303, \@eargs);
    asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y') if(defined($DBI::errstr));
    return;
  }
  
  # reconnect
  asmcmdbase_disconnect($dbh) if defined ($dbh);

  #undef since we connect to local asm instance
  $dbh = asmcmdbase_connect(undef);

  $sth = $dbh->prepare(q{
     begin
       dbms_diskgroup.asmcopy(:src_path, :dst_name, :spfile_number, 
                              :fileType, :blkSz, :dst_fnum, :dst_ftype,
			      :client_mode);
     exception when others then
       raise;
     end;
     });

  $sth->bind_param(":src_path", $src_path);
  $sth->bind_param(":dst_name", $dst_name);
  $sth->bind_param(":spfile_number", 0);
  $sth->bind_param(":fileType", $fileType);
  $sth->bind_param(":blkSz", $blkSz);
  $sth->bind_param(":dst_fnum", 0);
  $sth->bind_param(":dst_ftype", $spbackup_type);
  $sth->bind_param(":client_mode", $client_mode);

  $ret = $sth->execute();

  if (!defined($ret))
  {
    asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
    return;
  }

  asmcmdbase_disconnect($dbh) if defined ($dbh);

  #undef since we connect to local asm instance
  $dbh = asmcmdbase_connect(undef);
}



########
# NAME
#   asmcmdsys_get_target_name
#
# DESCRIPTION
#   This function gets the complete target file name when the
# destination is a directory or diskgroup for spcopy/spmove/spbackup.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#   src_name (IN) - spfile name
#   dst_path (IN) - destination path
#   dst_name (IN/OUT) - destination pathname
#
# RETURNS
#   Null.
#
# NOTES
# 
########
sub asmcmdsys_get_target_name
{
  my ($dbh,$src_name,$dst_path,$dst_name) = @_;
  my (@paths, @ref_ids, @par_ids, @dg_nums, @entry_list, $name ,$ret);
  my (%norm);
  my (@eargs);
  
  # target is ASM
  # if target is a relative path, complete it to be a valid ASM path
  %norm = asmcmdshare_normalize_path($dbh, $dst_path, 1, \$ret);

  # path exists, must be a directory, and need to complete it with
  # the source name
  if ( $ret == 0 )
  {
    # complete the path
    $dst_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,;

    if($ref_ids[0] == -1)
    {
      @eargs = ($dst_path);
      asmcmdshare_error_msg(8014, \@eargs);
      return;
    }
    asmcmdshare_get_subdirs($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] != -1) && 
         (($ref_ids[0] == $dg_nums[0] << 24 ) ||
          ($entry_list[0]->{'alias_directory'} eq 'Y')))
    {
      $$dst_name = $dst_path . '/' . $src_name;
    }
    elsif($entry_list[0]->{'alias_directory'} eq 'N')
    {
      $$dst_name = $dst_path;
    }
    else
    {
      @eargs = ($dst_path);
      asmcmdshare_error_msg(8014, \@eargs);
      return;
    }
   }
   else
   {
     $$dst_name = $dst_path;
   }
}


########
# NAME
#   asmcmdsys_process_spget
#
# DESCRIPTION
#   This function processes the asmcmd command spget.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_spget
{
    my ($dbh) = shift;
    my ($spfile);
    
    $spfile = asmcmdsys_dualget($dbh, 'asm_spfile'); 

    if (defined($spfile))
    {
	print $spfile ."\n";
    }
}


########
# NAME
#   asmcmdsys_process_dsset
#
# DESCRIPTION
#   This function processes the asmcmd command dsset.
#
#  USAGE
#   dsset [--normal] [--parameter] [--profile [-f]] pathname
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   second arg to gpnpsetds is false so that user supplied diskstring
#   gets pushed to gpnp profile.
#########
sub asmcmdsys_process_dsset
{
  
    my ($dbh) = @_;
    my ($ret, %args, $sth); 
    my ($forced);
    my ($qry);
    my ($gpnptool);
    my ($gpnp_sign);
    my ($gpnp_edit);
    my ($gpnp_seq);
    my ($prof_path);
    my ($peer_path);
    my ($seq_num);
    my ($buf);
    my ($normal);
    my ($num);
    my ($test);
    my ($status);
    my ($crs_home);
    my ($crsctl);
    my ($srvctl);
    my ($update);
    my ($path) = 0;
    my (@buff_arr);
    my ($check_siha);
    my (@eargs);

    # Problem:  If dsset is used multiple times in a asmcmd session
    # pathname gets messed up. Reason being, the buffer cache used for
    # storing pathname in SQL is not cleared on every call.
    # This workaround could be removed if the PL/SQL buffer problem is 
    # fixed.

    # reconnect
    asmcmdbase_disconnect($dbh) if defined ($dbh);

    #undef since we connect to local asm instance
    $dbh = asmcmdbase_connect(undef);
    
    $ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args); 
    return unless defined ($ret);

    #path should be specified
    if (!defined($ARGV[0]))
    {
      asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }

    $num = keys(%args);
    
    $forced = 0;
    $normal = 0;

    #dsset is executed in normal mode, if no option is specified
    if($num == 0)
    {
      $normal = 1;
    }

    if(defined($args{'normal'}))
    {
      $normal = 1;
    }

    if(defined($args{'f'}))
    {
      $forced = 1;
    }

    $path = $ARGV[0];
    # asm_diskstring should be of the format 'path1','path2','path3',.....
    $path =~ s/,/','/g;
    
    #ASM instance is required for all the options other than '--profile -f'
    if(!defined($dbh))
    {
      if(!(defined($args{'profile'}) && $forced == 1))
      {
        asmcmdshare_error_msg(8102, undef);
        return;
      }
    }

    if($normal == 1)
    {
      #force option is not supported with normal optoin
      if($num > 1)
      {
        asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
        return;
      }
      $qry = "alter system set asm_diskstring='" . $path . "' SCOPE=BOTH";
      
      $ret = asmcmdshare_do_stmt($dbh,$qry);

      if (!defined ($ret))
      {
        asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
      }
      return;
    }

    if(defined($args{'profile'}))
    {
      # the only option allowed with profile is -f
      if($num > 2 || ($num == 2 && $forced != 1))
      {
        asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
        return;
      }

      if($forced == 1)
      {
        $crsctl = "$ENV{'ORACLE_HOME'}/bin/crsctl";
        #The path in WIN is ORACLE_HOME/bin/crsctl.exe
        $crsctl .= ".exe" if($^O =~ /win/i);

        # Make sure the crsctl binary exists and can be executed. 
        if (! -x $crsctl)
        {
          #If not, try at a second locaction.
          $crsctl = "$ENV{'ORACLE_HOME'}/rdbms/bin/crsctl";
          $crsctl .= ".exe" if($^O =~ /win/i);

          if (! -x $crsctl)
          {
            @eargs = ($crsctl);
            asmcmdshare_error_msg(8313, \@eargs);
            return;
          }
        }

        #untaint crsctl
        $crsctl =~ /([^\n^\r^\t]+)/;
        $crsctl = $1;
     
        my $css = "$crsctl check css";

        asmcmdshare_trace(3, "NOTE: Checking the status of cluster.. ",
                             'y', 'n');
        eval
        {
          $buf = `$css`;
        };

        if($@ ne '')
        {
          asmcmdshare_error_msg(8309, undef);
          return;
        }

        # Check for the number at CRS-xxxx in the output.
        if($buf =~ /([^\d]+)([^:]+)/)
        {
           $status = $2;
           if(defined($status))
           {
            if($status == 4529)
             {
               #output is CRS-4529: Cluster Synchronization Services is online
               asmcmdshare_error_msg(8308, undef);
               return;
             }
           }
           else
           {
             asmcmdshare_error_msg(8309, undef);
             return;
           }
        }
        else
        {
          asmcmdshare_error_msg(8309, undef);
          return;
        }

        $check_siha = "$crsctl status resource ora.crsd -init -g";
        
        eval 
        {
          $buf = `$check_siha`;
        };

        if($@ ne '')
        {
          asmcmdshare_error_msg(8315, undef);
          return;
        }

        # Get the error code form the o/p
        if($buf =~ /([^\d]+)([^:]+)/)
        {
          $status = $2;
          if(!defined($status))
          {
            asmcmdshare_error_msg(8315, undef);
            return;
          }
        }
        # siha mode, crsd is not registered
        # CRS-212: Resource 'ora.crsd' is not registered.
        if($status == 212)
        {
          $srvctl = "$ENV{'ORACLE_HOME'}/bin/srvctl";
          #path in Win is ORACLE_HOME/bin/srvctl.bat
          $srvctl .= ".bat" if ($^O =~ /win/i);

          # Make sure the srvctl binary exists and can be executed. 
          if (! -x $srvctl)
          {
            #If not, try at a second locaction.
            $srvctl = "$ENV{'ORACLE_HOME'}/srvm/bin/srvctl";
            #path in Win is ORACLE_HOME/bin/srvctl.bat
            $srvctl .= ".bat" if ($^O =~ /win/i);

            if (! -x $srvctl)
            {
              @eargs = ($srvctl);
              asmcmdshare_error_msg(8316, \@eargs);
              return;
            }
          }

          $update = " $srvctl modify asm -d " . $path;
          $update =~ /([^\n^\r^\t]+)/;
          $update = $1;

          eval 
          {
            $buf = `$update 2>&1`;
          };
          if($@ ne '')
          {
            asmcmdshare_error_msg(8314, undef); 
          }

          return;
        }
        # cluster mode
        # CRS-4003 :Resource 'ora.crsd' is registered. 
        elsif($status == 4003)
        {
          #HOSTNAME should be defined
          if(!defined($ENV{'HOSTNAME'}))
          {
            $buf = "HOSTNAME";
            @eargs = ($buf);
            asmcmdshare_error_msg(8318, \@eargs);
            return;
          }

        $gpnptool = "$ENV{'ORACLE_HOME'}/bin/gpnptool"; 
        # Adjust path for win
        $gpnptool .= ".exe" if($^O =~ /win/i);

         # Make sure the gpnptool binary exists and can be executed. 
         if (! -x $gpnptool)
         {
           #If not, try at a second locaction.
           $gpnptool = "$ENV{'ORACLE_HOME'}/has/bin/gpnptool";
           # Adjust path for win
           $gpnptool .= ".exe" if($^O =~ /win/i);

           if (! -x $gpnptool)
           {
             @eargs = ($gpnptool);
             asmcmdshare_error_msg(8305, \@eargs);
             return;
           }
         }

        # The location of profile.xml is different for production and
        # dev environment now. The current environment is being determined by
        # checking whethe ADE_VIEW_ROOT is set or not. Bug 8579922 is filed
        # for this issue.
  
        $test = "$ENV{'ORACLE_HOME'}/gpnp/$ENV{'HOSTNAME'}/" . 
                "profiles/peer/profile.xml";

        if( -f $test) #production environment
        {
          $crs_home = "$ENV{'ORACLE_HOME'}";
        }
        else   # dev env
        {
          $test = "$ENV{'ORACLE_HOME'}/has_work/gpnp/$ENV{'HOSTNAME'}/" . 
                  "profiles/peer/profile.xml";
          if(-f $test)
          {
            $crs_home = "$ENV{'ORACLE_HOME'}/has_work";
          }
          else
          {
            asmcmdshare_error_msg(8319,undef);
            return;
          }

        }

        #if(!defined($ENV{'ADE_VIEW_ROOT'}))
        #{
        #  $crs_home = "$ENV{'ORACLE_HOME'}";
        #}
        #else
        #{
        #  $crs_home = "$ENV{'ORACLE_HOME'}/has_work";
        #}
        
        $prof_path = "$crs_home/gpnp/$ENV{'HOSTNAME'}/" . 
                     "profiles/peer/profile.xml";

        #untaint prof_path
        $prof_path =~ /([^\n^\r^\t]+)/;
        $prof_path = $1;

        $peer_path = "$crs_home/gpnp/$ENV{'HOSTNAME'}/wallets/peer";
        
        #untaint peer_path
        $peer_path =~ /([^\n^\r^\t]+)/;
        $peer_path = $1;

        # get the sequence number of the profile.
        $gpnp_seq = "$gpnptool getpval -p=$prof_path -prf_sq -o-";
        
        #untaint gpnp_seq
        $gpnp_seq =~ /([^\n^\r^\t]+)/;
        $gpnp_seq = $1;

        asmcmdshare_trace(3, "NOTE: Setting the gpnp profile sequence "
                                 ."number.. ", 'n', 'n'); 
        eval
        {
          $seq_num = `$gpnp_seq`;
        };
        if($@ ne '')
        {
          asmcmdshare_error_msg(8310, undef);
        }

        $seq_num = $seq_num + 3;

        $gpnp_edit = "$gpnptool edit -p=$prof_path -o=$prof_path -ovr " .
                    "-asm_dis=$path -prf_sq=$seq_num";
        
        #untaint gpnp_edit
        $gpnp_edit =~ /([^\n^\r^\t]+)/;
        $gpnp_edit = $1;

        asmcmdshare_trace(3, "NOTE: Editing the gpnp profile.. ", 'n', 'n');
        eval
        {
          $buf = `$gpnp_edit 2>&1`;
        };

        #check whether gpnp_edit succeeded.
        if ($@ ne '')
        {
          asmcmdshare_error_msg(8306, undef);
          return;
        }

        $gpnp_sign = "$gpnptool sign -p=$prof_path -o=$prof_path -ovr " .
          "-w=file:$peer_path";

        #untaint gpnp_sign
        $gpnp_sign =~ /([^\n^\r^\t]+)/;
        $gpnp_sign = $1;

        asmcmdshare_trace(3, "NOTE: Signing the gpnp profile.. ", 'n', 'n');
        # sign the gpnp profile once its edited.
        eval
        {
           @buff_arr = `$gpnp_sign 2>&1`;
        };
        
        #check whether gpnp_sign succeeded.
        if ($@ ne '')
        {
          asmcmdshare_error_msg(8307, undef);
          return;
        }
        }
        # could not determine mode
        else
        {
          asmcmdshare_error_msg(8315, undef);
          return;
        }
      }
      else
      {
        $sth = $dbh->prepare(q{
           begin
             dbms_diskgroup.gpnpsetds(:ds_path,0);
           exception when others then
              raise;
           end;
         });

        $sth->bind_param(":ds_path", $path);
      
        $ret = $sth->execute();  

        if (!defined ($ret))
         {
          asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
         }

      }
      return;
    }

    #parameter option.
    if(defined($args{'parameter'}))
    {
      #no other option should be given when parameter option is suuplied.
      if($num != 1)
      {
        asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
        return;
      }

      $qry = "alter system set asm_diskstring='" . $path . "' SCOPE=MEMORY";
      
      $ret = asmcmdshare_do_stmt($dbh,$qry);

      if (!defined ($ret))
        {
          asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
        }
      return;
    }
}

########
# NAME
#   asmcmdsys_process_dsget
#
# DESCRIPTION
#   This function processes the asmcmd command dsget.
#
# USAGE
#   dsget [[--normal] [--profile [-f]] [--parameter]]
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
#########
sub asmcmdsys_process_dsget
{
    my ($dbh) = shift;
    my ($diskstring, $sth, $ret, $row, $val);
    my (@what, @from, @where);
    my ($num);
    my ($buf);
    my ($test);
    my ($parameter);
    my ($profile);
    my ($forced);
    my ($status);
    my (%args);
    my ($gpnptool);
    my ($gpnp_exec);
    my ($prof_path);
    my ($crs_home);
    my ($crsctl);
    my ($srvctl);
    my ($get_dis);
    my ($check_siha);
    my (@eargs);

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

    $num       = keys(%args);
    $forced    = 0;
    $parameter = 0;
    $profile   = 0;

    if(defined($args{'f'}))
    {
      $forced = 1;
    }

    # normal mode should return both profile and parameter settings.
    # normal is the default option
    if(defined($args{'normal'}) || $num == 0)
    {
      if($num > 1)
      {
        asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
        return;
      }
      $parameter = 1;
      $profile = 1;
    }

    if(defined($args{'profile'}))
    {
      $profile = 1;
    }

    if(defined($args{'parameter'}))
    {
      $parameter = 1;
    }

    #ASM instance is required for all the options other than '--profile -f'
    if(!defined($dbh))
    {
      if(!(defined($args{'profile'}) && $forced == 1))
      {
        asmcmdshare_error_msg(8102, undef);
        return;
      }
    }

    if($parameter == 1)
    {
      #force option should error out when used with parameter
      if($num > 1)
      {
        asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
        return;
      }  
 
      push(@what, 'value');
      push(@from, 'v$parameter');
      push(@where, 'name=\'asm_diskstring\'');
    
      $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where);

      asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y') unless defined ($sth);
   
      $row = asmcmdshare_fetch($sth);
      asmcmdshare_finish($sth);

      $val = $row->{VALUE};

      #A row is always returned because asm_diskstring always exists.
      #If the value of row is NULL, then DBI returns undefined.
      #If the value of row is "", then DBI returns "".
      #It looks like empty string("") is being stored as NULL and hence DBI 
      #returns undefined value.
      
      #Adding code below to display empty string in dsget.
      $val = "" if !defined($val);
      
      if(defined($val))
      {
        print STDOUT "parameter:" . $val . "\n";
      }

    }

    if($profile == 1)
    { 
      # The only option allowed with profile is -f.
      if($num > 2 || ($num == 2 && $forced != 1))
      {
        asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
        return;
      } 

      # force with profile option.
      if($forced == 1)
      {
        $crsctl = "$ENV{'ORACLE_HOME'}/bin/crsctl";
        #The path in WIN is ORACLE_HOME/bin/crsctl.exe
        $crsctl .= ".exe" if($^O =~ /win/i);

        # Make sure the crsctl binary exists and can be executed. 
        if (! -x $crsctl)
          {
            #If not, try at a second locaction.
            $crsctl = "$ENV{'ORACLE_HOME'}/rdbms/bin/crsctl";
            #The path in WIN is ORACLE_HOME/bin/crsctl.exe
            $crsctl .= ".exe" if($^O =~ /win/i);

            if (! -x $crsctl)
              {
                @eargs = ($crsctl);
                asmcmdshare_error_msg(8313, \@eargs);
                return;
              }
          }

        #untaint crsctl
        $crsctl =~ /([^\n^\r^\t]+)/;
        $crsctl = $1;

        my $css = "$crsctl check css";
        asmcmdshare_trace(3, "NOTE: Checking the status of cluster.. ",
                                         'y', 'n');
        eval
        {
            $buf = `$css`;
        };

        if($@ ne '')
        {
            asmcmdshare_error_msg(8309, undef);
            return;
        }

        # Check for the number at CRS-xxxx in the output.
        # Could not check cluster status if anything fails.
        if($buf =~ /([^\d]+)([^:]+)/)
        {
          $status = $2;
          if(defined($status))
          {
            if($status == 4529)
            {
              # CRS-4529: Cluster Synchronization Services is online
              asmcmdshare_error_msg(8308, undef);
              return;
            }
          }
          else
          {
            asmcmdshare_error_msg(8309, undef);
            return;
          }
        }
        else
        {
          asmcmdshare_error_msg(8309, undef);
          return;
        }
        # gpnpd does not come up in SIHA mode
        $check_siha = "$crsctl status resource ora.crsd -init -g";
        
        eval 
        {
          $buf = `$check_siha`;
        };
        if($@ ne '')
        {
          asmcmdshare_error_msg(8315, undef);
          return;
        }

        #get the erro code
        if($buf =~ /([^\d]+)([^:]+)/)
        {
          $status = $2;
          if(!defined($status))
          {
             asmcmdshare_error_msg(8315, undef);
             return;
          }
        }

        # siha mode, crsd is not registered        
        if($status == 212)
        {
          $srvctl = "$ENV{'ORACLE_HOME'}/bin/srvctl";
          #path in Win is ORACLE_HOME/bin/srvctl.bat
          $srvctl .= ".bat" if ($^O =~ /win/i);

          # Make sure the srvctl binary exists and can be executed. 
          if (! -x $srvctl)
          {
            #If not, try at a second locaction.
            $srvctl = "$ENV{'ORACLE_HOME'}/srvm/bin/srvctl";
            #path in Win is ORACLE_HOME/bin/srvctl.bat
            $srvctl .= ".bat" if ($^O =~ /win/i);

            if (! -x $srvctl)
            {
              @eargs = ($srvctl);
              asmcmdshare_error_msg(8316, \@eargs);
              return;
            }
          }

          #untaint srvctl
          $srvctl =~ /([^\n^\r^\t]+)/;
          $srvctl = $1; 
       
          $get_dis = "$srvctl config asm";
          eval 
          {
            $buf = `$get_dis 2>&1`;
          };

          if($@ ne '')
          {
            asmcmdshare_error_msg(8317, undef);
            return;
          }

          # Extract the diskstring from the output
          if($buf =~ /string:([^\n]+)/)
          {
            if(defined($1))
            {
              print STDOUT "profile:" . $1 . "\n";
            }
          }
          else
          {
            asmcmdshare_error_msg(8317, undef);
          }
          return;
        }
        #cluster mode
        elsif($status == 4003)
        {
          #HOSTNAME should be defined
          if(!defined($ENV{'HOSTNAME'}))
          {
            $buf = "HOSTNAME";
            @eargs = ($buf);
            asmcmdshare_error_msg(8318, \@eargs);
            return;
          }

        $gpnptool = "$ENV{'ORACLE_HOME'}/bin/gpnptool";
        # Adjust path for win
        $gpnptool .= ".exe" if($^O =~ /win/i);

        # Make sure the gpnptool binary exists and can be executed. 
        if (! -x $gpnptool)
        {
          #If not, try at a second locaction.
          $gpnptool = "$ENV{'ORACLE_HOME'}/has/bin/gpnptool";
          # Adjust path for win
          $gpnptool .= ".exe" if($^O =~ /win/i);

          if (! -x $gpnptool)
          {
            @eargs = ($gpnptool);
            asmcmdshare_error_msg(8305, \@eargs);
            return;
          }
        } 
        
        # The location of profile.xml is different for production and
        # dev environment now. The current environment is being determined by
        # checking whethe ADE_VIEW_ROOT is set or not. Bug 8579922 is filed
        # for this issue.

        $test = "$ENV{'ORACLE_HOME'}/gpnp/$ENV{'HOSTNAME'}/" . 
                "profiles/peer/profile.xml";

        if( -f $test)  #production env
        {
          $crs_home = "$ENV{'ORACLE_HOME'}";
        }
        else    #dev env
        {
          $test = "$ENV{'ORACLE_HOME'}/has_work/gpnp/$ENV{'HOSTNAME'}/" . 
            "profiles/peer/profile.xml";

          if(-f $test)
          {
            $crs_home = "$ENV{'ORACLE_HOME'}/has_work";
          }
          else
          {
            asmcmdshare_error_msg(8319, undef);
            return;
          }

        }     

        #if(!defined($ENV{'ADE_VIEW_ROOT'}))
        #{
        #    $crs_home = "$ENV{'ORACLE_HOME'}";
        #}
        #else
        #{
        #    $crs_home = "$ENV{'ORACLE_HOME'}/has_work";
        #}

        #untaint gpnptool
        $gpnptool =~ /([^\n^\r^\t]+)/;
        $gpnptool = $1;

        $prof_path = "$crs_home/gpnp/$ENV{'HOSTNAME'}/" . 
                     "profiles/peer/profile.xml";
        
        #untaint prof_path
        $prof_path =~ /([^\n^\r^\t]+)/;
        $prof_path = $1;

        $gpnp_exec = "$gpnptool getpval -p=$prof_path -asm_dis -o-";
 
        $gpnp_exec =~ /([^\n^\r^\t]+)/;
        $gpnp_exec = $1;

        eval
        {
          $buf = `$gpnp_exec`;
        };

        if($@ ne '')
        {
          asmcmdshare_error_msg(8307, undef);
          return;
        }

        if(defined($buf))
        {
          print STDOUT "profile:" . $buf . "\n";
        }
       }
      else
      {
          asmcmdshare_error_msg(8315, undef);
          return;
        }
      }
      else
      {
        $diskstring = asmcmdsys_dualget($dbh, 'asm_diskstring'); 
    
        #Adding code below to display empty string in dsget.
        $diskstring = "" if !defined($diskstring);
        
        if(defined($diskstring))
        {  
          print STDOUT "profile:" . $diskstring ."\n";
        }
      }
      return;
    }
}

########
# NAME
#   asmcmdsys_dualget
#
# DESCRIPTION
#   This function returns the requested valure from sys_cluster_properties.
#
# USAGE
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null. 
#   $what (IN) - property/value requested
#   $val  (OUT) -
#
# RETURNS
#   Requested value querying from sys_context
#
#########

sub asmcmdsys_dualget
{
    my ($dbh, $what) = @_;
    my ($stmt, $ret, $val, $row);
=pod
    We use 4000 because asm_diskstring can be more than the default max(256)
    The default maximum size can be overriden by specifying the optional length 
    parameter, which must be a NUMBER or a value that can be implicitly 
    converted to NUMBER.The valid range of values is 1 to 4000 bytes. If you 
    specify an invalid value, then Oracle Database ignores it and uses the 
    default.For more details see SYS_CONTEXT in SQL Reference.
=cut
    $stmt = "select sys_context('sys_cluster_properties','" . $what . "',4000) ".
            "as val from dual";
    $ret = asmcmdshare_do_select($dbh, $stmt);

    if (!defined($ret))
    {
        asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
	return;
    }

    $row = asmcmdshare_fetch($ret);
    asmcmdshare_finish($ret);
    $val = $row->{VAL};
    return $val;
}


########
# NAME
#   asmcmdsys_spset
#
# DESCRIPTION
#   This function sets the spfile location in the gPnP profile.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#   spfile(IN) - spfile path.
#
# RETURNS
#   Path of the spfile.
#
# NOTES
#   The caller is responsible to check that the file exists.
#
########
sub asmcmdsys_spset
{  
  my ($dbh, $path) = @_;
  my ($ret, $sth);

  $sth = $dbh->prepare(q{
    begin
      dbms_diskgroup.gpnpsetsp(:spfile_path);
    exception when others then
      raise;
    end;
  });

  $sth->bind_param( ":spfile_path", $path);

  $ret = $sth->execute();

  return $ret;
}


########
# NAME
#   asmcmdsys_process_spset
#
# DESCRIPTION
#   This function processes the asmcmd command spset.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdsys_process_cmd() calls this function.
########
sub asmcmdsys_process_spset
{
  my ($dbh) = shift;
  my ($attr, $val);
  my ($ret, %args, $sth, $row, $dst_path);
  my ($spfile);
  my (@what , @from, @where, @order);
  $ret = asmcmdsys_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  if (!defined($ARGV[0]))
  {
    asmcmdsys_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

  $spfile = $ARGV[0];

  $ret = asmcmdsys_spset($dbh, $spfile);

  if (!defined($ret))
  {
    asmcmdshare_trace(1, "$DBI::errstr", 'y', 'y');
    return;
  }

  return;
}


########
# NAME
#   asmcmdsys_is_help
#
# DESCRIPTION
#   This function is the help function for the ASMCMDSYS module.
#
# PARAMETERS
#   command     (IN) - display the help message for this command.
#
# RETURNS
#   1 if command found; 0 otherwise.
########
sub asmcmdsys_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 (asmcmdsys_is_cmd ($command)) 
  {                              # User specified a command name to look up. #
    $desc = asmcmdshare_get_help_desc($command);
    print STDOUT "$desc\n";
    $succ = 1;
  }

  return $succ;
}



########
# NAME
#   asmcmdsys_is_cmd
#
# DESCRIPTION
#   This routine checks if a user-entered command is one of the known
#   ASMCMD internal commands that belong to the ASMCMDSYS module.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is one of the known commands, false otherwise.
########
sub asmcmdsys_is_cmd 
{
  my ($arg) = shift;

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


########
# NAME
#   asmcmdsys_is_wildcard_cmd
#
# DESCRIPTION
#   This routine determines if an ASMCMDSYS command allows the use 
#   of wild cards.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is a command that can take wildcards as part of its argument, 
#   false otherwise.
########
sub asmcmdsys_is_wildcard_cmd 
{
  my ($arg) = shift;
  my (%cmdhash); # Empty hash; no ASMCMDSYS command supports wildcards. # 

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

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

  return !defined ($asmcmdsys_cmds{ $arg }) ||
    !defined ($asmcmdsys_cmds{ $arg }{ no_instance });
}

########
# NAME
#   asmcmdsys_parse_int_args
#
# DESCRIPTION
#   This routine parses the arguments for flag options for ASMCMDSYS
#   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 ASMCMDSYS internal command.
########
sub asmcmdsys_parse_int_args 
{
  my ($cmd, $args_ref) = @_;
  my ($key);
  my (@string);
  #build the list of options to parse using GetOptions
  if($asmcmdsys_cmds{ $cmd }{ flags })
  {
    foreach $key(keys %{$asmcmdsys_cmds{ $cmd }{ flags }})
    {
      push(@string, $key);
    }
  }

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

########
# NAME
#   asmcmdsys_syntax_error
#
# DESCRIPTION
#   This function prints the correct syntax for a command to STDERR, used 
#   when there is a syntax error.  This function is responsible for 
#   only ASMCMDSYS commands.
#
# PARAMETERS
#   cmd   (IN) - user-entered command name string.
#
# RETURNS
#   1 if the command belongs to this module; 0 if command not found.
#
# NOTES
#   These errors are user-errors and not internal errors.  They are of type
#   record, not signal.  
# 
#   N.B. Functions in this module can call this function directly, without
#   calling the asmcmdshare::asmcmdshare_syntax_error equivalent.  The
#   latter is used only by the asmcmdcore module.
########
sub asmcmdsys_syntax_error 
{
  my ($cmd) = shift;
  my ($cmd_syntax);                               # Correct syntax for $cmd. #
  my ($succ) = 0;
  #display syntax only if the command belongs to this module
  if (asmcmdsys_is_cmd($cmd))
  {
    $cmd_syntax = asmcmdshare_get_help_syntax($cmd);  # Get syntax for $cmd. #
    $cmd_syntax = asmcmdshare_trim_str ($cmd_syntax);   # Trim blank spaces #
    if (defined ($cmd_syntax))
    {
      print STDERR 'usage: ' . $cmd_syntax . "\n";
      print STDERR 'help:  help ' . $cmd . "\n";
      $succ = 1;
    }
  }

  return $succ;
}


########
# NAME
#   asmcmdsys_get_asmcmd_cmds
#
# DESCRIPTION
#   This routine constructs a string that contains a list of the names of all 
#   ASMCMD internal commands and returns this string.
#
# PARAMETERS
#   None.
#
# RETURNS
#   A string contain a list of the names of all ASMCMD internal commands.
#
# NOTES
#   Used by the help command and by the error command when the user enters
#   an invalid internal command.
#
#   IMPORTANT: the commands names must be preceded by eight (8) spaces of
#              indention!  This formatting is mandatory.
########
sub asmcmdsys_get_asmcmd_cmds 
{
  return asmcmdshare_print_cmds(sort(keys %asmcmdsys_cmds));
}
1;
