IBM Support

Example of Program for Exit Point QIBM_QCA_RTV_COMMAND

Troubleshooting


Problem

This document provides an example of a program for Exit Point QIBM_QCA_RTV_COMMAND.

Resolving The Problem

A change command exit point (QIBM_QCA_CHG_COMMAND) and a retrieve command exit point (QIBM_QCA_RTV_COMMAND) were provided to allow customers and business partners to register their own exit programs that would be called when a CL command is used. In most cases, the change command exit point allows the command to be changed if it is not library-qualified or is library-qualified with *LIBL, *NLVLIBL, or *SYSTEM. Exit programs registered at either of these new exit points will be called before the command's CPP is given control.

Below is an example of an Exit Point Program that you can add to the QIBM_QCA_RTV_COMMAND exit point.

Examples can also be viewed in the following Redpaper: Best Practices for Managing IBM i Jobs and Output (and a few other special tips).

http://www.redbooks.ibm.com/redpapers/pdfs/redp4454.pdf

SMBJOB exit program

Run-away usage of the SMBJOB command can consume large numbers of job table entries. You can register and run a SBMJOB command exit program to avoid loops that use up job table entries. Example 2 provides sample code of the exit program. We provide this code as is.
 
Caution. This code is provided as is. IBM is not responsible for ensuring accuracy of the results from running this code.

Example 2 Sample SMBJOB exit program

Example

/*********************************************************************/
/* */
/* ZSBMEXIT - Exit program for SBMJOB command */
/* Registered under QIBM_QCA_RTV_COMMAND exit point. */
/* */
/* Purpose: Restrict use of the Submit Job (SBMJOB) command */
/* to avoid excessive system demand. This is primarily designed */
/* to detect and stop a looping job that is issuing SBMJOB */
/* commands. */
/* */
/* This is a sample program that demonstrates the concept of */
/* implementing a policy for the use of the SBMJOB command. */
/* To implement your own policy, you need to adjust time values */
/* and adjust the number of commands that you allow. */
/* Refer to the i5/OS Information Center for license and */
/* disclaimer information about sample programs. */
/* */
/* Note: */
/* You must create the program such that all users are */
/* authorized to find and call the program. Otherwise, */
/* you get CPI0001 "Exit program not called. Reason code &1." */
/* and no limit is enforced for those users. */
/* In some environments, it may be necessary to use *OWNER */
/* for the User profile (USRPRF) parameter of the */
/* Create CL Program (CRTCLPGM) command. */
/* */
/* Registration (sample): */
/* ADDEXITPGM EXITPNT(QIBM_QCA_RTV_COMMAND) FORMAT(RTVC0100) + */
/* PGMNBR(*HIGH) PGM(lib-name/ZSBMEXIT) + */
/* THDSAFE(*YES) MLTTHDACN(*RUN) + */
/* PGMDTA(*JOB 20 'SBMJOB QSYS ') + */
/* TEXT('Limit SBMJOB repetitions') */
/* */
/* You can use the Work with Registration Info (WRKREGINF) */
/* command, '8=Work with exit programs', and '4=Remove' to remove */
/* this registration. */
/* */
/* Implementation notes: */
/* This code keeps a data area (QTEMP/ZSBMEXIT) with the */
/* following information: */
/* */
/* Start Length Description */
/* 1 52 Descriptive eyecatcher */
/* 53 4 Count of SBMJOB commands done by this job. */
/* 57 8 Timestamp of beginning of interval */
/* 65 8 Timestamp of most recent SBMJOB */
/* */
/* If a single job tries to issue more than 100 SBMJOB commands, */
/* this exit program will stop that job. The count is reset */
/* if there is a significant gap (&TIMEGAP, 5 minutes) or if */
/* the overall rate is less than &SBMLIMIT (100) SBMJOB commands */
/* in &TIMELMT (3 hours). By working in UTC and using */
/* two different mechanisms to protect long-running jobs from */
/* exceeding the threshold, this program is less sensitive to */
/* clock changes that would produce a negative time interval. */
/* */
/* CHANGE ACTIVITY: */
/* Version Date Description */
/* --------- ----------- --------------------------------------- */
/* 1.0 2007-Oct-09 Prototype and demonstration */
/* */
/*********************************************************************/
PGM PARM(&INPARM)
/*********************************************************************/
/* Declares for input parameters. */
/* The SBMJOB command string is available, but not used by this */
/* program. */
/*********************************************************************/
DCL VAR(&INPARM) TYPE(*CHAR) LEN(32767)
/*********************************************************************/
/* Constants that define the policy being enforced. */
/* You can use a hex calculator to compute different time values */
/* and convert the first four bytes into a (base 10) integer. */
/* Close is good enough. It is possible to get the Convert Date and */
/* Time Format (QWCCVTDT) API to give you timestamps that you can */
/* use to produce an exact answer, but this program only looks at */
/* the first four bytes of the timestamps. */
/*********************************************************************/
DCL VAR(&SBMLIMIT) TYPE(*DEC) LEN(15 0) VALUE(100) /* +
The maximum number of SBMJOB commands allowed within +
a time interval. Adjust &CHSBMLMT, if necessary. */
DCL VAR(&CHSBMLMT) TYPE(*CHAR) LEN(3) /* Character view +
of &SBMLIMIT. Adjust LEN() if &SBMLIMIT > 999 */
DCL VAR(&TIMEGAP) TYPE(*DEC) LEN(15 0) VALUE(286) /* +
Comparison value for 5 minute interval. +
Full 8-byte TOD form is X'0000011E1A300000'. */
DCL VAR(&TIMELMT) TYPE(*DEC) LEN(15 0) VALUE(10299) /* +
Comparison value for 3 hour interval. +
Full 8-byte TOD form is X'0000283BAEC00000'. */
/*********************************************************************/
/* Variables used to access the information in the data area. */
/*********************************************************************/
DCL VAR(&DTAA) TYPE(*CHAR) LEN(2000) /* Copy of data area +
QTEMP/ZSBMEXIT */
DCL VAR(&INITCNT) TYPE(*CHAR) LEN(4) VALUE(X'00000001')
DCL VAR(&CHARCNT) TYPE(*CHAR) LEN(4) /* *CHAR view of count */
DCL VAR(&DECCNT) TYPE(*DEC) LEN(15 0) /* *DEC view of count */
DCL VAR(&STRTOD) TYPE(*CHAR) LEN(8) /* Starting time */
DCL VAR(&LSTTOD) TYPE(*CHAR) LEN(8) /* Last SBMJOB time */
/*********************************************************************/
/* Identification for the current job. This information can be used */
/* to provide different limits for different jobs. */
/*********************************************************************/
DCL VAR(&CJOBNAME) TYPE(*CHAR) LEN(10) /* Job name of +
the job that this command exit is running in. */
DCL VAR(&CJOBUSER) TYPE(*CHAR) LEN(10) /* Job user name of +
the job that this command exit is running in. */
DCL VAR(&CJOBNBR) TYPE(*CHAR) LEN(6) /* Job number of +
the job that this command exit is running in. */
DCL VAR(&CURUSER) TYPE(*CHAR) LEN(10) /* Current user name of +
the job that this command exit is running in. */
DCL VAR(&CJOBTYPE) TYPE(*CHAR) LEN(1) /* Job type of +
the job that this command exit is running in. */
/*********************************************************************/
/* Interface to the Convert Date and Time Format (QWCCVTDT) API */
/*********************************************************************/
DCL VAR(&INFMT) TYPE(*CHAR) LEN(10) VALUE('*CURRENT ')
DCL VAR(&INTOD) TYPE(*CHAR) LEN(8) VALUE(X'0000000000000000')
DCL VAR(&OUTFMT) TYPE(*CHAR) LEN(10) VALUE('*DTS ')
DCL VAR(&CURTOD) TYPE(*CHAR) LEN(8) VALUE(X'0000000000000000')
DCL VAR(&ERRCOD) TYPE(*CHAR) LEN(8) VALUE(X'0000000000000000')
DCL VAR(&INTZ) TYPE(*CHAR) LEN(10) VALUE('*UTC ')
DCL VAR(&OUTTZ) TYPE(*CHAR) LEN(10) VALUE('*UTC ')
DCL VAR(&TZINFO) TYPE(*CHAR) LEN(1)
DCL VAR(&LENTZI) TYPE(*CHAR) LEN(4) VALUE(X'00000000')
DCL VAR(&PRECIND) TYPE(*CHAR) LEN(1) VALUE('0')
/*********************************************************************/
/* Miscellaneous variables. */
/*********************************************************************/
DCL VAR(&CANRESET) TYPE(*CHAR) LEN(1) VALUE('N') /* Allowed to +
reset the times and count */
DCL VAR(&TIME1) TYPE(*DEC) LEN(15 0) /* For time calculation */
DCL VAR(&TIME2) TYPE(*DEC) LEN(15 0) /* For time calculation */
DCL VAR(&INTERVL) TYPE(*DEC) LEN(15 0) /* For time calculation */
DCL VAR(&MSGKEY) TYPE(*CHAR) LEN(4) /* Message key */
DCL VAR(&OPRRPLY) TYPE(*CHAR) LEN(1) /* Reply message */
/*********************************************************************/
/* Program-scoped error handling. */
/*********************************************************************/
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ENDOFPGM))
/*********************************************************************/
/* Find out where this exit program is running. */
/* For some cases, this program should not make any decisions. */
/*********************************************************************/
QSYS/RTVJOBA JOB(&CJOBNAME) USER(&CJOBUSER) NBR(&CJOBNBR) +
CURUSER(&CURUSER) TYPE(&CJOBTYPE) /* Retrieve info +
about the current job */
IF ( (&CJOBTYPE *EQ 'X') /* SCPF */ +
*OR (&CJOBTYPE *EQ 'S') /* System job */ +
*OR (&CJOBTYPE *EQ 'M') /* Subsystem monitor job */ +
) THEN(GOTO CMDLBL(ENDOFPGM)) /* Do not operate if invoked +
in system or subsystem jobs. */
/*********************************************************************/
/* When first invoked, create and initialize the data area. */
/* If two threads try this at the same time, one thread will receive */
/* an error and will continue by updating the data area. */
/*********************************************************************/
QSYS/CHKOBJ OBJ(QTEMP/ZSBMEXIT) OBJTYPE(*DTAARA)
MONMSG MSGID(CPF0000) EXEC(DO)
CHGVAR VAR(%SST(&DTAA 1 52)) VALUE('ZSBMEXIT DATA, +
QIBM_QCA_RTV_COMMAND FOR SBMJOB ') /* Eyecatcher */
CHGVAR VAR(%SST(&DTAA 53 4)) VALUE(&INITCNT) /* Initial count +
is 1. */
QSYS/CALL PGM(QSYS/QWCCVTDT) +
PARM(&INFMT &INTOD &OUTFMT &CURTOD &ERRCOD +
&INTZ &OUTTZ &TZINFO &LENTZI &PRECIND) /* Get +
current time in UTC. */
CHGVAR VAR(%SST(&DTAA 57 8)) VALUE(&CURTOD) /* Initial +
start-of-interval. */
CHGVAR VAR(%SST(&DTAA 65 8)) VALUE(&CURTOD) /* Initial +
time of last SBMJOB. */
QSYS/CRTDTAARA DTAARA(QTEMP/ZSBMEXIT) TYPE(*CHAR) LEN(2000) +
VALUE(&DTAA) /* Create and initialize */
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(UPDDTAA))
GOTO CMDLBL(ENDOFPGM) /* Finished, only one SBMJOB done. */
ENDDO
/*********************************************************************/
/* If the data area already exists, prepare to update the data area. */
/*********************************************************************/
UPDDTAA:
QSYS/ALCOBJ OBJ((QTEMP/ZSBMEXIT *DTAARA *EXCLRD)) +
WAIT(3600) SCOPE(*THREAD) /* Lock the data area */
MONMSG MSGID(CPF1002) EXEC(GOTO CMDLBL(UPDDTAA)) /* For timeout, +
try again. This may be needed to stop a thread from doing +
additional SBMJOB commands when another thread has already +
detected a problem. */
QSYS/CALL PGM(QSYS/QWCCVTDT) +
PARM(&INFMT &INTOD &OUTFMT &CURTOD &ERRCOD +
&INTZ &OUTTZ &TZINFO &LENTZI &PRECIND) /* Get +
current time in UTC. This is required to be greater than +
the values stored in the data area, so this time stamp +
must be obtained AFTER locking the data area. */
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(UNLKDTAA))
QSYS/RTVDTAARA DTAARA(QTEMP/ZSBMEXIT *ALL) RTNVAR(&DTAA)
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(UNLKDTAA))
CHGVAR VAR(&CHARCNT) VALUE(%SST(&DTAA 53 4)) /* *CHAR count */
CHGVAR VAR(&DECCNT) VALUE(%BIN(&CHARCNT 1 4)) /* *DEC count */
CHGVAR VAR(&STRTOD) VALUE(%SST(&DTAA 57 8)) /* Starting time */
CHGVAR VAR(&LSTTOD) VALUE(%SST(&DTAA 65 8)) /* Last SBMJOB time */
/*********************************************************************/
/* One way to handle the question of controlled-rate is to look for */
/* a gap that is large enough to be significant. */
/*********************************************************************/
IF COND(&CURTOD *GE &LSTTOD) THEN(DO) /* Alphabetic compare +
to ensure a positive time interval. */
CHGVAR VAR(&TIME1) VALUE(%BIN(&LSTTOD 1 4)) /* Use first half +
of 8-byte timestamp */
CHGVAR VAR(&TIME2) VALUE(%BIN(&CURTOD 1 4)) /* Use first half +
of 8-byte timestamp */
CHGVAR VAR(&INTERVL) VALUE(&TIME2 - &TIME1) /* Find time +
interval since last SBMJOB */
IF COND(&INTERVL *GT &TIMEGAP) +
THEN(CHGVAR VAR(&CANRESET) VALUE('Y')) /* Look for +
a 5 minute minute gap with no SBMJOB. If found, +
job is probably not doing an excessive number of +
SBMJOB commands, so set flag to reset the count. */
ENDDO
/*********************************************************************/
/* Another way to handle the question of controlled-rate is to check */
/* for a threshold number over a significant interval. In this case, */
/* we do not check the number of SBMJOB commands (just the interval) */
/* because exceeding the threshold is handled at the time we pass */
/* the threshold. */
/*********************************************************************/
IF COND(&CURTOD *GE &STRTOD) THEN(DO) /* Alphabetic compare +
to ensure a positive time interval. */
CHGVAR VAR(&TIME1) VALUE(%BIN(&STRTOD 1 4)) /* Use first half +
of 8-byte timestamp */
CHGVAR VAR(&TIME2) VALUE(%BIN(&CURTOD 1 4)) /* Use first half +
of 8-byte timestamp */
CHGVAR VAR(&INTERVL) VALUE(&TIME2 - &TIME1) /* Find time +
interval for all counted SBMJOB commands */
IF COND(&INTERVL *GT &TIMELMT) +
THEN(CHGVAR VAR(&CANRESET) VALUE('Y')) /* Look for +
a 3 hour sample size (that did not cause trouble). +
If found, job is probably not doing an excessive +
number of SBMJOB commands, so set flag to reset the count. */
ENDDO
/*********************************************************************/
/* If this job does not appear to be doing too many SBMJOB commands, */
/* we can reset the stored information so that long-running jobs */
/* do not exceed the threshold that would trigger corrective action. */
/*********************************************************************/
IF COND(&CANRESET *EQ 'Y') THEN(DO)
CHGVAR VAR(%SST(&DTAA 53 4)) VALUE(&INITCNT) /* Reset count +
to 1. */
CHGVAR VAR(%SST(&DTAA 57 8)) VALUE(&CURTOD) /* Reset +
start-of-interval. */
CHGVAR VAR(%SST(&DTAA 65 8)) VALUE(&CURTOD) /* Reset +
time of last SBMJOB. */
ENDDO
/*********************************************************************/
/* If we do not reset the count, we will increment the count. */
/*********************************************************************/
ELSE CMD(DO)
CHGVAR VAR(&DECCNT) VALUE(&DECCNT + 1) /* Increment count */
CHGVAR VAR(%BIN(&CHARCNT 1 4)) VALUE(&DECCNT) /* Convert to +
*CHAR view */
CHGVAR VAR(%SST(&DTAA 53 4)) VALUE(&CHARCNT) /* Update count +
in copy of data area. */
CHGVAR VAR(%SST(&DTAA 65 8)) VALUE(&CURTOD) /* Reset +
time of last SBMJOB. */
/*********************************************************************/
/* Check the count of SBMJOB commands against a threshold. */
/* Send inquiry message to operator, wait for a response. */
/* Need v5r4 or better to do ENDJOB JOB(*) OPTION(*IMMED) */
/* Probably best to DLYJOB DLY(99999) and allow dumps/etc. */
/* Could allow additional responses - for example, allow an */
/* unlimited number of additional SBMJOB commands from this job. */
/*********************************************************************/
IF COND(&DECCNT *GT &SBMLIMIT) THEN(DO)
CHGVAR VAR(&CHSBMLMT) VALUE(&SBMLIMIT) /* Convert the +
limit to a character string. */
MONMSG MSGID(CPF0000) EXEC(DO)
CHGVAR VAR(&CHSBMLMT) VALUE('NNNN') /* Use 'NNNN' if +
value was too large to convert. */
MONMSG MSGID(CPF0000)
ENDDO
QSYS/SNDPGMMSG MSG('Too many SBMJOB commands. Job' +
*BCAT &CJOBNBR *CAT '/' *CAT &CJOBUSER *TCAT '/' +
*CAT &CJOBNAME *BCAT 'appears to be looping.' +
*BCAT 'Use the ENDJOB command to end the looping' +
*BCAT 'job or reply ''I'' to allow an additional' +
*BCAT &CHSBMLMT *BCAT 'SBMJOB commands.' +
*BCAT 'All other replies result in DLYJOB.') +
TOMSGQ(*SYSOPR) MSGTYPE(*INQ) KEYVAR(&MSGKEY) /* Send +
an inquiry to QSYSOPR so that the operator can override +
the decision made by this exit program. */
MONMSG MSGID(CPF0000) EXEC(DLYJOB DLY(99998))
QSYS/RCVMSG MSGQ(*PGMQ) MSGTYPE(*RPY) MSGKEY(&MSGKEY) +
MSG(&OPRRPLY) WAIT(*MAX) RMV(*NO) /* Receive a reply. */
MONMSG MSGID(CPF0000) EXEC(DLYJOB DLY(99997))
IF COND( (&OPRRPLY *EQ 'I') +
*OR (&OPRRPLY *EQ 'i') ) THEN(DO) /* See if +
operator wishes to permit the job to continue (for +
another &SBMLIMIT number of jobs. */
CHGVAR VAR(%SST(&DTAA 53 4)) VALUE(&INITCNT) /* Reset count +
to 1. */
CHGVAR VAR(%SST(&DTAA 57 8)) VALUE(&CURTOD) /* Reset +
start-of-interval. */
CHGVAR VAR(%SST(&DTAA 65 8)) VALUE(&CURTOD) /* Reset +
time of last SBMJOB. */
ENDDO
ELSE CMD(DO)
DLYJOB DLY(99999) /* Stop this thread while holding the +
exclusive lock on the data area (thus stopping any +
other threads that try to do SBMJOB. */
ENDDO
ENDDO
ENDDO
/*********************************************************************/
/* Update the data area and unlock it. */
/*********************************************************************/
QSYS/CHGDTAARA DTAARA(QTEMP/ZSBMEXIT *ALL) VALUE(&DTAA) /* Update +
the data in the data area. */
MONMSG MSGID(CPF0000) /* On error, continue with unlock */
UNLKDTAA:
QSYS/DLCOBJ OBJ((QTEMP/ZSBMEXIT *DTAARA *EXCLRD)) +
SCOPE(*THREAD) /* Unlock the data area */
/*********************************************************************/
/* End of program */
/*********************************************************************/
ENDOFPGM:
ENDPGM

[{"Type":"MASTER","Line of Business":{"code":"LOB57","label":"Power"},"Business Unit":{"code":"BU058","label":"IBM Infrastructure w\/TPS"},"Product":{"code":"SWG60","label":"IBM i"},"Platform":[{"code":"PF012","label":"IBM i"}],"Version":"6.1.0"}]

Historical Number

520791508

Document Information

Modified date:
15 September 2020

UID

nas8N1013016