# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. 
#
#    NAME
#      asmcmdug - ASM CoMmanD line interface (users and groups)
#
#    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)
#    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/18/10 - Adding tracing messages
#    pvenkatr   09/03/09 - Help message from xml file
#    sanselva   07/07/09 - fix --privilege flag in GetOptions for orapwusr
#    sanselva   06/24/09 - fix help text msg for orapwusr,grpmod
#    sanselva   04/27/09 - add diskgroup filter in 'groups' command,remove
#                          -R from chmod,chgrp,chown & handle incorrect alias
#    sanselva   04/06/09 - ASMCMD long options and consistency
#    heyuen     03/23/09 - add rmusr -r
#    heyuen     10/14/08 - use dynamic modules
#    heyuen     09/10/08 - use dgname for lsusr
#    heyuen     07/28/08 - use command properties array
#    heyuen     05/22/08 - fix lsgrp
#    heyuen     04/30/08 - bug 6994254: supress aditional error messages
#    heyuen     04/22/08 - add order to lsusr and lsgrp
#    heyuen     04/15/08 - reorder help messages
#    heyuen     03/30/08 - add passwd
#    heyuen     03/11/08 - rename to mkusr, mkug, rmusr, rmug, chug
#    heyuen     02/12/08 - change help messages
#    heyuen     09/20/07 - creation
#
#############################################################################
#
############################ Functions List #################################
#
# asmcmdug_init
# asmcmdug_process_cmd 
# asmcmdug_process_help 
# asmcmdug_is_cmd 
# asmcmdug_is_wildcard_cmd 
# asmcmdug_is_no_instance_cmd 
# asmcmdug_parse_int_args 
# asmcmdug_syntax_error 
# asmcmdug_get_cmd_syntax 
# asmcmdug_get_asmcmd_cmds 
# asmcmdug_process_lsusr
# asmcmdug_process_mkgrp
# asmcmdug_process_rmgrp
# asmcmdug_process_mkusr
# asmcmdug_process_rmusr
# asmcmdug_process_lsgrp
# asmcmdug_process_chown
# asmcmdug_process_chmod
# asmcmdug_process_chgrp
# asmcmdug_process_grpmod
# asmcmdug_process_orapwusr
# asmcmdug_process_lspwusr
#############################################################################

package asmcmdug;
require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(asmcmdug_init
                 );

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

use List::Util qw[min max];

####################### ASMCMDTMPL Global Constants ######################
# ASMCMD Column Header Names:
# Below are the names of the column headers for lstmpl.
our (%asmcmdug_lsusr_header) = ('dgname'         , 'DG_Name',
                                'user_number'    , 'User_Num',
                                'compound_index' , 'Cmpnd_Idx',
                                'incarnation'    , 'Incarn',
                                'os_id'          , 'OS_ID',
                                'os_name'        , 'OS_Name',
                               );


our (%asmcmdug_lsgrp_header) = ('dgname'         , 'DG_Name',
                                'name'           , 'Grp_Name',
                                'member'         , 'Member',
                                'owner'          , 'Owner',
                                'members'        , 'Members'
                               );

our (%asmcmdug_lspwusr_header) = ('username',    'Username',
                                  'sysdba',      'sysdba',
                                  'sysoper',     'sysoper',
                                  'sysasm',      'sysasm'
                                 );

####################### ASMCMDTMPL Global Variables ######################
our (%asmcmdug_cmds) = ( lsusr     => {no_instance => 'True',
                                       flags       =>  {'G=s'=>'diskGroup',
                                                        'suppressheader'=>
                                                       'suppressHeaders', 
                                                        'a'=>'all'}
                                                      },        
                         mkgrp     => {no_instance => 'True'
                                                      },
                         rmgrp     => {no_instance => 'True'
                                                      },
                         mkusr     => {no_instance => 'True'
                                                      },
                         rmusr     => {no_instance => 'True',
                                       flags       =>  {'r'=>'recursive'}
                                                      },
                         lsgrp     => {no_instance => 'True',
                                       flags       =>  {'G=s'=>'diskGroup',
                                                        'suppressheader'=>
                                                       'suppressHeaders',
                                                        'a'=>'all'}
                                                      },
                         chown     => {no_instance => 'True'
                                                      },
                         chmod     => {no_instance => 'True'
                                                      },
                         chgrp     => {no_instance => 'True'
                                                      },
                         grpmod    => {no_instance => 'True',
                                       flags       =>  {'add'=>'addUser',
                                                        'delete'=>'deleteUser'}
                                                      },
                         groups    => {no_instance => 'True'
                                                      },
                         orapwusr  => {no_instance => 'True',
                                       flags       =>  {'add'=>'addUser',
                                                        'delete'=>'deleteUser',
                                                        'modify'=>'modifyUser',
                                                        'privilege=s'=>
                                                       'systemPrivilege',
                                                        'password'=>
                                                       'passwordChange'}
                                                      },
                         lspwusr   => {no_instance => 'True',
                                       flags       =>  {'suppressheader'=>
                                                       'suppressHeaders'}
                                                      },
                         passwd    => {no_instance => 'True',
                                                      }
                       );

sub is_asmcmd
{
  return 1;
}

########
# NAME
#   asmcmdug_init
#
# DESCRIPTION
#   This function initializes the asmcmdug 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, \&asmcmdug_process_cmd);
  push (@asmcmdglobal_help_callbacks, \&asmcmdug_process_help);
  push (@asmcmdglobal_command_list_callbacks, \&asmcmdug_get_asmcmd_cmds);
  push (@asmcmdglobal_is_command_callbacks, \&asmcmdug_is_cmd);
  push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmdug_is_wildcard_cmd);
  push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdug_syntax_error);
  push (@asmcmdglobal_no_instance_callbacks, \&asmcmdug_is_no_instance_cmd);
  %asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdug_cmds);

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

########
# NAME
#   asmcmdug_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 asmcmdug module; 0 if not.
#
# NOTES
#   Only asmcmdcore_shell() calls this routine.
########
sub asmcmdug_process_cmd 
{
  my ($dbh) = @_;
  my ($succ) = 0;

  # Get current command from global value, which is set by 
  # asmcmdug_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 ASMCMDUG command.
  my (%cmdhash) = ( lsusr         => \&asmcmdug_process_lsusr,
                    mkgrp         => \&asmcmdug_process_mkgrp,
                    rmgrp         => \&asmcmdug_process_rmgrp,
                    mkusr         => \&asmcmdug_process_mkusr,
                    rmusr         => \&asmcmdug_process_rmusr,
                    lsgrp         => \&asmcmdug_process_lsgrp,
                    chown         => \&asmcmdug_process_chown,
                    chmod         => \&asmcmdug_process_chmod,
                    chgrp         => \&asmcmdug_process_chgrp,
                    grpmod        => \&asmcmdug_process_grpmod,
                    groups        => \&asmcmdug_process_groups,
                    orapwusr      => \&asmcmdug_process_orapwusr,
                    lspwusr       => \&asmcmdug_process_lspwusr,
                    passwd        => \&asmcmdug_process_passwd);


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

  return $succ;
}


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

  return $succ;
}


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

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


########
# NAME
#   asmcmdug_is_wildcard_cmd
#
# DESCRIPTION
#   This routine determines if an ASMCMDUG 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 asmcmdug_is_wildcard_cmd 
{
  my ($arg) = shift;
  # Empty hash; no ASMCMDUG command supports wildcards. # 

  return defined ($asmcmdug_cmds{ $arg }) &&
         defined ($asmcmdug_cmds{ $arg }{ wildcard });
}


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

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


########
# NAME
#   asmcmdug_parse_int_args
#
# DESCRIPTION
#   This routine parses the arguments for flag options for ASMCMDUG 
#   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 ASMCMDUG internal command.
########
sub asmcmdug_parse_int_args 
{
  my ($cmd, $args_ref) = @_;
  my ($key);
  my (@string);

  #build the list of options to parse using GetOptions
  if($asmcmdug_cmds{ $cmd }{ flags })
  {
    foreach $key(keys %{$asmcmdug_cmds{ $cmd }{ flags }})
    {
      push(@string, $key);
    }
  }

  #include deprecated options if any
  if($asmcmdglobal_deprecated_options{ $cmd })
  {
    foreach my $key(keys %{$asmcmdglobal_deprecated_options{ $cmd }})
    {
      push(@string, $asmcmdglobal_deprecated_options{$cmd}{$key}[0]);
    }
  }

  # Use GetOptions() from the Getopt::Long package to parse arguments for
  # internal commands.  These arguments are stored in @ARGV.
  if (!GetOptions($args_ref,@string))
  {
    # Print correct command format if syntax error. #
    asmcmdug_syntax_error($cmd);
    return undef;
  }
  return 0;
}


########
# NAME
#   asmcmdug_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 ASMCMDUG 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 asmcmdug_syntax_error 
{
  my ($cmd) = shift;
  my ($cmd_syntax);                               # Correct syntax for $cmd. #
  my ($succ) = 0;

  # display syntax only for commands from this module.
  if(asmcmdug_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
#   asmcmdug_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 asmcmdug_get_asmcmd_cmds 
{
  return asmcmdshare_print_cmds(sort(keys %asmcmdug_cmds));
}


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

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

  #Set the correct options if deprecated options were used and print WARNING.
  asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);

  # print headers?
  $headers = defined($args{'suppressheader'});

  # get disk group name
  if (defined($args{'G'}))
  {
    $dgname = $args{'G'};
    $gnum = asmcmdshare_get_gnum_from_gname($dbh, $dgname);
    if (!defined($gnum))
    {
      @eargs = ($dgname);
      asmcmdshare_error_msg(8001, \@eargs);
      return;
    }
    push (@where, 'v$asm_diskgroup.group_number = ' . $gnum);
  }
  else
  {
    push (@what, 'v$asm_diskgroup.name as dgname');
  }

  # get pattern
  if (defined($ARGV[0]))
  {
    $pattern = $ARGV[0];
    $pattern =~ s,$ASMCMDGLOBAL_WCARD_CHARS,\%,g;

    push (@where, "v\$asm_user.os_name like \'%" . $pattern . "%\'");
  }

  push (@what, 'v$asm_user.user_number as user_number');
  push (@what, 'v$asm_user.os_id as os_id');
  push (@what, 'v$asm_user.os_name as os_name');

  push (@from, 'v$asm_user');
  push (@from, 'v$asm_diskgroup');

  #join
  push (@where, 'v$asm_user.group_number = v$asm_diskgroup.group_number');

  #order
  push (@order, 'v$asm_user.group_number');
  push (@order, 'v$asm_user.os_name');

  $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{$_} = length($asmcmdug_lsusr_header{$_});
  }

  #get the rows
  while (defined($row = asmcmdshare_fetch($sth)))
  {
    my(%usr_info) = ();

    while(($k,$v) = each(%{$row}))
    {
      $k =~ tr/[A-Z]/[a-z]/;
      $usr_info{$k}    = $v;
      $min_col_wid{$k} = max($min_col_wid{$k}, length($v));
    }

    push (@usr_list, \%usr_info);
  }
  asmcmdshare_finish($sth);

  #create print format
  $print_format = '';

  foreach (@what)
  {
    $print_format .= "%-$min_col_wid{$_}s ";
  }
  $print_format .= "\\n";

  #print header
  if (!defined ($args{'suppressheader'}) )
  {
    $printf_code = "printf \"$print_format\", ";
    @what_print = ();
    foreach (@what)
    {
      push (@what_print, "\'" . $asmcmdug_lsusr_header{$_} . "\'");
    }
    $printf_code .= "(" . join (", ", @what_print) . ")";

    eval $printf_code;
  }

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


########
# NAME
#   asmcmdug_process_mkgrp
#
# DESCRIPTION
#   This function processes the asmcmd command mkgrp.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_mkgrp
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($sth, $qry);
  my ($dgname, $grpname);
  my (@usrs);

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

  # get dgname and group name
  if (@ARGV < 2)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $dgname = shift(@ARGV);
  $grpname = shift(@ARGV);

  # get the users
  foreach (@ARGV)
  {
    push (@usrs, "\'".$_."\'");
  }

  $qry = "alter diskgroup " . $dgname . " add usergroup \'" . $grpname . "\'";

  if (@usrs)
  {
    $qry .= " with member " . join(',', @usrs);
  }

  $ret = asmcmdshare_do_stmt($dbh, $qry);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}


########
# NAME
#   asmcmdug_process_rmgrp
#
# DESCRIPTION
#   This function processes the asmcmd command rmgrp.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_rmgrp
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($sth, $qry, $dgname);
  my ($grpname);

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

  # get dgname and group name
  if (@ARGV < 2)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $dgname  = $ARGV[0];
  $grpname = $ARGV[1];

  $qry = "alter diskgroup " . $dgname . " drop usergroup \'" . $grpname . "\'";

  $ret = asmcmdshare_do_stmt($dbh, $qry);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}


########
# NAME
#   asmcmdug_process_mkusr
#
# DESCRIPTION
#   This function processes the asmcmd command mkusr.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_mkusr
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($sth, $qry, $dgname);
  my ($username);

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

  # get disk group name and user name
  if (@ARGV != 2)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $dgname   = $ARGV[0];
  $username = $ARGV[1];

  $qry = "alter diskgroup " . $dgname . " add user \'" . $username ."\'";

  $ret = asmcmdshare_do_stmt($dbh, $qry);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}


########
# NAME
#   asmcmdug_process_rmusr
#
# DESCRIPTION
#   This function processes the asmcmd command rmusr.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_rmusr
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($sth, $qry, $dgname);
  my ($username);

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

  # get disk group name and user name
  if (@ARGV != 2)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $dgname   = $ARGV[0];
  $username = $ARGV[1];

  $qry = "alter diskgroup " . $dgname . " drop user \'" . $username . "\'";

  if (defined($args{'r'}))
  {
    $qry .= ' cascade';
  }

  $ret = asmcmdshare_do_stmt($dbh, $qry);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}

########
# NAME
#   asmcmdug_get_users
#
# DESCRIPTION
#   This function gets the users given a diskgroup name and a usergroup name.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#
########
sub asmcmdug_get_users
{
  my ($dbh, $gname, $ugname) = @_;
  my ($gnum, $ugnum);
  my (@what, @where, @from, @order);
  my ($sth, $row, $res);

  $gnum = asmcmdshare_get_gnum_from_gname($dbh, $gname);
  return if (!defined($gnum));

  $ugnum = asmcmdshare_get_ugnum_from_ugname($dbh, $gnum, $ugname);
  return if (!defined($ugnum));

  push (@what, 'v$asm_user.os_name');

  push (@from, 'v$asm_user');
  push (@from, 'v$asm_usergroup_member');

  push (@where, 'v$asm_user.group_number = ' . $gnum);
  push (@where, 'v$asm_usergroup_member.group_number = ' . $gnum);
  push (@where, 'v$asm_usergroup_member.usergroup_number = ' . $ugnum);

  push (@where, 'v$asm_usergroup_member.group_number = v$asm_user.group_number');
  push (@where, 'v$asm_usergroup_member.member_number = v$asm_user.user_number');

  push (@order, 'v$asm_user.os_name');

  $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where, \@order);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);

  $res = '';
  while (defined($row = asmcmdshare_fetch($sth)))
  {
    $res = $res . $row->{'OS_NAME'} . ' ';
  }
  asmcmdshare_finish($sth);

  return $res;
}

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

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

  #Set the correct options if deprecated options were used and print WARNING.
  asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);

  # filter disk group
  if (defined($args{'G'}))
  {
    $dgname = $args{'G'};
    $gnum = asmcmdshare_get_gnum_from_gname($dbh, $dgname);
    if (!defined($gnum))
    {
      @eargs = ($dgname);
      asmcmdshare_error_msg(8001, \@eargs);
      return;
    }
    push (@where, 'v$asm_usergroup.group_number =' . $gnum);
  }

  # filter user group
  if (defined($ARGV[0]))
  {
    $pattern = $ARGV[0];
    push (@where, "v\$asm_usergroup.name like \'" . $pattern . "\'");
  }

  push (@what, 'v$asm_diskgroup.name as dgname');
  push (@what, 'v$asm_usergroup.name');

  push (@from, 'v$asm_usergroup');
  push (@from, 'v$asm_diskgroup');
  push (@from, 'v$asm_user u1');        #owner
  push (@what, 'u1.os_name as owner');

  #join
  push (@where, 'u1.user_number = v$asm_usergroup.owner_number' );
  push (@where, 'u1.group_number = v$asm_diskgroup.group_number' );
  push (@where, 'v$asm_usergroup.group_number = v$asm_diskgroup.group_number' );

  #order
  push (@order, 'v$asm_usergroup.name');

  $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{$_} = length($asmcmdug_lsgrp_header{$_});
  }

  if (defined($args{'a'}))
  {
    $min_col_wid{'members'} = length($asmcmdug_lsgrp_header{'members'});
  }

  while (defined($row = asmcmdshare_fetch($sth)))
  {
    my(%grp_info) = ();
    while(($k,$v) = each(%{$row}))
    {
      $k =~ tr/[A-Z]/[a-z]/;
      $grp_info{$k}    = $v;

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

    if (defined($args{'a'}))
    {
      $v = asmcmdug_get_users($dbh, $grp_info{'dgname'}, $grp_info{'name'});
      $grp_info{'members'} = $v;

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

    push (@grp_list, \%grp_info);
  }
  asmcmdshare_finish($sth);

  if (defined($args{'a'}))
  {
    push (@what, 'members');
  }

  #create print format
  $print_format = '';

  foreach (@what)
  {
    $print_format .= "%-$min_col_wid{$_}s  ";
  }
  $print_format .= "\\n";

  #print header
  if (!defined ($args{'suppressheader'}) )
  {
    $printf_code = "printf \"$print_format\", ";
    @what_print = ();
    foreach (@what)
    {
      push (@what_print, "\'" . $asmcmdug_lsgrp_header{$_} . "\'");
    }
    $printf_code .= "(" . join (", ", @what_print) . ")";

    eval $printf_code;
  }

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


########
# NAME
#   asmcmdug_process_chown
#
# DESCRIPTION
#   This function processes the asmcmd command chown.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_chown
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($sth, $qry, $dgname);
  my ($strperm, $strwho, $alphabet, $oper, $inst, @files);
  my ($owner, $group);
  my (@eargs);
  my (%norm);

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

  #-R not implemented

  # get owner/group and list of files
  if (@ARGV < 2)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $owner = shift(@ARGV);

  foreach (@ARGV)
  {
    push (@files, $_);
  }

  if ($owner =~ m/(\w+)[\:]?(\w+)?/)
  {
    $owner = $1;
    $group = $2 if defined($2);
  }

  foreach (@files)
  {
    if ($_ =~ m/(\w+)[\/]?.*/)
    {

      #Normalize path and fetch the grp and file_number
      %norm = asmcmdshare_normalize_path($dbh, $_, 0, \$ret);

      if($ret != 0)
      {
          @eargs = ($_);
          asmcmdshare_error_msg(8408, \@eargs);
          asmcmdshare_trace(1, $DBI::errstr, 'y', 
                       'y') if(defined($DBI::errstr));
          next;#continue with next file
      }

      $dgname = (split(/\//, $norm{'path'}->[0]))[0];
      $dgname =~s/\+//g;


      $qry = "alter diskgroup " . $dgname . " set ownership ";
      $qry .= " owner=\'" . $owner . "\'";
      if (defined($group))
      {
        $qry .= ", group=\'" . $group . "\'";
      }
      $qry .= " for file \'" . $norm{'path'}->[0] . "\'";

      $ret = asmcmdshare_do_stmt($dbh, $qry);

      if (!defined($ret))
      {
        @eargs = ($_);
        asmcmdshare_error_msg(8402, \@eargs);
        asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') if(defined($DBI::errstr));
      }
    }
    else
    { 
      @eargs = ($_);
      asmcmdshare_error_msg(8408, \@eargs); 
      asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') if(defined($DBI::errstr));
    }
  }
}


########
# NAME
#   asmcmdug_process_chmod
#
# DESCRIPTION
#   This function processes the asmcmd command chmod.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_chmod
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($sth, $qry, $dgname);
  my ($strperm, $strwho, $alphabet, $oper, $inst, @files);
  my (@sqlperm);
  my ($x, $ct);
  my (@eargs);
  my (%norm);
  my (@info);
  my ($qry_perm, $fnum, $fname, $row, $end_perm, $gnum);
  my ($req_perm);
  my ($not_digit) = 0;

  use warnings qw/FATAL all/;

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

  #-R not implemented

  # get the permissions
  if (@ARGV < 2)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $inst = shift(@ARGV);

  # get the files
  foreach (@ARGV)
  {
    push (@files, $_);
  }

  #check if it is in 3 digit format 
  if( $inst =~ m/\d{3}/  && length($inst) == 3)
  {
    my (@perm);

    if($inst =~ m/(0|4|6)/)
    {
      @perm = split('', $inst);

      push(@sqlperm, "owner=read write") if ($perm[0] == 6);
      push(@sqlperm, "owner=read only")  if ($perm[0] == 4);
      push(@sqlperm, "owner=none")  if ($perm[0] == 0);

      push(@sqlperm, "group=read write") if ($perm[1] == 6);
      push(@sqlperm, "group=read only")  if ($perm[1] == 4);
      push(@sqlperm, "group=none")  if ($perm[1] == 0);

      push(@sqlperm, "other=read write") if ($perm[2] == 6);
      push(@sqlperm, "other=read only")  if ($perm[2] == 4);
      push(@sqlperm, "other=none")  if ($perm[2] == 0);
    }
    else
    {
      asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }
  }
  elsif( $inst =~ m/^(.*)(\+|\-)(r|w|rw|wr)$/ )   #permissions in the ug+r/w format
  {
      
    $not_digit =1;
    if (defined($1))             # we are setting it to someone other than all
    {
      $strwho = join('', sort(split('', $1)));    #sort the string
    }
    else
    {
      $strwho = "gou";
    }

    $oper = "add" if ($2 eq '+');
    $oper = "del" if ($2 eq '-');

    $req_perm = $3;
    $req_perm = 'rw' if($req_perm eq 'wr');

    $x = $strwho;

    $x =~ s/[^agou]//g;              #remove invalid characters
    if ($x ne $strwho)
    {
      asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }

    #now look for repetitions of the valid characters
    if ( ($x =~ tr/a/a/) > 1 )          # excess of a
    {
      asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }

    if ( ($x =~ tr/u/u/) > 1 )          # excess of u
    {
      asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }

    if ( ($x =~ tr/g/g/) > 1 )          # excess of g
    {
      asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }

    if ( ($x =~ tr/o/o/) > 1 )          # excess of o
    {
      asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
      return;
    }

    if ( $strwho =~ tr/a/a/ )
    {
      $strwho = "gou";
    }
  }
  else
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
 
  my %hash   = map { $_ => 1 } @files;
  @files = keys %hash;

  foreach (@files)
  {
    if ($_ =~ m/(\w+)[\/]?.*/)
    {
      #$dgname = $1;

      #If not in numeric format
      $fname =(split(/\//, $_))[-1];

      #Normalize path and fetch the grp and file_number
      %norm = asmcmdshare_normalize_path($dbh, $_, 0, \$ret);
        
      if($ret != 0)
      {
          @eargs = ($_);
          asmcmdshare_error_msg(8408, \@eargs);
          asmcmdshare_trace(1, $DBI::errstr, 'y', 
                           'y') if(defined($DBI::errstr));
          next;#continue with next file
      }

      $dgname = (split(/\//, $norm{'path'}->[0]))[0]; 
      $dgname =~s/\+//g;

      if($not_digit == 1)
      {
         #Retreive the file perms and make sure that after granting requested
         #we do not end up in write only

         #retreive the unique file number given the parent_id,reference_id,
         #group_number and filename
         asmcmdshare_get_subdirs($dbh, \@info,$norm{'gnum'}->[0],
                $norm{'ref_id'}->[0],$norm{'par_id'}->[0],$fname, undef, 0, 0);

         $fnum = $info[0]->{'file_number'};
         $gnum = $norm{'gnum'}->[0];

         if(!defined($fnum))
         {
             @eargs = ($_);
             asmcmdshare_error_msg(8408, \@eargs);
             asmcmdshare_trace(1, $DBI::errstr, 'y', 
                                            'y') if(defined($DBI::errstr));
             next;
         }
 
         $qry_perm ="select permissions from v\$asm_file where 
                       file_number=$fnum and group_number=$gnum";
         $sth = asmcmdshare_do_select($dbh, $qry_perm);
         if(!defined($sth))
         {
             @eargs = ($_);
             asmcmdshare_error_msg(8408, \@eargs);
             asmcmdshare_trace(1, $DBI::errstr, 'y', 
                                 'y') if(defined($DBI::errstr));
             next;
         }

         $row = asmcmdshare_fetch($sth);
         asmcmdshare_finish($sth);
         if(!defined($row))
         {
             @eargs = ($_);
             asmcmdshare_error_msg(8408, \@eargs);
             asmcmdshare_trace(1, $DBI::errstr, 'y', 
                                    'y') if(defined($DBI::errstr));
             next;
         }
     
         #user,group and other permissions
         my $user = substr($row->{'PERMISSIONS'},0,2);
         $user =~s/-//g;
   
         my $group = substr($row->{'PERMISSIONS'},3,2);   
         $group =~s/-//g;

         my $other = substr($row->{'PERMISSIONS'},6,2);
         $other  =~s/-//g;

         #check if operation can be allowed to user
         if ( $strwho =~ tr/u/u/ )
         {
             if(asmcmdug_chmod_get_end_perm($user,$req_perm,$oper,\$end_perm))
             {
                push( @sqlperm, "owner=" . $end_perm );
             }
             else
             {
                @eargs = ($_);
                asmcmdshare_error_msg(8409, \@eargs);
                asmcmdshare_trace(1, $DBI::errstr, 'y',
                                       'y') if(defined($DBI::errstr));
                return;
             } 
         } 
         #check if operation can be allowed to group
         if ( $strwho =~ tr/g/g/ )
         {
            if(asmcmdug_chmod_get_end_perm($group,$req_perm,$oper,\$end_perm))
            {
               push( @sqlperm, "group=" . $end_perm ) ;
            }
            else
            {
               @eargs = ($_);
               asmcmdshare_error_msg(8409, \@eargs);
               asmcmdshare_trace(1, $DBI::errstr, 'y', 
                                            'y') if(defined($DBI::errstr));
               return;
            }
         }
         #check if operation can be allowed to other
         if ( $strwho =~ tr/o/o/ )
         {
            if(asmcmdug_chmod_get_end_perm($other,$req_perm,$oper,\$end_perm))
            {
               push( @sqlperm, "other=" . $end_perm );
            }
            else
            {
               @eargs = ($_);
               asmcmdshare_error_msg(8409, \@eargs);
               asmcmdshare_trace(1, $DBI::errstr, 'y', 
                                            'y') if(defined($DBI::errstr));
               return;
            }
         }
      } 

      $qry = "alter diskgroup " . $dgname . " set permission ";
      $qry .= join (' , ', @sqlperm);
      $qry .= " for file \'" .  $norm{'path'}->[0] . "\'";

      $ret = asmcmdshare_do_stmt($dbh, $qry);

      if (!defined($ret))
      {
        @eargs = ($_);
        asmcmdshare_error_msg(8402, \@eargs);
        asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') if(defined($DBI::errstr));
      }

      #Clear out previous effective permissions 
      if($not_digit == 1)
      {
        @sqlperm=();
      }
    }
    else
    {
        @eargs = ($_);
        asmcmdshare_error_msg(8408, \@eargs);
        asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') if(defined($DBI::errstr));
    }
  }
}

########
# NAME
#   asmcmdug_chmod_get_end_perm
# DESCRIPTION
#   This function checks whether the current chmod is allowed.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_chmod() calls this function.
########
sub asmcmdug_chmod_get_end_perm
{
   my ($role,$req_perm,$oper,$end_perm) = @_;
   my ($retval) = 1;

   if($oper eq 'add')
   { 
      $role .= $req_perm;
   }
   elsif($oper eq 'del')
   {
     $role  =~ s/$req_perm/-/g ;
   }

   # write only option is not allowed
   if(($role=~ /w/) and !($role =~ /r/))
   {
     $retval = 0;
     return $retval;
   }
  
   $$end_perm = "none";
   $$end_perm = "read only" if($role =~ /r/);
   $$end_perm = "read write" if($role =~ /rw/);
   
   return $retval; 
}

########
# NAME
#   asmcmdug_process_chgrp
#
# DESCRIPTION
#   This function processes the asmcmd command chgrp.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_chgrp
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($sth, $qry, $dgname, $group);
  my (@files);
  my (@eargs);
  my (%norm);

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

  # get group 
  if (@ARGV < 2)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $group = shift(@ARGV);

  # get files
  foreach (@ARGV)
  {
    push (@files, $_);
  }

  foreach (@files)
  {
    if ($_ =~ m/(\w+)[\/]?.*/)
    {
      #Normalize path and fetch the grp and file_number
      %norm = asmcmdshare_normalize_path($dbh, $_, 0, \$ret);

      if($ret != 0)
      {
          @eargs = ($_);
          asmcmdshare_error_msg(8408, \@eargs);
          asmcmdshare_trace(1, $DBI::errstr, 'y', 
                                 'y') if(defined($DBI::errstr));
          next;#continue with next file
      }

      $dgname = (split(/\//, $norm{'path'}->[0]))[0];
      $dgname =~s/\+//g;

      $qry = "alter diskgroup " . $dgname . " set ownership ";
      $qry .= "group=\'" . $group . "\'";
      $qry .= " for file \'" . $norm{'path'}->[0] . "\'";

      $ret = asmcmdshare_do_stmt($dbh, $qry);

      if (!defined($ret))
      {
        @eargs = ($_);
        asmcmdshare_error_msg(8402, \@eargs);
        asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') if(defined($DBI::errstr));
      }
    }
    else
    {
       @eargs = ($_);
       asmcmdshare_error_msg(8408, \@eargs);
       asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') if(defined($DBI::errstr));
    }    
  }
}


########
# NAME
#   asmcmdug_process_grpmod
#
# DESCRIPTION
#   This function processes the asmcmd command grpmod.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_grpmod
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($sth, $qry, $dgname, $grpname, $action);
  my (@usrs);

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

  # get group
  if (@ARGV < 2)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $dgname = shift(@ARGV);
  $grpname = shift(@ARGV);

  # get the users
  foreach (@ARGV)
  {
    push (@usrs, "\'".$_."\'");
  }

  if ($args{'add'})   #add
  {
    $action = "add";
  }

  if ($args{'delete'})   #drop
  {
    if (defined($action) && $action eq "add")
    {
      asmcmdshare_error_msg(8401, undef);
      return;
    }

    $action = "drop";
  }

  if (!defined($action))
  {
    asmcmdshare_error_msg(8403, undef);
    return;
  }

  $qry = "alter diskgroup " . $dgname . " modify usergroup \'" .$grpname."\' ";
  $qry .= $action . " member " . join(',', @usrs);

  $ret = asmcmdshare_do_stmt($dbh, $qry);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret); 
}


########
# NAME
#   asmcmdug_process_groups
# 
# DESCRIPTION
#   Returns the groups that a user belongs to (ASM context).
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
# 
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_groups
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($dgname, $grpname, $usrname);
  my (@what , @from, $sth, $qry, @where, @order);
  my (@usrs, $row);
  my (@groups);

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

  # get parameters
  if (@ARGV < 2)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }
  $dgname = shift(@ARGV);
  $dgname =~ tr/a-z/A-Z/;

  $usrname = shift(@ARGV);

  push (@what, 'v$asm_usergroup.name as usergroup_name');

  push (@from, 'v$asm_usergroup');
  push (@from, 'v$asm_user');
  push (@from, 'v$asm_usergroup_member');
  push (@from, 'v$asm_diskgroup_stat');

  push (@where, "v\$asm_user.os_name = '" . $usrname . "'" );
  push (@where, "v\$asm_diskgroup_stat.name = '" . $dgname . "'");

  push (@where, 'v$asm_usergroup_member.usergroup_number = ' .
        'v$asm_usergroup.usergroup_number');
  push (@where, 'v$asm_usergroup_member.member_number = ' .
        'v$asm_user.user_number');
  push (@where, 'v$asm_usergroup_member.group_number = ' .
        'v$asm_user.group_number');
  push (@where, 'v$asm_user.group_number = ' .
        'v$asm_diskgroup_stat.group_number');
  push (@where, 'v$asm_usergroup_member.group_number = ' .
        'v$asm_diskgroup_stat.group_number');
  push (@where, 'v$asm_usergroup.group_number = ' .
        'v$asm_diskgroup_stat.group_number');

  push (@order, 'v$asm_diskgroup_stat.name');
  push (@order, 'usergroup_name');

  $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where, 
                                         \@order);

  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth); 

  while (defined($row = asmcmdshare_fetch($sth)))
  {
    push (@groups, $row->{'USERGROUP_NAME'});
  }

  print join(',', @groups) . "\n";
}


########
# NAME
#   asmcmdug_process_orapwusr
# 
# DESCRIPTION
#   Handles Oracle pwfile users.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
# 
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_orapwusr
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($dgname, $grpname, $usrname);
  my (@what , @from, $sth, $qry, @where);
  my (@usrs, $row);
  my (@groups, $user, $passwd, $stmt);

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

  if (@ARGV != 1)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

  $user = shift(@ARGV);

  if (defined($args{'add'}))                    #add user
  {
    $passwd = asmcmdshare_getpswd();

    $stmt = "create user " . $user . " identified by " . $passwd;

    $sth = asmcmdshare_do_stmt($dbh, $stmt);
    if (!defined($sth))
    {
      asmcmdshare_trace(1, $DBI::errstr, 'y', 'y');
      return;
    }

    if (defined($args{'privilege'}))
    {
      #Run GetOpts on the value to support first unique chars for option 
      push(@ARGV,"--".$args{'privilege'});
      GetOptions (\%args, 'sysasm','sysdba','sysoper');
      pop(@ARGV);
    
      if ($args{'sysdba'})
      {
        $stmt = "grant sysdba to " . $user;
        $sth = asmcmdshare_do_stmt($dbh, $stmt);
        asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);
      }
      if ($args{'sysoper'})
      {
        $stmt = "grant sysoper to " . $user;
        $sth = asmcmdshare_do_stmt($dbh, $stmt);
        asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);
      }
      if ($args{'sysasm'})
      {
        $stmt = "grant sysasm to " . $user;
        $sth = asmcmdshare_do_stmt($dbh, $stmt);
        asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);
      }
    }
  }

  if (defined($args{'modify'}))               # modify user
  {
    if (defined($args{'password'}))
    {
      $passwd = asmcmdshare_getpswd();
      $stmt = "alter user " . $user . " identified by " . $passwd;

      $sth = asmcmdshare_do_stmt($dbh, $stmt);
      asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);
    }

    if (defined($args{'privilege'}))
    {
      #Run GetOpts on the value to support first unique chars for option 
      push(@ARGV,"--".$args{'privilege'});
      GetOptions (\%args, 'sysasm','sysdba','sysoper');
      pop(@ARGV);

      if ($args{'sysdba'})
      {
        $stmt = "grant sysdba to " . $user;
      }
      else
      {
        $stmt = "revoke sysdba from " . $user;
      }
      $sth = asmcmdshare_do_stmt($dbh, $stmt);
      asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);

      if ($args{'sysoper'})
      {
        $stmt = "grant sysoper to " . $user;
      }
      else
      {
        $stmt = "revoke sysoper from " . $user;
      }
      $sth = asmcmdshare_do_stmt($dbh, $stmt);
      asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);

      if ($args{'sysasm'})
      {
        $stmt = "grant sysasm to " . $user;
      }
      else
      {
        $stmt = "revoke sysasm from " . $user;
      }

      $sth = asmcmdshare_do_stmt($dbh, $stmt);
      asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);
    }
  }

  if (defined($args{'delete'}))               # drop user
  {
    $stmt = "drop user " . $user;
    $sth = asmcmdshare_do_stmt($dbh, $stmt);
    asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);
  }
}


########
# NAME
#   asmcmdug_process_lspwusr
# 
# DESCRIPTION
#   Lists Oracle pwfile users.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
# 
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_lspwusr
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($dgname, $grpname, $usrname);
  my (@what , @from, $sth, $qry, @where);
  my (@usrs, $row);
  my (@groups);

  my (%min_col_wid, $print_format, $printf_code, @what_print);
  my ($k, $v, $h);

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

  #Set the correct options if deprecated options were used and print WARNING.
  asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);

  push (@what, 'username');
  push (@what, 'sysdba');
  push (@what, 'sysoper');
  push (@what, 'sysasm');

  push (@from, 'v$pwfile_users');

  $sth = asmcmdshare_do_construct_select($dbh, \@what, \@from, \@where);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($sth);

  foreach(@what)
  {
    $min_col_wid{$_} = length($asmcmdug_lspwusr_header{$_});
  }

  #get the rows
  while (defined($row = asmcmdshare_fetch($sth)))
  {
    my (%usr_info) = ();

    while(($k,$v) = each(%{$row}))
    {
      $k =~ tr/[A-Z]/[a-z]/;
      $usr_info{$k}    = $v;
      $min_col_wid{$k} = max($min_col_wid{$k}, length($v));
    }

    push (@usrs, \%usr_info);
  }
  asmcmdshare_finish($sth);

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

    eval $printf_code;
  }

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

    eval $printf_code;
  }

}


########
# NAME
#   asmcmdug_process_passwd
# 
# DESCRIPTION
#   Sets/changes the password of a user.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
# 
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdug_process_cmd() calls this function.
########
sub asmcmdug_process_passwd
{
  my ($dbh) = @_;
  my (%args);
  my ($ret);
  my ($dgname, $usr, $oldpasswd, $newpasswd, $stmt);
  my (@eargs);

  my (%min_col_wid, $print_format, $printf_code, @what_print);
  my ($k, $v, $h);

  if (@ARGV != 1)
  {
    asmcmdug_syntax_error($asmcmdglobal_hash{'cmd'});
    return;
  }

  $usr = $ARGV[0];

  $oldpasswd = asmcmdshare_getpswd('Enter old password (optional): ');
  $newpasswd = asmcmdshare_getpswd('Enter new password: ');

  if ($newpasswd eq '')
  {
    @eargs = ('');
    asmcmdshare_error_msg(8406, \@eargs);
    return;
  }

  $stmt = ' ALTER USER ' . $usr . ' IDENTIFIED BY ' . $newpasswd;

  if ($oldpasswd ne '')
  {
    $stmt .= ' REPLACE ' . $oldpasswd;
  }

  $ret = asmcmdshare_do_stmt($dbh, $stmt);
  asmcmdshare_trace(1, $DBI::errstr, 'y', 'y') unless defined ($ret);
}
1;
