This example program illustrates the use of the Send Nonprogram Message API, QMHSNDM, the Receive Program Message API, QMHRCVPM, and the Change Exception Message API, QMHCHGEM. The program produces a diagnostic report of errors that occur when the QMHSNDM API is used to send a message to more than one message queue.
The program calls the QMHSNDM API to send a message to message queues that do not exist. The QMHSNDM API returns a generic exception message, CPF2469. This message indicates that the API also returned one or more diagnostic messages describing the errors. After the program receives the exception message and verifies that it is message CPF2469, it uses the QMHCHGEM API to handle the exception message. The QMHRCVPM API is used to receive the diagnostic messages. The program prints the exception message, the diagnostic messages, and the message help.
/********************************************************************/ /* */ /* MODULE NAME: DIAGRPT - Diagnostic Report */ /* LANGUAGE: ILE C for OS/400 */ /* */ /* FUNCTION: This module will produce a diagnostic report that */ /* could be used in diagnosing the errors that */ /* occurred using the QMHSNDM API to send a message */ /* to multiple message queues. */ /* */ /* This program purposely causes the QMHSNDM API to */ /* try to send a message to message queues that do */ /* not exist. As a result, the generic CPF2469 */ /* exception is returned indicating that one or more */ /* diagnostic messages were returned identifying the */ /* error(s) on the send operation. */ /* */ /* The program looks for and handles the CPF2469 */ /* exception. It then receives and prints out the */ /* exception and the previous diagnostics. */ /* */ /* Dependency: A print file must be created before calling */ /* program DIAGRPT. The print file should be created */ /* using the following command: */ /* */ /* CRTPRTF FILE(PRTDIAG) CTLCHAR(*FCFC) */ /* CHLVAL((1 (13))) */ /********************************************************************/ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <except.h> #include <qmhchgem.h> /* From QSYSINC/H */ #include <qmhrcvpm.h> /* From QSYSINC/H */ #include <qmhsndm.h> /* From QSYSINC/H */ #include <qusec.h> /* From QSYSINC/H */ #define DIAG_TYPE "02" #define BUF_SIZE 80 /*********************************************************************/ /* Type definition for error code structure */ /*********************************************************************/ typedef struct error_code_struct { Qus_EC_t ec_fields; char Exception_Data[100]; } error_code_struct; /*********************************************************************/ /* Type definition for qualified name structure */ /*********************************************************************/ typedef struct qual_name_struct { char name[10]; char libr[10]; } qual_name_struct; /*********************************************************************/ /* Type definition for message information structure used on the */ /* receive. F is the fixed portion of the record and V is the */ /* variable length portion of the record. */ /*********************************************************************/ typedef struct msg_info_struct { Qmh_Rcvpm_RCVM0200_t F; char V[1200]; } msg_info_struct; FILE *prtf; char buf[80]; char received[7]; int exception_count; /*********************************************************************/ /* Function to handle errors received on the API calls. */ /*********************************************************************/ static void excp_handler(_INTRPT_Hndlr_Parms_T *excp_info) { error_code_struct Error_Code; /* If the exception is CPF2469, increment the exception counter, */ /* and mark the exception as handled by the QMHCHGEM API */ if (strncmp(excp_info->Msg_Id,"CPF2469",7) == 0) { memcpy(received,(excp_info->Msg_Id),7); exception_count++; QMHCHGEM(&(excp_info->Target), 0, (char *)(&(excp_info->Msg_Ref_Key)), "*HANDLE ", "", 0, &Error_Code); } } /********************************************************************/ /* BuildQList: Routine to build the message queue list. */ /********************************************************************/ void BuildQList( qual_name_struct *QueueList, int NumQueue) { int i; strncpy(QueueList[0].name,"QPGMR ",10); strncpy(QueueList[1].name,"SNOOPY ",10); strncpy(QueueList[2].name,"QSECOFR ",10); strncpy(QueueList[3].name,"PEANUTS ",10); strncpy(QueueList[4].name,"QUSER ",10); for (i = 0; i < NumQueue ; i++ ) { strncpy(QueueList[i].libr,"*LIBL ",10); } } /********************************************************************/ /* PrintError: Routine to print error information and exit. */ /********************************************************************/ void PrintError(char *errstring, char exception[7]) { memset(buf,' ',BUF_SIZE); buf[0] = '0'; strncpy(buf+1,errstring,strlen(errstring)); fwrite(buf,1,BUF_SIZE,prtf); memset(buf,' ',BUF_SIZE); buf[0] = '0'; strncpy(buf+1,"Exception received->",20); strncpy(buf+21,exception,strlen(exception)); fwrite(buf,1,BUF_SIZE,prtf); fclose(prtf); exit(1); } /********************************************************************/ /* PrintData: Routine to print varying length character string data.*/ /********************************************************************/ void PrintData(char *strname, void *strptr, int strlgth) { char *strdata = strptr; int i,lgth,remain; /* Write the description and the data that will fit on one line */ memset(buf,' ',BUF_SIZE); buf[0] = '0'; lgth = strlen(strname); strncpy(buf+1,strname,lgth); lgth++; /* remain = MIN(strlgth,80 - lgth) */ remain = (strlgth < 80 - lgth) ? strlgth : 80 - lgth; strncpy(buf+lgth,strdata,remain); fwrite(buf,1,BUF_SIZE,prtf); /* Now write the remainder of the data */ if (strlgth > (80 - lgth )) { /* Adjust pointer to data not printed yet */ strdata = strdata + (80 - lgth); for (i = 0; i < strlgth; i = i + 70, strdata = strdata + 70 ) { /* lgth = MIN(strlgth-i,70) */ lgth = (strlgth-i < 70) ? strlgth-i : 70; memset(buf,' ',BUF_SIZE); strncpy(buf,"0 ",10); memcpy(buf+10,strdata,lgth); fwrite(buf,1,BUF_SIZE,prtf); } } } /********************************************************************/ /* PrintMessage: Routine to print the message data and text. */ /********************************************************************/ void PrintMessage(msg_info_struct *Msg) { char *DataPtr; /* Pointer to the varying length character data*/ int DataLen; /* Length of the varying length character data */ char CharType[10]; /* Message type as a string */ PrintData("Message ID->",Msg->F.Message_Id,7); /* Convert Message Type to a character string to be printed out */ if (memcmp(Msg->F.Message_Type,"02",2)==0) strncpy(CharType,"DIAGNOSTIC", 10); else if (memcmp(Msg->F.Message_Type,"15",2)==0) strncpy(CharType,"ESCAPE ",10); PrintData("Message Type->",CharType,10); /* First point to the beginning of the message data */ /* in the structure and get the length of data returned. */ DataPtr = Msg->V; DataLen = Msg->F.Length_Data_Returned; /* If there is non-blank data, print it out */ if ((DataLen > 0) && (strspn(DataPtr," ") < DataLen)) PrintData("Message data received->",DataPtr,DataLen); /* Point to the beginning of the message text field and get the */ /* length of message text returned. */ DataPtr += DataLen; DataLen = Msg->F.Length_Message_Returned; /* If there is non-blank text, print it out */ if ((DataLen > 0) && (strspn( DataPtr," ") < DataLen)) PrintData("Message text received->",DataPtr,DataLen); /* Now update to point to the beginning of the message */ /* help text field and get the length of message help text */ /* returned. */ DataPtr += DataLen; DataLen = Msg->F.Length_Help_Returned; /* If there is non-blank message help text, print it out */ if ((DataLen > 0) && (strspn( DataPtr," ") < DataLen)) PrintData("Message help text received->",DataPtr,DataLen); strncpy(buf,"- ",43); fwrite(buf,1,BUF_SIZE,prtf); } /*********************************************************************/ /* */ /* Start of main program. */ /* */ /*********************************************************************/ main() { error_code_struct ErrorCode; qual_name_struct MsgQList[5]; qual_name_struct MsgFile; qual_name_struct RpyMsgQ; msg_info_struct MsgInfo; char MsgData[128]; char MsgText[512]; char MsgHelp[512]; char PgmMsgQ[10]; char MsgType[10]; char MsgAction[10]; char Format[8]; char MsgId[7]; char MsgKey[4]; int MsgTextLen; int MsgInfoLen; int NumMsgQ; int PgmCount; int WaitTime; int morediag; /* Initialize variables */ exception_count = 0; memcpy(ErrorCode.ec_fields.Exception_Id," ",7); ErrorCode.ec_fields.Bytes_Provided = 0; memcpy(MsgId," ",7); memcpy(MsgFile.name," ",10); memcpy(MsgFile.libr," ",10); strcpy(MsgText,"This is an immediate, informational message"); MsgTextLen = strlen(MsgText); memcpy(MsgType,"*INFO ",10); memcpy(RpyMsgQ.name," ",10); memcpy(RpyMsgQ.libr," ",10); /* Build the list of message queues to send the message to */ NumMsgQ = 5; BuildQList(MsgQList,NumMsgQ); /* Enable the exception handler around the call to QMHSNDM */ #pragma exception_handler(excp_handler, 0, 0, _C2_MH_ESCAPE) /* Send the message to the list of message queues. */ QMHSNDM( MsgId, &MsgFile, MsgText, MsgTextLen, MsgType, &MsgQList, NumMsgQ, &RpyMsgQ, &MsgKey, &ErrorCode); /* Disable the exception handler */ #pragma disable_handler /* If an error occurred on the send, produce an exception report */ /* identifying what errors occurred. */ if (exception_count != 0) { /* Open printer file using first character forms control and */ /* write the header information. */ prtf = fopen ("PRTDIAG", "wb type=record recfm=FA lrecl=80"); memset(buf,' ',BUF_SIZE); strncpy(buf,"1 DIAGNOSTIC REPORT",43); fwrite(buf,1,BUF_SIZE,prtf); strncpy(buf," -----------------",43); fwrite(buf,1,BUF_SIZE,prtf); strncpy(buf,"- ",43); fwrite(buf,1,BUF_SIZE,prtf); /* Do the setup to first receive the exception signalled. */ memcpy(Format,"RCVM0200",8); memcpy(PgmMsgQ,"* ",10); memcpy(MsgType,"*EXCP ",10); memcpy(MsgKey," ",4); memcpy(MsgAction,"*OLD ",10); PgmCount = 0; WaitTime = 0; MsgInfoLen = 1276; /* Now change bytes_provided to 116 so that if any errors occur */ /* on the receive, the error information will be returned in the*/ /* error code structure instead of generating more exceptions */ /* which will clutter up the program message queue. */ ErrorCode.ec_fields.Bytes_Provided = 116; /* Receive the last exception type message on the program */ /* message queue */ QMHRCVPM(&MsgInfo, MsgInfoLen, Format, PgmMsgQ, PgmCount, MsgType, MsgKey, WaitTime, MsgAction, &ErrorCode); /* Test for any errors on the receive */ if (ErrorCode.ec_fields.Bytes_Available > 0) { PrintError("QMHRCVPM - Did not complete successfully", ErrorCode.ec_fields.Exception_Id); } /* An exception message was received successfully. Now see if */ /* the message received is the same exception that was signalled*/ /* If not, there is an error. */ if (strncmp(MsgInfo.F.Message_Id,received,7) != 0) { PrintError("QMHRCVPM - Wrong exception received", MsgInfo.F.Message_Id); } /* The exception message was received successfully. */ /* Print the message data and text for the exception message. */ PrintMessage(&MsgInfo); /* If the message was the generic CPF2469, there are one or */ /* more diagnostic messages to go with the CPF2469 on the queue.*/ /* Receive the diagnostic messages previous to the CPF2469 until*/ /* a non-diagnostic message is received or there are no more */ /* messages. */ if (strncmp(MsgInfo.F.Message_Id,"CPF2469",7) == 0) { memcpy(MsgType,"*PRV ",10); memcpy(MsgKey,MsgInfo.F.Message_Key,4); morediag = 1; while(morediag) { /* Receive the previous diagnostic */ QMHRCVPM(&MsgInfo, MsgInfoLen, Format, PgmMsgQ, PgmCount, MsgType, MsgKey, WaitTime, MsgAction, &ErrorCode); /* Test for error on the receive */ if (ErrorCode.ec_fields.Bytes_Available > 0) { PrintError("QMHRCVPM - Did not complete successfully", ErrorCode.ec_fields.Exception_Id); } /* If bytes available = 0 OR the next message is not a */ /* diagnostic message, we are done. */ if ((MsgInfo.F.Bytes_Available == 0) || (strncmp(MsgInfo.F.Message_Type,DIAG_TYPE,2) != 0) ) { morediag = 0; } else /* A diagnostic was received */ { /* Print the message data and text for the diagnostic */ /* message */ PrintMessage(&MsgInfo); /* Now copy the message key of the diagnostic message */ /* received to the MsgKey parameter to use on the next */ /* call to QMHRCVPM. */ memcpy(MsgKey,MsgInfo.F.Message_Key,7); } } /* End of while morediag = 1 */ } /* End of if CPF2469 received */ /* Write trailer */ memset(buf,' ',BUF_SIZE); strncpy(buf,"- END OF DIAGNOSTIC REPORT",48); fwrite(buf,1,BUF_SIZE,prtf); /* Close the print file */ fclose(prtf); } /* End of if error on send */ } /* End mainline */
The DIAGRPT program produces a report like this:
Message ID->CPF2469 Message Type->ESCAPE Message text received->Error occurred when sending message. Message help text received->Recovery . . . : See messages previously listed for a description of the error. Correct the error, and then try the command again. Message ID->CPF2403 Message Type->DIAGNOSTIC Message data received->PEANUTS *LIBL Message text received->Message queue PEANUTS in *LIBL not found. Message help text received->Cause . . . . . : The message queue you specified was not found in the library you specified. One of the following occurred: -- The queue name was not entered correctly. -- The queue does not exist in the specified library. -- You specified the wrong library name. Recovery . . . : Do one of the following and try the request again: -- Correct or change the message queue name or library name used in the message queue (MSGQ) parameter or the to-message queue (TOMSGQ) parameter. -- Create the message queue using the Create Message Queue (CRTMSGQ) command. Message ID->CPF2403 Message Type->DIAGNOSTIC Message data received->SNOOPY *LIBL Message text received->Message queue SNOOPY in *LIBL not found. Message help text received->Cause . . . . . : The message queue you specified was not found in the library you specified. One of the following occurred: -- The queue name was not entered correctly. -- The queue does not exist in the specified library. -- You specified the wrong library name. Recovery . . . : Do one of the following and try the request again: -- Correct or change the message queue name or library name used in the message queue (MSGQ) parameter or the to-message queue (TOMSGQ) parameter. -- Create the message queue using the Create Message Queue (CRTMSGQ) command. End of Diagnostic Report