#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2000,2019 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# sccsid = "@(#)09   1.49   src/rsct/rmc/cli/bin/chrsrc.perl, rmccli, rsct_rady, rady2035a 11/12/15 16:30:51"
######################################################################
#                                                                    #
# Module: chrsrc                                                     #
#                                                                    #
# Purpose:                                                           #
#   chrsrc - Changes the persistent attribute values of a resource   #
#            or resource class.                                      #
#                                                                    #
# Syntax:                                                            #
#   To change the persistent attribute values of a resource or       #
#   resource class (-c flag), using data entered on the              #
#   command line:                                                    #
#   chrsrc [-h] -s "selection_string" [-v] [-a] [-T] [-V]            #
#          Resource_class Attr=value...                              #
#   chrsrc [-h] -r [-v] [-a] [-T] [-V] Resource_handle Attr=value... #
#   chrsrc [-h] -c [-v] [-a] [-T] [-V] Resource_class Attr=value...  #
#   chrsrc [-h] -C Peer_domain_names [-v] [-T] [-V]                  #
#          Resource_class Attr=value...                              #
#                                                                    #
#   To change the persistent attribute values of a resource or       #
#   resource class (-c flag), using data predefined in an            #
#   input file:                                                      #
#   chrsrc [-h] -f Resource_data_input_file -s "Selection_string"    #
#          [-v] [-a] [-T] [-V] Resource_class                        #
#   chrsrc [-h] -f Resource_data_input_file -r                       #
#          [-v] [-a] [-T] [-V] Resource_handle                       #
#   chrsrc [-h] -f Resource_data_input_file -c                       #
#          [-v] [-a] [-T] [-V] Resource_class                        #
#   chrsrc [-h] -f Resource_data_input_file -C Peer_domain_names     #
#          [-v] [-T] [-V] Resource_class                             #
#                                                                    #
# Flags:                                                             #
#   -h      Help. Writes the command's usage statement to stdout.    #
#   -c      Class. Changes the resource class persistent attribute   #
#           values. By default, the resource persistent attributes   #
#           are changed. Use this flag if you want to change the     #
#           resource class persistent attributes.                    #
#   -C Peer_domain_names  Class. Changes the resource class          #
#           persistent attribute values for peer domains in a        #
#           management domain. Specify one or more peer domain names #
#           separated by a comma.  To specify all peer domains in the#
#           management domain, use -c with -a.                       #
#   -r      Resource Handle.  Change the specific resource that      #
#           matches the specified Resource_handle.                   #
#   -s Selection_string  All selection strings must be enclosed      #
#           within either double or single quotation marks. If the   #
#           selection string contains double quotation marks,        #
#           enclose the entire selection string in single            #
#           quotation marks. For example:                            #
#           -s 'Name == "testing"'                                   #
#           -s 'Name ?= "test"'                                      #
#           Only persistent attributes may be listed in a selection  #
#           string.                                                  #
#   -f Resource_data_input_file File input. Specifies the name of    #
#           the file which contains resource attribute information.  #
#           PersistentResourceAttributes::                           #
#               attr1 = value                                        #
#               attr2 = value                                        #
#           - or -                                                   #
#           PersistentResourceClassAttributes::                      #
#               attr1 = value                                        #
#               attr2 = value                                        #
#   -v      Verify. Verifies that all of the specified attributes    #
#           are valid persistent attribute names that do not have a  #
#           "read_only" property. The chrsrc command cannot verify   #
#           that all of the attributes specified on the command line #
#           or in the input file can be changed. The underlying      #
#           resource manager that controls the specified resource    #
#           determines which attributes can be changed. After this   #
#           command is run (without the -v flag), any attribute name #
#           that could not be changed is included in an error        #
#           message. This command does not actually change the attr. #
#   -a      All nodes. The chrsrc command applies to all nodes in    #
#           the cluster. The cluster scope is determined by the      #
#           environment variable CT_MANAGEMENT_SCOPE. If it is not   #
#           set, first the management domain scope is used, then     #
#           peer domain scope, and then local scope is used until    #
#           the scope is a valid scope for the command. The command  #
#           will run once for the first valid scope found.           #
#           If -a is used with -c, it applies to all peer domains in #
#           the management domain.                                   # 
#   -T      Trace. Writes the command's trace messages to standard   #
#           error. For your software-service organization use only.  #
#   -V      Verbose. Writes this command's verbose messages to       #
#           standard output.                                         #
#                                                                    #
# Operands:                                                          #
#   Resource_class  A resource class name. Enter the lsrsrcdef       #
#                   command for a list of defined resource class     #
#                   names.                                           #
#                                                                    #
#   Resource_handle The specific resource handle that is associated  #
#                   with the resource that you wish to change. Use   #
#                   the lsrsrc command to obtain valid resource      #
#                   handles. The resource handle must be enclosed    #
#                   by double quotation marks. An example is:        #
#                   "0x4017 0x0001 0x00000000 0x0069684c 0x0d4715b0  #
#                    0xe963f69"                                      #
#                                                                    #
#   Attr=value      Enter one or more attribute value pairs. If the  #
#                   -f flag is specified, Attr=value pair operands   #
#                   should not be entered on the command line. Attr  #
#                   is any defined persistent attribute name. Use    #
#                   the lsrsrcdef command to get a list of the       #
#                   defined persistent attributes and their data     #
#                   types for the specified resource class. The      #
#                   value entered must be the appropriate data type  #
#                   for the specified attribute. For example, if     #
#                   NodeNumber is defined as a uint32 data type,     #
#                   enter a positive numeric value.                  #
#                                                                    #
# Description:                                                       #
#   The chrsrc command changes the persistent attribute values for   #
#   a resource or resource class.                                    #
#                                                                    #
#   By default, this command changes the resource persistent         #
#   attributes. If you wish to change the resource class peristent   #
#   attributes, specify the -c flag.                                 #
#                                                                    #
#   Only persistent attributes that do not have a "read_only"        #
#   property can be changed.                                         #
#                                                                    #
#   Use the -v flag to verify that the attribute names specified by  #
#   the command line or resource data input file are valid           #
#   persistent attributes for the specified resource and that these  #
#   attributes do not have a property of "read_only". When chrsrc is #
#   run with the -v flag, the specified attributes are not changed.  #
#                                                                    #
#   After the chrsrc command is run without the -v flag, if any      #
#   specified attribute could not be changed, it is noted in an      #
#   error message. If a particular attribute passes when the         #
#   chrsrc -v command is run, this does not ensure that the          #
#   attribute can be changed. The underlying Resource Manager        #
#   controls which attributes can be changed.                        #
#                                                                    #
#   To change the persistent attributes for a resource, use the -r   #
#   flag to change just the resource linked with the specified       #
#   Resource_handle, or use the -s flag to change all the resources  #
#   that match the specified "Selection_string".                     #
#                                                                    #
# Exit Values:                                                       #
#   0  MC_CLI_SUCCESS        Command completed successfully.         #
#   1  MC_CLI_RMC_ERROR      Command terminated due to an underlying #
#                            RMC error.                              #
#   2  MC_CLI_ERROR          Command terminated due to an underlying #
#                            error in the command script.            #
#   3  MC_CLI_BAD_FLAG       Command terminated due to user          #
#                            specifying an invalid flag.             #
#   4  MC_CLI_BAD_OPERAND    Command terminated due to user          #
#                            specifying a bad operand.               #
#   5  MC_CLI_USER_ERROR     Command terminated due to a user error. #
#                            For example specifying an undefined     #
#                            Resource name as the Resource operand.  #
#   6  MC_CLI_NO_RSRC_FOUND  No resources were found that matched    #
#                            the specified selection string.         #
#                                                                    #
# Examples:                                                          #
#   chrsrc -s "NodeNumber == 1" IBM.Foo Name=c175n05                 #
#   chrsrc -r -f IBM.Foo.chresource                                  #
#     "0x4000 0x0001 0x00000000 0x0069684c 0x0d450f64 0xb8dfee2f"    #
#                                                                    #
# Man Page:                                                          #
#   For the most current detailed description of this command see    #
#   the mkrsrc man page in /opt/rsct/man.                       #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /opt/rsct/msgmaps/mccli.chrsrc.map - message mapping        #
#   /opt/rsct/msgmaps/mccli.mccli.map - message mapping         #
#                                                                    #
# Outputs:                                                           #
#   stdout - any verbose messages.                                   #
#   stderr - any error message.                                      #
#                                                                    #
# External Ref:                                                      #
#   Commands: ctdspmsg                                               #
#   Modules:  MC_cli_utils.pm, MC_cli_display_utils.pm, MC_cli_rc.pm #
#             CT_cli_utils.pm, CT_cli_input_utils.pm,                #
#             CT_cli_display_utils.pm                                #
#   Extensions:  CT::MC, CT::MCerr, CT::CT, CT::CU                   #
#   Perl library routines: Getopt::Long                              #
#                                                                    #
# Tab Settings:                                                      #
#   4 and tabs should be expanded to spaces before saving this file. #
#   in vi:  (:set ts=4  and   :%!expand -4)                          #
#                                                                    #
# Change Activity:                                                   #
#   000315 SAB 60910: Initial design & write.                        #
#   001115 GTM 67900: Add -c, -r support                             #
#   010311 SAB 63852: Prepared for GA.                               #
#   010406 SAB 71892: Support new MC_CLI_NO_RSRC_FOUND return code.  #
#   020717 JAC 84819: Add -a to mean change all nodes in cluster.    #
#   020801 JAC 85417: Update usage message.                          #
#   020925 JAC 87393: Change CT_cli_display_utils to .._displayext.. #
#   021203 JAC 88085: Make changes to remove perl extensions.        #
#   021209 JAC 89902: Fix index used in setup_chrsrc.                #
#   030404 JAC 92787: Don't allow -s "" for selection string.        #
#   040111 JAC 100165: Add -C processing for peer domain class in DM.#
#   040121 JAC 103355: Some fixes for -C flag.                       #
#   040407 JAC 105863: Use escape_chars for "\" searches.            #
#   040426 JAC 108105: Fix a typo.                                   #
#   051116 JAC 131200: Check for multiple non-array attributes.      #
#   061024 JAC 140150: Fix help message.                             #
#   080104 JAC 148342: Fix output of error message.                  #
######################################################################

#--------------------------------------------------------------------#
# General Program Flow/Logic:                                        #
#                                                                    #
# A. Parse command line flags and operands, determine which flavor   #
#    of this command we are actually invoking.                       #
#    This command allows a user to change attribute values for       #
#       * Resources                                                  #
#       * Resource Classes                                           #
#    The Attribute=Value pairs can be specified via the command line #
#    or via an input files (see -f flag).                            #
#                                                                    #
#    When resource attributes are being changed, the default,        #
#    the resources that get changed are all of the resources that    #
#    match the specified selection string (-s "selection_string"     #
#    flag).                                                          #
#                                                                    #
#    If a resource handle is specified (-r flag & operand is a       #
#    resource handle), then only the attributes for that specific    #
#    resource will be changed.                                       #
#                                                                    #
#    When resource class attributes are being changed (-c flag)      #
#    only the resource class instance will be changed.               #
#                                                                    #
# B. Initialize a session with RMC.                                  #
# C. If a resource handle was specified we need to determine which   #
#    resource class it belongs to. So we can get the data types.     #
# D. Get the data types for the persistent attributes for the        #
#    specified resource or resource class.                           #
# E. To change resource attributes with selection string             #
#      call underlying mc_set_select_bp                              #
#    To change resource attributes for specified resource handle     #
#      call underlying mc_set_handle_bp                              #
#    To change resource class attributes                             #
#      call underlying mc_class_set_bp                               #
# F. Display any attributes that couldn't be set                     #
#    underlying Resource Manager actually controls which attributes  #
#    can and cannot be changed / set for the resource classes that   #
#    it controls.                                                    #
# G. Cleanup.                                                        #
#--------------------------------------------------------------------#

#--------------------------------------------------------------------#
# Included Libraries and Extensions                                  #
#--------------------------------------------------------------------#
use lib "/opt/rsct/pm";
use locale;
use Getopt::Long;

use autouse CT_cli_utils => qw(
    printIMsg
    printEMsg
);
use autouse CT_cli_input_utils => qw(
    process_input_file
    process_cmdline_input
    string_to_rsrc_handle
    escape_chars
    check_input_file
);
use autouse CT_cli_displayext_utils => qw(
    rsrc_handle_to_string
);

use MC_cli_rc qw(:return_codes); 
use autouse MC_cli_utils => qw(
    build_HoAttr
    error_exit
    printCEMsg
    validate_rsrc_hndl
    get_class_from_rsrc_hndl_api
    get_p_attr_defs_api
    get_sd_defs_api
    convert_input_value_api
    process_exit_code
    process_api_error
    remove_api_error
    read_from_Stdin
);


#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
Getopt::Long::Configure ("bundling", "no_auto_abbrev", 
                         "no_ignore_case", "require_order",
                         "prefix_pattern=(--|-)");
$TRUE = 1;
$FALSE = 0;

$Verbose = $FALSE;                      # default - verbose turned off

# By default output, attribute name value pairs should be entered
# via the command line.  Attr=value   e.g. Name=c175n06 NodeNumber=1
$Opt_File_Input = $FALSE;               # default - see -f (file form)
$Opt_Ch_Class = $FALSE;                 # default - see -c (rsrc class)
$Opt_Ch_Class_DM = $FALSE;              # default - see -C (rsrc class)
$Opt_Class = $FALSE;                    # set to true if -c or -C used 
$Opt_RSRC_Handle = $FALSE;              # default - see -r (rsrc hndle)
$Opt_Verify = $FALSE;                   # default - see -v (verify)
$Opt_Cmd_Cluster = $FALSE;              # -a all nodes
$Opt_Node_File= $FALSE;                 # default - see -N
$Opt_Stdin= $FALSE;                     # default - see -N with Stdin

$PROGNAME = "chrsrc";                   # Program Name for messages
$MSGCAT = "mccli.cat";                  # msg catalogue for this cmd

$CTDIR = "/opt/rsct";              # RSCT root directory 
$CTBINDIR = "$CTDIR/bin";               # Cluster Bin directory path
$LSMSG = "$CTBINDIR/ctdspmsg";          # list / display message rtn.
$ENV{'MSGMAPPATH'} = "$CTDIR/msgmaps";  # msg maps used by $LSMSG 

#--------------------------------------------------------------------#
# Variables                                                          #
#--------------------------------------------------------------------#
%HoPAttrDefs = ();
%HoSDAttrDefs = ();
my $badrc = 0;
my $rsrc_class_id = 0;
my @chrsrcapi_parms = ();               # parameters to send to chrsrcapi
my $node_file_name;                     # to hold node filename
$DELIMITER = "tvrtvrtvr";               # delimiter for chrsrc-api
$CMD_LIMIT = 20000;                     # chrsrc-api cmd limit
 
#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#

# parse the command line, exit if there are errors 
my ($rc, $resource, $r_rsrc_handle, $filename, $select_str, $node_file_name,
    $peer_domains)= &parse_cmd_line;
($rc == 0) || error_exit($rc);

# if -a was specified for all nodes, make sure CT_MANAGEMENT_SCOPE
# is set.  If it isn't, set it to 4, DM/SR/Local
if (($Opt_Cmd_Cluster || $Opt_Node_File) && !defined $ENV{CT_MANAGEMENT_SCOPE}) {
   $ENV{CT_MANAGEMENT_SCOPE} = 4;
}

# if -C was specified for peer domains in a management domain,
# make sure CT_MANAGEMENT_SCOPE is set to DM scope.
if ($Opt_Ch_Class_DM) {
   $ENV{CT_MANAGEMENT_SCOPE} = 3;
}

# set $Opt_Class if either $Opt_Ch_Class or $Opt_Ch_Class_DM is set
if ($Opt_Ch_Class_DM || $Opt_Ch_Class) {
   $Opt_Class = $TRUE;
}

# Determine which resource class this resource handle is
# associated with.
if ($Opt_RSRC_Handle) {
    $r_rsrc_handle = $resource;
    ($rc, $resource) = get_class_from_rsrc_hndl_api($r_rsrc_handle);
    ($rc == 0) || error_exit($rc);
}

# Get the persistent attribute definitions for this resource  
# and build a hash indexed using the persistent attribute program
# name that will allow easy access to each attributes id, 
# data type and property definitions. 
# To change a resource attribute value that attribute must not 
# have a "read_only" property. 
my $req_properties = 0xFFFF;
my @req_attributes = ();
$rc = get_p_attr_defs_api($resource, $Opt_Class, 
    $req_properties, \@req_attributes, \@LoPAttr, \%HoPAttrDefs); 
($rc == 0) || error_exit($rc);

# get_p_attr_defs only supports filtering/selecting the required
# attributes. Here we need to only select attributes that do not 
# have the MC_RSRC_PATTR_READ_ONLY property, so delete the attributes
# we cannot change (the ones with read_only properties).
foreach $attribute (@LoPAttr) {
    ($HoPAttrDefs{$attribute}{at_properties} =~ /read_only/) &&
        delete $HoPAttrDefs{$attribute};
}

my @sd_attr_names = ();

# scan through the Hash of Persistent Attributes checking
# to see if any are SDs or SDArrays
my @pattr_names = keys %HoPAttrDefs;

foreach $attr_name (@pattr_names) {
    $dtype = $HoPAttrDefs{$attr_name}{at_dtype};
    if ( ($dtype =~ /^CT_SD_PTR$/) || ($dtype =~ /^CT_SD_PTR_ARRAY$/) )
    {
        # Add these SD attributes to array of all SD attr names
        push @sd_attr_names, $attr_name;
    }
}

# If we found any persistent attributes that are SD get their
# definition from RMC
if (scalar(@sd_attr_names) > 0) {

    # Get the SD data and
    # Format the Query Definition Structured Data attributes
    # and elements into a form suitable for quick look up by
    # attribute name the for the element data_types.
    get_sd_defs_api($resource, $Opt_Class, \@sd_attr_names, \%HoSDAttrDefs);

}

# Process either the attr=value pairs from the command line or 
# from the input file if -f flag was used
($rc, $rLoRsrcData) = process_input($Opt_File_Input, $filename,
    $Opt_Class, \@ARGV);
($rc == 0) || error_exit($rc);

# Merge the attr=value pairs you got from the user with the 
# resources actual attribute definitions to build a Hash of 
# Persistent Attributes which contains the equivalent data as 
# the mc_attribute_t structure ({at_name} {at_dtype} {at_value}) 
# The data you need to actually change the resource - also 
# check that the user did not supply any read_only attributes
foreach $new_resource (@$rLoRsrcData) {
    ($rc, $rHoPAttr) = build_resource($resource, $new_resource, 
            \%HoPAttrDefs, \%HoSDAttrDefs); 
    if ($rc != 0) {
        # Do not continue if fatal RMC error
        ($rc == MC_CLI_USER_ERROR) || error_exit($rc);
        $badrc = $rc;   
        next;
    }

    # If running in Verify mode, skip to next resource
    $Opt_Verify && next;            

    # Build parameters for chrsrc-api command
    @chrsrcapi_parms = setup_chrsrcapi(\@chrsrcapi_parms, $resource,
                    $r_rsrc_handle, $Opt_RSRC_Handle, $Opt_Ch_Class,
                    $Opt_Ch_Class_DM, $peer_domains,
                    $select_str, $rHoPAttr);
        
}   # end foreach $new_resource

# Change the resource. 
$rc = change_resource(@chrsrcapi_parms);

if ($rc != 0) {
    # Do not continue if fatal RMC error
    ($rc == MC_CLI_USER_ERROR) || error_exit($rc);
    $badrc = $rc; 
}

($badrc == 0) || error_exit($badrc);

exit $rc;


#--------------------------------------------------------------------#
# End Main Code                                                      #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# parse_cmd_line - Parse the command line for options and operands.  #
#   Set appropriate global variables as outlined below, make sure we #
#   have a valid combination of arguments / options.                 #
#                                                                    #
# Return:                                                            #
#   $rc   0                  Command line parsed fine, no problem.   #
#         SR_CLI_BAD_FLAG    Command line contained a bad flag.      #
#   $resource                Resource Class Name.                    #
#   $rsrc_file               Name of file that contains rsrc data    #
#   $select_str              Selection String                        #
#                                                                    #
# Global Variables Modified:                                         #
#   $Opt_Ch_Class      output   True (-c) change resource class      #
#                               instead of changing the resource     #
#   $Opt_Ch_Class_DM   output   True (-C) change resource class      #
#                               in peer domain.                      #
#   $Opt_RSRC_Handle   output   True (-r) operand will be a          #
#                               resource handle not a resource       #
#                               name. No -s or -c when -r.           #
#   $Opt_File_Input    output   True (-f) read rsrc data from file.  #
#   $Opt_Verify        output   True (-v) verify cmd line or file    #
#                               input is valid format.               #
#   $Opt_Cmd_Cluster   output   True (-a) all nodes.                 #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#   $Trace             output   True (-T) turn Trace mode on.        #
#--------------------------------------------------------------------#
sub parse_cmd_line 
{
my(@original_argv) = @ARGV;
my $resource = "";
my $r_rsrc_handle = ();
my $rsrc_file = ""; 
my $select_str = "";
my $peer_domains = "";
my $node_file_name= "";
my $rc;
my %opts = ();

# Process the command line...
if (!GetOptions(\%opts,
                'h|help|version' ,
                'a' ,
                'c' ,
                'r' ,
                'T' ,                                              
                'v' ,                                              
                'V' ,      
                'C=s' ,
                'f=s' ,
                'N=s' ,     
                's=s' ))
{
    &print_usage;                       # display proper usage
    return MC_CLI_BAD_FLAG;             # return bad rc - bad flag 
}

# Always accept the -h help flag regardless of other flags or operands
if (defined $opts{h}) {                 # -h, help request  
    &print_usage;                       # print usage statement
    exit(0);                            # all done with good return!
}

# mkrsrc requires at a minimum the resource class name as an
# operand.

# Get the arguments...
if ($#ARGV >= 0) {
    $resource = shift @ARGV;            # user specified resource or
                                        # resource handle if -r
}
else {
    if (defined $opts{r}) {
        # Print Missing Resource Handle Operand message
        printCEMsg("EMsgMCcliMissingRsrcHandle");
    }
    else {
        # Print Missing Resource Class Name Operand message
        printCEMsg("EMsgMCcliMissingRsrcClass");
    }
    &print_usage;                       # display proper usage
    return MC_CLI_BAD_OPERAND;          # return bad rc - bad operand
}


# Get the flags...

if (defined $opts{c}) {                 # -c, change resource class 
    $Opt_Ch_Class = $TRUE;
}

if (defined $opts{C}) {                 # -C, change resource class 
    if (defined $opts{c}) {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-c", "-C");
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
    $Opt_Ch_Class_DM = $TRUE;
    $peer_domains = $opts{C};           # input peer domain names
}

if (defined $opts{f}) {                 # -f, filename for rsrc data
    $Opt_File_Input = $TRUE;
    $rsrc_file = $opts{f};              # input rsrc file name
    if ($#ARGV >= 0) {
        # Print Too many operands message 
        printCEMsg("EMsgMCcliTooManyOperands");
        &print_usage;
        return MC_CLI_BAD_OPERAND;
    }       
}
elsif (!defined $opts{r} && $#ARGV < 0) {
    # expecting some Attr=value pairs
    printCEMsg("EMsgMCcliMissingAttrAndValue");
    &print_usage;
    return MC_CLI_BAD_OPERAND;
}

if (defined $opts{a}) {                 # -a, all nodes
    $Opt_Cmd_Cluster = $TRUE;
}

if (defined $opts{v}) {                 # -v, verify rsrc data input
    $Opt_Verify = $TRUE;
}

if (defined $opts{r}) {                 # -r, Resource Handle operand
    $Opt_RSRC_Handle = $TRUE;
    ($rc, $r_rsrc_handle) = string_to_rsrc_handle($resource);
    if ($rc != 0) {
        printCEMsg("EMsgMCcliBadRsrcHandle", $resource); 
        &print_usage;
        return MC_CLI_BAD_OPERAND; 
    $resource = "";
    }
    if (defined $opts{c}) {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-c", "-r");
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
    if (defined $opts{C}) {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-C", "-r");
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
}

if (defined $opts{s}) {                 # -s, selection string
    $select_str = $opts{s};
    # don't allow empty selection string
    if ($select_str =~ /^$/) {
        printCEMsg("EMsgMCcliSelectStrError");
        &print_usage;
        return MC_CLI_BAD_OPERAND;
    }
    # A selection string cannot be specified with the -c or -r flags.
    if (defined $opts{c}) { 
        printCEMsg("EMsgMCcliImproperUsageCombination", "-s", "-c" );
        &print_usage;            
        return MC_CLI_BAD_FLAG;
    }
    if (defined $opts{r}) {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-s", "-r");
        &print_usage;
        return MC_CLI_BAD_FLAG; 
    }
    if (defined $opts{C}) {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-s", "-C");
        &print_usage;
        return MC_CLI_BAD_FLAG; 
    }
}
elsif (!defined $opts{c} && !defined $opts{r} && !defined $opts{C}) {
    # A selection string is required when changing a resource
    printCEMsg("EMsgMCcliMissingSelectionStr");
    &print_usage;          
    return MC_CLI_BAD_FLAG;
}

if (defined $opts{N}) 
{
    # -N, node file name
    if (!defined $opts{s})
    {
        # A selection string is required when using -N
        printCEMsg("EMsgMCcliMissingSelectionStr");
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
    elsif (defined $opts{r}) 
    {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-N", "-r");
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
    elsif (defined $opts{c}) 
    {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-N", "-c" );
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
    elsif (defined $opts{C})
    {
        printCEMsg("EMsgMCcliImproperUsageCombination", "-N", "-C" );
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
    elsif (defined $opts{a})
    {   
        printCEMsg("EMsgMCcliImproperUsageCombination", "-N", "-a" );
        &print_usage;
        return MC_CLI_BAD_FLAG;
    }
    elsif ($opts{N} eq "-")
    {
        $Opt_Stdin= $TRUE;
    }
    else
    {
        $node_file_name= $opts{N};
        $rc= check_input_file($node_file_name);
        if ($rc)
        {
            return MC_CLI_BAD_OPERAND;
        }    
    }
    $Opt_Node_File= $TRUE;
}

if (defined $opts{T}) {                 # -T, turns tracing on 
    $Trace = $TRUE;
}

if (defined $opts{V}) {                 # -V, turns verbose on
    $Verbose = $TRUE;
}

return(0, $resource, $r_rsrc_handle, $rsrc_file, $select_str,
       $node_file_name, $peer_domains);# success
}   # end parse_cmd_line


#--------------------------------------------------------------------#
# print_usage : print the usage statement (syntax) to stdout.        #
#   See this command's prologue syntax section for current usage.    #
#--------------------------------------------------------------------#
sub print_usage
{
printIMsg("IMsgchrsrcUsage5");
}   # end print_usage


#--------------------------------------------------------------------#
# get_sd_element_dtypes - for the specified attribute name return    #
#   and array of this SDs element's data types in element index      #
#   order.                                                           #
#                                                                    #
# Parameters:                                                        #
#   %$rHoSDAttrDefs   input   Reference to a hash of persistent      #
#                             structured data attributes.            #
#                             key  = attribute name                  #
#                             data = array of hashes, each hash      #
#                             represents one of this SDs elements    #
#   $attr_name        input   Attribute name.                        #
#                                                                    #
# Return:                                                            #
#   @sd_element_dtypess       Returns a reference to an array of     #
#                             the SD element data types in element   #
#                             index order for this attribute.        #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub get_sd_element_dtypes {
my ($rHoSDAttrDefs, $attr_name) = @_;
my @sd_element_dtypes;
my %index_to_dtype = ();
my $element;
my $element_cnt = 0;

my @elements = keys %{ $rHoSDAttrDefs->{$attr_name} };
foreach $element (@elements) {
    $index = $rHoSDAttrDefs->{$attr_name}{$element}->{sd_index};
    $index_to_dtype{$index} =
        $rHoSDAttrDefs->{$attr_name}{$element}->{sd_dtype};
    $element_cnt++;
}

for ($i = 0; $i < $element_cnt; $i++) {
    push @sd_element_dtypes, $index_to_dtype{$i};
}

return (@sd_element_dtypes);
}   # end get_sd_element_dtypes


#--------------------------------------------------------------------#
# process_input - process the attr=value pairs that are used to      #
#   define this resource and were input by the user on the command   #
#   line or via an input file.                                       #
#                                                                    #
# Parameters:                                                        #
#   $Opt_Input_File   input   True - input should come from file.    #
#                             False - input should come from command #
#                             line.                                  #
#   $filename         input   Name of file if $Opt_Input_File = TRUE #
#   $Opt_Ch_Class     input   True if looking for ResourceClass      #
#                             stanza in the input file.              #
#   @$r_cmdline       input   Reference to attr=value pairs via      #
#                             command line and ARGV.                 #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub process_input 
{
my ($Opt_Input_File, $filename, $Opt_Ch_class, $r_cmdline) = @_;
my $rc = 0;
my $rLoRsrcData;

if ($Opt_File_Input) {
    # Set the name of the stanza that should be in the input file
    my $file_stanza;
    if ($Opt_Ch_Class) {
        $file_stanza = "PersistentResourceClassAttributes";
    }
    else {
        $file_stanza = "PersistentResourceAttributes";
    }

    ($rc, $rLoRsrcData) = process_input_file($filename, $file_stanza);
    if ($rc != 0) {
        # Print Error processing input file msg
        # process_input_file will write a more detailed error message
        printEMsg("EMsgchrsrcInputFileError", $filename);
        return(MC_CLI_USER_ERROR);
    }
}
else {
    ($rc, $rLoRsrcData) = process_cmdline_input($r_cmdline);
    if ($rc != 0) {
         # Print Command Line Error Attr=value error msg
         printEMsg("EMsgchrsrcCmdLineError", "\"@$r_cmdline\"");
         return(MC_CLI_BAD_OPERAND);
    }
}

if ($Verbose) {
    my ($attribute, $value, $entry, $row_header);
    foreach $entry (@$rLoRsrcData) {
        $row_header = $entry->[0];
        foreach $element (@{$entry->[1]}) {
            $attribute = $element->[0];
            $value = $element->[1];
            print "$attribute = \"$value\"\n";
        }
    }
}
return ($rc, $rLoRsrcData);
}   # end process_input


#--------------------------------------------------------------------#
# build_resource - function to build up the hash of persistent attr  #
#   to be used in defining the resource. Merging the {at_name},      #
#   {at_value}, {at_dtype} all into one structure and validating     #
#   the input data as its processed.                                 #
#                                                                    #
# Parameters:                                                        #
#   $resource         input   Resource name.                         #
#   $rRsrcData        input   Reference to array of attr,value pairs.#
#   $rHoPAttrDefs     input   Reference to hash of all the required  #
#                             and optional attribute definitions     #
#                             for this resource.                     #
#   $rHoSDAttrDefs    input   Reference to hash of all the required  #
#                             and optional SD attribute definitions  #
#                             for this resource.                     #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub build_resource
{
my ($resource, $rRsrcData, $rHoPAttrDefs, $rHoSDAttrDefs) = @_; 

my %HoPAttr = ();
my @required_attributes = ();
my ($new_attr, $dtype);
my $badrc = 0;
my $resource_def;

foreach $new_attr (@{$rRsrcData->[1]}) {
    $attr_name = $new_attr->[0];
    # first check to make sure this is a valid attribute name
    if (!defined($$rHoPAttrDefs{$attr_name})) {
        if ($Opt_File_Input)
        {
            $resource_def= $rRsrcData->[0];
            $resource_def=~ s/^\s+//; 
            $resource_def=~ s/\s+$//; 
            printEMsg("EMsgchrsrcAttrNotDefForSet3", $attr_name,
                      $resource, $resource_def);            
        }
        else
        {
            printEMsg("EMsgchrsrcAttrNotDefForSet2", $attr_name, $resource);
        }
        $badrc = MC_CLI_USER_ERROR;
        next;                   # continue validating rest of input
    }   
    
    # next check to make sure the value is of the right type
    # SDs require some special handling...
    $dtype = $$rHoPAttrDefs{$attr_name}{at_dtype};  
    if ( ($dtype =~ /^CT_SD_PTR$/) || ($dtype =~ /^CT_SD_PTR_ARRAY$/) ) {
        @r_sd_element_dtypes = get_sd_element_dtypes($rHoSDAttrDefs,
            $attr_name);
        ($rc, $attr_value) = convert_input_value_api($dtype, $new_attr->[1],
            \@r_sd_element_dtypes);
    }
    else {
        ($rc, $attr_value) = convert_input_value_api($dtype, $new_attr->[1]); 
    }
    if ($rc != 0) {      
        printCEMsg("EMsgMCcliBadAttrValue", $attr_name, $new_attr->[1]);
        if ($badrc == 0) {$badrc = $rc; }
        next;
    }   
    %elements = ();
    $elements{at_name} = $attr_name;
    $elements{at_dtype} = $$rHoPAttrDefs{$attr_name}{at_dtype};
    $elements{at_value} = $attr_value;
    # for 2 non-array values specified in a row, save the last value 131200
    if (($#$attr_value >0) && ($dtype !~ /ARRAY/)) {
       $elements{at_value} = $$attr_value[$#$attr_value];
    }
    # save original SD value
    if ( ($dtype =~ /^CT_SD_PTR$/) || ($dtype =~ /^CT_SD_PTR_ARRAY$/) ) {
       $elements{at_SD_value} = $new_attr->[1];
    }

    $HoPAttr{$attr_name} = { %elements };
}

return($badrc, \%HoPAttr); 
}   # end build_resource


#--------------------------------------------------------------------#
# setup_chrsrcapi - function to add a parameter specification to be  #
#   used later for chrsrc-api.                                       #
#                                                                    #
# Parameters:                                                        #
#   $chrsrcapi_parm   in/out  Name of the string variable that has   #
#                             any previous resource definition.      #
#   $rsrc_class       input   Name of the resource class to which we #
#                             want to change the resource.           #
#   $res_handle       input   Resource handle to change.             #
#   $handle_flag      input   True means resource handle used.       #
#   $class_flag       input   True mean class attribute.             #
#   $class_DM_flag    input   True mean class attribute for DM (-C). #
#   $pd_names         input   peer domain names for -C.              #
#   $sel_str          input   Selection string.                      #
#   %$rHoAttr         input   Reference to complex hash of attribute #
#                             names and value pairs.                 #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   Trace             input   Trace call & return of extension.      #
#   Verbose           input   Print newly defined rsrc handle.       #
#--------------------------------------------------------------------#
sub setup_chrsrcapi
{
my ($chrsrcapi_parm, $rsrc_class, $res_handle, $handle_flag, 
    $class_flag, $class_DM_flag, $pd_names, $sel_str, $rHoAttr) = @_;
my $newparms= "";
my $tmpparms= "";
my $i=0;
my $j=0;
my $comma = "";

# format being created:
# -c ClassName::<AttrName>::<AttrValue]...
# -C ClassName::<PeerDomainNames>::<AttrName>::<AttrValue]...
# -s ClassName::<SelectStr>::<AttrName>::<AttrValue]...
# -w ClassName::<SelectStr>::<NodeFile>::<AttrName>::<AttrValue]...
# -r ResHandle::<AttrName>::<AttrValue]...

# start new parameter list with proper flag
if ($handle_flag) {
   $newparms = "-r $res_handle";
}
elsif ($class_flag) {
   $newparms = "-c $rsrc_class";
}
elsif ($class_DM_flag) {
   $newparms = "-C ${rsrc_class}${DELIMITER}${pd_names}";
}
elsif ($Opt_Node_File) {
   # create temp file based on Stdin input
   if ($Opt_Stdin)
   {
       $node_file_name= read_from_Stdin();
   }
   $newparms = "-w ${rsrc_class}${DELIMITER}${sel_str}${DELIMITER}${node_file_name}";
}
else {
   $newparms = "-s ${rsrc_class}${DELIMITER}${sel_str}";
}

# add attributes and values
my @attr_list = keys %$rHoAttr;
foreach $attr (@attr_list) {
   if ( ( $$rHoAttr{$attr}{at_dtype} =~ /^CT_SD_PTR$/ ) ||
        ( $$rHoAttr{$attr}{at_dtype} =~ /^CT_SD_PTR_ARRAY$/ ) ) {
      $newparms .= $DELIMITER . $attr . $DELIMITER . $$rHoAttr{$attr}{at_SD_value};
   }
   elsif ( $$rHoAttr{$attr}{at_dtype} =~ /ARRAY/ ) {
      $tmpparms = "{";
      $i = scalar(@{$rHoAttr->{$attr}->{at_value}});
      for ($j=0; $j<$i; $j++){
          $tmpparms .= $comma . $rHoAttr->{$attr}->{at_value}[$j];
          $comma = ",";
      }
      $tmpparms .= "}";
      $comma = "";

      $newparms .= $DELIMITER . $attr . $DELIMITER . $tmpparms;

   }
   else {
      $newparms .= $DELIMITER . $attr . $DELIMITER . $$rHoAttr{$attr}{at_value};
   }
}
# check for any null attribute values
# should be ok but chrsrc-api doesn't like it now
$newparms =~ s/${DELIMITER}${DELIMITER}/${DELIMITER}''${DELIMITER}/g;
$newparms =~ s/${DELIMITER}$/${DELIMITER}''/;

# going to put double quotes around the whole thing so
# escape any inner quotes
$newparms = escape_chars($newparms);

# now put quotes around the whole thing
$newparms =~ s/^-s /-s \"/;
$newparms =~ s/^-r /-r \"/;
$newparms =~ s/^-c /-c \"/;
$newparms =~ s/^-C /-C \"/;
$newparms =~ s/^-w /-w \"/;
$newparms =~ s/$/\"/;

# append the new parameters to the existing one
$i =  $#$chrsrcapi_parm;
if ( ($i<0) || (length($$chrsrcapi_parm[$i]) > $CMD_LIMIT) ) {
    $$chrsrcapi_parm[$i+1] .= " " . $newparms;
}
else {
    $$chrsrcapi_parm[$i] .= " " . $newparms;
}

return (@$chrsrcapi_parm);

}   # end setup_chrsrcapi

#--------------------------------------------------------------------#
# change_resource - function to call the chrsrc-api command to       #
#   change one or more resources.                                    #
#                                                                    #
# Parameters:                                                        #
#   @parms            input   Parameter string to send to chrsrc-api.#
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   Trace             input   Trace call & return of extension.      #
#   Verbose           input   Print newly defined rsrc handle.       #
#--------------------------------------------------------------------#
sub change_resource
{
my @parms = @_;
my $rc = 0;
my $i = 0;
my @ch_out = ();

for  ($i=0; $i<=$#parms;$i++){

   #$Verbose && printIMsg("IMsgmkrsrcDefiningRsrc", $rsrc_class);

   if ($main::Trace) { print STDERR "Calling chrsrc-api:\n";
      print STDERR "chrsrc-api parameters:";
      print STDERR "@parms\n";
   }

   @ch_out = `$CTBINDIR/chrsrc-api -I $DELIMITER -D $DELIMITER $parms[$i] 2>&1`;

   # remove temp node file if from STDIN
   if ($Opt_Stdin)
   {
       unlink($node_file_name);
   }

   # capture the return code from chrsrc-api
   $rc = $?;
   $rc = process_exit_code($rc);

   if ($main::Trace) { print STDERR "chrsrc-api results:\n";
      print STDERR "chrsrc-api returned $rc \n";
      print STDERR "@ch_out";
      print STDERR "Return from chrsrc-api\n";
   }

   # show any errors if there was a bad rc
   if ($rc != 0) {
      process_api_error($DELIMITER,$rc,@ch_out);
   }
}

return $rc;
}   # end change_resource


#--------------------------------------------------------------------#
# error_check - checks the return code from the RMC function and     #
#   the error response return code.  If an error is detected         #
#   appropriate error messages will be displayed.                    #
#                                                                    #
# Parameters:                                                        #
#   $rmc_function     in      Name of the rmc function that was      #
#                             called and whose error code we are     #
#                             checking.                              #
#   $rmc_class        in      The rmc class name.                    #
#   $response         in      RMC response.                          #
#   $rmc_rc           in      The rmc function return code.          #
#   $error            in      The error response.                    #
#                                                                    #
# Return:                                                            #
#   $rc                       return code.                           #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub error_check
{
my ($rmc_function, $rmc_class, $response, $rmc_rc, $error) = @_;

my $rc = 0;
my $err_rc = $error->errnum();

# TODO:  be able to handle resource handle from here too
if ($rmc_rc != 0) {
    printEMsg("EMsgchrsrcChRsrcError", $rmc_class);
    my $rmc_rc_hex = sprintf "0x%8.8lx", $rmc_rc;
    printCEMsg("EMsgMCcliMCFunctionFailure", $rmc_function, 
        $rmc_rc, $rmc_rc_hex);
    $rc = MC_CLI_RMC_ERROR;
    return $rc;
}

# Check the errnum in each of the RMC responses
for (my $r = 0; $r < $response->array_count; $r++) {
    # get the error that goes with the specific response
    if ($r > 0) {
        $response->error($error, $r);
        $err_rc = $error->errnum();
    }
    if ($err_rc != 0) {
        if ($err_rc == RMC_ECLASSNOTDEFINED) {
            printCEMsg("EMsgMCcliClassNotDef", $rmc_class);
            $rc = MC_CLI_USER_ERROR;
        }
        elsif ($err_rc == RMC_EBADRSRCHANDLE) {
            printEMsg("EMsgchrsrcBadRsrcHandle");
            print STDERR $error->error_msg;
            $rc = MC_CLI_USER_ERROR;
        }
        elsif ($err_rc == RMC_ENORSRCSFOUND) {
            printCEMsg("EMsgMCcliNoRsrcFound");
            $rc = MC_CLI_NO_RSRC_FOUND;
        }
        elsif ($err_rc == RMC_EACCESS) {
            print STDERR $error->error_msg;
            $rc = MC_CLI_USER_ERROR;
        }
        elsif ($err_rc >= 0x60000 && $err_rc <= 0x6ffff) {
            # Selection string errors are in this range
            printCEMsg("EMsgMCcliSelectStrError");
            print STDERR $error->error_msg;
            $rc = MC_CLI_USER_ERROR;
        }
        else {
            printEMsg("EMsgchrsrcChRsrcError", $rmc_class);
            my $err_rc_hex = sprintf "0x%8.8lx", $err_rc;
            printCEMsg("EMsgMCcliMCFunctionFailure", $rmc_function, 
                $err_rc, $err_rc_hex);
            print STDERR $error->error_msg; 
            $rc = MC_CLI_RMC_ERROR;
        }
    }   # end if
}   # end for

return $rc;
}   # end error_check