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
GRTOBJAUT OBJ(QTEMP/WORKFILEA) OBJTYPE(*FILE) USER(*PUBLIC)
AUT(*ALL)
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.
<
http://www.ibm.com/support/knowledgecenter/api/content/ssw_ibm_i_71/apis/QMHRCVPM.htm>
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:
RMVMSG PGMQ(*SAME (*)) MSGQ(*PGMQ) CLEAR(*NEW) MSGKEY(*N)
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
RCVMSG MSGTYPE(*LAST) RMV(*YES)
RCVMSG MSGTYPE(*LAST) RMV(*YES)
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
joblog?
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:
<
http://www.ibm.com/support/knowledgecenter/api/content/ssw_ibm_i_71/rbam6/rcvmsgpgm.htm>
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)
As an Amazon Associate we earn from qualifying purchases.