/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* */ /* */ /* Licensed Materials - Property of IBM */ /* */ /* (C) COPYRIGHT International Business Machines Corp. 1996,2019 */ /* All Rights Reserved */ /* */ /* US Government Users Restricted Rights - Use, duplication or */ /* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* */ /* IBM_PROLOG_END_TAG */ /* @(#)42 1.3 src/rsct/pem/emtools/emapi_test/emapi_ex/emapi_v02_ex01.c, emtools, rsct_rady, rady2035a 6/30/98 10:47:07 */ #include #include #include #include #include #include #include #include #include #include #define HA_EM_VERSION 2 #include /* * emapi_v02_ex01.c * * This program presents an example of using the Event Manager Application * Programming Interface (EMAPI). The program uses the EMAPI to monitor * four programs running on various nodes in two SP partitions. Since two * SP partitions are involved, the program establishes two sessions with the * EMAPI, one per SP partition. Since the program is not multi-threaded, * select() is used to multiplex responses from the two EMAPI sessions. * * This program can be compiled with the following command: * * cc -O emapi_v02_ex01.c -o emapi_v02_ex01 -lha_em */ /* * If a MAX macro isn't defined by one of the included header files, define * it. */ #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif /* * The following macros specify the programs this program will monitor through * the EMAPI. */ #define PART_1 "k21s" /* 1st SP partition */ #define PROG_1A "subsys_pgrm1" /* 1st program in 1st SP partition, */ #define USER_1A "root" /* ... and user running 1st program, */ #define NODE_1A 5 /* ... and node running 1st program. */ #define PROG_1B "subsys_pgrm2" /* 2nd program in 1st SP partition, */ #define USER_1B "root" /* ... and user running 2nd program, */ #define NODE_1B 6 /* ... and node running 2nd program. */ #define PART_2 "k21sp2" /* 2nd SP partition */ #define PROG_2A "subsys_pgrm1" /* 1st program in 2nd SP partition, */ #define USER_2A "root" /* ... and user running 1st program, */ #define NODE_2A 14 /* ... and node running 1st program. */ #define PROG_2B "subsys_pgrm2" /* 2nd program in 2nd SP partition, */ #define USER_2B "root" /* ... and user running 2nd program, */ #define NODE_2B 15 /* ... and node running 2nd program. */ /* * This program saves information about each EMAPI session it has established * in a session structure. */ struct session { char *name; /* Name of partition used by session */ int fd; /* Session file descriptor */ int restart; /* Boolean - Session's connection has been lost */ }; /* * This program saves information about each program it is monitoring through * the EMAPI in a program structure. */ struct program { char *name; /* Name of a program to be monitored */ char *user; /* Name of user running program */ int node; /* Node on which program is running */ ha_em_eid_t eid; /* Event identifier for program */ int unreged; /* Event is unregistered */ }; /* * The terminate_requested global variable is set to a non-zero value when * the user of this program requests that the program be terminated. The * user requests program termination via the interrupt key (typically Ctrl-C). * The interrupt key causes a SIGINT signal to be delivered to this program. * This program installs a signal handler for the SIGINT signal, which sets * the terminate_requested global variable to a non-zero value when SIGINT * is delivered. */ static int terminate_requested = 0; /* * The unregistrations_requested global variable indicates whether this * program has sent unregistration requests to the Event Manager. * Unregistration requests are sent when the user requests program * termination. */ static int unregistrations_requested = 0; /* * Function prototypes for internal functions. */ static void interrupt_catch(int signo); static void setup_signals(void); static void start_session(struct session *sess_p); static void register_for_events(struct session *sess_p, struct program *progs_p, int progs_elems, int *reg_cnt_p); static void unregister_events(struct session *sess_p, struct program *progs_p, int progs_elems); static void select_session(struct session *sess_p, fd_set *read_fds_p, int *fd_limit_p, int *timeout_p); static int session_response_ready(struct session *sess_p, fd_set *read_fds_p); static void process_response(struct session *sess_p, struct program *progs_p, int progs_elems, int *reg_cnt_p); static void process_response_reg(struct session *sess_p, struct program *progs_p, int progs_elems, struct ha_em_rsp_blk *rsp_blk); static void process_response_rerr(struct session *sess_p, struct program *progs_p, int progs_elems, struct ha_em_rsp_blk *rsp_blk, int *reg_cnt_p); static void process_response_unreg(struct session *sess_p, struct program *progs_p, int progs_elems, struct ha_em_rsp_blk *rsp_blk, int *reg_cnt_p); static void format_timestamp(struct timeval *timestamp_p, char *fmt_timestamp_p); static void breakdown_rsrc_ID(char *rsrc_ID_p, char *prog_name_p, char *user_name_p, int *node_p); static void find_rsrc_ID_value(char *rsrc_ID_p, char *name_p, char **value_pp, size_t *value_len_p); static void end_session(struct session *sess_p); /* * The main() function calls functions to initialize the program, establish * EMAPI sessions, register for events, wait for Event Manager responses, * process Event Manager responses, and respond to the user request for * program termination. When the program termination request is received, * main() calls functions to send unregistration requests through the EMAPI. * Once Event Manager responses have been received indicating that the * events have been unregistered, the program ends its EMAPI sessions, * and terminates. */ int main(int argc, char **argv) { struct session sess1 = {PART_1, -1, 0}; struct session sess2 = {PART_2, -1, 0}; struct program progs1[] = {{PROG_1A, USER_1A, NODE_1A, 0, 0}, {PROG_1B, USER_1B, NODE_1B, 0, 0}}; struct program progs2[] = {{PROG_2A, USER_2A, NODE_2A, 0, 0}, {PROG_2B, USER_2B, NODE_2B, 0, 0}}; int progs1_elems = sizeof progs1 / sizeof progs1[0]; int progs2_elems = sizeof progs2 / sizeof progs2[0]; int reg_cnt; /* Count of registered events */ fd_set read_fds; /* select() file descriptor mask */ int fd_limit; /* select() file descriptor limit */ int need_timeout; /* Boolean - select() timeout needed */ int rc; /* Return code */ struct timeval timeout_value = {15, 0}; /* Fifteen second timeout */ setup_signals(); /* Initialize signal dispositions */ start_session(&sess1); /* Start EMAPI session for 1st SP partition */ start_session(&sess2); /* Start EMAPI session for 2nd SP partition */ /* * Register for events in both EMAPI sessions that will monitor the * programs of interest. Maintain count of registered events. */ reg_cnt = 0; register_for_events(&sess1, progs1, progs1_elems, ®_cnt); register_for_events(&sess2, progs2, progs2_elems, ®_cnt); /* * Continue looking for Event Manager responses as long as there are * registered events. */ while (reg_cnt > 0) { /* * If program termination has been requested, and unregistration * requests have not been sent, send them now. */ if (terminate_requested && !unregistrations_requested) { printf("Termination requested.\n"); unregister_events(&sess1, progs1, progs1_elems); unregister_events(&sess2, progs2, progs2_elems); unregistrations_requested = 1; } /* * Construct the parameters to be passed to select(), and then * call select() to wait for Event Manager responses. */ FD_ZERO(&read_fds); fd_limit = 0; need_timeout = 0; select_session(&sess1, &read_fds, &fd_limit, &need_timeout); select_session(&sess2, &read_fds, &fd_limit, &need_timeout); rc = select(fd_limit + 1, &read_fds, NULL, NULL, need_timeout ? &timeout_value : NULL); if (rc == -1) { /* select() indicates error */ if (errno == EINTR) { /* select() interrupted by signal */ continue; /* Return to top of loop */ } perror("select()"); /* select() really encountered error */ exit(1); /* End program */ } /* * If an Event Manager response is present for the 1st EMAPI session, * process it. Also, maintain the count of registered events. */ if (session_response_ready(&sess1, &read_fds)) { process_response(&sess1, progs1, progs1_elems, ®_cnt); } /* * If an Event Manager response is present for the 2nd EMAPI session, * process it. Also, maintain the count of registered events. */ if (session_response_ready(&sess2, &read_fds)) { process_response(&sess2, progs2, progs2_elems, ®_cnt); } } end_session(&sess1); /* End EMAPI session for 1st SP partition */ end_session(&sess2); /* End EMAPI session for 2nd SP partition */ return 0; /* Indicate program was successful */ } /* * The interrupt_catch() function is installed as the signal handler for the * SIGINT signal. The SIGINT signal is delivered to this process when the * user presses the interrupt key (usually Ctrl-C). When this routine is * invoked, it sets the global variable terminate_requested to a non-zero * value, indicating the program is to be terminated. */ static void interrupt_catch(int signo) { terminate_requested = 1; return; } /* * The setup_signals() function initializes the disposition of a couple of * signals. * * 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. * * The interrupt_catch() function is installed as the signal handler for the * SIGINT signal. See the comments pertaining to the interrupt_catch() * function to find out why this is done. */ 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); } sa.sa_handler = interrupt_catch; /* SIGINT signal handler */ sigemptyset(&sa.sa_mask); /* No signals to mask off */ sa.sa_flags = SA_RESTART; /* Restart interrupted syscalls */ if (sigaction(SIGINT, &sa, NULL) == -1) { /* Change SIGINT disposition */ perror("sigaction(SIGINT)"); 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 void start_session(struct session *sess_p) { struct ha_em_err_blk errb; /* EMAPI error block */ /* * The ha_em_start_session() routine receives the name of the partition * with which a session is to be established, and returns a file * descriptor with which the session can be used. */ sess_p->fd = ha_em_start_session(HA_EM_DOMAIN_SP, sess_p->name, &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_p->fd == -1) { fprintf(stderr, "%s:\tha_em_start_session() returned EM errno %d:\n%s", sess_p->name, errb.em_errno, errb.em_errmsg); exit(1); } /* * A session has been established with the EMAPI, and the session's file * descriptor has been saved in the session structure passed to this * routine. Set the flag in the session structure that indicates the * session does not need to be restarted. */ sess_p->restart = 0; return; } /* * The register_for_events() function uses the EMAPI routine * ha_em_send_command() to request the registration of events pertaining to * programs of interest that are in the same SP partition. * * The resource variable name specified is "IBM.PSSP.Prog.xpcount". This is a * state variable whose value indicates whether or not processes are executing * a specific program. The resource ID for this resource variable * specifies the program name, the name of the user running the program, and * the node on which the program is running. The resource variable's value * is a structured byte string of three fields. Field 0 is of type long; its * value is the number of processes currently running the specified program * for the specified user on the specified node. Field 1 is also of type * long; its value is the previous number of processes running the program. * Field 2 is of type character string; its value is a comma separated list * of the PIDs of the processes running the program. * * When the events are registered, two expressions are specified. The primary * expression, "X@0 == 0", specifies that an event is to be generated when * field 0 of the resource variable's structured byte string has a value of 0. * Taking into account the semantics of the resource variable, the expression * causes an event to be generated when no processes are running the specified * program for the specified user on the specified node. The re-arm * expression, "X@0 > 0", specifies that after the primary expression has * generated an event, the primary expression is to be re-armed once a process * starts running the specified program for the specified user on the * specified node. Since the HA_EM_CMD_REG2 command is specified, the re-arm * expression will generate events. * * Notice that when events are registered, the HA_EM_SCMD_REVAL subcommand is * specified. As a result, the initial value of the programs being monitored * will be returned. */ static void register_for_events(struct session *sess_p, struct program *progs_p, int progs_elems, int *reg_cnt_p) { int num_events; /* Number of events to register */ size_t alloc_size; /* Size of memory to allocate */ struct ha_em_cmd_blk *cmd_blk; /* Pointer to command block */ struct ha_em_rb_reg *re; /* Pointer to registered event */ int i; /* Index */ struct ha_em_err_blk errb; /* EMAPI error block */ int rc; /* Return code */ char rsrc_ID_buf[80]; /* Resource ID buffer */ num_events = progs_elems; /* Register one event per program */ /* * 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_reg * structure. Additional space must be allocated when more than one * event is to be registered. */ alloc_size = sizeof(struct ha_em_cmd_blk) + ((num_events - 1) * sizeof(struct ha_em_rb_reg)); if ((cmd_blk = malloc(alloc_size)) == NULL) { perror("malloc()"); exit(1); } /* * Fill in the em_rb_reg array entries. There is one entry per event to * be registered. */ for (i = 0; i < num_events; i++) { re = &cmd_blk->em_res_blk.em_rb_reg[i]; re->em_name = "IBM.PSSP.Prog.xpcount"; /* Resource variable name */ sprintf(rsrc_ID_buf, "ProgName=%s;UserName=%s;NodeNum=%d", progs_p[i].name, progs_p[i].user, progs_p[i].node); re->em_rsrc_ID = strdup(rsrc_ID_buf); /* Resource ID */ if (re->em_rsrc_ID == NULL) { perror("strdup()"); exit(1); } re->em_expr = "X@0 == 0"; /* Expression */ re->em_raexpr = "X@0 > 0"; /* Re-arm expression */ re->em_cb = NULL; /* Callback routine not used*/ re->em_cb_arg = NULL; /* Callback routine not used*/ } /* * Fill in command block header, specifying number of elements in * em_rb_reg array, command, and subcommand. */ cmd_blk->em_cmd_num_elem = num_events; cmd_blk->em_cmd = HA_EM_CMD_REG2; cmd_blk->em_subcmd = HA_EM_SCMD_REVAL; /* * 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_p->fd, cmd_blk, &errb); if (rc == -1) { fprintf(stderr, "%s:\tha_em_send_command() returned EM errno %d:\n%s", sess_p->name, errb.em_errno, errb.em_errmsg); exit(1); } /* * Save the event identifiers assigned to the events just registered. */ for (i = 0; i < num_events; i++) { re = &cmd_blk->em_res_blk.em_rb_reg[i]; progs_p[i].eid = re->em_event_id; progs_p[i].unreged = 0; free(re->em_rsrc_ID); } free(cmd_blk); *reg_cnt_p += num_events; /* Update count of registered events */ return; } /* * The unregister_events() function uses the EMAPI routine * ha_em_send_command() to request the unregistration of events pertaining to * programs of interest that are in the same SP partition. */ static void unregister_events(struct session *sess_p, struct program *progs_p, int progs_elems) { int num_events; /* Number of events to unreg. */ size_t alloc_size; /* Size of memory to allocate */ struct ha_em_cmd_blk *cmd_blk; /* Pointer to command block */ ha_em_eid_t *re; /* Pointer to unreg. event */ int i; /* Index */ struct ha_em_err_blk errb; /* EMAPI error block */ int rc; /* Return code */ /* * Allocate enough memory to hold the command block to be given to the * EMAPI. The ha_em_cmd_blk structure contains one ha_em_eid_t * element. Additional space must be allocated when more than one * event is to be unregistered. */ alloc_size = sizeof(struct ha_em_cmd_blk) + ((progs_elems - 1) * sizeof(ha_em_eid_t)); if ((cmd_blk = malloc(alloc_size)) == NULL) { perror("malloc()"); exit(1); } /* * Fill in the event identifier array entries. There is one entry per * event to be unregistered. Only unregister events that are not already * unregistered. */ num_events = 0; for (i = 0; i < progs_elems; i++) { if (!progs_p[i].unreged) { re = &cmd_blk->em_res_blk.em_rb_unreg[num_events]; *re = progs_p[i].eid; num_events++; } } /* * Fill in command block header, specifying the number of elements in * the event identifier array, and the unregister command. */ cmd_blk->em_cmd_num_elem = num_events; cmd_blk->em_cmd = HA_EM_CMD_UNREG; /* * Send the unregister 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_p->fd, cmd_blk, &errb); if (rc == -1) { fprintf(stderr, "%s:\tha_em_send_command() returned EM errno %d:\n%s", sess_p->name, errb.em_errno, errb.em_errmsg); exit(1); } free(cmd_blk); return; } /* * The select_session() routine determines how the specified session affects * the next call to select(). If the session is marked as needing to be * restarted, ha_em_restart_session() is called to try to restart the session. * If the restart attempt fails, the next call to select() should specify * a timeout period - so the restart can be attempted again. If the restart * attempt succeeds, the new session file descriptor is saved, and the session * is marked as not needing to be restarted. If the session did not need * to be restarted, or had been successfully restarted, the next call to * select() should examine the session's file descriptor. */ static void select_session(struct session *sess_p, fd_set *read_fds_p, int *fd_limit_p, int *timeout_p) { struct ha_em_err_blk errb; /* EMAPI error block */ int new_fd; /* New session file descriptor */ /* * If the specified session needs to be restarted, try to do so. */ if (sess_p->restart) { new_fd = ha_em_restart_session(sess_p->fd, &errb); if (new_fd == -1) { /* * The session could not be restarted. If the Event Manager * error number indicates connection refused, indicate the * next select() should specify a timeout value (so the restart * can be tried again later), and return without putting the * session's file descriptor in the select() file descriptor * mask. */ if (errb.em_errno == HA_EM_ECONNREFUSED) { *timeout_p = 1; return; } /* * If execution reaches here, the session restart attempt failed * for an unexpected reason; terminate the program. */ fprintf(stderr, "%s:\tha_em_restart_session() returned EM errno " "%d:\n%s", sess_p->name, errb.em_errno, errb.em_errmsg); exit(1); } /* * If execution reaches here, a session restart was attempted and * was successful. Save the new session file descriptor, and indicate * that the session no longer needs to be restarted. */ sess_p->fd = new_fd; sess_p->restart = 0; } /* * If execution reaches here, the specified session is not known to be * in need of a restart. Put the session's file descriptor in the * select() file descriptor mask. Also, adjust the maximum file * descriptor to select, if appropriate. */ FD_SET(sess_p->fd, read_fds_p); *fd_limit_p = MAX(sess_p->fd, *fd_limit_p); return; } /* * The session_response_ready() function determines if a select() file * descriptor mask indicates that a session's file descriptor is ready for * reading. */ static int session_response_ready(struct session *sess_p, fd_set *read_fds_p) { return FD_ISSET(sess_p->fd, read_fds_p); } /* * The process_response() 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() indicates that the * session's connection with the Event Manager has been lost, the session * is marked as being in need of a restart. Any other error will terminate * the program. 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. */ static void process_response(struct session *sess_p, struct program *progs_p, int progs_elems, int *reg_cnt_p) { 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 i; /* Index */ /* * Receive response from session, if there is one to receive. */ rc = ha_em_receive_response(sess_p->fd, &rsp_blk, &errb); if (rc == -1) { /* * If the connection with the Event Manager has been lost, * mark the session as needing to be restarted, and return. */ if (errb.em_errno == HA_EM_ECONNLOST) { printf("%s:\tconnection to session lost.\n", sess_p->name); sess_p->restart = 1; return; } /* * Some error has occurred other than the loss of the connection. * Terminate the program. */ fprintf(stderr, "%s:\tha_em_receive_response() returned EM errno %d:\n" "%s", sess_p->name, 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; } /* * 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_REG: /* Event response */ case HA_EM_CMD_REG2: /* Event response (possible re-arm) */ process_response_reg(sess_p, progs_p, progs_elems, rsp_blk); break; case HA_EM_CMD_RERR: /* REG event registration error */ case HA_EM_CMD_R2ERR: /* REG2 event registration error */ process_response_rerr(sess_p, progs_p, progs_elems, rsp_blk, reg_cnt_p); break; case HA_EM_CMD_UNREG: /* Event unregistration response */ process_response_unreg(sess_p, progs_p, progs_elems, rsp_blk, reg_cnt_p); break; default: /* Unexpected response */ fprintf(stderr, "%s:\tProgram received unexpected command " "response: %d.\n", sess_p->name, 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; } /* * The process_response_reg() function processes event responses from the * Event Manager. Several points should be kept in mind: * * - The response block may contain multiple event responses. The * number of responses included in the response block is given in * the response block header. * * - An event response may be an error response. An event error response * does not indicate that a registered event has occurred. Instead, it * indicates the Event Manager no longer knows the current value of * the associated resource variable, and cannot generate events for * the resource variable. This may be a temporary condition. If the * Event Manager later obtains the current value of the associated * resource variable, an event will be generated (possibly indicating * that the expression is false). * * - The event for which an event error response is generated is still * registered. There is no need to re-register the event. * * - Since events are registered by this program using the HA_EM_CMD_REG2 * command, the re-arm expressions will generate events. Therefore, the * HA_EM_EVENT_RE_ARM flag must be tested to see if the expression or * the re-arm expression generated the event. * * - It is possible for an event to be delivered indicating the expression * is false. This can happen for two reasons. First, when events are * registered by this program, the HA_EM_SCMD_REVAL subcommand is * specified. That subcommand requests the initial value of the * associated resource variable. Second, the current value of the * associated resource variable is returned through an event once the * Event Manager obtains the current value of a resource variable after * an error has occurred. * * - If an event response does not have the HA_EM_EVENT_RE_ARM nor * HA_EM_EVENT_EXPR_FALSE flags set, the event response indicates that * the event's expression is true. */ static void process_response_reg(struct session *sess_p, struct program *progs_p, int progs_elems, struct ha_em_rsp_blk *rsp_blk) { struct ha_em_rpb_event *event_p; /* Pointer to event response */ struct ha_em_rpb_event *last_event_p; /* Beyond last event response*/ char time_stamp[80]; /* Formatted event time stamp */ char prog_name[80]; /* Name of program to which event pertains */ char user_name[80]; /* Name of user to which event pertains */ int node; /* Node to which event pertains */ event_p = rsp_blk->em_resp_blk.em_rpb_event; last_event_p = event_p + rsp_blk->em_rsp_num_resp; for ( ; event_p < last_event_p; event_p++) { /* * Format the event's time stamp; extract the program name, user * name, and node number from the resource ID. */ format_timestamp(&event_p->em_timestamp, time_stamp); breakdown_rsrc_ID(event_p->em_rsrc_ID, prog_name, user_name, &node); /* * If this is an event error response, print a message which includes * the general and specific error codes. */ if (event_p->em_errnum != 0) { printf("%s:\t%s State of %s run by %s on node %d unknown (%d, %d)." "\n", sess_p->name, time_stamp, prog_name, user_name, node, event_p->em_generr, event_p->em_specerr); continue; } /* * If the event response was generated for the re-arm expression, or * the event response was generated for the primary expression but * it is false, print a message indicating the program associated * with the event is being run. */ if ((event_p->em_event_flags & HA_EM_EVENT_RE_ARM) || (event_p->em_event_flags & HA_EM_EVENT_EXPR_FALSE)) { printf("%s:\t%s %s being run by %s on node %d.\n", sess_p->name, time_stamp, prog_name, user_name, node); continue; } /* * If execution reaches this point, the event response was generated * for the primary expression, and it is true. Print a message * indicating the program associated with the event is not being run. */ printf("%s:\t%s %s NOT being run by %s on node %d.\n", sess_p->name, time_stamp, prog_name, user_name, node); } return; } /* * The process_response_rerr() function processes registration error * responses. When an event cannot be registered due to some detected error, * a registration error response is sent to the EMAPI client. This function * marks the event, as tracked in a program structure, as being unregistered. * This will prevent an attempt to unregister a non-registered event later * in the program. */ static void process_response_rerr(struct session *sess_p, struct program *progs_p, int progs_elems, struct ha_em_rsp_blk *rsp_blk, int *reg_cnt_p) { struct ha_em_rpb_rerr *error_p; /* Pointer to error response */ struct ha_em_rpb_rerr *last_error_p; /* Beyond last error response*/ struct program *prog_p; /* Pointer to program struct */ char prog_name[80]; /* Name of program to which error pertains */ char user_name[80]; /* Name of user to which error pertains */ int node; /* Node to which error pertains */ error_p = rsp_blk->em_resp_blk.em_rpb_rerr; last_error_p = error_p + rsp_blk->em_rsp_num_resp; for ( ; error_p < last_error_p; error_p++) { /* * Find the program structure which describes the program associated * with the event whose registration failed. The match is made * with event identifiers. */ for (prog_p = progs_p; prog_p < progs_p + progs_elems; prog_p++) { if (prog_p->eid == error_p->em_event_id) { break; } } if (prog_p == progs_p + progs_elems) { fprintf(stderr, "%s:\tUnknown event identifier encountered (0x%x)." "\n", sess_p->name, error_p->em_event_id); exit(1); } /* * Extract the program name, user name, and node number from the * resource ID. */ breakdown_rsrc_ID(error_p->em_rsrc_ID, prog_name, user_name, &node); /* * Print a message about the registration error. */ printf("%s:\tRegistration error for %s run by %s on node %d (%d, %d)." "\n", sess_p->name, prog_name, user_name, node, error_p->em_generr, error_p->em_specerr); /* * Mark the program as not having an associated event registered. */ prog_p->unreged = 1; } /* * Update the number of events that are registered. */ *reg_cnt_p -= rsp_blk->em_rsp_num_resp; return; } /* * The process_response_unreg() function processes unregistrations responses. * Note that the unregistration response uses the same type of structure * as the event response, but not all the fields in the structure have * meaningful values for an unregistration response. */ static void process_response_unreg(struct session *sess_p, struct program *progs_p, int progs_elems, struct ha_em_rsp_blk *rsp_blk, int *reg_cnt_p) { struct ha_em_rpb_event *event_p; /* Pointer to unreg response */ struct ha_em_rpb_event *last_event_p; /* Beyond last unreg response*/ struct program *prog_p; /* Pointer to program struct */ char time_stamp[80]; /* Formatted unregister time stamp */ event_p = rsp_blk->em_resp_blk.em_rpb_event; last_event_p = event_p + rsp_blk->em_rsp_num_resp; for ( ; event_p < last_event_p; event_p++) { /* * Find the program structure which describes the program associated * with the event just unregistered. The match is made * with event identifiers. */ for (prog_p = progs_p; prog_p < progs_p + progs_elems; prog_p++) { if (prog_p->eid == event_p->em_event_id) { break; } } if (prog_p == progs_p + progs_elems) { fprintf(stderr, "%s:\tUnknown event identifier encountered (0x%x)." "\n", sess_p->name, event_p->em_event_id); exit(1); } /* * Format the unregistration time stamp. */ format_timestamp(&event_p->em_timestamp, time_stamp); /* * Print a message indicating the program associated with the * unregistered event is no longer being monitored. */ printf("%s:\t%s no longer monitoring %s run by %s on node %d.\n", sess_p->name, time_stamp, prog_p->name, prog_p->user, prog_p->node); /* * Note: em_errnum could indicate an error, but there is no point * looking at it here, since the program would not do anything * differently. */ /* * Mark the program as not having an associated event registered. */ prog_p->unreged = 1; } /* * Update the number of events that are still registered. */ *reg_cnt_p -= rsp_blk->em_rsp_num_resp; return; } /* * The format_timestamp() function takes a time stamp returned by the * Event Manager and converts it into 24-hour time. */ static void format_timestamp(struct timeval *timestamp_p, char *fmt_timestamp_p) { struct tm *broken_down_time; broken_down_time = localtime((time_t *) ×tamp_p->tv_sec); (void) strftime(fmt_timestamp_p, 20, "(%X)", broken_down_time); return; } /* * The breakdown_rsrc_ID() function takes a resource ID * associated with the IBM.PSSP.Prog.xpcount resource variable and extracts * from it the program name, user name, and node number. */ static void breakdown_rsrc_ID(char *rsrc_ID_p, char *prog_name_p, char *user_name_p, int *node_p) { char *value_p; size_t value_len; find_rsrc_ID_value(rsrc_ID_p, "ProgName=", &value_p, &value_len); strncpy(prog_name_p, value_p, value_len); *(prog_name_p + value_len) = '\0'; find_rsrc_ID_value(rsrc_ID_p, "UserName=", &value_p, &value_len); strncpy(user_name_p, value_p, value_len); *(user_name_p + value_len) = '\0'; find_rsrc_ID_value(rsrc_ID_p, "NodeNum=", &value_p, &value_len); *node_p = atoi(value_p); return; } /* * The find_rsrc_ID_value() takes a resource ID and extracts * a value from it. */ static void find_rsrc_ID_value(char *rsrc_ID_p, char *name_p, char **value_pp, size_t *value_len_p) { char *bp; char *ep; size_t len; if ((bp = strstr(rsrc_ID_p, name_p)) == NULL) { fprintf(stderr, "Unexpected resource ID encountered.\n"); exit(1); } bp += strlen(name_p); if ((ep = strchr(bp, ';')) != NULL) { len = ep - bp; } else { len = strlen(bp); } *value_pp = bp; *value_len_p = len; return; } /* * The end_session() function terminates a session with the EMAPI by * calling the EMAPI routine ha_em_end_session(). */ static void end_session(struct session *sess_p) { struct ha_em_err_blk errb; if (ha_em_end_session(sess_p->fd, &errb) == -1) { fprintf(stderr, "%s:\tha_em_end_session() returned EM errno %d:\n%s", sess_p->name, errb.em_errno, errb.em_errmsg); exit(1); } return; }