/* 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 */ /* @(#)45 1.3 src/rsct/pem/emtools/emapi_test/emapi_ex_r/emapi_v02_ex03.c, emtools, rsct_rady, rady2035a 6/30/98 10:48:47 */ #include #include #include #include #include #include #include #include #define HA_EM_VERSION 2 #include /* * emapi_v02_ex03.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. Each session is managed by a separate thread. * * This program can be compiled with the following command: * * cc_r -O emapi_v02_ex03.c -o emapi_v02_ex03 -lha_em_r -lpthreads -lc_r */ /* * 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. */ /* * The STRERROR_BUF and STRERROR macros make it a little more convenient to * use the thread safe version of strerror(), strerror_r(). */ #define STRERROR_BUF char strerror_buf[256] #define STRERROR(en) ( \ strerror_r(en, strerror_buf, sizeof strerror_buf) == 0 \ ? strerror_buf : "Unknown error" \ ) /* * 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 thread_data structure is used by the initial thread to pass data to * a secondary thread when it is created. */ struct thread_data { struct session *sess; /* EMAPI session the thread is to establish */ struct program *progs; /* Programs the thread is to monitor */ int progs_cnt; /* Number of programs thread is to monitor */ }; /* * Following are handles for threads created by the initial thread. */ pthread_t thread1; /* Handle to thread 1 - a program monitor */ pthread_t thread2; /* Handle to thread 2 - a program monitor */ pthread_t thread3; /* Handle to thread 3 - cancellation monitor */ /* * Function prototypes for internal functions. */ static void setup_signals(void); static void create_thread(pthread_t *thread_p, void *(* thread_rtn)(void *), void *data_p); static void cancel_thread(pthread_t thread); static void wait_for_thread(pthread_t thread); static void *program_thread_main(void *parm_p); static void program_thread_main_cleanup(void *parm_p); static void *cancel_thread_main(void *parm_p); 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 ensure_session_connected(struct session *sess_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 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 is executed by this program's initial thread. * The function creates three other threads. The first two threads * use the EMAPI to monitor other programs. One thread is used per * EMAPI session, and one EMAPI session is used per SP partition. * The third thread waits for the user to request the termination of this * program. When the third thread detects the user would like this program * to terminate, it cancels the two threads using the EMAPI. When those * two threads terminate, the initial thread cancels the third thread, waits * for the third thread to terminate, and terminates the program. */ 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]; struct thread_data td1 = {&sess1, progs1, progs1_elems}; struct thread_data td2 = {&sess2, progs2, progs2_elems}; setup_signals(); /* Initialize signal dispositions */ /* * Create 2 threads that will monitor programs with the EMAPI. */ create_thread(&thread1, program_thread_main, &td1); /* Create thread 1 */ create_thread(&thread2, program_thread_main, &td2); /* Create thread 2 */ /* * Create a thread that will wait for the user to request termination. */ create_thread(&thread3, cancel_thread_main, NULL); /* Create thread 3 */ /* * Wait for the 2 threads that are monitoring programs to end. They * will end due to errors or they will be canceled by thread 3 when * the user requests termination. */ wait_for_thread(thread1); /* Wait for thread 1 to terminate */ wait_for_thread(thread2); /* Wait for thread 2 to terminate */ /* * Cancel the thread waiting for the user to request termination, and * then wait for that thread to terminate. */ cancel_thread(thread3); /* Cancel thread 3 */ wait_for_thread(thread3); /* Wait for thread 3 to terminate */ return 0; /* Indicate program was successful */ } /* * 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 SIGINT signal is blocked in the initial thread's signal mask. * Threads created by the initial thread will inherit the signal mask from * the initial thread. One thread will wait for the SIGINT signal * to be delivered by using sigwait(). */ static void setup_signals(void) { struct sigaction sa; /* Signal action description */ int rc; /* Return code (error number) */ STRERROR_BUF; /* Buffer for strerror_r() */ sigset_t sigset; /* Signal mask to block SIGINT */ 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); } sigemptyset(&sigset); /* Initialize signal set */ sigaddset(&sigset, SIGINT); /* Add SIGINT to signal set */ rc = sigthreadmask(SIG_BLOCK, &sigset, NULL); /* Block SIGINT signal */ if (rc != 0) { fprintf(stderr, "sigthreadmask(): %s\n", STRERROR(rc)); exit(1); } return; } /* * The create_thread() function creates a thread, passing along the specified * main function address and parameter address. The function returns the * thread's handle. The thread is created such that it can be joined later * on. */ static void create_thread(pthread_t *thread_p, void *(* thread_rtn)(void *), void *data_p) { pthread_attr_t thread_attr; /* Thread attributes */ int rc; /* Return code (error number) */ STRERROR_BUF; /* Buffer for strerror_r() */ rc = pthread_attr_init(&thread_attr); if (rc != 0) { fprintf(stderr, "pthread_attr_init(): %s\n", STRERROR(rc)); exit(1); } rc = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_UNDETACHED); if (rc != 0) { fprintf(stderr, "pthread_attr_setdetachstate(): %s\n", STRERROR(rc)); exit(1); } rc = pthread_create(thread_p, &thread_attr, thread_rtn, data_p); if (rc != 0) { fprintf(stderr, "pthread_create(): %s\n", STRERROR(rc)); exit(1); } rc = pthread_attr_destroy(&thread_attr); if (rc != 0) { fprintf(stderr, "pthread_attr_destroy(): %s\n", STRERROR(rc)); exit(1); } return; } /* * The cancel_thread() function requests the cancellation of the specified * thread. */ static void cancel_thread(pthread_t thread) { int rc; /* Return code (error number) */ STRERROR_BUF; /* Buffer for strerror_r() */ rc = pthread_cancel(thread); if (rc != 0) { fprintf(stderr, "pthread_cancel(): %s\n", STRERROR(rc)); exit(1); } return; } /* * The wait_for_thread() function waits for the specified thread to * terminate. */ static void wait_for_thread(pthread_t thread) { int rc; /* Return code (error number) */ STRERROR_BUF; /* Buffer for strerror_r() */ rc = pthread_join(thread, NULL); if (rc != 0) { fprintf(stderr, "pthread_join(): %s\n", STRERROR(rc)); exit(1); } return; } /* * The program_thread_main() function is the main function for threads that * are created to monitor programs in a SP partition. The function is told * what programs are to be monitored in what partition through the function * parameter, which is a pointer to a thread_data structure. The function * calls functions to establish an EMAPI session, register for events, wait * for process Event Manager responses, and cleanup when the thread is * canceled. The thread cleanup function that is installed ends the EMAPI * session that was established for the thread. */ static void *program_thread_main(void *parm_p) { struct thread_data *td; /* Thread data (parameters) */ int reg_cnt; /* Count of registered events */ int rc; /* Return code */ td = (struct thread_data *) parm_p; /* Get parameters for the thread */ start_session(td->sess); /* Start EMAPI session for SP partition */ /* * Install a clean up handler that will end the session when the thread * is canceled. */ pthread_cleanup_push(program_thread_main_cleanup, (void *)td->sess); /* * Register for events that will monitor the programs of interest. * Maintain count of registered events. */ reg_cnt = 0; register_for_events(td->sess, td->progs, td->progs_cnt, ®_cnt); /* * Continue looking for Event Manager responses as long as there are * registered events. */ while (reg_cnt > 0) { /* * Ensure the session is still connected to the Event Manager. */ ensure_session_connected(td->sess); /* * Process the next response from the Event Manager. */ process_response(td->sess, td->progs, td->progs_cnt, ®_cnt); } /* * Pop and execute the thread cleanup handler installed by this routine. * Note that the cleanup handler will end the EMAPI session. */ pthread_cleanup_pop(1); return NULL; /* The thread is finished. */ } /* * The program_thread_main_cleanup() function is the thread cancellation * cleanup handler for the program_thread_main() function. When this * function is installed as a thread cancellation cleanup handler, the * thread has an EMAPI session. This function ends that session. The * function receives as input a pointer to the session structure describing * the session. */ static void program_thread_main_cleanup(void *parm_p) { struct session *sess_p; sess_p = (struct session *) parm_p; end_session(sess_p); printf("%s:\tno longer monitoring programs in this partition.\n", sess_p->name); return; } /* * The cancel_thread_main() function waits for the user to request * the termination of this program. The user requests program termination * by pressing the interrupt key (usually Ctrl-C). Pressing the interrupt * key will cause this program to receive the SIGINT signal. This function * waits for the SIGINT signal to be delivered. Once the SIGINT signal has * been delivered, this function cancels the two threads monitoring programs * through the EMAPI. */ static void *cancel_thread_main(void *parm_p) { int rc; /* Return code (error number) */ sigset_t sigset; /* Signal mask to unblock SIGINT */ int sig; /* Received signal */ STRERROR_BUF; /* Buffer for strerror_r() */ sigemptyset(&sigset); /* Initialize signal set */ sigaddset(&sigset, SIGINT); /* Add SIGINT to signal set */ for ( ; ; ) { rc = sigwait(&sigset, &sig); if (rc != 0) { fprintf(stderr, "sigwait(): %s\n", STRERROR(rc)); exit(1); } if (sig == SIGINT) { printf("Termination requested.\n"); cancel_thread(thread1); /* Request cancellation of thread 1 */ cancel_thread(thread2); /* Request cancellation of thread 2 */ break; /* No longer wait for SIGINT */ } fprintf(stderr, "sigwait() reported unexpected signal: %d.\n", sig); exit(1); } return NULL; /* The thread is finished */ } /* * 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 ensure_session_connected() function ensures the specified session * is not known to be disconnected. If the session is marked as needing * to be restarted, the function does not return until the session has been * successfully restarted. Restart attempts are tried every 15 seconds. */ static void ensure_session_connected(struct session *sess_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. */ while (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, wait 15 seconds * before trying the restart again. */ if (errb.em_errno == HA_EM_ECONNREFUSED) { sleep(15); continue; } /* * 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. Just return. */ return; } /* * 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; 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 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; localtime_r((time_t *) ×tamp_p->tv_sec, &broken_down_time); (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; }