First in a series of 2 articles 


A Change in Fundamentals

Exit Points for i/OS AS/400 Control Language Commands


By Dan Riehl

©2004-2009 Dan Riehl, All Rights Reserved 


Ever since the first version of the CPF operating system was shipped with the System/38, system administrators have grappled with the problems inherent in customizing the IBM supplied Control Language command set for the System/38 and later for the AS/400 and System i. One major source of irritation was that when a new release of the operating system was installed, any changes made directly to the IBM supplied commands in the QSYS library were obliterated. So, for each new operating system release, the system administrator would have to re-apply all of the command changes.


Over time, we all got a bit smarter and figured out that we could make a copy of the IBM supplied command in our own library, and modify the copy. By strategically placing our own library, perhaps named ALTQSYS, ahead of QSYS in the system portion of the library list, we could effectively override the IBM supplied version. So when a new release of the operating system was installed, it would replace the IBM supplied version of the command in the QSYS library, but leave our customized version in our ALTQSYS intact.   


Since Control Language commands do not often undergo fundamental changes that impact the majority of us, the ALTQSYS copy of a command created at a previous release of the operating system normally functioned correctly. However, there were certainly exceptions, in which the ALTQSYS version of the commands would fail after installing a new release of the operating system.  Still not a perfect solution, but normally manageable.


If you examine the system portion of the library list on your System i using the command DSPSYSVAL QSYSLIBL, you will most likely see an ALTQSYS library ahead of QSYS. You will many times also see the name of a vendor supplied library. Some System i software vendors seem to make a habit of placing their library on your system library list ahead of QSYS in order to override the processing of the IBM supplied command set. This is particularly true in the case of software utility and tool vendors.


Most of us have accepted the fact that we need libraries above QSYS in our library list to support our customized versions and vendor supplied versions of CL commands and other objects. But there is an inherent danger in this scheme. Any library existing above QSYS on the system library list can potentially open the System i to a severe security exposure; the Trojan Horse. If a user can create or change objects in a library above QSYS, or in QSYS for that matter, that user can introduce their own program or command that can do major damage to your system.


In an attempt to eliminate the need for libraries above QSYS, IBM, in OS/400 V4R5 has enabled a facility that may make it possible to remove the libraries above QSYS from the system portion of the library list. I say may, because the support provided as of OS/400 V4R5 deals specifically with CL command customization, but does not account for other customized objects that may need to appear before QSYS.    


The enhanced support as of V4R5 is the CL command analyzer exit points. These exit points provide the ability to change the processing done by a Control Language command at run-time.



CL Command Exit Point Programs


CL has always had validity checking programs and command processing programs. Over the years IBM has added prompt override programs, prompt choice programs and prompt control programs. V4R5 added the support for command analyzer exit programs that further enhance CL's capabilities, and more importantly, can enhance and simplify your system administration, configuration and security.


In V4R5 two new exit points were added to the OS/400 registration facility (WRKREGINF) that allow you to control the processing of CL commands. These two exit points are the Command Analyzer Change exit point, and the Command Analyzer Retrieve exit point.  In this article, the focus will be on the command analyzer change exit point, the retrieve exit point will be covered in a later issue of iSeries.



The Command Analyzer Change exit point


This exit point allows you to intercept a CL command before it is executed, and perform your own custom processing, which might simply be changing some parameter values, or might even consist of running a totally different command in a totally different library.


As an example, if the SBMJOB command is run, you may use the exit point program to tell the command analyzer to run the SBMJOB command found in another library, instead of the one found first on the job's library list.  It is this capability that allows us to possibly remove our libraries from the system portion of the library list.


Here's another example that points out other possibilities and is the theme of the sample exit program provided in Figure 2. If the command WRKJOB is executed by someone that has a user class of *USER, you can, in the exit program, tell the command analyzer to run the DSPJOB command instead.


This exit point opens up a whole new world of possibilities for software vendors and AS/400e system administrators.     


When I first heard of this new capability, my assumption was that I would be able to add an exit program to a CL command using the CHGCMD(Change Command) CL command. After all, this was the way you change the command processing program and the validity checker. Upon my first perusal of the documentation for these new exit points, it became clear to me that my assumption was incorrect. Over the last several releases of OS/400, IBM has really embraced the OS/400 exit point registration facility. The exit point programs are added to a command through the Registration facility, WRKREGINF(Work with  Registration Information) and the ADDEXITPGM( Add Exit Program) command.


Exit Points and Exit Programs---Revisted


An exit point is a point in an application at which it can optionally call an external program to perform customized processing. The OS/400 command analyzer as of V4R5 includes exit points where you can hook your own program into the command processing logic. To identify your exit program, and the command for which it will be called, you use the ADDEXITPGM (Add Exit Program) command, specifying the command name and library for which this exit program will be used, and the name and library of your exit program that will process the particular command. When a request is made to the command analyzer to run a command, it checks the exit point registry to see if you have an exit program assigned to the requested command. If you do, it calls your exit program, passing parameters that contain the information about the requested command. Your exit program then processes that information, and takes the appropriate action.


Each exit point has an assigned name and an exit point interface. The exit point interface is a list of input and output parameters the command analyzer exchanges with your exit program. The name of the exit point for the command analyzer change command exit point is QIBM_QCA_CHG_COMMAND. This exit point occurs before the command analyzer prompts the command, and before the validity checking program is called. The name of the exit point interface for this exit point is named CHGC0100. This exit point interface is different from most in that your exit program is not passed the CHGC0100 interface in separate parameters, but as one big lump of data. Your program needs to parse out the individual data elements of the CHGC0100 parameter. In addition to the CHGC0100 parameter, your exit program is passed two additional parameters as shown in Figure 1.


Figure 1 The Change Command Exit Point Interface











Parameter 1 












Input or Output



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

Change Allowed  Indicator


0= No Change allowed

1= Change Allowed

Char    1

Command Prompt Indicator


0= Command has not been prompted

1= Command has been prompted

Char    1

Reserved for future use



Char    2

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)

Original Command String


The original Command String before any changes

Char     * (Variable Length)

Parameter 2

Replacement Command String


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

Char     * (Variable Length)

Parameter 3

Length of Replacement Command


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

Binary (4)



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 Change 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.



           FORMAT(CHGC0100)                +

           PGMNBR(1)                       +

           PGM(MYLIB/WRKJOBEXIT)           +

           TEXT('Exit program for WRKJOB') +

           PGMDTA(*JOB 20 'WRKJOB    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 WRKJOBEXIT in library MYLIB whenever a request is made to run the WRKJOB 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 WRKJOB command. The exit program checks to see if the user running the WRKJOB command has a user class of *USER. If so, the program tells the command analyzer to run the DSPJOB command instead. While the functionality of the example is quite simple, it does address the difficult parts of writing your own exit program.



Figure 2  Command Exit Point Program Example


 /* Program Name: WRKJOBEXIT                                          */                                              

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

 /*          Exit Point IS QIBM_QCA_CHG_COMMAND                       */                                              

 /*          Parameter format for parm1 is CHGC0100.                  */                                              

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

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


  PGM    PARM(&ExitInfo  &NewString   &Newlength)            A    


       DCL  &Class       *CHAR   10                                                                                 

       DCL  &OffsetDec   *DEC  (7 0)

       DCL  &CmdLenDec   *DEC  (7 0)


       DCL  &ExitInfo    *CHAR 2000   /* CHGC0100 interface data      */

/* Exit point interface CHGC0100 for QIBM_CA_CHG_COMMAND exit point   */

/* Input parameters                                                   */

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

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

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

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

       DCL  &Change      *CHAR    1   /* Change allowed? 1=yes 0=no   */B

       DCL  &Prompt      *CHAR    1   /* Prompt requested? 1=yes 0=no */B 
       DCL  &Filler      *CHAR    2   /* Reserved by IBM              */B

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

       DCL  &CmdLength   *CHAR    4   /* Command string length        */B

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

/* Output Parameters                                                  */

       DCL  &NewString   *CHAR 2000   /* Replace with this command    */

       DCL  &NewLength   *CHAR    4   /* Length of new command        */

                                      /* 0 = no new command           */ 


/* Error handling variables                                           */

       DCL  &MsgID       *CHAR    7

       DCL  &MsgFile     *CHAR   10

       DCL  &MsgFLib     *CHAR   10

       DCL  &MsgData     *CHAR  512




/* Parse out the exit info data                                       */

       CHGVAR  &ExitPoint  %SST(&ExitInfo   1  20)             C

       CHGVAR  &ExitFormat %SST(&ExitInfo  21   8)             C

       CHGVAR  &CmdName    %SST(&ExitInfo  29  10)             C

       CHGVAR  &CmdLib     %SST(&ExitInfo  39  10)             C

       CHGVAR  &Change     %SST(&ExitInfo  49   1)             C

       CHGVAR  &Prompt     %SST(&ExitInfo  50   1)             C

       CHGVAR  &Filler     %SST(&ExitInfo  51   2)             C

       CHGVAR  &Offset     %SST(&ExitInfo  53   4)             C

       CHGVAR  &CmdLength  %SST(&ExitInfo  57   4)             C 

       CHGVAR  &CmdLenDec  %BIN(&Cmdlength)                   

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

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


       IF  (&Change = '0')  GOTO ENDIT    /* If Change is not allowed */  D

       RTVUSRPRF  USRCLS(&Class)


       IF  (&Class = '*USER')  DO       /* If a user class of *USER */

          CHGVAR   &NewString    'QSYS/DSPJOB'                            E

          CHGVAR   %BIN(&Newlength)    11               



ENDIT: RETURN /* Normal end of Program */


/* Error handling */


                  MSGID(&MsgID) MSGF(&MsgFile)    +                                                                   




                  MSGDTA(&MsgData) MSGTYPE(*ESCAPE)                                                                





Examining the WRKJOB command Exit Program


At letter A in Figure 2, you can see that the exit programs receives the three parameters that are discussed in Figure 1. As mentioned, the first parameter contains a CHGC0100 data structure that must be parsed into it's individual fields. At B, the fields are defined that will be used to store the CHGC0100 format data once it is parsed. At C , the CHGC0100 format is parsed into it's component parts. In this example, some extra work is done to parse the command string, and determine the offset to the command. While the sample doesn't need or use this information, the code is provided for instructional purposes so that you can use this section as a template to write your own exit programs.


At D in Figure 2, the change indicator is queried to determine if the command is changeable by the exit program. If a command is library qualified when it is passed to the command analyzer, as in the command QSYS/WRKJOB, it is not changeable by the exit program, and the change indicator will have a value of '0'. This is by design. There are a few other occasions when a command cannot be changed, these consist of commands that:


<![if !supportLists]>·         <![endif]>Have a parameter defined with RTNVAL(*YES), as in all CL retrieve (RTVxxxx) commands

<![if !supportLists]>·         <![endif]>Have parameters defined with DSPINPUT(*NO) or DSPINPUT(*PROMPT)

<![if !supportLists]>·         <![endif]>Are running in a System State program



In V4R5, IBM introduced the new qualifier value of *SYSTEM. The purpose of this new qualifier is to allow a sort of "non qualified" qualified reference to an object. For example, if a command is specified as   *SYSTEM/WRKJOB  the WRKJOB command in the QSYS library will be used. The *SYSTEM qualifier means to get the object from QSYS, while not explicitly identifying QSYS. IF you want to qualify commands in your applications, and still want to use exit programs to change the commands, you can use the *SYSTEM qualifier, which, under the covers will actually be a qualification to library QSYS.   


At E in Figure 2, the replacement command is set to QSYS/DSPJOB if the user running the WRKJOB command has a user class of *USER. The exit program then ends.


You may notice that CL variables used in the exit program to store command strings are 2000 bytes long. Control Language character variables cannot be longer than 2000 bytes. While 2000 bytes is certainly sufficient to hold the WRKJOB command string in this example, the actual length of a command string that can be processed through the exit point interface is 32,000 bytes. If you are writing an exit program for a command that can be longer than 2000 bytes, you'll need to use a language other than CL.


The sample program is written in Control Language in an attempt to make the exit program simple to read and understand. Exit programs do not need to be written in any particular language, and do not need to be written for ILE, but certainly can be.


The Exit Point


Hopefully this walk through and example code will be helpful to you. This new capability to control commands at run-time opens whole realms of possibilities in application development and system administration. Try this out on a few commands that you want to customize. You may find that you can remove a library or two that sit above QSYS on your system library list, and that would certainly be worth the time and effort.


For more information on this exit point check the iSeries Information Center documentation at:


Using the Replace Command (QCARPLCM) API


In V4R5, IBM has supplied an exit point program for use with the Command Analyzer Change Exit point. If you specify this program, QCARPLCM, as the exit point program for a command, a useful function is performed.  If the command was originally qualified with *SYSTEM or *NLVLIBL, the exit program will replace the qualifier with *LIBL. Here's an example of how to add QCARPLCM as the exit program for the RSTOBJ(Restore Object) command in QSYS.



           FORMAT(CHGC0100)                +

           PGMNBR(1)                       +

           PGM(QSYS/QCARPLCM)              +

           TEXT('Exit program for RSTOBJ') +

           PGMDTA(*JOB 20 'RSTOBJ    QSYS')


©2004-2009 Dan Riehl      All Rights Reserved