/* 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                                                     */
/*===========================================================================*/
/* @(#)44   1.9   src/rsct/pem/emtools/rmapi_samples/rmapi_smpsig.c, emtools, rsct_rady, rady2035a 6/5/98 11:25:45 */
 
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/events.h>
#include <ha_rmapi.h>
 
/*
 *  rmapi_smpsig.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 server
 *  monitor (Resource Monitor Managers connect to the monitor) function that could
 *  be incorporated into a daemon or subsystem.  This example monitor demonstrates
 *  using SIGIO as the notification protocol and the use of dynamically instantiable
 *  variables.
 *
 *  Event Management objects defined for this monitor:
 *
 *  Monitor:
 *      IBM.PSSP.SampleSigMon                     # server monitor definition
 *
 *  Class:
 *      IBM.PSSP.SampleSigDynInstClass            # class for dynamically instantable vars
 *      IBM.PSSP.SampleSigNonInstClass            # class for non-instantable vars
 *
 *  Variables:
 *      IBM.PSSP.SampleSigMon.DynInstVar.var        # valuetype quantity, datatype long
 *          Resource Identifiers:  NodeNum          # used as locator field of var
 *                                 InstName         # instance name of variable
 *      IBM.PSSP.SampleSigMon.NonInstVars.var1      # valuetype counter, datatype long
 *          Resource Identifiers:  NodeNum          # used as locator field of var
 *      IBM.PSSP.SampleSigMon.NonInstVars.var2      # valuetype counter, datatype long
 *          Resource Identifiers:  NodeNum          # used as locator field of var
 *      IBM.PSSP.SampleSigMon.NonInstVars.var3      # valuetype counter, datatype long
 *          Resource Identifiers:  NodeNum          # used as locator field of var
 *
 * What this monitor does:  rmapi_smpsig provides an example of a server monitor
 *   which has 1 dynamically instantable variable and 3 non-instantiable variables.
 *
 *   The monitor will continue to run until it either receives a signal to terminate, or
 *   all clients have closed their connections.
 *
 *  This program can be compiled with the following command:
 *
 *      cc -O rmapi_smpsig.c -o rmapi_smpsig -lha_rr
 */
 
#ifndef MIN
#define MIN(x, y)   ((x) < (y) ? (x) : (y))
#endif
 
/* local defines
PROGNAME                - Name of this program.
MAX_INTERVAL            - Max number of seconds before ha_rr_send_value or ha_rr_touch must be called.
RESOURCE_MONITOR_NAME   - Name of this monitor as defined in the config data.
MONITOR_CLASS_NAME      - Class name as defined in the config data.
SOCKET_TABLE_SIZE       - HA_RR_MAX_SESSIONS + 1 for the RMAPI server socket.
RMAPI_SERVER_INDEX      - Index in the socket call table for the RMAPI server socket.
STATIC_VAR_NAME_PREFIX  - Common portion of the static vars as defined in the config data.
INST_VAR_NAME_PREFIX    - Common portion of the inst vars as defined in the config data.
INST_VAR_RESID_FORMAT   - Resource ID format for instantiable vars.
INITIAL_VALUE           - Variable initial value used on ha_rr_add_var().
MAX_VALUE               - Maximum variable value.
NUM_STATIC_VARS         - Number of static variables defined for monitor.
NUM_INST_VARS           - Number of instantiable variables defined for monitor.
*/
#define PROGNAME                  "rmapi_smpsig"
#define MAX_INTERVAL              500
#define RESOURCE_MONITOR_NAME     "IBM.PSSP.SampleSigMon"
#define DYN_INST_CLASS_NAME       "IBM.PSSP.SampleSigDynInstClass"
#define NON_INST_CLASS_NAME       "IBM.PSSP.SampleSigNonInstClass"
#define NUM_NONINST_VARS          3
#define SOCKET_TABLE_SIZE         (HA_RR_MAX_SESSIONS + 1)
#define RMAPI_SERVER_INDEX        (HA_RR_MAX_SESSIONS)
#define DYN_INST_VAR_NAME         "IBM.PSSP.SampleSigMon.DynInstVar.var"
#define NON_INST_VAR_NAME_PREFIX  "IBM.PSSP.SampleSigMon.NonInstVars.var"
#define DYN_INST_VAR_RESID        "InstName"
#define INITIAL_VALUE             10
#define MAX_VALUE                 500
#define NULL_RESID                ""
 
/* local structs */
struct sock_table_entry {
    int  socket_fd;             /* fd for manager session or RMAPI server socket. */
    void (*sock_funcp)(int);    /* function to be called for this socket fd. */
};
 
struct local_vars {
    char var_name[128];        /* Variable name (as in config data). */
    char var_resid[32];        /* Qualified resource ID (for instantiable variables). */
    short registered;          /* flag set when variable is registered. */
    long value;                /* variables value. */
    void *var_handle;          /* place for RMAPI to store the var handle. */
};
 
/* local functions */
void display_rmapi_err(struct ha_em_err_blk *errblk);
void server_socket_handler(int table_index);
void session_socket_handler(int table_index);
void end_session(int table_index);
void register_variables();
void inst_variables(struct ha_rr_ctrl_msg *);
void add_variables(int sock_fd, struct ha_rr_ctrl_msg *ctrl_msg);
void del_variables(int sock_fd, struct ha_rr_ctrl_msg *ctrl_msg);
void send_values();
void control_loop();
void set_signals();
void catch_io_signal(int);
void catch_alrm_signal(int);
void catch_exit_signal(int);
void mon_exit(int);
 
/* globals */
int                     NumMgrs = 0;                              /* number of resource managers connected. */
struct ha_rr_variable   *Variables = (struct ha_rr_variable *)0;  /* RMAPI variable array. */
struct ha_rr_val        *Values = (struct ha_rr_val *)0;          /* RMAPI val array. */
struct sock_table_entry SocketTable[SOCKET_TABLE_SIZE];           /* RMAPI sever and session sockets. */
struct ha_em_err_blk    ErrBlock;                                 /* global error block for RMAPI calls. */
struct local_vars       *LocalVars = (struct local_vars *)0;      /* array of local variables. */
static sigset_t         SignalMask;                               /* mask for signals. */
timer_t                 IntervalTimerId;                          /* ID for var update interval timer. */
int                     Interval;                                 /* Update interval for this monitor. */
int                     SampleTime = 0;                           /* flag set by catching interval SIGALRM. */
int                     Terminate  = 0;                           /* flag set by catching SIGTERM. */
int                     SigioCaught = 0;                          /* flag set by catching SIGIO. */
int                     SigCaught = 0;                            /* actual signal caught by term. */
int                     TimeOut = MAX_INTERVAL;                   /* time out counter to call ha_rr_touch. */
int                     NumVariables = 0;                         /* Number of variables available. */
int                     RmapiInit = 0;                            /* flag to indicate RMAPI initialized. */
 
/******************************************************************************/
/* Main - Main - Main - Main - Main - Main - Main - Main - Main - Main - Main */
/******************************************************************************/
main(int argc, char **argv)
{
int interval1, interval2;
int i, rc;
struct itimerstruc_t itimer;
 
    fprintf(stdout,"%s: %s resource monitor started.\n",PROGNAME,RESOURCE_MONITOR_NAME);
 
    /*
     * Setup signal handlers.
     */
    set_signals();
 
    /*
     * Initialize the socket table. Socket file descriptors are set to -1.
     */
    for (i=0;i<SOCKET_TABLE_SIZE;i++) {
        SocketTable[i].socket_fd = -1;
        if (i == RMAPI_SERVER_INDEX) {
            SocketTable[i].sock_funcp = server_socket_handler;
        } else {
            SocketTable[i].sock_funcp = session_socket_handler;
        }
    }
 
    /*
     * Initialize the RMAPI.
     */
    rc = ha_rr_init(RESOURCE_MONITOR_NAME, &ErrBlock);
    if (rc == HA_RR_FAIL) {
        display_rmapi_err(&ErrBlock);
        mon_exit(1);
    }
    RmapiInit = 1;
 
    /*
     * Query the update intervals for each class from the RMAPI.
     */
    interval1 = ha_rr_get_interval(DYN_INST_CLASS_NAME,&ErrBlock);
    if (interval1 <= 0) {
        display_rmapi_err(&ErrBlock);
        mon_exit(1);
    }
    fprintf(stdout,"Interval for class %s is %d.\n",DYN_INST_CLASS_NAME,interval1);
 
    interval2 = ha_rr_get_interval(NON_INST_CLASS_NAME,&ErrBlock);
    if (interval2 <= 0) {
        display_rmapi_err(&ErrBlock);
        mon_exit(1);
    }
    fprintf(stdout,"Interval for class %s is %d.\n",NON_INST_CLASS_NAME,interval2);
 
    /*
     * For simplicity, the real interval will be the smallest of the two.
     */
     Interval = MIN(interval1,interval2);
 
    /*
     * Allocate the local and RMAPI arrays for the non-instantiable variables.
     */
    NumVariables = NUM_NONINST_VARS;
    LocalVars = (struct local_vars *)malloc(sizeof(struct local_vars) * NumVariables);
    Variables = (struct ha_rr_variable *)malloc(sizeof(struct ha_rr_variable) * NumVariables);
    Values = (struct ha_rr_val *)malloc(sizeof(struct ha_rr_val) * NumVariables);
 
    /*
     * Initialize the arrays.
     */
    memset(LocalVars,0,sizeof(struct local_vars) * NumVariables);
    memset(Variables,0,sizeof(struct ha_rr_variable) * NumVariables);
    memset(Values,0,sizeof(struct ha_rr_val) * NumVariables);
 
    /*
     * Initialize the local variable structure for the non-instantiables.
     */
    for (i=0;i<NUM_NONINST_VARS;i++) {
        sprintf(LocalVars[i].var_name,"%s%d",NON_INST_VAR_NAME_PREFIX,i+1);
        LocalVars[i].var_resid[0] = '\0';
        LocalVars[i].value = 0;
    }
 
    /*
     * Register the non-instantiable variables with the RMAPI.
     */
    register_variables();
 
    /*
     * Make this Resource Monitor a server.
     */
    rc = SocketTable[RMAPI_SERVER_INDEX].socket_fd = ha_rr_makserv(HA_RR_NOTIFY_SIGIO, &ErrBlock);
    if (rc == HA_RR_FAIL) {
        display_rmapi_err(&ErrBlock);
        mon_exit(1);
    }
    fprintf(stdout,"Assigned rmapi server socket_fd(%d) to table index(%d).\n",
                rc,RMAPI_SERVER_INDEX);
 
    /*
     * Set up sampling timer.
     */
    IntervalTimerId = gettimerid(TIMERID_REAL, DELIVERY_SIGNALS);
    itimer.it_value.tv_sec = Interval;
    itimer.it_value.tv_nsec = 0;
    itimer.it_interval.tv_sec = Interval;
    itimer.it_interval.tv_nsec = 0;
 
    if (incinterval(IntervalTimerId, &itimer, (struct itimerstruc_t *)0) < 0) {
        fprintf(stderr,"incinterval() failed with errno=%d.\n",errno);
        mon_exit(1);
    }
 
    /*
     * Start the control loop - should never return.
     */
    control_loop();
 
    /*
     * Should never get here.
     */
    mon_exit(0);
}
 
/*
 * Sets the signal handlers.
 */
void set_signals() {
struct sigaction sigactn;
 
    sigemptyset(&SignalMask);
    sigaddset(&SignalMask, SIGALRM);
    sigaddset(&SignalMask, SIGTERM);
    sigaddset(&SignalMask, SIGIO);
 
    /*
     * SIGIO signal handler.
     */
    sigactn.sa_handler = catch_io_signal;
    sigactn.sa_mask = SignalMask;
    sigactn.sa_flags = 0;
 
    if (sigaction(SIGIO, &sigactn, (struct sigaction *)0) < 0) {
        fprintf(stderr,"sigaction() failed for SIGIO signal. errno=%d.\n",errno);
        mon_exit(1);
    }
 
    /*
     * SIGALRM caught on the interval boundary.
     * Handler sets a global flag to do sampling.
     */
    sigactn.sa_handler = catch_alrm_signal;
    sigactn.sa_mask = SignalMask;
    sigactn.sa_flags = 0;
 
    if (sigaction(SIGALRM, &sigactn, (struct sigaction *)0) < 0) {
        fprintf(stderr,"sigaction() failed for ALRM signal. errno=%d.\n",errno);
        mon_exit(1);
    }
 
    /*
     * Need a terminate handler to cleanup RMAPI on exit.
     */
    sigactn.sa_handler = catch_exit_signal;
    sigactn.sa_mask = SignalMask;
    sigactn.sa_flags = 0;
 
    if (sigaction(SIGTERM, &sigactn, (struct sigaction *)0) < 0) {
        fprintf(stderr,"sigaction() failed for TERM signal. errno=%d.\n",errno);
        mon_exit(1);
    }
 
    /*
     * Set signal mask, i.e. block SIGIO, SIGALRM, SIGTERM
     */
    if (sigprocmask(SIG_SETMASK, &SignalMask, (sigset_t *)0) < 0) {
        fprintf(stderr,"sigprocmask() failed. errno=%d.\n",errno);
        mon_exit(1);
    }
}
 
/*
 * Sends new values to the RMAPI - called when SIGALRM is caught.
 */
void send_values() {
int i, rc;
int num_vals_to_send = 0;
static int times_called = 0;
 
    times_called++;
    for (i=0;i<NumVariables;i++) {
 
        if (i < NUM_NONINST_VARS) {
            /*
             * Randomly increment the non-instantiable variable value (Counter).
             */
            LocalVars[i].value += (random() % 50);
        } else {
            /*
             * Assign a random value to the DynInstVar variable value (Quantity).
             */
            LocalVars[i].value = (random() % MAX_VALUE);
        }
 
        /*
         * Only need to send values for variables with non-NULL handles.
         */
        if (LocalVars[i].var_handle != (void *)0) {
 
            /*
             * Copy the handle and value to the next available RMAPI structure.
             */
            Values[num_vals_to_send].rr_var_hndl = LocalVars[i].var_handle;
            Values[num_vals_to_send].rr_value = &(LocalVars[i].value);
 
            /*
             * Increment the number to send count.
             */
            num_vals_to_send++;
        }
    }
 
    if (num_vals_to_send) {
        /*
         * Send the Value array to the RMAPI.
         */
        rc = ha_rr_send_val(Values,num_vals_to_send,0,&ErrBlock);
        if (rc == HA_RR_FAIL) {
            display_rmapi_err(&ErrBlock);
            mon_exit(1);
        }
 
        /*
         * Reset the TimeOut counter.
         */
        TimeOut = MAX_INTERVAL;
 
    } else if ((TimeOut -= Interval) <= 0) {
 
        /*
         * No values have been sent in the TimeOut period. Need
         * to call ha_rr_touch to meet server requirements.
         */
        if (ha_rr_touch(&ErrBlock) == HA_RR_FAIL) {
            display_rmapi_err(&ErrBlock);
            mon_exit(1);
        }
 
        /*
         * Reset the TimeOut counter.
         */
        TimeOut = MAX_INTERVAL;
 
    }
}
 
/*
 * Catches SIGIO and sets global flag SigioCaught to send values.
 */
void catch_io_signal(int sig) {
    SigioCaught = 1;
}
 
/*
 * Catches SIGALRM and sets global flag SampleTime to send values.
 */
void catch_alrm_signal(int sig) {
    SampleTime = 1;
}
 
/*
 * Catches SIGTERM and sets global flag Terminate to exit.
 */
void catch_exit_signal(int sig) {
    Terminate = 1;
    SigCaught = sig;
}
 
/*
 * Handles a connection request on the RMAPI server
 * socket returned by ha_rr_makserv().
 */
void server_socket_handler(int table_index) {
int i, mgr_sock, rc;
 
    mgr_sock = ha_rr_start_session(HA_RR_NOTIFY_SIGIO,&ErrBlock);
    if (mgr_sock < 0) {
        display_rmapi_err(&ErrBlock);
        if ((ErrBlock.em_errno != HA_RR_EMAXSESSIONS) && (ErrBlock.em_errno != HA_RR_EAGAIN)) {
            /*
             * Severe error in the RMAPI - exit.
             */
            mon_exit(1);
        }
        return;
    }
    /*
     * Find an available element in the session socket table.
     */
    for (i=0;(i < HA_RR_MAX_SESSIONS)&&(SocketTable[i].socket_fd >= 0);i++);
 
    if (i < HA_RR_MAX_SESSIONS) {
        /*
         * Save the manager socket file descriptor returned by the RMAPI.
         * The socket function was initialized in main.
         */
        SocketTable[i].socket_fd = mgr_sock;
        NumMgrs++;
        fprintf(stdout,"New session accepted to index(%d) socket_fd(%d).\n",i,mgr_sock);
    } else {
        /*
         * Should never get here, RMAPI handles too many sessions.
         */
        rc = ha_rr_end_session(mgr_sock,&ErrBlock);
    }
}
 
/*
 * Handles a message being received from the manager session 
 * specified by [table_index].
 */
void session_socket_handler(int table_index) {
int len, session_sock;
struct ha_rr_ctrl_msg *ctrl_msg_start, *ctrl_msg;
 
    /*
     * Get the socket file descriptor for this session.
     */
    if ((session_sock = SocketTable[table_index].socket_fd) < 0) {
        return;
    }
 
    /*
     * Call ha_rr_get_ctrl_msg() to read the command from the manager.
     */
    fprintf(stdout,"Calling ha_rr_get_ctrlmsg for session(%d) socket_fd(%d).\n",
                table_index,session_sock);
    len = ha_rr_get_ctrlmsg(session_sock, &ctrl_msg, &ErrBlock);
 
    if (len == 0) {
        /*
         * Message was for the RMAPI or was incomplete.
         */
        return;
    }
 
    if (len == HA_RR_FAIL) {
        display_rmapi_err(&ErrBlock);
        if (ErrBlock.em_errno == HA_RR_EDISCONNECT) {
            end_session(table_index);
        } else if (ErrBlock.em_errno != HA_RR_EAGAIN) {
            /*
             * Severe RMAPI error.
             */
            mon_exit(1);
        }
        return;
    }
 
    /*
     * Since SIGIO is the notification protocol there could be
     * more than 1 message to read.
     */
    ctrl_msg_start = ctrl_msg;
    while ((char *)ctrl_msg < (((char *)ctrl_msg_start) + len)) {
        switch (ctrl_msg->rr_ctrl_cmd) {
            case HA_RR_CMD_ADDALL  :
            case HA_RR_CMD_ADDV    :
                add_variables(table_index,ctrl_msg);
                break;
            case HA_RR_CMD_DELALL  :
            case HA_RR_CMD_DELV    :
                del_variables(table_index,ctrl_msg);
                break;
            case HA_RR_CMD_INSTV   :
                inst_variables(ctrl_msg);
                break;
            default                :
                /*
                 * Unknown or unsupported msg - ignore it.
                 */
                fprintf(stderr,"Received an unexpected command(%d) from socket_fd(%d), ignoring...\n",
                            ctrl_msg->rr_ctrl_cmd,session_sock);
                break;
        }
        ctrl_msg = (struct ha_rr_ctrl_msg *)(((char *)ctrl_msg) + ctrl_msg->rr_ctrl_msg_len);
    }
 
    /*
     * Free the message allocated by the RMAPI.
     */
    free(ctrl_msg_start);
}
 
/*
 * Dynamically instantiates the variables requested in the control message. If the message
 * does not wildcard the resource ID, this routine creates instances of the requested
 * variables, otherwise it creates several variables to simulate wildcard matching.
 */
void inst_variables(struct ha_rr_ctrl_msg *ctrl_msg) {
int num_var, num_to_create;
struct ha_rr_ctrl_var *vp;
char *inst_name;
static int inst_num = 10;
int i, j;
 
    fprintf(stdout,"Processing command HA_RR_CMD_INSTV to create %d variables.\n",
            ctrl_msg->rr_ctrl_num_vars);
 
    for (i=0;i < ctrl_msg->rr_ctrl_num_vars;i++) {
 
        num_to_create = 0;
 
        /*
         * Get a pointer to the variable structure in the control message.
         */
        vp = &(ctrl_msg->rr_ctrl_vars[i]);
 
        if (strcmp(DYN_INST_VAR_NAME, vp->rr_ctrl_name)) {
            /*
             * Variable name didn't match the dynamically instantiable variable name.
             */
            fprintf(stderr,"%s: Invalid variable (%s) name sent in INSTV ctrl msg.\n",
                    PROGNAME,vp->rr_ctrl_name);
            continue;
        }
 
        if (strcmp(vp->rr_ctrl_rsrc_ID,"") != 0) {
            /*
             * A specific instance is requested. Accommodate the request by creating
             * the instance. If this were a real monitor, it would be created if
             * the request matched a valid variable.
             */
            if (strncmp(vp->rr_ctrl_rsrc_ID,DYN_INST_VAR_RESID,strlen(DYN_INST_VAR_RESID))) {
                /*
                 * The resource ID in the control message does not match the resource ID 
                 * for the variable.
                 */
                fprintf(stderr,"%s: Invalid resource ID (%s) sent in INSTV ctrl msg.\n",
                        PROGNAME,vp->rr_ctrl_rsrc_ID);
                continue;
            }
            /*
             * Get a pointer passed the resource ID name - should be pointing to an '=' now.
             * The format of the resource ID is:  ResourceID_Element_Name=Value
             */
            inst_name = vp->rr_ctrl_rsrc_ID + strlen(DYN_INST_VAR_RESID);
            if (*inst_name != '=' || *(inst_name + 1) == '\0') {
                /*
                 * '=' not found or value missing in the ctrl message.
                 */
                fprintf(stderr,"%s: Invalid resource ID (%s) sent in INSTV ctrl msg.\n",
                        PROGNAME,vp->rr_ctrl_rsrc_ID);
                continue;
            }
            /*
             * Adjust the instance name pointer to the value in the control message.
             */
            inst_name++;
            num_to_create = 1;
        } else {
            /*
             * Resource ID in the message was NULL which means wildcard the value.
             * Since this is just a sample monitor, we'll make up some variable names
             * to simulate the wildcard match. The contrived instances will have
             * resource IDs: InstName=Inst<nn> where nn is the value of the local static
             * variable inst_num.
             */
            num_to_create = 5;
        }
 
        fprintf(stdout,"Instantiating %d new variables.\n",num_to_create);
        /*
         * realloc() the local and RMAPI variable arrays to accommodate the new instances.
         */
        num_var = NumVariables + num_to_create;
        LocalVars = (struct local_vars *)realloc(LocalVars, sizeof(struct local_vars) * num_var);
        Variables = (struct ha_rr_variable *)realloc(Variables, sizeof(struct ha_rr_variable) * num_var);
        Values = (struct ha_rr_val *)realloc(Values, sizeof(struct ha_rr_val) * num_var);
 
        /*
         * Initialize the new local variables.
         */
        memset(LocalVars+NumVariables,0,sizeof(struct local_vars) * num_to_create);
        for (j=NumVariables;j<num_var;j++) {
            /*
             * Copy the dynamic variable name to the local struct.
             */
            sprintf(LocalVars[j].var_name,DYN_INST_VAR_NAME);
            if (num_to_create == 1) {
                /*
                 * Resource ID was specified in the control message - use the
                 * value in the new variable's resource ID.
                 */
                sprintf(LocalVars[j].var_resid,"%s=%s",DYN_INST_VAR_RESID,inst_name);
                fprintf(stdout,"Created dynamic instance: %s\n",LocalVars[j].var_resid);
            } else {
                /*
                 * Resource ID not specified in the control message - contrive
                 * a resource ID for the new instance.
                 */
                sprintf(LocalVars[j].var_resid,"%s=Inst%d",DYN_INST_VAR_RESID,inst_num);
                /*
                 * Increment the inst_num counter to keep new resource IDs unique.
                 */
                inst_num++;
            }
            LocalVars[j].value = INITIAL_VALUE;
        }
        /*
         * Update the variable array size.
         */
        NumVariables = num_var;
    }
    /*
     * Register the new variables with the RMAPI.
     */
    register_variables();
}
 
/*
 * Adds the variables referenced in the ctrl_msg to the 
 * manager session specified by [table index].
 */
void add_variables(int table_index, struct ha_rr_ctrl_msg *ctrl_msg) {
int i;
int sock_fd;
int inst_id;
int num_added, num_to_add = 0;

    sock_fd = SocketTable[table_index].socket_fd;
    switch (ctrl_msg->rr_ctrl_cmd) {
        case HA_RR_CMD_ADDALL :
            /*
             * Add all variables to this manager session.
             */
            fprintf(stdout,"Processing cmd HA_RR_CMD_ADDALL to add all variables to session_fd %d.\n",sock_fd);
            for (i=0;i<NumVariables;i++) {
                /*
                 * Copy the variable to the next RMAPI structure.
                 */
                Variables[num_to_add].rr_var_name = LocalVars[i].var_name;
                Variables[num_to_add].rr_var_rsrc_ID = LocalVars[i].var_resid;
                Variables[num_to_add].rr_varu.rr_var_hndl = &(LocalVars[i].var_handle);
                Variables[num_to_add].rr_value = &(LocalVars[i].value);
 
                /*
                 * Increment the count of variables to be added.
                 */
                num_to_add++;
            }
            break;
        case HA_RR_CMD_ADDV   :
            /*
             * Add a vector of variables to the session.
             */
            fprintf(stdout,"Processing cmd HA_RR_CMD_ADDV to add %d vars for session_fd %d.\n",
                        ctrl_msg->rr_ctrl_num_vars,sock_fd);
            for (i=0;i < ctrl_msg->rr_ctrl_num_vars;i++) {
 
                /*
                 * The instance id field in the ccontrol message is
                 * the index into the LocalVars array.
                 */
                inst_id =  ctrl_msg->rr_ctrlv.rr_ctrl_vari[i];
 
                if (inst_id < NumVariables) {
                    /*
                     * Copy the variable to the next RMAPI structure.
                     */
                    Variables[num_to_add].rr_var_name = LocalVars[inst_id].var_name;
                    Variables[num_to_add].rr_var_rsrc_ID = LocalVars[inst_id].var_resid;
                    Variables[num_to_add].rr_varu.rr_var_hndl = &(LocalVars[inst_id].var_handle);
                    Variables[num_to_add].rr_value = &(LocalVars[inst_id].value);
 
                    num_to_add++;
                }
            }
            break;
        default                  :
            break;
    }
    if (num_to_add) {
        /*
         * Add the variables by calling ha_rr_add_var.
         */
        num_added = ha_rr_add_var(sock_fd, Variables, num_to_add, 1, &ErrBlock);
 
        if (num_added == HA_RR_FAIL) { 
            
            display_rmapi_err(&ErrBlock);
            
            if (ErrBlock.em_errno == HA_RR_EDISCONNECT) {
                 /*
                  * Session closed by manager.
                  */
                 end_session(table_index);
            } else {
                mon_exit(1);
            }
 
        } else {
 
            fprintf(stdout,"ha_rr_add_var() added %d vars to session_fd %d.\n",num_added,sock_fd);
            if (num_to_add != num_added) {
                /*
                 * Loop through the variables that were attempted
                 * to be added and report any that had errors.
                 */
                for (i=0;i<num_to_add;i++) {
                    if (Variables[i].rr_var_errno != 0) {
                        fprintf(stderr,"Variable(%s) resource ID(%s) had 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);
                    }
                }
            }
        }
    }
}
 
/*
 * Deletes the variables referenced in the control message from 
 * the manager session specified by [table_index].
 */
void del_variables(int table_index, struct ha_rr_ctrl_msg *ctrl_msg) {
int i;
int sock_fd;
int ctrl_cmd;
int inst_id;
int num_deleted, num_to_del = 0;
 
    sock_fd = SocketTable[table_index].socket_fd;
    if (ctrl_msg == (struct ha_rr_ctrl_msg *)0) {
        /*
         * Called with NULL message when a session is ending. Need to delete
         * all variables from the session before calling ha_rr_end_session.
         */
        ctrl_cmd = HA_RR_CMD_DELALL;
    } else {
        ctrl_cmd = ctrl_msg->rr_ctrl_cmd;
    }
 
    switch (ctrl_cmd) {
        case HA_RR_CMD_DELALL :
            fprintf(stdout,"Processing command HA_RR_CMD_DELALL to delete all variables for session_fd %d.\n",
                    sock_fd);
            /*
             * Delete all variables from this manager session.
             */
            for (i=0;i<NumVariables;i++) {
                if (LocalVars[i].var_handle != (void *)0) {
                    /*
                     * Copy the variable handle to the next RMAPI structure.
                     */
                    Variables[num_to_del].rr_varu.rr_var_hndl = &(LocalVars[i].var_handle);
 
                    /*
                     * Increment the count of variables to be deleted.
                     */
                    num_to_del++;
                }
            }
            break;
        case HA_RR_CMD_DELV   :
            fprintf(stdout,"Processing command HA_RR_CMD_DELV to delete %d variables for session_fd %d.\n",
                    ctrl_msg->rr_ctrl_num_vars,sock_fd);
            /*
             * Delete a vector of variables from this manager session.
             */
            for (i = 0; i < ctrl_msg->rr_ctrl_num_vars; i++) {
                inst_id = ctrl_msg->rr_ctrlv.rr_ctrl_vari[i];
                if ((inst_id < NumVariables) &&
                    (LocalVars[i].var_handle != (void *)0)) {
                    /*
                     * Copy the variable handle to the next RMAPI structure.
                     */
                    Variables[num_to_del].rr_varu.rr_var_hndl = &(LocalVars[inst_id].var_handle);
 
                    /*
                     * Increment the count of variables to be deleted.
                     */
                    num_to_del++;
                }
            }
            break;
        default                  :
            break;
    }
    if (num_to_del) {
        /*
         * Delete the variables by calling ha_rr_del_var.
         */
        num_deleted = ha_rr_del_var(sock_fd, Variables, num_to_del, &ErrBlock);
 
        if (num_deleted == HA_RR_FAIL) {
            
            display_rmapi_err(&ErrBlock);
            
            if (ErrBlock.em_errno == HA_RR_EDISCONNECT) {
                 /*
                  * Session closed by manager.
                  */
                 end_session(table_index);
            } else {
                mon_exit(1);
            }
 
        }
 
        /*
         * rc>0 from ha_rr_del_var is the number of variables that no longer need
         * their values updated. The RMAPI will have set their handles to NULL.
         */
        fprintf(stdout,"%d variables no longer need to be updated.\n",num_deleted);
    }
}
 
/*
 * Ends the manager session indexed by the [table_index] parameter
 * in the socket table.
 */
void end_session(int table_index) {
int rc;
 
    fprintf(stdout,"Ending session index(%d) session_fd(%d).\n",
                table_index,SocketTable[table_index].socket_fd);
 
    /*
     * Make sure this is a valid session.
     */
    if ((table_index < 0) || (table_index > HA_RR_MAX_SESSIONS) ||
        (SocketTable[table_index].socket_fd < 0)) {
        return;
    }
 
    /*
     * Delete all variables for this manager.
     */
    del_variables(table_index, (struct ha_rr_ctrl_msg *)0);
 
    /*
     * Call RMAPI to end the session.
     */
    rc = ha_rr_end_session(SocketTable[table_index].socket_fd, &ErrBlock);
    if (rc == HA_RR_FAIL) {
        display_rmapi_err(&ErrBlock);
        mon_exit(1);
    }
 
    /*
     * Reset the socket file descriptor in the SocketTable and 
     * decrement the manager count.
     */
    SocketTable[table_index].socket_fd = -1;
    NumMgrs--;
 
    if (NumMgrs <= 0) {
        /*
         * This was the last manager to end - exit with a good rc.
         */
        fprintf(stdout,"Last manager session closed, exiting...\n");
        mon_exit(0);
    }
}
 
/*
 * Registers variables with the RMAPI.
 */
void register_variables() {
int i, j, num_var;
int num_registered, num_to_reg = 0;
 
    for (i=0;i<NumVariables;i++) {
        if (LocalVars[i].registered == 0) {
 
            /*
             * Variable not registered yet. Copy name, resource ID and and value
             * pointers. Use the index of the variable as the inst id.
             */
            LocalVars[i].registered = 1;
            Variables[num_to_reg].rr_var_name = LocalVars[i].var_name;
            Variables[num_to_reg].rr_var_rsrc_ID = LocalVars[i].var_resid;
            Variables[num_to_reg].rr_var_iid = i;
            num_to_reg++;
        }
    }
 
    if (num_to_reg) {
 
        /*
         * Call the RMAPI to  register the variables.
         */
        num_registered = ha_rr_reg_var(Variables,num_to_reg,&ErrBlock);
        if (num_registered != num_to_reg) {
            if (num_registered == HA_RR_FAIL) {
                display_rmapi_err(&ErrBlock);
                mon_exit(1);
            }
            fprintf(stdout,"%d variables registered with the RMAPI.\n",
                    num_registered);
            for (i=0; i < num_to_reg; i++) {
                if ((Variables[i].rr_var_errno) || (num_registered == HA_RR_FAIL)) {
                    /*
                     * Display a message for each variable with a registration error.
                     */
                    fprintf(stderr,"ha_rr_reg_var() error with variable(%s) instid(%d) RMAPI errno=(%d).\n",
                                Variables[i].rr_var_name,
                                Variables[i].rr_varu.rr_var_inst_id,
                                Variables[i].rr_var_errno);
                }
            }
        }
    }
}
 
/*
 * Called once by main to establish the control loop.
 */
void control_loop() {
int i, rc;
int sock;
 
    for(;;) {
 
        /*
         * Unblock the signals.
         */
        if (sigprocmask(SIG_UNBLOCK, &SignalMask, (sigset_t *)0) < 0) {
            /*
             * sigprocmask sys call failed.
             */
            fprintf(stderr,"Cannot unblock signals. sigprocmask() errno=%d.\n",errno);
            mon_exit(1);
        }
 
        /*
         * Pause to receive a signal.
         */
        pause();
 
        /*
         * Block the signals.
         */
        if (sigprocmask(SIG_BLOCK, &SignalMask, (sigset_t *)0) < 0) {
            fprintf(stderr,"Cannot block signals. sigprocmask() errno=%d.\n",errno);
            mon_exit(1);
        }
 
        if (Terminate) {
            /*
             * SIGTERM caught - call mon_exit() to clean up.
             */
            fprintf(stdout,"Caught signal(%d)...calling exit...\n",SigCaught);
            mon_exit(SigCaught);
        }
 
        if (SampleTime) {
            /*
             * SIGALRM caught - call send_values()
             * and reset the sample flag.
             */
            send_values();
            SampleTime = 0;
        }
 
        if (SigioCaught) {
            /*
             * Call the socket function for each valid socket.
             */
            SigioCaught = 0;
            for (i=0;i<SOCKET_TABLE_SIZE;i++) {
                if (SocketTable[i].socket_fd >=0) {
                    fprintf(stdout,"Calling socket function for session(%d) socket_fd(%d).\n",
                                i,SocketTable[i].socket_fd);
                    (SocketTable[i].sock_funcp)(i);
                }
            }
        }
    }
}
 
/*
 * Routine to terminate the monitor.
 */
void mon_exit(int s)
{
static int recursively_called = 0;
int i, rc;
 
    /*
     * Check for recursive call - some routines mon_exit()
     * calls can likewise call mon_exit().
     */
    if (recursively_called) return;
    recursively_called = 1;

    if (RmapiInit) { 
        /*
         * Gracefully close all sessions.
         */
        for (i=0;i<HA_RR_MAX_SESSIONS;i++) {
            if (SocketTable[i].socket_fd >= 0) {
                end_session(i);
            }
        }
        /*
         * Terminate the RMAPI.
         */
        rc = ha_rr_terminate(&ErrBlock);
        if (rc) {
            display_rmapi_err(&ErrBlock);
        }
    }   

    fprintf(stdout,"%s: %s resource monitor exiting.\n",PROGNAME,RESOURCE_MONITOR_NAME);
    exit(s);
}
 
/*
 * Display 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);
}
