/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/*                                                                        */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* (C) COPYRIGHT International Business Machines Corp. 1996,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                                                     */
/*===========================================================================*/
/* @(#)42   1.9   src/rsct/pem/emtools/rmapi_samples/rmapi_smpcmd.c, emtools, rsct_rady, rady2035a 6/5/98 11:25:29 */
 
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ha_rmapi.h>
 
/*
 *  rmapi_smpcmd.c
 *
 *  This program presents an example of using the Resource Monitor Application
 *  Programming Interface (RMAPI).  The configuration data for this monitor
 *  is in the file RmapiSample.loadsdr.  This monitor is an example of a command
 *  based client monitor (connects to the Event Management daemon) and uses
 *  state type variables.
 *
 *  Event Management objects defined for this monitor:
 *
 *  Monitor:
 *      IBM.PSSP.SampleCmdMon                     # client monitor
 *                                                #
 *  Class:                                        #
 *      IBM.PSSP.SampleCmdClass                   # variable class
 *                                                #
 *  Variables:                                    #
 *      IBM.PSSP.SampleCmdMon.state               # Valuetype state, datatype long used to
 *                                                # reflect the state of the resource:
 *                                                # Configured, Unconfigured, Running.
 *        Resource Identifiers: NodeNum           # Used as locator field of variable.
 *                               NAME             # Name of the resource instance.
 *                                                #
 *    IBM.PSSP.SampleCmdMon.call                  # Valuetype state, datatype SBS used to
 *                                                # reflect the state of the last command
 *                                                # for this resource instance.
 *        Resource Identifiers: NodeNum           # Used as locator field of variable.
 *                               NAME             # Name of the resource instance.
 *        SBS Field:         Action               # Datatype long, sn=0.
 *                                                # Last action attempted for this resource.
 *                           Options              # Datatype char, sn=1.
 *                                                # Dummy options string to put into SBS.
 *                           StateChange          # Datatype long, sn=2.
 *                                                # 0 if action failed, else !0.
 *                           State                # Datatype long, sn=3.
 *                                                # Final state of the resource (same as the state var).
 *
 * What this monitor does:  rmapi_smpcmd provides an example of a command which
 *   controls some imaginary resource. The command is used to change the "state"
 *   of the resource to one of the following: Unconfigured, Configured, or Running,
 *   by passing an "action" argument: config, unconfig, start, or stop. One
 *   or both of the state variables will be updated through the RMAPI on each
 *   invocation of this command.
 *
 * Legal state transitions (action):
 *    Unconfigured -> Configured    (config)
 *    Configured   -> Unconfigured  (unconfig)
 *    Configured   -> Running       (start)
 *    Running      -> Configured    (stop)
 *
 * If a "legal" action is requested, the command will add both of the variables
 * for this resource to the Event Management client session. The IBM.PSSP.SampleCmdMon.state
 * variable will be added by the call ha_rr_add_var() with a value of TRANSITION and then
 * updated to the new state value by ha_rr_send_val().
 *
 * If the action is "illegal" for the current state, only the IBM.PSSP.SampleCmdMon.call variable
 * will be updated so that the IBM.PSSP.SampleCmdMon.state variable remains unchanged.
 *
 * Parameters:
 *
 *  -a <action>      Required. Possible options are config, unconfig, start or stop. This is
 *                   the action requested.
 *
 *  -c <curr_state>  Required. Possible options are Configured, Unconfigured, or Running.
 *                   Since this command does no real work, this option tells the command
 *                   what "state" the resource is in before attempting the action.
 *
 *  -n <name>        Required. Used as the resource ID NAME element to uniquely
 *                   identify different instances of the resource variables.
 *
 *  -o <option>      Optional. Any string, used to demonstrate a char datatype within an SBS
 *                   variable.
 *
 * Eamples:
 *
 *  1. rmapi_smpcmd -a config -c Unconfigured -n test_resource -o test_opt_str
 *
 *  Will cause both variables defined for this monitor to be created
 *  using "test_resource" as the NAME resource ID. Both variables will
 *  be registered and added to the RMAPI. The IBM.PSSP.SampleCmdMon.call
 *  will have the following values sent to the Event Manager daemon on
 *  the call to ha_rr_add():
 *      action:           0                 # CONFIG
 *      options:          test_opt_str      # -o flag parm
 *      state_change:     1                 # successful state change
 *      state:            1                 # CONFIGURED
 *
 *  The IBM.PSSP.SampleCmdMon.state variable will have a initial value of TRANSITION
 *  sent to the RMAPI on ha_rr_add(). It's value will then be updated to CONFIGURED
 *  and sent to the RMAPI using ha_rr_send_val().
 *
 *  2. rmapi_smpcmd -a unconfig -c Running -n test_resource
 *
 *  Will cause only the IBM.PSSP.SampleCmdMon.call variable to be created
 *  using "test_resource" as the NAME resource ID value. The "state"
 *  variable is not sent to the RMAPI because it was unchanged. IBM.PSSP.SampleCmdMon.call
 *  will have the following values sent to the Event Manager daemon on
 *  the call to ha_rr_add():
 *      action:           1                 # UNCONFIG
 *      options:          No options.       # default value for -o flag
 *      state_change:     0                 # state change failed
 *      state:            2                 # RUNNING
 *
 *  This program can be compiled with the following command:
 *
 *      cc -O rmapi_smpcmd.c -o rmapi_smpcmd -lha_rr
 */
 
/* local defines
CMDNAME                  - Name of this command.
RESOURCE_MONITOR_NAME    - Name of this monitor as defined in the configuration database.
STATE_VARIABLE_NAME      - Name of the "state" variable defined for this monitor.
CALL_VARIABLE_NAME       - Name of the "call" variable defined for this monitor.
RES_ID                   - Resource ID.
MAX_VARIABLES            - Number of variables defined for this monitor.
DEFAULT_OPTION_STRING    - Default string for -o opt.
USAGE_MSG                - Error msg for cmd syntax errors.
*/
#define CMDNAME                  "rmapi_smpcmd"
#define RESOURCE_MONITOR_NAME    "IBM.PSSP.SampleCmdMon"
#define STATE_VARIABLE_NAME      "IBM.PSSP.SampleCmdMon.state"
#define CALL_VARIABLE_NAME       "IBM.PSSP.SampleCmdMon.call"
#define RES_ID                   "NAME="
#define MAX_VARIABLES            2
#define DEFAULT_OPTION_STRING    "No options."
#define USAGE_MSG    "Usage: "CMDNAME" -a <action> -c <curr_state> -n <name> -o <options>\n"\
            "\t-a <action>\tResource action (config,unconfig,start or stop).\n"\
            "\t-c <curr_state>\tCurrent \"state\" of the resource (Configured,Unconfigured,Running).\n"\
            "\t-n <name>\tName of the resource.\n"\
            "\t-o <options>\tResource options.\n"
 
/* local functions */
void display_rmapi_err(struct ha_em_err_blk *errblk);
enum States process_action(enum States, enum Actions);
void * create_sbs(enum Actions, char *, long, enum States);
void rr_start();
void rr_reg_var();
void rr_add_var();
void rr_del_var();
void rr_end();
 
/* globals */
struct ha_rr_variable   Variables[MAX_VARIABLES];   /* RMAPI variable array. */
struct ha_rr_val        Values[MAX_VARIABLES];      /* RMAPI value array. */
int                     NumVariables = 0;           /* Number of RMAPI variables to use. */
struct ha_em_err_blk    ErrBlock;                   /* global error block for RMAPI calls. */
int                     ClientSock = HA_RR_FAIL;    /* Client session socket. */
enum Actions {                                      /* State processing actions. */
    CONFIG,
    UNCONFIG,
    START,
    STOP,
    NUM_ACTIONS
};
char *ActionNames[] = {"config", "unconfig", "start", "stop"};
enum States {                                 /* Resource states. */
    CONFIGURED,
    UNCONFIGURED,
    RUNNING,
    NUM_STATES,
    TRANSITION
};
char *StateNames[] = {"Configured", "Unconfigured", "Running"};
 
/******************************************************************************/
/* Main - Main - Main - Main - Main - Main - Main - Main - Main - Main - Main */
/******************************************************************************/
main(int argc, char **argv)
{
enum States new_state;
enum States curr_state;
enum States trans_state = TRANSITION;
enum Actions action;
char *action_name = NULL;
char *curr_state_name = NULL;
char *res_name = NULL;
char *res_opts = NULL;
char *resid;
int c, i, rc;
 
    /*
     * Parse the command line arguments.
     */
    while ((c = getopt(argc, argv, "a:c:n:o:")) != EOF) {
        switch (c) {
            case 'a' : action_name = optarg;     break;
            case 'c' : curr_state_name = optarg; break;
            case 'n' : res_name = optarg;        break;
            case 'o' : res_opts = optarg;        break;
            default  :
                fprintf(stderr,USAGE_MSG);
                exit(1);
            }
    }
 
    /*
     * Check for required options.
     */
    if ((res_name == NULL) || (action_name == NULL) || (curr_state_name == NULL)) {
        fprintf(stderr,"%s: missing -a, -c or -n flag.\n",CMDNAME);
        fprintf(stderr,USAGE_MSG);
        exit(1);
    }
 
    /*
     * Check for valid action and state options.
     */
    for (i=0; (i < NUM_ACTIONS) && strcmp(ActionNames[i],action_name); i++);
    action = i;
 
    for (i=0; (i < NUM_STATES) && strcmp(StateNames[i],curr_state_name); i++);
    curr_state = i;
 
    if ((action == NUM_ACTIONS) || (curr_state == NUM_STATES)) {
        fprintf(stderr,"%s: invalid -a or -c parameter.\n",CMDNAME);
        fprintf(stderr,USAGE_MSG);
        exit(1);
    }
 
    /*
     * Make sure an option string is put into the sbs variable.
     */
    if (res_opts == NULL) {
        res_opts = DEFAULT_OPTION_STRING;
    }
 
    fprintf(stdout,"%s: Current state of resource %s is %s. Requested action is %s.\n",
            CMDNAME,res_name,curr_state_name,action_name);
    /*
     * Dummy resource processing. Just checks that the "action" is legal for the
     * current "state" and returns the new state (new_state == curr_state if not
     * a legal action for the current state).
     */
    new_state = process_action(curr_state,action);
 
    /*
     * Initialize the RMAPI variables.
     */
    memset(Variables, 0 , sizeof(struct ha_rr_variable) * MAX_VARIABLES);
    memset(Values, 0 , sizeof(struct ha_rr_val) * MAX_VARIABLES);
 
    /*
     * Create an resource ID for the variables.
     */
    resid = malloc(strlen(res_name) + strlen(RES_ID) + 1);
    sprintf(resid,"%s%s",RES_ID,res_name);
 
    Variables[0].rr_var_name = CALL_VARIABLE_NAME;
    Variables[0].rr_var_rsrc_ID = resid;
    Variables[0].rr_varu.rr_var_inst_id = 0;
 
    Variables[1].rr_var_name = STATE_VARIABLE_NAME;
    Variables[1].rr_var_rsrc_ID = resid;
    Variables[1].rr_varu.rr_var_inst_id = 1;
 
    if (new_state != curr_state) {
        /*
         * The "action" was successful indicated by the state change.
         * Send both the new "state" variable and the "call" variable,
         * otherwise only send the failed "call" variable.
         */
        fprintf(stdout,"%s: Action %s successful. Resource %s will change states %s->TRANSITION->%s.\n",
                CMDNAME,action_name,res_name,curr_state_name,StateNames[new_state]);
        NumVariables = 2;
    } else {
        fprintf(stderr,"%s: Cannot perform action %s when in state %s. The "
                "\"state\" variable will not be updated.\n",
                CMDNAME,action_name,curr_state_name);
        NumVariables = 1;
    }
 
    /*
     * Initialize the RMAPI and client session.
     */
    rr_start();
 
    /*
     * Register variable(s) with the RMAPI.
     */
    rr_reg_var();
 
    /*
     * Set the handle addresses and initial values in the
     * RMAPI variables and add them to the client session.
     * The initial value for IBM.PSSP.SampleCmdMon.state is
     * TRANSITION. If the state changed, the new state value
     * will then be sent by the RMAPI routine ha_rr_send_val().
     * If the state did not change, IBM.PSSP.SampleCmdMon will
     * not be added.
     */
    Variables[0].rr_varu.rr_var_hndl = &(Values[0].rr_var_hndl);
    Variables[0].rr_value = create_sbs(action,res_opts,(new_state==curr_state),new_state);
    Variables[1].rr_varu.rr_var_hndl = &(Values[1].rr_var_hndl);
    Variables[1].rr_value = &(trans_state);
 
    /*
     * Add the variable(s) to the RMAPI. This will also cause the initial
     * values (Variables[n].rr_value) to be sent to the Event Manager.
     */
    rr_add_var();
 
    if (new_state != curr_state) {
        /*
         * State change occurred. ha_rr_add_var() sent the initial value for
         * the "state" variable as being in TRANSITION state. Now call
         * the RMAPI to send the new state of the resource. First check that
         * an error didn't occur when register or adding the variable.
         */
        if ((NumVariables == 2) && (Variables[1].rr_var_errno == 0)) {
            /*
             * Sleep to look like were actually performing 
             * an action on the resource.
             */
            sleep(2);
 
            /*
             * Send the new state value of variable IBM.PSSP.SampleCmdMon.state.
             */
            Values[1].rr_value = &(new_state);
            rc = ha_rr_send_val(&Values[1],1,0,&ErrBlock);
            if (rc == HA_RR_FAIL) {
                display_rmapi_err(&ErrBlock);
            }
        }
    }
 
    /*
     * Delete the variable(s) that were registered and added.
     */
    rr_del_var();
 
    /*
     * Close the EM session and terminate the RMAPI.
     */
    rr_end();
 
    /*
     * Exit return code.
     */
    exit (new_state==curr_state);
}
 
/*-------------------------------------------------------------------------------------
 * Resource "processing" routine. Returns the new state if the action
 * is valid for the current state, otherwise returns the current state.
 * Legal state transitions:
 *    Unconfigured -> Configured    (config)
 *    Configured   -> Unconfigured  (unconfig)
 *    Configured   -> Running       (start)
 *    Running      -> Configured    (stop)
 -------------------------------------------------------------------------------------*/
enum States process_action(enum States state, enum Actions action) {
enum States new_state = state;
 
    switch (state) {
        case CONFIGURED :
            switch (action) {
                case CONFIG   :
                case STOP     :
                    break;
                case UNCONFIG :
                    new_state = UNCONFIGURED; break;
                case START    :
                    new_state = RUNNING; break;
            }
            break;
        case UNCONFIGURED :
            switch (action) {
                case UNCONFIG :
                case START    :
                case STOP     :
                    break;
                case CONFIG   :
                    new_state = CONFIGURED; break;
            }
            break;
        case RUNNING :
            switch (action) {
                case CONFIG   :
                case UNCONFIG :
                case START    :
                    break;
                case STOP     :
                    new_state = CONFIGURED; break;
            }
            break;
    }
    return new_state;
}
 
/*-------------------------------------------------------------------------------------
 * Creates an SBS variable value for the call variable.
 -------------------------------------------------------------------------------------*/
void * create_sbs(enum Actions action, char *opts, long state_chg, enum States state) {
int buff_sz;        /* size of the buffer needed for the sbs value. */
int slen;           /* length of the opts sbs field. */
char *sbs, *p;      /* sbs buffer pointers. */
 
    /*
     * Get the length of the "opts" field - including terminating character.
     */
    slen = strlen(opts) + 1;
 
    /*
     * Calculate the size of the buffer needed:
     * length of the opts field +
     * length of the 3 long fields +
     * 1 long for each sbs field header.
     */
    buff_sz = slen + (sizeof(long) * 7);
 
    /*
     * Allocate an sbs buffer and prefix the buffer size.
     * Note: The actual buffer is sizeof(long) larger than the 
     * calculated length (the SBS is prefixed with a long value 
     * which contains the length of the SBS data that follows).
     */
    sbs = p = malloc(buff_sz + sizeof(long));
    *((long *)p) = buff_sz;  p += sizeof(long);
 
    /*
     * Copy the first (Action) sbs field. The field prefixes are a
     * short field length, 1 character for the data type and 1
     * character for the field serial number.
     */
    *((short *)p) = (short)sizeof(long); p += sizeof(short);
    *p = (char)ha_emFTlong; p++;
    *p = 0; p++;
    *((long *)p) = (long)action; p += sizeof(long);
 
    /*
     * Copy the second (Options) field (sbs serial number 1).
     */
    *((short *)p) = (short)slen; p += sizeof(short);
    *p = (char)ha_emFTchar; p++;
    *p = 1; p++;
    strcpy(p,opts); p += slen;
 
    /*
     * Copy the third (StateChange) field (sbs serial number 2).
     */
    *((short *)p) = (short)sizeof(long); p += sizeof(short);
    *p = (char)ha_emFTlong; p++;
    *p = 2; p++;
    *((long *)p) = (long)state_chg; p += sizeof(long);
 
    /*
     * Copy the forth (State) field (sbs serial number 3).
     */
    *((short *)p) = (short)sizeof(long); p += sizeof(short);
    *p = (char)ha_emFTlong; p++;
    *p = 3; p++;
    *((long *)p) = (long)state; p += sizeof(long);
 
    /*
     * Return the pointer to the sbs value.
     */
    return sbs;
}
 
/*-------------------------------------------------------------------------------------
 * Initializes the RMAPI and starts the Client session with haemd.
 -------------------------------------------------------------------------------------*/
void rr_start() {
int rc;
 
    /*
     * Initialize the RMAPI.
     */
    rc = ha_rr_init(RESOURCE_MONITOR_NAME, &ErrBlock);
    if (rc == HA_RR_FAIL) {
        display_rmapi_err(&ErrBlock);
    } else {
        /*
         * Create a client session with the Event Manager daemon.
         */
        ClientSock = ha_rr_start_session(HA_RR_NOTIFY_SELECT, &ErrBlock);
        if (ClientSock == HA_RR_FAIL) {
            display_rmapi_err(&ErrBlock);
        }
    }
    if ((rc == HA_RR_FAIL) || (ClientSock == HA_RR_FAIL)) {
        /*
         * RMAPI Initialize or start session failed. Set the number of 
         * variable to 0 so that RMAPI calls to register, add, send,
         * and delete variables are ignored.
         */
        NumVariables = 0;
    }
}
 
/*-------------------------------------------------------------------------------------
 * Registers the variables to be added with the RMAPI.
 -------------------------------------------------------------------------------------*/
void rr_reg_var() {
int i, num_registered;
 
    if (NumVariables) {
        num_registered = ha_rr_reg_var(Variables,NumVariables,&ErrBlock);
        if (num_registered != NumVariables) {
            if (num_registered == HA_RR_FAIL) {
                display_rmapi_err(&ErrBlock);
            } else {
                for (i=0; i < NumVariables; i++) {
                    /*
                     * Loop through the variables that were attempted
                     * to be registered and report any the errors.
                      */
                    if (Variables[i].rr_var_errno) {
                        fprintf(stderr,"ha_rr_reg_var() error with variable(%s) resource id(%s) "
                                "instance_id(%d) RMAPI errno=(%d).\n",
                                Variables[i].rr_var_name,
                                Variables[i].rr_var_rsrc_ID,
                                Variables[i].rr_varu.rr_var_inst_id,
                                Variables[i].rr_var_errno);
                    }
                }
            }
        }
        if ((num_registered == HA_RR_FAIL) || (num_registered == 0)) {
            /*
             * Register failed - set NumVariables to 0
             * so that ha_rr_add_var() isn't attempted.
             */
            NumVariables = 0;
        }
    } else {
        fprintf(stdout,"%s: No variables to register.\n",CMDNAME);
    }
}
 
/*-------------------------------------------------------------------------------------
 * Adds variables to the Client session with haemd.
 -------------------------------------------------------------------------------------*/
void rr_add_var() {
int i, num_added;
 
    if (NumVariables) {
        num_added = ha_rr_add_var(ClientSock, Variables, NumVariables, 1, &ErrBlock);
        if (num_added != NumVariables) {
            if (num_added == HA_RR_FAIL) {
                display_rmapi_err(&ErrBlock);
            } else {
                /*
                 * Loop through the variables that were attempted
                 * to be added and report the errors.
                 */
                for (i=0; i < NumVariables; i++) {
                    if (Variables[i].rr_var_errno != 0) {
                        fprintf(stderr,"Variable(%s) resource id(%s) bad errno(%d) from ha_rr_add_var().\n",
                                Variables[i].rr_var_name,
                                Variables[i].rr_var_rsrc_ID,
                                Variables[i].rr_var_errno);
                    }
                }
            }
        }
        if ((num_added == HA_RR_FAIL) || (num_added == 0)) {
            /*
             * Add failed - set NumVariables to 0 so that ha_rr_send_var()
             * and ha_rr_del_var() are not called.
             */
            NumVariables = 0;
        }
    } else {
        fprintf(stdout,"%s: No variables to add.\n",CMDNAME);
    }
}
 
/*-------------------------------------------------------------------------------------
 * Deletes any variables added to the RMAPI for the Client session with haemd.
 -------------------------------------------------------------------------------------*/
void rr_del_var() {
int num_deleted;
 
    if (NumVariables) {
        /*
         * Delete any variables that have been added.
         */
        num_deleted = ha_rr_del_var(ClientSock, Variables, NumVariables, &ErrBlock);
        if (num_deleted == HA_RR_FAIL) {
            display_rmapi_err(&ErrBlock);
        }
    } else {
        fprintf(stdout,"%s: No variables to delete.\n",CMDNAME);
    }
}
 
/*-------------------------------------------------------------------------------------
 * Ends the Client session with haemd (if started) and terminates the RMAPI.
 -------------------------------------------------------------------------------------*/
void rr_end() {
int rc;
 
    if (ClientSock != HA_RR_FAIL) {
        /*
         * Call the RMAPI to close the client session with the Event Manager.
         */
        rc = ha_rr_end_session(ClientSock, &ErrBlock);
        if (rc == HA_RR_FAIL) {
            display_rmapi_err(&ErrBlock);
        }
    }
 
    /*
     * Terminate the RMAPI.
     */
    rc = ha_rr_terminate(&ErrBlock);
    if (rc && ErrBlock.em_errno != HA_RR_EACCESS) {
        display_rmapi_err(&ErrBlock);
    }
}
 
/*-------------------------------------------------------------------------------------
 * Displays an RMAPI error.
 -------------------------------------------------------------------------------------*/
void display_rmapi_err(struct ha_em_err_blk *errblk) {
  fprintf(stderr,
          "RMAPI Error: File(%s) Version(%s) Line(%d) Errno(%d)\n\t%s",
          errblk->em_errfile,
          errblk->em_errlevel,
          errblk->em_errline,
          errblk->em_errno,
          errblk->em_errmsg);
 }
 
