Exit Points for AS/400 Control Language Commands

Part 2 --- Auditing Command Usage in Real Time


By Dan Riehl

©2004-2009 Dan Riehl,  All Rights Reserved



Do you want to be alerted when someone tries to restore objects onto your system? Or maybe you want to know when someone starts up a TCP/IP server program, or changes a system value. In the past it was quite difficult to collect these events in real time, but with the capability to monitor for sensitive commands, this is now a trivial task.


In part one of this series the focus of the column was the exit points available for CL commands, and dealt quite specifically with the Change Command exit point. As noted in that article, there is another command exit point that may be used to monitor and audit commands as they are executing. This is the  Command Analyzer Retrieve exit point.


This new capability allows you to capture commands as they execute and do something with the information collected. Unlike the Command Analyzer Change exit point discussed last month, the Retrieve exit point does not allow you to send information back to the command analyzer. This limits the capabilities of this exit point to be a logging and auditing exit point only.


The example program supplied in this article is called whenever someone executes the Restore Object(RSTOBJ) command. It retrieves the command information and records the RSTOBJ command string and other useful information in the system's QHST history log. You could certainly do other things with the command string data, but the purpose of this simple example is to illustrate the process of creating and registering an exit program.



The Command Analyzer Retrieve exit point



Each IBM supplied exit point has an assigned name and an exit point interface. The exit point interface is a list of parameters the command analyzer exchanges with your exit program. The name of the exit point for the command analyzer retrieve command exit point is QIBM_QCA_RTV_COMMAND. This exit point occurs after the command analyzer has done much of its work, but before control is passed to the Command Processing Program(CPP).


Since the exit program is called before the CPP, the exit program cannot predict whether the command will complete normally or abnormally. It only knows that the command is being attempted, with no knowledge of, or potential impact on, the outcome of the command processing program(i.e. whether the operation will fail or succeed).


The name of the exit point interface for this exit point is named RTVC0100. This exit point interface is similar to the CHGC0100 interface discussed in last month's issue of iSeries. The exit program is not passed the RTVC0100 interface in separate parameters, but as one big lump of data. Your program needs to parse out the individual data elements of the RTVC0100 parameter which is illustrated in Figure 1.

Figure 1 The Retrieve Command Exit Point Interface











Parameter 1 














Type and length

Exit Point Name


Char  20

Exit Point Interface Format Name


Char    8

Command Name

Name of the command for which the exit program is registered

Char  10

Command Library Name

Name of the library where the Command resides

Char  10

Reserved for future use


Char    4

Offset to Original Command String

The offset to the beginning of the original Command String

Binary (4)

Length of Original Command String

The length of the original Command String

Binary (4)

Offset to Replacement Command String

The offset to the beginning of the replacement Command String if the command was changed by the Change command exit point.

Otherwise, the value is 0.

Binary (4)

Length of Replacement Command String

The length of the replacement Command String if the command was changed by the Change command exit point.

Otherwise, the value is 0.

Binary (4)

Original Command String

The original Command String before any changes

Char   *   (Variable Length)

Replacement Command String

The Command String that you want to run in place of the original command

Char   *   (Variable Length)




As with all registered exit points, you can add your own exit point program using the ADDEXITPGM(Add Exit program) command. In the case of the Retrieve Command exit point, you need to specify the name of the command, and the library in which the command resides in the PGMDTA parameter as in the following example. For the Retrieve command exit point, you can register up to 10 programs per command, incrementing the PGMNBR parameter for each consecutive program. This allows you to run a sequence of programs, or allows you to run a third party, or vendor supplied, exit program, and still run your own program.



           FORMAT(RTVC0100)                +

           PGMNBR(1)                       +

           PGM(MYLIB/RSTOBJEXIT)           +

           TEXT('Exit program for RSTOBJ') +

           PGMDTA(*JOB 20 'RSTOBJ    QSYS')


The PGMDTA parameter must be specified with a data length of 20. You must specify the command name in the first 10 positions, and the command library in the next 10 positions, as in the example. The command analyzer is directed to use the exit program RSTOBJEXIT in library MYLIB whenever a request is made to run the RSTOBJ command found in the QSYS library.


There are certain commands for which exit point programs may not be defined. These include CL compiler directing statements, commands found in libraries QSYS38 and QUSER38, and a few others.  Following is the list of commands for which exit programs may not be registered.





























Figure 2 contains a sample exit program that may be used for the RSTOBJ command. The exit program sends a message to a message queue each time the RSTOBJ command is used. It also sends a message to the QHST history log. The processing performed in this program is quite simple, but it does provide a template that you can use to create your own program.


You should note that this exit program is registered to only monitor for the usage of the RSTOBJ command. However, you could use the same program to monitor for all RSTxxx commands by adding the same exit point program(ADDEXITPGM)  for each RSTxxx command(e.g. RSTLIB, RST, RSTLICPGM, etc).



Figure 2  Command Exit Point Program Example



 /* Program Name: RSTOBJEXIT                                          */                                              

 /* Purpose: This is the exit program for the Command RSTOBJ.         */                                               

 /*          Exit Point IS QIBM_QCA_RTV_COMMAND                       */                                              

 /*          Parameter format is RTVC0100.                            */                                               

 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */                                              

 /* Copyright© 2002 Dan Riehl, The 400 School, Inc.                   */


PGM    PARM(&ExitInfo)                                                  


DCL  &Exitinfo    *Char 2000   /* RTVC0100 Interface Data */           


DCL  &User        *Char   10                                           

DCL  &JobName     *Char   10                                           


DCL  &OffsetDec   *Dec  (7 0)                                           

DCL  &CmdLenDec   *Dec  (7 0)                                          


/* Exit point interface RTVC0100 for QIBM_CA_RTV_COMMAND exit point */ 

DCL  &ExitPoint   *CHAR   20   /* Exit Point name              */      

DCL  &ExitFormat  *CHAR    8   /* Exit Point Format            */      


DCL  &CmdName     *CHAR   10   /* Command name being executed  */      

DCL  &CmdLib      *CHAR   10   /* Command Library              */      

DCL  &OffsetO     *CHAR    4   /* Offset to command string     */


DCL  &CmdLengthO  *CHAR    4   /* Command string length        */      

DCL  &OffsetR     *CHAR    4   /* Offset to command string     */      

DCL  &CmdLengthR  *CHAR    4   /* Command string length        */      


DCL  &CmdString   *CHAR 2000   /* Command String               */      


/* Error handling variables                                    */      

DCL  &MsgID       *CHAR    7                                           

DCL  &MsgFile     *CHAR   10                                           

DCL  &MsgFLib     *CHAR   10                                           

DCL  &MsgData     *CHAR  512                                           


MONMSG   CPF0000    EXEC(GOTO ERROR)                                    


/* Parse out the exit info data                     */                 

     CHGVAR  &ExitPoint  %SST(&ExitInfo   1  20)                       

     CHGVAR  &ExitFormat %SST(&ExitInfo  21   8)

     CHGVAR  &CmdName    %SST(&ExitInfo  29  10)                       

     CHGVAR  &CmdLib     %SST(&ExitInfo  39  10)                       


     CHGVAR  &OffsetO    %SST(&ExitInfo  53   4)                       

     CHGVAR  &CmdLengthO %SST(&ExitInfo  57   4)                       

     CHGVAR  &OffsetR    %SST(&ExitInfo  61   4)                       

     CHGVAR  &CmdLengthR %SST(&ExitInfo  65   4)                       


/* Check to see if command was replaced by Change Command Exit */      

     IF   (%BIN(&OffsetR) = 0)  DO  /* Command not replaced */         

        CHGVAR  &CmdLenDec  %BIN(&CmdlengthO)                          


        CHGVAR  &OffsetDec  (%BIN(&OffsetO) + 1) /* Set offset */      


     ELSE   DO                      /* Command was replaced  */        

        CHGVAR  &CmdLenDec  %BIN(&CmdlengthR)                          

        CHGVAR  &OffsetDec  (%BIN(&OffsetR) + 1) /* Set offset */



     CHGVAR  &CmdString  %SST(&ExitInfo &OffsetDec &CmdLenDec)         


     RTVJOBA    JOB(&JobName) USER(&User)                              


     SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Restore +         


                      operation in progress from user' *BCAT +        

                      &User *BCAT 'from job' *BCAT &JobName +         

                      *TCAT '. The command executed is:' *BCAT +      

                      &CmdString) TOMSGQ(RESTORE QHST)                  


ENDIT: RETURN /* Normal end of Program */                               


/* Error handling */

ERROR: RCVMSG     MSGTYPE(*LAST) MSGDTA(&MsgData) +                    

                  MSGID(&MsgID) MSGF(&MsgFile)    +                     


       MONMSG CPF0000 EXEC(RETURN)                                     


       SNDPGMMSG  MSGID(&MsgID) MSGF(&MSGFLIB/&MsgFile) +              

                  MSGDTA(&MsgData) MSGTYPE(*ESCAPE)                    

       MONMSG CPF0000 EXEC(RETURN)                                     





Examining the RSTOBJ command Exit Program


The exit program receives only one parameter. As mentioned, the parameter contains the RTVC0100 data structure that must be parsed into its individual fields. At A in Figure 2, the fields are defined that will be used to store the RTVC0100 format data once it is parsed. At B , the RTVC0100 format is parsed into it's component parts.


At C , a determination is made as to whether the original command was replaced by a Command Analyzer Change Exit program. If it was replaced, then the command string that is being executed is found at the offset stored in the variable &OFFSETR, otherwise the offset to the command is found in the variable &OFFSETO. As you can see, the determination is made by checking the replacement offset(&OFFSETR) for a value of 0(zero). If the replacement offset is 0, the command was not replaced by a Change Command Exit program.



At D in Figure 2, the User and Job name are retrieved from the job running the RSTOBJ command. This information is collected so that it can be included in the informational message that is sent, also at D . The message is sent to two message queues; RESTORE and QHST. You can replace these in the code to suit your individual requirements.


Figure 3 shows the resulting Display Messages screen when after a few RSTOBJ commands are executed. The same information is written to the QHST history log to assist in your auditing for the use of the RSTOBJ command.



Don't forget to Exit


OS/400 has added numerous exit points over the last several releases. As you become familiar with command exit points and the customized processing you can perform, you may want to delve further into more of the exit points. There are exit points for all kinds of things, like Save and Restore functions, User Profile maintenance, and network access control. The toolbox is booming! You can use exit point programs to make your system administration tasks a lot easier than they might have been in the past…… So don't forget to Exit!



Figure 3… The resulting Display Messages screen



                                  Display Messages                               

                                                       System:   MYAS400      

 Queue . . . . . :   RESTORE                   Program . . . . :   *DSPMSG       

   Library . . . :     QUSRSYS                    Library . . . :                 

 Severity  . . . :   00                        Delivery  . . . :   *BREAK        


 Type reply (if required), press Enter.                                        

     Restore operation in progress from user TESTUSER from job QPADEV0013. The      

       command executed is: RSTOBJ OBJ(QXXXXX) SAVLIB(MYLIB) DEV(*SAVF)          


     Restore operation in progress from user ANYUSER from job QPADEV0015. The      

       command executed is: RSTOBJ OBJ(QCLSRC) SAVLIB(YOURLIB) DEV(*SAVF)          


     Restore operation in progress from user QSYSOPR from job QPADEV0010. The       

       command executed is: RSTOBJ OBJ(MYPROGRAM) SAVLIB(QGPL) DEV(*SAVF)          



 F3=Exit              F11=Remove a message                 F12=Cancel          

 F13=Remove all       F16=Remove all except unanswered     F24=More keys



©2004-2009 Dan Riehl      All Rights Reserved