On 31-Jul-2014 09:51 -0500, John Allen wrote:
We have a long running job that generates unnecessary
messages in the joblog.

So I am starting at the top and working my way through the
program and removing as many unnecessary messages from the
joblog as possible.

The program issues a


Side note: Unless that work file is /moved/ into a permanent library, that Grant Object Authority work is often for naught; i.e. the objects in the library QTEMP would not require any authorizations to be added outside of those for the group\owner, mostly because *PUBLIC will rarely have the opportunity or even ability [irrespective of authority] to access the object in the temporary library for the job. Of course, perhaps the example is contrived and not representative of the work more generally, or the job will be operating under a switched user that either is not known until later or can not be known in advance, to whom authority might be granted specifically.

This generates 2 messages in the joblog:

CPI2201 "Authority given to user *PUBLIC for object WORKFILEA in
QTEMP object type *FILE."

Notably, the program [message queue] to which that informational message CPI2201 was sent, is *not* the CL program issuing the Grant request. That is the origin for the difficulty. The program QSYGRAUT to which the message was sent, is no longer active on the stack, thus the Program Message Queue (PGMQ) of the system program QSYGRAUT is no longer accessible to the CL program that requested the GRTOBJAUT; that message is no longer addressable to any program by a _combination of_ both the named Program Message Queue and the specified Message Type. Note: The message is available by the Message Key, irrespective of the Program Message Queue; learning the value of the message-key is another story.

CPC2201 "Object authority granted."

Notably, the program [message queue] to which that completion message CPC2201 was sent, *is* the CL program issuing the Grant request. That is the reason the message _can be_ accessed; the program message queue where the message is located is the PGMQ() of that currently active program, and that program message queue is identified with the special value *SAME on RCVMSG and RMVMSG.

Note: The F9=Display Message Details after F1=Help is chosen against a Message appearing in an [inter]active joblog will show to which program a message was sent, thus enabling learning whether the message will be available to the program that made the request for which the message was logged. The spooled joblog will also reveal that same information.

Immediately after the GRTOBJAUT there is

CALL PGM(QMHRMVPM) PARM('*' 0 ' ' '*NEW' ' ')
CALL PGM(QMHRMVPM) PARM('*' 0 ' ' '*NEW' ' ')

Warnings about the above CALL requests: the parameter definitions [documented for the API] do not match the data types of the data being passed [as literals] in the corresponding arguments.

Warning 1: The 5th argument incorrectly informs the API that there is an unbelievably large amount of storage available in which to return exception data; i.e. the INPUT portion for the Error Code suggests that there were x'40404040' Bytes provided for the OUTPUT portion of that parameter; when in fact, a literal specification provides implicitly only 32-bytes of reserved storage, although as a literal, effectively there is no storage provided for return data per the parameter being inherently input-only to the invoked program.

Warning 2: The 2nd parameter is functional, albeit deceptively, and possibly only accidentally. Although the decimal value zero [shown as the digit 0 for the second argument in the above CALL requests] will be understood by the API to be the value x'00000000', the actual value being passed to the API for that parameter would actually be the value x'000000000000000F', the value of the PackedDecimal(15,5) with value of zero. However, purposely coding in that manner to both coincidentally and somewhat deceptively achieve that effect, is discouraged. Another programmer who does not understand how that result is achieved only with subterfuge might /copy/ that line of code, and then deciding a relative call stack entry should be /one/ for their purposes, they may think simply changing the decimal digit zero to the decimal digit one will suffice, but of course that change would do nothing; any of the legitimate possible _decimal values_ that would be functional [beyond the zero-value] would be nonsensical as compared with an appropriately specified binary\integer value, per having to increment by (N*100) instead of just increment solely by (N) to direct the API to the Nth relative Call Stack Entry.

Additionally, the '*NEW' specification is a generic, such that *if* the invocation was able to do what was desired, then only one invocation would be required. That is because after the first invocation, there would be no _new_ messages; well, unless the API was both allowed to and did, issue new message(s) to that call stack entry.

These two calls only remove message CPC2201 from the joblog,
message CPI2201 remains in the joblog

Although not a helpful response, the effect is proper and expected, given what the messaging effects were from the request, with regards to where the messages are stored and how those messages are located. The appropriate /corrections/ to the API invocation [those alluded earlier] would not change that effect. The invocation removed the /new/ message from the specified program message queue, as requested.

The special value '*' [asterisk] as the first argument indicates that the Program Message Queue (PGMQ) to be processed is the currently active program; "The message queue of the current call stack entry (that is, the call stack entry removing the messages)." The value of zero as the second argument indicates the relative offset from the currently active program identified by the first argument; "Remove the messages from the message queue of the call stack entry specified in the Call stack entry parameter." Any value specified for the fourth parameter except '*BYKEY' [which is a request to operate on the key specified in the third parameter] can act only against "messages in the call message queue"; together, the specifications in the first two arguments ask the API to process only the messages located within the program message queue of the active program as the requester of the Remove Message API. But because [as noted earlier] the Information message CPC2201 is not in [was not sent to] the program message queue of that active program [which is where the API was asked to look, per the combination of values specified for the 1st and 2nd arguments], the info message can not be accessed using that invocation of the Remove Program Message API.

Note: The duplicate API invocations are effectively the equivalent of:

FWiW, while the value '*NEW' that was specified for the 4th argument signifies in terms of IBM i messaging the equivalent of not "*OLD" whereby '*OLD' signifies "already received\processed", and although the message CPI2201 was never _received_ such that the message should remain "*NEW", because the informational message is not in an\the active program message queue, the info msg is not available to a request to process the active program message queue PGMQ(*SAME (*)). For the same reason, the specification of CLEAR(*ALL) also would not assist to remove those info messages.

If the system program QSYGRAUT had sent [or moved] the informational message(s) to the invoker of the GRTOBJAUT, then the RMVMSG [or RCVMSG] or equivalent API invocation would have had little difficulty removing those messages along with the completion messages. Unfortunately there is no parameter [nor an environment variable or anything else] that can be specified to influence\persuade the Grant request to /move/ those messages to the PGMQ of the requester :-( Nothing that I am aware of is available, that would force the informational messages to be available [sent or moved] directly to the invoker, rather than being left in an inactive program message queue.

I tried replacing the two calls to QMHRMVPM with


With the same results

FWiW: For a replacement, the command Remove Message (RMVMSG) might be expected instead, as described earlier; i.e. use of Receive Message (RCVMSG) as an effective /replacement/ for an API call might be considered more likely, had the previous API invocation been to the Receive Program Message (QMHRCVPM) API.

No matter, the /same results/ [though actually they may not have been] is of no surprise, because the PGMQ(*SAME (*)) [as the default for RCVMSG (and RMVMSG)] has the same effects and limitations as the '*' and zero specifications on the Remove Message API [or similar RMVMSG request].

As with the API invocations shown, the duplicate RCVMSG requests are also suspect. In this case however, they are a potential issue; as contrasted with being simply redundancy of a generic request for the two API requests, the two RCVMSG requests might cause problems. Those two CL command requests performed consecutively will either remove two messages [the completion and some prior message] or remove just the completion msg such that the second invocation would receive no message. For lack of any return values [esp. on the second request], what or if a message was returned can not be easily determined within the CL program. Having included a parameter specification like KEYVAR(&RTNKEY), the condition of (&RTNKEY *EQ ' ') indicates that no message was found using the specified criteria to locate a message; having included a parameter specification like MSGID(&MID), the value of &MID could be reviewed [in testing] to determine if the expected message identifier was being received.

What needs to be done to remove the message CPI2201 from the

The typical resolution is to use the technique [for which an example is provided in the documentation] to /remove inactive messages/ from the job message queue. Various searches for such terms in the archives should reveal many past discussions with alternate examples and the link to the docs. On a second try, but I do not recall the search tokens, I finally found the KnowledgeCenter link:

FWiW: The Remove Program Messages (QMHRMVPM) API does enable removing /all/ of the /inactive/ messages. The /scope/ for the definition of /all/ however, is comprehensive; i.e. all of the messages within the entire _job message queue_ will be removed, not just those inactive since some particular request\invocation or some message key. I do not recall ever using that invocation in my code due to the effects being so drastic; instead I have found usefulness only in the technique of obtaining a message key and then looping to remove successive messages by key, to remove the inactive messages since a particular message key.

There are probably few scenarios whereby the extreme effect of removing _all inactive messages_ from the job message queue will be desirable. Nonetheless, here is a simple CL invocation using the Remove Message API [with appropriately data typed literal values for the arguments, matched to the parameter definitions; though CLP variables often are good\better choices for passing the values]:

call qsys/qmhrmvpm ('*ALLINACT ' x'00000000' +
' ' '*ALL ' x'0000000800000000')

What should be the equivalent request, and much simpler coded within a CLP, is the following Remove Message (RMVMSG) request:

rmvmsg pgmq(*allinact) msgq(*pgmq) clear(*all)

This thread ...

Return to Archive home page | Return to MIDRANGE.COM home page