/* 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                                                     */
/*===========================================================================*/
/* @(#)43   1.9   src/rsct/pem/emtools/rmapi_samples/rmapi_smpdae.c, emtools, rsct_rady, rady2035a 6/5/98 11:25:36 */
 
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/signal.h>
#include <sys/events.h>
 
#include <ha_rmapi.h>
 
/*
 *  rmapi_smpdae.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.
 *
 *  Event Management objects defined for this monitor:
 *
 *  Monitor:
 *      IBM.PSSP.SampleDaeMon                      # server monitor definition
 *
 *  Class:
 *      IBM.PSSP.SampleDaeClass                    # variable class
 *
 *  Variables:
 *      IBM.PSSP.SampleDaeMon.StaticVars.static_var1    # valuetype quantity, datatype long
 *          Resource Identifiers: NodeNum               # used as locator field of var
 *      IBM.PSSP.SampleDaeMon.StaticVars.static_var2    # valuetype quantity, datatype long
 *          Resource Identifiers: NodeNum               # used as locator field of var
 *      IBM.PSSP.SampleDaeMon.StaticVars.static_var3    # valuetype quantity, datatype long
 *          Resource Identifiers: NodeNum               # used as locator field of var
 *      IBM.PSSP.SampleDaeMon.InstVars.inst_var1        # valuetype quantity, datatype long
 *          Resource Identifiers: NodeNum               # used as locator field of var
 *                                Name                  # instance name of the resource
 *      IBM.PSSP.SampleDaeMon.InstVars.inst_var2        # valuetype quantity, datatype long
 *          Resource Identifiers: NodeNum               # used as locator field of var
 *                                Name                  # instance name of the resource
 *
 * What this monitor does:  rmapi_smpdae provides an example of a server monitor
 *   that has 3 non-instantiable and 2 instantiable Quantity variables which
 *   are updated with random values on a fixed interval defined in the variable class.
 *   When the monitor is started, it creates and registers the static and 1 set of the
 *   instantiable variables.  To further demonstrate instantiation, new instances are
 *   created and registered on the 5th and 12th call to the function send_values().
 *
 *   The monitor will continue executing, adding/deleting and sending values for the
 *   variables as requested by control messages sent from the Event Management daemon
 *   through the RMAPI.  This monitor uses select notification protocol to determine when a
 *   connection is ready to accept on the server socket created by ha_rr_makserv() or
 *   when a message is ready to be read through the RMAPI from one of the monitor's
 *   client sessions.
 *
 *   The monitor ends execution when it receives a terminating signal, or when all
 *   all clients have closed their connections.
 *
 *   This program also provides an example of executing multiple copies of a resource
 *   monitor at the same time. The monitor may be started from the command line, and is 
 *   not configured to be started by resource monitor managers. The following command
 *   line arguments may be used when starting the monitor:
 *
 *             -i <number>    Optional: start the requested number of monitor copies,
 *                            where <number> is in the range 1 - HA_RR_RM_INSTID_MAX. 
 *             -h             Displays command line help.
 *             -H <name>      Optional: Specifies the name of the HACMP domain the
 *                            monitor is to execute in.
 *             -S <name>      Optional: Specifies the name of the SP domain (system
 *                            partition name) the monitor is to execute in.
 *  
 *  The [-H, -S] options are mutually exclusive. If neither is supplied, the monitor
 *  will assume it is executing in the default system partition of a SP environment.
 *  
 *  The static variables are intended to represent global values which are common
 *  to all instances of the resource being monitored. Since by definition only one
 *  instance of the each static variable may exist on a node, they are supplied only 
 *  by the resource monitor whose monitor instance id is 0. This ensures that they are
 *  available for both performance and event monitoring. Each monitor copy contrives
 *  unique values for the Name resource ID element of the instantiable variables.
 *  The intention is to provide an example of multiple copies of a monitor, each 
 *  monitoring a set of instances of the same resource. 
 *  
 *  Examples:
 *
 *  # rmapi_smpdae
 *    - Will start one copy of the monitor in the default system partition of the
 *      local SP node.
 *
 *  # rmapi_smpdae -i 4 -P x10s    
 *    - Will start four copies of the monitor which will execute in the system 
 *      partition named "x10s" on the local SP node.
 *
 *  This program can be compiled with the following command:
 *
 *      cc -O rmapi_smpdae.c -o rmapi_smpdae -lha_rr
 */
 
/* 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_smpdae"
#define MAX_INTERVAL            500
#define RESOURCE_MONITOR_NAME   "IBM.PSSP.SampleDaeMon"
#define MONITOR_CLASS_NAME      "IBM.PSSP.SampleDaeClass"
#define SOCKET_TABLE_SIZE       (HA_RR_MAX_SESSIONS + 1)
#define RMAPI_SERVER_INDEX      (HA_RR_MAX_SESSIONS)
#define STATIC_VAR_NAME_PREFIX  "IBM.PSSP.SampleDaeMon.StaticVars.static_var"
#define INST_VAR_NAME_PREFIX    "IBM.PSSP.SampleDaeMon.InstVars.inst_var"
#define INST_VAR_RESID_FORMAT   "InstName=Resource%d"
#define INITIAL_VALUE           10
#define MAX_VALUE               500
#define NUM_STATIC_VARS         3
#define NUM_INST_VARS           2
 
/* 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 the CDB). */
    char var_resid[32];          /* Qualified resource ID (for instantiable variables). */
    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 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_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 value array. */
struct sock_table_entry SocketTable[SOCKET_TABLE_SIZE];           /* RMAPI server 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 interval alrm sig. */
int                     Terminate  = 0;                           /* flag set by terminate signal. */
int                     SigCaught = 0;                            /* signal caught by term. */
int                     TimeOut = MAX_INTERVAL;                   /* time out counter to call ha_rr_touch. */
int                     NumVariables = 0;                         /* Number of variables in arrays. */
int                     MonitorInstanceID;                        /* Instance ID of the running monitor */
int                     Pid;                                      /* resource monitor process id. */
char                    DomainHACMP[] = "HA_DOMAIN_TYPE=HACMP";   /* string for HACMP domain type. */
char                    DomainSP[]    = "HA_DOMAIN_TYPE=SP";      /* string for HACMP domain type. */
int                     RmapiInit = 0;                            /* flag to inidicate RMAPI initialized */
 
/******************************************************************************/
/* Main - Main - Main - Main - Main - Main - Main - Main - Main - Main - Main */
/******************************************************************************/
main(int argc, char **argv)
{
int c, i, rc;
int num_rm_insts = 1;
struct itimerstruc_t itimer;
char *domain_name = NULL, *domain_type = NULL, *bp;
struct ha_rr_args rr_args;

    while ((c = getopt(argc, argv, "i:hH:S:")) != EOF) {
        switch (c) {
            case 'i':
                num_rm_insts = atoi(optarg);
                if ((num_rm_insts < 1) || (num_rm_insts > HA_RR_RM_INSTID_MAX)) {
                    fprintf(stderr, "%s(%d): Invalid number of monitor instances, %d. "
                                    "Allowable 1-%d.\n",
                                    PROGNAME,Pid,num_rm_insts,HA_RR_RM_INSTID_MAX);
                    mon_exit(1);
                }
                break;
            case 'H':
                if (domain_name != NULL) {
                    fprintf(stderr, "%s(%d): Specify either the -S or -H options once.\n",
                            PROGNAME,Pid);
                    mon_exit(1);
                }               
                domain_name = optarg;
				domain_type = DomainHACMP;
                break;
            case 'S':
                if (domain_name != NULL) {
                    fprintf(stderr, "%s(%d): Specify either the -S or -H options once.\n",
                            PROGNAME,Pid);
                    mon_exit(1);
                }               
                domain_name = optarg;
				domain_type = DomainSP;
                break;
            case 'h':
            default:
                fprintf(stdout, "Usage:\t%s [-h] [-i number] [-H domain_name | -S domain_name]\n"
                        "\t\t-h\tDisplays this help message\n"
                        "\t\t-i\tCreate the number of monitor instances specified\n"
                        "\t\t-H\tUse the HACMP domain name\n"
                        "\t\t-S\tUse the SP domain name\n",
                        PROGNAME);
                exit(0);
        }
    }
    
    Pid = getpid(); 
    fprintf(stdout,"%s(%d): %s resource monitor started. %d instance(s) of the monitor requested.\n",
            PROGNAME,Pid,RESOURCE_MONITOR_NAME,num_rm_insts);

    /*
     * Set up the domain type and name environment variables (if
     * supplied on the command line). Make the default domain
     * type "SP".
     */
    if (domain_type == NULL) domain_type = DomainSP;
    if (putenv(domain_type)) {
        fprintf(stderr, "%s(%d): putenv() failed, errno=%d.\n",PROGNAME,Pid,errno);
        mon_exit(1);
    }
    if (domain_name != NULL) {
        bp = (char *)malloc(strlen(domain_name) + 
                            strlen("HA_DOMAIN_NAME=") + 1);
        if (bp == NULL) {
            fprintf(stderr, "%s(%d): malloc() failed.\n",PROGNAME,Pid);
            mon_exit(1);
        }
        sprintf(bp,"HA_DOMAIN_NAME=%s",domain_name);
        if (putenv(bp)) {
            fprintf(stderr, "%s(%d): putenv() failed, errno=%d.\n",PROGNAME,Pid,errno);
            mon_exit(1);
        }
    } 
    fprintf(stdout, "%s(%d): Resource monitor environment: DomainType=%s DomainName=%s.\n",
            PROGNAME,Pid,getenv("HA_DOMAIN_TYPE"),getenv("HA_DOMAIN_NAME"));

    /*
     * Setup signal handlers.
     */
    set_signals();
 
    /*
     * Initialize the socket table. Socket file descriptors are initialized 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;
        }
    }

    /*
     * Set the monitor instance number. If only 1 instance was specified,
     * request only instance number 0, so that the monitor can supply
     * variables to both the EM daemon and PTPE. If more than 1 copy is
     * to be started, specify HA_RR_RM_INSTID_ANY, so that for each copy, 
     * the RMAPI attempts to lock the first available instance id within
     * the range defined for this monitor. Note that this is the default
     * action the RMAPI would take.
     */
    rr_args.rr_instance_id = num_rm_insts > 1 ? HA_RR_RM_INSTID_ANY : 0;
    rc = ha_rr_rm_ctl(&rr_args,HA_RR_RM_ARGS_SET_INSTID,&ErrBlock);
    if (rc == HA_RR_FAIL) {
        display_rmapi_err(&ErrBlock);
        mon_exit(1);
    }

    fflush(stdout);
    if (num_rm_insts > 1) {
   	    /*
         * fork() multiple copies of the monitor.
         */
        for (i=0;i<num_rm_insts-1;i++) {
            if ((rc = fork()) < 0) {
                /*
                 * fork() system call failed.
                 */
                fprintf(stderr, "%s(%d): Attempting to fork() monitor copy %d failed. Error: %d-%s.\n",
                                PROGNAME,Pid,i,errno,strerror(errno));
                mon_exit(1);
            }
            if (rc > 0) {
                /*
                 * Child RM, break from the loop. 
                 */
                break;
            } 
        }
    }
    Pid = getpid(); 

    /*
     * 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 and report the monitor instance number and domain name.
     */
    rc = ha_rr_rm_ctl(&rr_args,HA_RR_RM_ARGS_GET,&ErrBlock);
    if (rc == HA_RR_FAIL) {
        display_rmapi_err(&ErrBlock);
        mon_exit(1);
    }
	MonitorInstanceID = rr_args.rr_instance_id;
 
    fprintf(stdout,"%s(%d): Resource monitor instance id=%d domain=%s.\n",
            PROGNAME,Pid,rr_args.rr_instance_id,rr_args.rr_domain_name);
    
    /*
     * Query the update interval from the RMAPI.
     */
    Interval = ha_rr_get_interval(MONITOR_CLASS_NAME,&ErrBlock);
    if (Interval == HA_RR_FAIL) {
        display_rmapi_err(&ErrBlock);
        mon_exit(1);
    }
    fprintf(stdout,"%s(%d): Interval for class %s is %d.\n",
            PROGNAME,Pid,MONITOR_CLASS_NAME,Interval);
 
    /*
     * Register 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_SELECT, &ErrBlock);
    if (rc == HA_RR_FAIL) {
        display_rmapi_err(&ErrBlock);
        mon_exit(1);
    }
    fprintf(stdout,"%s(%d): Assigned RMAPI server socket_fd(%d) to table index(%d).\n",
                PROGNAME,Pid,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,"%s(%d): incinterval() failed with errno=%d.\n",
                PROGNAME,Pid,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);
 
    /*
     * 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,"%s(%d): sigaction() failed for ALRM signal. errno=%d.\n",
                PROGNAME,Pid,errno);
        mon_exit(1);
    }
 
    /*
     * Need 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,"%s(%d): sigaction() failed for TERM signal. errno=%d.\n",
                PROGNAME,Pid,errno);
        mon_exit(1);
    }
 
    /*
     * Set signal mask, i.e. block SIGALRM, SIGTERM
     */
    if (sigprocmask(SIG_SETMASK, &SignalMask, (sigset_t *)0) < 0) {
        fprintf(stderr,"%s(%d): sigprocmask() failed. errno=%d.\n",PROGNAME,Pid,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++) {
 
        /*
         * Give the variable a new random value.
         */
        LocalVars[i].value = (random() % MAX_VALUE);
 
        /*
         * Only need to send values for vars 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;
 
    }
 
    /*
     * Add some new instantiable variables
     * on the 5th and 12th time called.
     */
    if ((times_called == 5) || (times_called == 12)) {
        fprintf(stdout,"%s(%d): send_values() called %d times - calling " 
                "register_variables() to create new variables.\n",
                PROGNAME,Pid,times_called);
        register_variables();
    }
}
 
/*
 * Catches SIGALRM and sets global flag SampleTime to send values.
 */
void catch_alrm_signal(int sig) {
    SampleTime = 1;
    /*
     * flush stdout/stderr once in a while in 
     * case the output has been redirected.
     */
    fflush(stdout);
    fflush(stderr);
}
 
/*
 * 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_SELECT,&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 mgr socket fd returned by the RMAPI.
         * The socket function was initialized in main.
         */
        SocketTable[i].socket_fd = mgr_sock;
        NumMgrs++;
        fprintf(stdout,"%s(%d): New session accepted to index(%d) socket_fd(%d).\n",
                PROGNAME,Pid,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]. Called following select.
 */
void session_socket_handler(int table_index) {
int rc, session_sock;
struct ha_rr_ctrl_msg *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,"%s(%d): Calling ha_rr_get_ctrlmsg for session(%d) socket_fd(%d).\n",
            PROGNAME,Pid,table_index,session_sock);
    rc = ha_rr_get_ctrlmsg(session_sock, &ctrl_msg, &ErrBlock);
 
    if (rc == 0) {
        /*
         * Message was for the RMAPI or was incomplete.
         */
        return;
    }
 
    if (rc == 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;
    }
 
    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;
        default                :
            /*
             * Unknown or unsupported msg - ignore it.
             */
            fprintf(stderr,"%s(%d): Received an unexpected cmd(%d) from socket_fd(%d), ignoring...\n",
                    PROGNAME,Pid,ctrl_msg->rr_ctrl_cmd,session_sock);
            break;
    }
 
    /*
     * Free the message allocated by the RMAPI.
     */
    free(ctrl_msg);
}
 
/*
 * Adds the variables referenced in the control message 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,"%s(%d): Processing cmd HA_RR_CMD_ADDALL to add all variables to session_fd %d.\n",
                    PROGNAME,Pid,sock_fd);
            for (i=0;i<NumVariables;i++) {
                /*
                 * Copy the var to the next RMAPI var struct.
                 */
                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 manager session.
             */
            fprintf(stdout,"%s(%d): Processing cmd HA_RR_CMD_ADDV to add %d vars for session_fd %d.\n",
                    PROGNAME,Pid,ctrl_msg->rr_ctrl_num_vars,sock_fd);
            for (i=0;i < ctrl_msg->rr_ctrl_num_vars;i++) {
 
                /*
                 * The inst id field in the ctrl 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,"%s(%d): ha_rr_add_var() added %d vars to session_fd %d.\n",
                    PROGNAME,Pid,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,"%s(%d): Variable(%s) resource ID(%s) had bad errno(%d) from ha_rr_add_var().\n",
                                PROGNAME,Pid,Variables[i].rr_var_name,
                                Variables[i].rr_var_rsrc_ID,Variables[i].rr_var_errno);
                    }
                }
            }
        }
    }
}
 
/*
 * Deletes the variables referenced in the ctrl_msg for 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,"%s(%d): Processing cmd HA_RR_CMD_DELALL to delete all vars for session_fd %d.\n",
                    PROGNAME,Pid,sock_fd);
            /*
             * Delete all variables for this manager session.
             */
            for (i=0;i<NumVariables;i++) {
                if (LocalVars[i].var_handle != (void *)0) {
                    /*
                     * Copy the variable 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,"%s(%d): Processing cmd HA_RR_CMD_DELV to delete %d vars for session_fd %d.\n",
                    PROGNAME,Pid,ctrl_msg->rr_ctrl_num_vars,sock_fd);
            /*
             * Delete a vector of variables for 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[inst_id].var_handle != (void *)0)) {
 
                    /*
                     * Copy the variable 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,"%s(%d): %d variables no longer need to be updated.\n",
                PROGNAME,Pid,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,"%s(%d): Ending session index(%d) session_fd(%d).\n",
            PROGNAME,Pid,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--;
}
 
/*
 * Registers variables with the RMAPI.
 */
void register_variables() {
int i, j, num_var;
int num_registered, num_to_reg = 0;
static int times_called = 0;
 
    fprintf(stdout,"%s(%d): Call number %d to register_variables().\n",
            PROGNAME,Pid,times_called);
    times_called++;
    if (times_called == 1) {
        /*
         * First time called, allocate the LocalVars, Variables and
         * Value arrays. Only register the StaticVars if this is
         * resource monitor instance 0. Since only 1 monitor will 
         * supply these non-instantiable common attributes, we want it to
         * be instance 0, so that the variables are made available to
         * both the EM daemon and PTPE.
         */
        NumVariables = NUM_INST_VARS;

        if (MonitorInstanceID == 0) NumVariables += NUM_STATIC_VARS;
 
        fprintf(stdout,"%s(%d): Creating %d initial variables.\n",
                PROGNAME,Pid,NumVariables);
        
        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);

        if (MonitorInstanceID == 0) { 
            /*
             * Initialize the local variable structures and copy the name, resource ID
             * and value pointers to the RMAPI variables for registration.
             */
            for (i=0;i<NUM_STATIC_VARS;i++) {
 
                /*
                 * Create the non-instantiable variable names. The resource IDs are NULL
                 * strings because they are not instantiable. The only resource ID, NodeNum,
                 * defined for these variables is used as the locator field and does not
                 * need to be sent to the RMAPI. The index of the variable in the local
                 * is used as the inst_id (Variables[n].rr_var_iid). This id is returned by
                 * resource monitor managers in control messages to identify variables.
                 */
                sprintf(LocalVars[num_to_reg].var_name,"%s%d",STATIC_VAR_NAME_PREFIX,i+1);
                LocalVars[num_to_reg].var_resid[0] = '\0';
                LocalVars[num_to_reg].value = INITIAL_VALUE;
 
                Variables[num_to_reg].rr_var_name = LocalVars[num_to_reg].var_name;
                Variables[num_to_reg].rr_var_rsrc_ID = LocalVars[num_to_reg].var_resid;
                Variables[num_to_reg].rr_var_iid = num_to_reg;
 
                num_to_reg++;
            }
        }
 
        for (i=0;i<NUM_INST_VARS;i++) {
 
            /*
             * Initialize the instantiable variables. The difference here is that
             * they have a resource ID that needs to be supplied. The resource ID
             * value will be InstSet<n> where <n> is the number of the call to 
             * this routine plus the instance id of the monitor times 100. There
             * is no significance to this naming scheme - it is only used to
             * generate unique instances between multiple copies of the monitor.
             */
            sprintf(LocalVars[num_to_reg].var_name,"%s%d",INST_VAR_NAME_PREFIX,i+1);
            sprintf(LocalVars[num_to_reg].var_resid,INST_VAR_RESID_FORMAT,
                    times_called + (MonitorInstanceID * 100));
            LocalVars[num_to_reg].value = INITIAL_VALUE;
 
            Variables[num_to_reg].rr_var_name = LocalVars[num_to_reg].var_name;
            Variables[num_to_reg].rr_var_rsrc_ID = LocalVars[num_to_reg].var_resid;
            Variables[num_to_reg].rr_var_iid = num_to_reg;
 
            num_to_reg++;
        }
    
    } else {
        
        /*
         * Subsequent call to this routine. This is an example of new instantations
         * being created during normal execution. A new set of instantiable variables
         * will be registered using the current number of times this routine was called
         * in the resource ID.
         */
        fprintf(stdout,"%s(%d): Creating %d new variables.\n",
                PROGNAME,Pid,NUM_INST_VARS);
 
        /*
         * Get the new number of variables.
         */
        num_var = NumVariables + NUM_INST_VARS;
 
        /*
         * realloc the arrays to the new size and initialize the local array.
         */
        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);
        memset(LocalVars+NumVariables,0,sizeof(struct local_vars) * NUM_INST_VARS);
 
        for (i=NumVariables,j=0;i<num_var;i++,j++) {
 
            /*
             * Create a new set of variables as above and copy them to the
             * RMAPI variable structure to be registered.
             */
            sprintf(LocalVars[i].var_name,"%s%d",INST_VAR_NAME_PREFIX,j+1);
            sprintf(LocalVars[i].var_resid,INST_VAR_RESID_FORMAT,
                    times_called + (MonitorInstanceID * 100));
            LocalVars[i].value = INITIAL_VALUE;
 
            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++;
        }
        NumVariables = num_var;
    }
 
    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,"%s(%d): %d variables registered with the RMAPI.\n",
                    PROGNAME,Pid,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,"%s(%d): ha_rr_reg_var() error with variable(%s) instid(%d) RMAPI errno=(%d).\n",
                            PROGNAME,Pid,
                            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;
int CallSockFunctions;
fd_set sockfd_set;
 
    for(;;) {
 
        /*
         * Initialize the socket mask for select.
         */
        FD_ZERO(&sockfd_set);
        for(i=0;i<SOCKET_TABLE_SIZE;i++) {
             if (SocketTable[i].socket_fd >= 0) FD_SET(SocketTable[i].socket_fd,&sockfd_set);
        }
        CallSockFunctions = 0;

        /*
         * Unblock the SIGTERM and SIGALRM signals.
         */
        if (sigprocmask(SIG_UNBLOCK, &SignalMask, (sigset_t *)0) < 0) {
            /*
             * sigprocmask sys call failed.
             */
            fprintf(stderr,"%s(%d): Cannot unblock signals. sigprocmask() errno=%d.\n",
                    PROGNAME,Pid,errno);
            mon_exit(1);
        }
 
        /*
         * Only select if not terminating or SIGALRM caught to sample values.
         */
        if (!(SampleTime || Terminate)) {
            sock = select(FD_SETSIZE, &sockfd_set, NULL, NULL, NULL);
            if (sock > 0) {
                CallSockFunctions = 1;
            } else if (sock < 0) {
                /*
                 * select() call failed - exit if not an interrupt.
                 */
                if (errno != EINTR) {
                    fprintf(stderr,"%s(%d): select() failed with errno=%d.\n",
                            PROGNAME,Pid,errno);
                    mon_exit(1);
                }
                continue;
            }
        }
 
        /*
         * Block the SIGTERM and SIGALRM sigs.
         */
        if (sigprocmask(SIG_BLOCK, &SignalMask, (sigset_t *)0) < 0) {
            fprintf(stderr,"%s(%d): Cannot block signals. sigprocmask() errno=%d.\n",
                    PROGNAME,Pid,errno);
            mon_exit(1);
        }
 
        if (Terminate) {
            /*
             * SIGTERM caught - call mon_exit() to clean up.
             */
            fprintf(stdout,"%s(%d): Caught signal(%d)...calling exit...\n",
                    PROGNAME,Pid,SigCaught);
            mon_exit(SigCaught);
        }

        if (CallSockFunctions) {
            /*
             * Call the socket function for each socket returned by select.
             */
            for (i=0;i<SOCKET_TABLE_SIZE;i++) {
                if (SocketTable[i].socket_fd >=0 &&
                    FD_ISSET(SocketTable[i].socket_fd,&sockfd_set)) {
                    fprintf(stdout,"%s(%d): Calling socket function for session(%d) socket_fd(%d).\n",
                            PROGNAME,Pid,i,SocketTable[i].socket_fd);
                    (SocketTable[i].sock_funcp)(i);
                }
            }
        }
        
        if (SampleTime) {
            /*
             * SIGALRM caught - call send_values()
             * and reset the sample flag.
             */
            send_values();
            SampleTime = 0;
        }
    }
}
 
/*
 * 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(%d): %s resource monitor exiting.\n",
            PROGNAME,Pid,RESOURCE_MONITOR_NAME);
    exit(s);
}
 
/*
 * Display an RMAPI error.
 */
void display_rmapi_err(struct ha_em_err_blk *errblk)
{
    fprintf(stderr,
        "%s(%d): RMAPI Error: File(%s) Version(%s) Line(%d) Errno(%d)\n\t%s",
        PROGNAME,Pid,
        errblk->em_errfile,
        errblk->em_errlevel,
        errblk->em_errline,
        errblk->em_errno,
        errblk->em_errmsg);
}
