/* 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.3   src/rsct/pem/emtools/emapi_test/emapi_ex/emapi_v02_ex04.c, emtools, rsct_rady, rady2035a 6/30/98 10:47:45 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

#define  HA_EM_VERSION		2
#include <ha_emapi.h>

/*
 *  emapi_v02_ex04.c
 *
 *  This program presents an example of using the Event Manager Application
 *  Programming Interface (EMAPI).  The program uses the EMAPI to obtain
 *  the definition of some resource variables.
 *
 *  This program can be compiled with the following command:
 *
 *      cc -O emapi_v02_ex04.c -o emapi_v02_ex04 -lha_em
 */


/*
 *  The following array contains query requests that will be sent to the
 *  EMAPI.  Each array element is of type struct ha_em_rb_query, which is
 *  defined by the EMAPI.  Each element specifies a class, a resource
 *  variable, and a resource ID.
 *
 *  Request 1 requests the definitions of resource variables in the
 *  IBM.PSSP.Prog class.
 *
 *  Request 2 requests the definitions of resource variables in the
 *  IBM.PSSP.SP_HW class whose resource variable names start with
 *  IBM.PSSP.SP_HW.Node.
 *
 *  Request 3 requests the definitions of resource variables in all
 *  classes whose resource IDs include a resource ID element
 *  named CPU.
 */

static struct ha_em_rb_query qry_array[] = {
	{"IBM.PSSP.Prog", "", "*"},								/* Request 1     */
	{"IBM.PSSP.SP_HW", "IBM.PSSP.SP_HW.Node", "*"},			/* Request 2     */
	{"", "", "CPU;*"}										/* Request 3     */
};

static int qry_cnt = sizeof qry_array / sizeof qry_array[0];


/*
 *  Function prototypes for internal functions.
 */

static void setup_signals(void);
static int start_session(void);
static ha_em_qid_t send_defn_request(int sess_fd,
									 struct ha_em_rb_query *qry_array,
									 int qry_cnt);
static int recv_defn_reply(int sess_fd, ha_em_qid_t qid);
static int process_response_query(ha_em_qid_t qid,
								  struct ha_em_rsp_blk *rsp_blk);
static int process_response_qerr(ha_em_qid_t qid,
								 struct ha_em_rsp_blk *rsp_blk);
static void end_session(int sess_fd);


/*
 *  The main() function calls functions to initialize the program, establish
 *  an EMAPI session, send query requests, wait for and process Event Manager
 *  responses, and terminate the EMAPI session.
 */

int main(int argc, char **argv)
{
    int			sess_fd;						/* Session file descriptor   */
	ha_em_qid_t	qid;							/* Query identifier          */
	int			qend;							/* Query is finished         */

	
    setup_signals();							/* Set signal dispositions   */

    sess_fd = start_session();					/* Start EMAPI session       */

	qid = send_defn_request(sess_fd, qry_array, qry_cnt);
												/* Send query request        */
	
	/*
	 *  Receive query replies until all replies have been received.
	 */
	
	while ((qend = recv_defn_reply(sess_fd, qid)) == 0) {
		/* Nothing to do inside the loop */
	}

	end_session(sess_fd);						/* End EMAPI session         */
	
    return 0;
}


/*
 *  The setup_signals() function initializes the disposition of a signal.
 *
 *  The disposition of the SIGPIPE signal is set such that the signal is
 *  ignored.  When a process writes to a pipe or connection-oriented socket for
 *  which there is no reader, the SIGPIPE signal is delivered to the process.
 *  The default disposition for SIGPIPE is to kill the receiving process.  If
 *  SIGPIPE is ignored, the SIGPIPE signal does not kill the process, and the
 *  system call used to write to the pipe or connection-oriented socket sets
 *  errno to EPIPE and returns an error indication.  It is recommended that
 *  clients of the EMAPI ignore the SIGPIPE signal, since the EMAPI uses
 *  connection-oriented sockets to communicate with the Event Manager.
 */
 
static
void setup_signals(void)
{
    struct sigaction    sa;


    sa.sa_handler = SIG_IGN;                /* SIGPIPE to be ignored         */
    sigemptyset(&sa.sa_mask);               /* No signals to mask off        */
    sa.sa_flags = 0;                        /* No flags needed               */

    if (sigaction(SIGPIPE, &sa, NULL) == -1) {  /* Change SIGPIPE disposition*/
        perror("sigaction(SIGPIPE)");          
        exit(1);
    }

    return;
}


/*
 *  The start_session() function establishes a session with the EMAPI by
 *  calling the EMAPI routine ha_em_start_session().  If a session cannot be
 *  established, the program is terminated.
 */

static
int start_session(void)
{
    int	sess_fd;								/* Session file descriptor   */
    struct ha_em_err_blk errb;                  /* EMAPI error block         */


    /*
     *  Since no specific partition name is passed to the
	 *  ha_em_start_session() routine, it attempts to establish a session
     *  with the default partition.  The routine returns a file
     *  descriptor with which the session can be used.
     */
    
    sess_fd = ha_em_start_session(HA_EM_DOMAIN_SP, "", &errb);

    /*
     *  If ha_em_start_session() indicates a session could not be established,
     *  print the error information returned by the routine, and exit the
     *  program.
     */
    
    if (sess_fd == -1) {
        fprintf(stderr, "ha_em_start_session() returned EM errno %d:\n%s",
                errb.em_errno, errb.em_errmsg);
        exit(1);
    }

    return sess_fd;								/* Return session fd         */
}


/*
 *  The send_defn_request() function uses the EMAPI routine
 *  ha_em_send_command() to send the query defined resource variables requests
 *  specified by the qry_array and qry_cnt parameters.
 */

static
ha_em_qid_t send_defn_request(int sess_fd,
							  struct ha_em_rb_query *qry_array, int qry_cnt)
{
    size_t                  alloc_size;      /* Size of memory to allocate   */
    struct ha_em_cmd_blk    *cmd_blk;        /* Pointer to command block     */
    struct ha_em_err_blk    errb;            /* EMAPI error block            */
    int                     rc;              /* Return code                  */
	ha_em_qid_t				qid;			 /* Query identifier             */

    
    /*
     *  Allocate enough memory to hold the command block to be given to the
     *  EMAPI.  The ha_em_cmd_blk structure contains one ha_em_rb_query
     *  structure.  Additional space must be allocated when more than one
     *  query is to be made.
     */

    alloc_size = sizeof(struct ha_em_cmd_blk) +
                     ((qry_cnt - 1) * sizeof(struct ha_em_rb_query));

    if ((cmd_blk = malloc(alloc_size)) == NULL) {
        perror("malloc()");
        exit(1);
    }

    /*
     *  Fill in command block header, specifying number of elements in
     *  em_rb_query array, command, and subcommand.  Indicate a callback
	 *  routine will not be used to receive the responses.
     */
    
    cmd_blk->em_cmd_num_elem = qry_cnt;
    cmd_blk->em_cmd          = HA_EM_CMD_QUERY;
    cmd_blk->em_subcmd       = HA_EM_SCMD_QDEF;
	cmd_blk->em_qcb          = NULL;
	cmd_blk->em_qcb_arg      = NULL;

	/*
	 *  Copy the array of query requests into the command block.
	 */
	
	memcpy(cmd_blk->em_res_blk.em_rb_query, qry_array,
		   qry_cnt * sizeof(struct ha_em_rb_query));

    /*
     *  Send the command.  A more elaborate program might check for the
     *  HA_EM_ECONNLOST Event Manager error number when an error is
     *  returned, and attempt to restart the session.  But, this program
     *  just terminates if ha_em_send_command() detects a lost connection.
     */
    
    rc = ha_em_send_command(sess_fd, cmd_blk, &errb);

    if (rc == -1) {
        fprintf(stderr, "ha_em_send_command() returned EM errno %d:\n%s",
                errb.em_errno, errb.em_errmsg);
        exit(1);
    }

    /*
     *  Save the query identifier assigned to the query request just sent.
     */

    qid = cmd_blk->em_qid;
	
    free(cmd_blk);			/* Free the allocated command block.             */

    return qid;				/* Return the query identifier                   */
}

/*
 *  The recv_defn_reply() function processes an Event Manager response
 *  in the specified EMAPI session.  First, ha_em_receive_response() is called
 *  to receive the response.  If ha_em_receive_response() returns an error
 *  the program is terminated.  The ha_em_receive_response() function may
 *  indicate that there really isn't any response for the EMAPI client to
 *  process at this time.  In that case, this function just returns.  If a
 *  response has been received, it is processed depending on what type of
 *  response it is.
 *
 *  This function returns 0 if all query responses have not been received yet.
 *  If all query responses have been received, 1 is returned.
 */

static
int recv_defn_reply(int sess_fd, ha_em_qid_t qid)
{
    struct ha_em_rsp_blk    *rsp_blk;   /* Pointer to the response block     */
    int                     rc;         /* Return code                       */
    struct ha_em_err_blk    errb;       /* Event Manager error block         */
    int                     qend;       /* Query end flag                    */


    /*
     *  Receive response from session, if there is one to receive.
     */
    
    rc = ha_em_receive_response(sess_fd, &rsp_blk, &errb);

    if (rc == -1) {

        /*
         *  Some error has occurred receiving a response; terminate the
		 *  program.  A more elaborate program might check for the
		 *  HA_EM_ECONNLOST Event Manager error number when an error is
		 *  returned, and attempt to restart the session.  But, this program
		 *  just terminates if ha_em_receive_response() detects a lost
		 *  connection.
         */
        
        fprintf(stderr, "ha_em_receive_response() returned EM errno %d:\n%s",
                errb.em_errno, errb.em_errmsg);
        exit(1);

    }

    /*
     *  If the ha_em_receive_response() routine returned zero,
     *  there is no response for the EMAPI client (this program) to process
     *  at this time.  The response may have been for the EMAPI itself, or
     *  a full response may not be available yet.  Just return.
     */
    
    if (rc == 0) {
        return 0;			/* Return indicating query is not ended          */
    }

    /*
     *  A response for the EMAPI client (this program) has been returned.
     *  The response buffer is pointed to by the rsp_blk variable.  Appropriate
     *  processing for the response depends on the command type.
     */
    
    switch (rsp_blk->em_cmd) {

        case HA_EM_CMD_QUERY:           /* Query response                    */
            qend = process_response_query(qid, rsp_blk);
            break;

        case HA_EM_CMD_QERR:            /* Query error response              */
            qend = process_response_qerr(qid, rsp_blk);
            break;

        default:                        /* Unexpected response               */
            fprintf(stderr, "Program received unexpected command "
                    "response: %d.\n", rsp_blk->em_cmd);
            exit(1);
            break;

    }

    /*
     *  The EMAPI client (this program) must free the memory associated with
     *  the returned response block when it is no longer needed.
     */
    
    free(rsp_blk);

    return qend;
}


/*
 *  The process_response_query() function processes query responses from the
 *  Event Manager.  Several points should be kept in mind:
 *
 *      - The response block may contain multiple query responses.  The
 *        number of responses included in the response block is given in
 *        the response block header.
 *
 *      - An query response may be an error response.  Such a response
 *        may indicate an error of a temporary nature.
 *
 *      - This routine compares the query identifier in the response block
 *        with the query identifier assigned to the request sent by
 *        this program to the EMAPI.  This should not really be necessary.
 *        If the program had sent multiple query requests to the EMAPI,
 *        through multiple calls to ha_em_send_command(), the query identifiers
 *        in the response blocks could be used to associate responses with
 *        requests.
 *
 *      - Multiple response blocks may be associated with one request block.
 *        The response to a query request is not complete until a response
 *        block is received with the em_qend field set to a non-zero value.
 */

static
int process_response_query(ha_em_qid_t qid, struct ha_em_rsp_blk *rsp_blk)
{
    struct ha_em_rpb_qdef	*qp;           /* Pointer to query response      */
    struct ha_em_rpb_qdef   *last_qp;      /* Beyond last query response     */


	/*
	 *  Check query identifier in response block.
	 */
	
    if (rsp_blk->em_qid != qid) {
		fprintf(stderr, "Query response block contains unexpected "
			   "query identifier: %d.\n", rsp_blk->em_qid);
		exit(1);
	}

	/*
	 *  Look at all the response elements in the response block.
	 */
	
    qp = rsp_blk->em_resp_blk.em_rpb_qdef;
    last_qp = qp + rsp_blk->em_rsp_num_resp;

    for ( ; qp < last_qp; qp++) {

		printf("============================================================"
			   "\n\n");
		
        /*
         *  Check for error code.
         */
        
        if (qp->em_errnum != 0) {
            printf("Query response for class \"%s\", "
				   "resource variable \"%s\", resource ID \"%s\" "
				   "unexpectedly contains an error (%d, %d).\n\n",
                   qp->em_class, qp->em_name, qp->em_rsrc_ID,
                   qp->em_generr, qp->em_specerr);
            continue;
        }

		/*
		 *  Print definition of a resource variable.
		 */
		
		printf("Resource Variable Name: \"%s\"\n"
			   "Variable Value Type:    %s\n"
			   "Variable Data  Type:    %s\n"
			   "Variable SBS Format:    \"%s\"\n"
			   "Variable Initial Value: \"%s\"\n"
			   "Variable Class:         \"%s\"\n"
			   "Resource ID:            \"%s\"\n"
			   "PTX Name:               \"%s\"\n"
			   "Default Expression:     \"%s\"\n"
			   "Locator:                \"%s\"\n"
			   "\n"
			   "Variable Description\n"
			   "--------------------\n"
			   "%s\n"
			   "\n"
			   "Resource ID Description\n"
			   "-----------------------\n"
			   "%s\n"
			   "\n"
			   "Event Description\n"
			   "-----------------\n"
			   "%s\n"
			   "\n",

			   qp->em_name,
			   qp->em_value_type == ha_emVTcounter  ? "Counter"  :
		       qp->em_value_type == ha_emVTquantity ? "Quantity" :
		       qp->em_value_type == ha_emVTstate    ? "State"    : "Unknown", 
		       qp->em_data_type  == ha_emDTlong     ? "long"     :
		       qp->em_data_type  == ha_emDTfloat    ? "float"    :
		       qp->em_data_type  == ha_emDTsbs      ? "SBS"      : "Unknown", 
		       qp->em_sbs_format,
		       qp->em_init_value,
		       qp->em_class,
		       qp->em_rsrc_ID,
		       qp->em_ptx_name,
		       qp->em_dflt_expr,
		       qp->em_locator,
			   qp->em_descrp,
		       qp->em_rsrc_ID_descrp,
		       qp->em_event_descrp);

	}

    return rsp_blk->em_qend;
}


/*
 *  The process_response_qerr() function processes query error
 *  responses.
 */

static
int process_response_qerr(ha_em_qid_t qid, struct ha_em_rsp_blk *rsp_blk)
{
    struct ha_em_rpb_qerr   *error_p;           /* Pointer to error response */
    struct ha_em_rpb_qerr   *last_error_p;      /* Beyond last error response*/


	/*
	 *  Check query identifier in response block.
	 */
	
    if (rsp_blk->em_qid != qid) {
		fprintf(stderr, "Query response block contains unexpected "
			   "query identifier: %d.\n", rsp_blk->em_qid);
		exit(1);
	}
    
    error_p = rsp_blk->em_resp_blk.em_rpb_qerr;
    last_error_p = error_p + rsp_blk->em_rsp_num_resp;

    for ( ; error_p < last_error_p; error_p++) {

		printf("============================================================"
			   "\n\n");

		/*
		 *  Print error information for a resource variable.
		 */
		
		printf("Resource Variable Name: \"%s\"\n"
			   "Variable Class:         \"%s\"\n"
			   "Resource ID:            \"%s\"\n"
			   "General  Error:         %d\n"
			   "Specific Error:         %d\n"
			   "Additional Error Info:  %d\n",

		       error_p->em_name,
		       error_p->em_class,
		       error_p->em_rsrc_ID,
			   error_p->em_generr,
			   error_p->em_specerr,
			   error_p->em_errinfo);
			   

    }

    return rsp_blk->em_qend;
}


/*
 *  The end_session() function terminates a session with the EMAPI by
 *  calling the EMAPI routine ha_em_end_session().
 */

static
void end_session(int sess_fd)
{
    struct ha_em_err_blk errb;


    if (ha_em_end_session(sess_fd, &errb) == -1) {
        fprintf(stderr, "ha_em_end_session() returned EM errno %d:\n%s",
                errb.em_errno, errb.em_errmsg);
        exit(1);
    }

    return;
}
