Write an exit program for the Watch for Event exit point.
This is the second in a series of articles related to watch support on the system. The previous column, "One Approach to System Automation," introduced the concept of watches and the Start Watch (STRWCH) command. Also provided in that article was the source for a watch exit program that would be called when the message CPD1689 (Local system time has been adjusted) was sent to the QHST history log. We did not, however, have time to discuss the mechanics of how the program worked. In this article, we will look at the actual program flow. The source for the exit program will not be repeated here. Refer to the previous article if you need to refresh your memory on how the program was written.
When the Watch for Event exit point calls a user exit program, four parameters are passed to the exit program. These parameters are documented here, with the bare essentials of the parameter list also shown below.
Required Parameter Group:
1 |
Watch option setting |
Input |
Char(10) |
2 |
Session ID |
Input |
Char(10) |
3 |
Error detected |
Output |
Char(10) |
4 |
Event data |
Input |
Char(*) |
QSYSINC/H member name: ESCWCHT
The first parameter, Watch option setting, is a 10-byte input value that identifies why the exit program has been called. In the case of WCHCPD1689, the value passed will be *MSGID, indicating that a watched-for message ID was sent to a watched-for message queue.
The second parameter, Session ID, is a 10-byte input value identifying the name of the session calling the exit program. In our case, the value passed will be TIME_CHG as that is the value we specified for the SSNID keyword of the STRWCH command in the previous article.
The third parameter, Error detected, is a 10-byte output value that is returned by the exit program. The exit program must set this parameter to a value of all blanks upon return to the system exit point in order to indicate that the exit program detected no error. The special value *ERROR--or any non-blank value for that matter--indicates to the system that the exit program encountered an error and that the watch session identified by the second parameter is to be ended. Ending the session means that the user exit program will no longer be called when the watched-for message is sent.
The fourth parameter, Event data, is a variable-length input value that further identifies the reason the exit program was called. When the first parameter has a value of *MSGID, the format of the fourth parameter for V5R4 is this:
Fourth Parameter Format |
|||
Offset |
Type |
Field |
|
Dec |
Hex |
||
0 |
0 |
BINARY(4) |
Length of trace information |
4 |
4 |
CHAR(7) |
Message ID |
11 |
B |
CHAR(1) |
Reserved |
12 |
C |
CHAR(10) |
Message queue name |
22 |
16 |
CHAR(10) |
Message queue library |
32 |
20 |
CHAR(10) |
Job name |
42 |
2A |
CHAR(10) |
Job user name |
52 |
34 |
CHAR(6) |
Job number |
58 |
3A |
CHAR(4) |
Reserved |
62 |
3E |
CHAR(256) |
Sending program name |
318 |
13E |
CHAR(10) |
Sending module name |
328 |
148 |
BINARY(4) |
Offset to sending procedure name |
332 |
14C |
BINARY(4) |
Length of sending procedure name |
336 |
150 |
CHAR(10) |
Receiving program name |
346 |
15A |
CHAR(10) |
Receiving module name |
356 |
164 |
BINARY(4) |
Offset to receiving procedure name |
360 |
168 |
BINARY(4) |
Length of receiving procedure name |
364 |
16C |
BINARY(4) |
Message severity |
368 |
170 |
CHAR(10) |
Message type |
378 |
17A |
CHAR(8) |
Message timestamp |
386 |
182 |
CHAR(4) |
Message key |
390 |
186 |
CHAR(10) |
Message file name |
400 |
190 |
CHAR(10) |
Message file library |
410 |
19A |
CHAR(2) |
Reserved |
412 |
19C |
BINARY(4) |
Offset to comparison data |
416 |
1A0 |
BINARY(4) |
Length of comparison data |
420 |
1A4 |
CHAR(10) |
Compare against |
430 |
1AE |
CHAR(2) |
Reserved |
432 |
1B0 |
BINARY(4) |
Comparison data CCSID |
436 |
1B4 |
BINARY(4) |
Offset where comparison data was found |
440 |
1B8 |
BINARY(4) |
Offset to replacement data |
444 |
1BC |
BINARY(4) |
Length of replacement data |
448 |
1C0 |
BINARY(4) |
Replacement data CCSID |
* |
* |
CHAR(*) |
Sending procedure name |
* |
* |
CHAR(*) |
Receiving procedure name |
* |
* |
CHAR(*) |
Message comparison data |
* |
* |
CHAR(*) |
Message replacement data |
In V6R1, IBM did introduce some additional fields to this parameter. But they are not needed for our example program.
Additional formats for the fourth parameter are defined. If interested, you can refer to the exit point documentation in the Information Center (here for V5R4 and here for V6R1) for other formats that might be returned. These other formats would provide similar information on events such as watched-for LIC log or PAL entries.
As you can see, a wealth of information is made available to the exit program in order to allow processing based on the sending of a message on the system. These are the key pieces of information used in the WCHCPD1689 exit program:
•· Message ID is the message ID being sent that caused the exit program to be called. In the current scenario, this field should always be set to CPD1689, but recall that you can use the STRWCH command to specify more than one message ID. In the case of multiple watched-for messages, it would obviously be worth knowing which specific message has been sent! In addition, validating the message ID within the exit program can help you avoid unpleasant surprises if, for instance, a user mistakenly specified the WCHCPD1689 exit program with the wrong WCHMSG value on the STRWCH command.
•· Offset to replacement data is the offset from the start of the fourth parameter to where the replacement data for the message identified by message ID is located.
•· Length of replacement data is the length of the replacement data in bytes. This value is not actually used by the WCHCPD1689 exit program but is a critical piece of information all the same. Just as IBM strives for upward compatibility with APIs and outfiles by adding new fields to the end of a data structure or record format and not removing or changing the data type and/or size of previously existing fields, so too does IBM provide for upward compatibility with message description replacement data. Currently, message CPD1689 defines only two replacement variables (we will see how to determine this shortly), but in a future release, IBM may decide to add additional information. In this case, IBM would add these new or changed replacement variables to the end of the existing replacement variables. You could then use the Length of replacement data to determine what amount of replacement data is available to the exit program for processing. In the current case, this is not a concern; both V5R4 and V6R1 provide the same amount of replacement data.
•· Message replacement data is the variable data inserted into the message. This information is accessed by taking the address of the Event data parameter (the fourth parameter) and adding to this address the Offset to replacement data.
We will be using the QSYSINC-provided definition for the Event data parameter as there is no good reason to define and test our own data structure. The RPG data structure, which is found in member ESCWCHT of QSYSINC/QRPGLESRC, is shown below. This member name is provided in the exit point documentation immediately following the parameter list.
D*****************************************************************
D*Watch Exit Program called because a message id and any
D*associated comparison data is matched.
D*This structure is for the user exit program called by
D*STRWCH cmd or Start Watch (QSCSWCH) API
D*****************************************************************
DESCQWFM DS
D* Qsc Watch For Msg
D ESCLWI 1 4B 0
D* Length Watch Information
D ESCMID00 5 11
D* Message ID
D ESCERVED01 12 12
D* Reserved
D ESCMQN 13 22
D* Message Queue Name
D ESCMQL 23 32
D* Message Queue Lib
D ESCJN 33 42
D* Job Name
D ESCUN 43 52
D* User Name
D ESCJNBR 53 58
D* Job Number
D ESCRSV2 59 62
D* Reserved2
D ESCSPGMN 63 318
D* Sending Program Name
D ESCSPGMM 319 328
D* Sending Program Module
D ESCOSP 329 332B 0
D* Offset Sending Procedure
D ESCLOSP 333 336B 0
D* Length Of Sending Procedure
D ESCRPGMN 337 346
D* Receiving Program Name
D ESCRPGMM 347 356
D* Receiving Program Module
D ESCORP 357 360B 0
D* Offset Receiving Procedure
D ESCLORP 361 364B 0
D* Length Of Receiving Procedure
D ESCMS 365 368B 0
D* Msg Severity
D ESCMT 369 378
D* Msg Type
D ESCMT00 379 386
D* Msg Timestamp
D ESCMK 387 390
D* Msg Key
D ESCMFILN 391 400
D* Msg File Name
D ESCMFILL 401 410
D* Msg File Library
D ESCRSV3 411 412
D* Reserved3
D ESCOCD01 413 416B 0
D* Offset Comparison Data
D ESCLOCD01 417 420B 0
D* Length Of Comparison Data
D ESCCA 421 430
D* Compare Against
D ESCRSV4 431 432
D* Reserved4
D ESCCCSID 433 436B 0
D* Comparison Data CCSID
D ESCOCDF 437 440B 0
D* Offset Comparison Data Found
D ESCORD 441 444B 0
D* Offset Replacement Data
D ESCLORD 445 448B 0
D* Length Of Replacement Data
D ESCCCSID00 449 452B 0
D* Replacement Data CCSID
D*ESCSP 453 453
D*
D* variable length data @B2M
D*ESCRP 454 454
D*
D* variable length data @B2M
D*ESCCD01 455 455
D*
D* variable length data @B2M
D*ESCRD 456 456
D*
D* variable length data @B2M
From the QSYSINC-provided data structure ESCQWFM, the subfields ESCMID00, ESCORD, ESCLORD, and ESCRD correspond to the previously described Message ID, Offset to replacement data, Length of replacement data, and Message replacement data fields, respectively. Within the WCHCPD1689 program, we will not actually use the commented field ESCRD. Instead, we will define a based data structure (CPD1689), which describes the replacement data variables associated with the message being processed.
Having now gotten the Information Center documentation and QSYSINC definitions behind us, program WCHCPD1689 first declares the prototype and program interface specifications that are appropriate for a watch exit program. The four parameters declared--Type, SsnID, Error, and MsgDta--correspond to the documented parameters Watch option setting, Session ID, Error detected, and Event data, respectively. In the case of the Event data parameter, the WCHCPD1689 program declares this parameter as being likeds(ESCQWFM). ESCQWFM is the RPG data structure shown previously and is copied into WCHCPD1689 with the statement /copy qsysinc/qrpglesrc,escwcht.
Following this, the data structure CPD1689 is defined. This data structure defines the replacement data associated with message CPD1689. In order to determine what replacement data variables are available, we use the Display Message Description (DSPMSGD) command for message CPD1689. Looking at the message text (option 1), we can see that replacement variable &1 is the number of minutes that the local system time is offset from Coordinated Universal Time (UTC) and that variable &2 is a reason code for why the UTC offset value has been changed. In addition, we see that a reason code value of 1 indicates that the system has experienced a Daylight Saving Time transition. A reason code value of 2 indicates that the QTIMZON system value has been changed. Examining the field data (option 2 of DSPMSGD), we can see that &1 is a 4-byte integer value and that &2 is a 2-byte integer value. These two replacement variables are reflected in the CPD1689 data structure as subfields MinutesAdj and ReasonCode, respectively. The data structure is defined as being based(RplDtaPtr) and qualified. Using a based data structure simplifies access to the message replacement data, while qualifying the data structure aids in reducing the possibility of name collisions across message descriptions (ReasonCode, for instance, is a rather generic name that might be found in many messages). In the case of exit program WCHCPD1689, there is little chance of name collisions because the program is so simple. But in a more-complex application name, collisions could start to occur. I suggest that you simply set a standard of qualified message description data structures and be done with it.
Following this, we define the pointer variable RplDtaPtr (Replacement data pointer). Strictly speaking, this definition isn't necessary as the RPG compiler will implicitly create a pointer definition for us due to the previous use of RplDtaPtr in basing the CPD1689 data structure. But as with using qualified data structure names to minimize name collisions, I prefer to "across the board" explicitly define my pointer variables in order to avoid possible problems in more-complex applications. The particular problem I'm attempting to avoid here is that pointers implicitly defined by the compiler are scoped as local procedure variables, not global program variables. I have been burned more than once in the past by "accidently" having two occurrences of a pointer variable due to implicit compiler declarations.
After declaring the pointer variable RplDtaPtr, WCHCPD1689 then defines five named constants. These are used to better document what is being done in subsequent processing. RqdNbrParms is the minimum number of parameters that WCHCPD1689 requires, SessionName is the name of the watch session that WCHCPD1689 is written for, and the remaining three named constants represent the three documented reason codes that might be returned as replacement data in message CPD1689.
Following these definitions, we get to the actual processing of WCHCPD1689, which is very straightforward.
After starting a global monitor group to catch unexpected errors, the program checks to ensure that it has been called properly--that is, that the environment the program is running in is the one expected when we initially wrote the program. In a perfect world, none of this checking would be necessary, but I find that most of my programs do not run in a perfect world.
The first check is to make sure that at least four parameters were passed. If, for some reason, fewer than four parameters were passed and we did not perform this check, then message MCH3601 (Pointer not set for location referenced) would be the result. I find that problem determination is easier if, rather than "Pointer not set for location referenced," the program displays the message "WCHCPD1689 received only X parms."
The other checks being done are simply to verify that the parameters WCHCPD1689 received appear to be of the format expected. That type is *MSGID, SsnID is TIME_CHG, and the message ID is CPD1689. If any of these checks fail, the program sends a message identifying the problem and then ends. Failing any of these checks suggests that "something" is very wrong and that WCHCPD1689 should not continue. Either the program is not being called by the Watch for Event exit point or the parameters used when starting the watch were not correct.
There is some redundancy in the provided source code. Rather than setting Error to the value *ERROR in each of the checks, I could have simply set the variable Error once prior to the parameter value checks and allowed the subsequent validation checks to pick up the value of *ERROR. I elected to code the setting of Error as I did so that any developer looking at this program a year from now could easily see my intent--my intent being to exit the program and to tell the system "Don't call me again until this is fixed."
Upon successfully passing the previous checks, WCHCPD1689 accesses the message replacement data by setting the basing pointer RplDtaPtr to the correct address and then performs various processing based on the ReasonCode found in the message replacement data. This processing is shown as comments as it will vary significantly for each system. After performing any necessary processing, WCHCPD1689 returns control to the operating system with the Error parameter set to success (all blanks). This tells the operating system to continue calling WCHCPD1689 if more CPD1689 messages are sent to the QHST history log.
You might notice that even in the case of an unexpected ReasonCode (the other operation of the select group), WCHCPD1689 indicates to the system that it's OK to continue calling the exit program in the future. To take this action (displaying a message to indicate an unexpected input was encountered and continuing to process subsequent message occurrences) is a judgment call that needs to be made on a case-by-case basis. In this particular situation, the exit program is not doing any processing of a permanent nature (data base updates, object changes, etc.) related to the "other" ReasonCode (it's just displaying a message) and, if the watch session wasn't active, the system would still have "done" whatever was causing the system to send the CPD1689 message in the first place. So I elected to essentially ignore the fact that the message was sent, other than displaying a message documenting that some investigation needs to be done.
Contrast this to the previous environment checks, where we ended the watch session in an Error = '*ERROR' situation. In the environment cases, there is a clear indication that we have a configuration problem and that the exit program is not able to watch for the message it was designed for. In the ReasonCode cases, the exit program is still capable of watching and processing the message it was designed for; it is simply detecting ReasonCodes above and beyond its design point. This is a subtle, but important, distinction that you should keep in mind when developing any exit program.
This concludes our discussion of the WCHCPD1689 watch exit program and hopefully gives you a feel for how easy it might be write a watch exit program. In the next article, you will see how to watch, and react to, message CPF1393 (Subsystem &1 disabled user profile &2 on device &3) in order to selectively re-enable user profiles without operator intervention. In doing so, you'll also discover a few other capabilities of the system watch support.
Meanwhile, if you have other API questions, send them to me at
LATEST COMMENTS
MC Press Online