The API Corner: Running CL Commands from RPG

APIs
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Use the QCMDEXC, system, and QCAPCMD APIs to run CL commands from within your RPG application.

 

In last month's column, "Do I Really Need to Call a CL Program to Perform This Function?," the QCMDEXC, system, and QCAPCMD APIs were introduced. All three of these APIs are available to enable the running of CL commands from an application program. Today, we'll look at what is required to implement these APIs within your application program as well as some of the considerations that exist with each of the APIs.

What We Hope to Accomplish by Running CL from an RPG Program

To initially demonstrate the three APIs, we'll use a rather simple scenario: clearing a physical file member in preparation of further processing (this further processing will not be shown but could be along the lines of loading a new batch of records). The name of the file is SOMEFILE, and the name of the member is the user profile name associated with the job running the program. This initial scenario will either successfully clear the member and continue processing, or return an escape message to the caller of the program. Two different user escape messages are defined because of the characteristics of the APIs we will be using. To create message file OURMSGS, along with escape messages ESC0001 and ESC0002, use the following commands.

 

CRTMSGF MSGF(OURMSGS)

ADDMSGD MSGID(ESC0001) MSGF(OURMSGS) +

   MSG('Unexpected error encountered. Examine job log for further information.')

ADDMSGD MSGID(ESC0002) MSGF(OURMSGS) +

   MSG('Unexpected error &1 encountered .') FMT((*CHAR 7))

 

In a future scenario demonstrating error recovery using these APIs, we'll add checks for anticipated errors. These checks will include the following:

  • If the clear fails due to the member not being found, the program will add the member and continue processing.
  • If the clear fails due to the file not being found, the program will create the file and member and then continue processing
  • If any other error is encountered, the program will end with one of the escape messages previously defined.

Using the Execute Command (QCMDEXC) API

Using the Execute Command (QCMDEXC) API, documented here, the following program implements the initial scenario described above.

 

h dftactgrp(*no)                                               

                                                               

dRunCmd           pr                  extpgm('QCMDEXC')        

d Cmd                          512    const options(*varsize)  

d LenCmd                        15p 5 const                    

d IGC                            3    const options(*nopass)   

                                                               

dSndMsg           pr                  extpgm('QSYS/QMHSNDPM')  

d MsgID                          7    const                    

d QualMsgF                      20    const                    

d MsgDta                     65535    const options(*varsize)  

d LenMsgDta                     10i 0 const                    

d MsgType                       10    const                    

d CSE                        65535    const options(*varsize)  

d CSECtr                        10i 0 const                    

d MsgKey                         4                             

d QUSEC                               likeds(QUSEC)          

d LenCSE                        10i 0 const options(*nopass) 

d CSEQual                       20    const options(*nopass) 

d DSPWaitTime                   10i 0 const options(*nopass) 

d CSEType                       10    const options(*nopass) 

d CCSID                         10i 0 const options(*nopass) 

                                                             

 /copy qsysinc/qrpglesrc,qusec                               

                                                             

dPSDS            sds           429    qualified              

d JobUsr                254    263                           

                                                              

dCmd              s            512                           

dMsgKey           s              4                           

                                                             

 /free                                                        

                                                             

  QUSBPRV = 0;                                                 

                                                               

  monitor;                                                      

     Cmd = 'CLRPFM FILE(SOMEFILE) MBR(' + PSDS.JobUsr + ')';   

     RunCmd(Cmd :%len(%trim(Cmd)));                            

  on-error;                                                    

     SndMsg('ESC0001' :'OURMSGS   *LIBL' :' ' :0               

            :'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);          

  endmon;                                                      

                                                               

  // Do further processing                                     

                                                               

  *inlr = *on;                                                 

  return;                                                      

                                                                

 /end-free                                                     

 

The program source, named CMDEXC0, starts by prototyping the QCMDEXC API along with the Send Program Message (QMHSNDPM) API. If you are not familiar with the QMHSNDPM API, you may want to review the article "Inform Users of Problems by Sending Error Messages from Application Programs." Related to the use of QMHSNDPM, the program later also copies the QSYSINC QRPGLESRC include QUSEC, defines the variable MsgKey, and sets the Bytes provided field of the QUSEC error code structure to zero. As the name of the member to be cleared is the same as the name of the user running the job, the program also defines the Program Status Data Structure (PSDS) and sub-field JobUsr (the job user profile name). This is followed by the definition for the Cmd variable. The Cmd variable will be used to hold the CL command to be run.

 

Actually running the command is very simple. The program starts a monitor group to detect if any errors are encountered (as mentioned in the previous article, "Do I Really Need to Call a CL Program to Perform This Function?," QCMDEXC returns errors as escape messages), sets the Cmd variable to the CL command string we want to run (CLRPFM with the MBR keyword set to the job user profile name PSDS.JobUsr), and then calls the QCMDEXC API (prototyped as function RunCmd). When calling QCMDEXC, the second parameter is set to the blank-trimmed length of the command string to run. This trimming of trailing blanks is not actually necessary (we could have just as easily used %size(Cmd) because the API tolerates trailing blanks), but is done as a personal preference.

 

If the CLRPFM command runs without error, the program resumes execution following the associated endmon statement. Note that, if you have a file named SOMEFILE in your library list, this demonstration program (and those that follow) will clear a member of that file named the same as the user profile you signed on with. To avoid inadvertently clearing a production file on your system if you do have a production file by this name, you will want to change the CLRPFM command string to reference another file name or ensure that the library containing SOMEFILE is not in your library list.

 

If any error is encountered when running the CLRPFM command, the associated on-error block is run. The on-error block sends the escape message ESC0001, which then ends the program. ESC0001 is used as QCMDEXC returns errors as exceptions, and while the monitor group handles the exception, the exception message can still be found in the job log.

 

To compile and run the program, you can use these commands:

 

CRTBNDRPG PGM(CMDEXC0)

CALL PGM(CMDEXC0)

 

If the file SOMEFILE is not currently in your jobs library list, you will receive the escape message ESC0001 and in your job log find CPF3142, File SOMEFILE in library *LIBL not found. If the file SOMEFILE does exist but does not contain a member with the name of the user profile you signed on with, you will receive the escape message ESC0001 and in your job log find CPF3141, Member XXXXXX not found, where XXXXXX is the name of your user profile. If the file SOMEFILE does exist and contains a member with the name of the user profile you signed on with, your job log will contain CPC3101, Member XXXXXX file SOMEFILE in YYYYYY cleared, where YYYYYY is the name of the library containing file SOMEFILE.

 

It's that easy. Now let's look at what's required to clear the member using the system API, which is documented in Chapter 2 here.

 

Using the Execute a Command (System) API

h dftactgrp(*no) bnddir('QC2LE')                                 

                                                                 

dRunCmd           pr            10i 0 extproc('system')          

d Cmd                             *   value options(*string)     

                                                                 

dSndMsg           pr                  extpgm('QSYS/QMHSNDPM')    

d MsgID                          7    const                      

d QualMsgF                      20    const                      

d MsgDta                     65535    const options(*varsize)    

d LenMsgDta                     10i 0 const                      

d MsgType                       10    const                      

d CSE                        65535    const options(*varsize)    

d CSECtr                        10i 0 const                      

d MsgKey                         4                               

d QUSEC                               likeds(QUSEC)              

d LenCSE                        10i 0 const options(*nopass)     

d CSEQual                       20    const options(*nopass)  

d DSPWaitTime                   10i 0 const options(*nopass)  

d CSEType                       10    const options(*nopass)  

d CCSID                         10i 0 const options(*nopass)  

                                                              

 /copy qsysinc/qrpglesrc,qusec                                

                                                               

dPSDS            sds           429    qualified               

d JobUsr                254    263                            

                                                              

dCmd              s            512                            

dMsgID            s              7    import('_EXCP_MSGID')   

dMsgKey           s              4                            

                                                              

 /free                                                         

                                                              

  QUSBPRV = 0;                                                 

                                                               

  Cmd = 'CLRPFM FILE(SOMEFILE) MBR(' + PSDS.JobUsr + ')';      

  if RunCmd(Cmd) = 1;                                          

     SndMsg('ESC0002' :'OURMSGS   *LIBL' :MsgID :%size(MsgID)  

            :'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);          

  endif;                                                       

                                                               

  // Do further processing                                     

                                                               

  *inlr = *on;                                                 

  return;                                                      

                                                               

 /end-free                                                      

 

The program source, named SYSTEM0, is initially similar to the earlier program CMDEXC0 in terms of providing prototypes for the called APIs and declaring variables. There are two differences:

  • The specification bnddir('QC2LE') in the H-spec—This addition is necessary if you will be creating the sample program and your System i is at V5R4 or earlier.
  • The new variable, MsgID—MsgID is used to import the global variable _EXCP_MSGID, which is set by the system API if the CL command being run encounters an error.  _EXCP_MSGID reflects the exception message ID encountered.

The actual running of the command, and related error detection, however, is quite different, though still easy to implement. Rather than the two parameters (command to run and length of the command string) of QCMDEXC, the system API accepts one parameter: the command to run. The length of the command is determined by a null byte and implemented by the options(*string) specification of the prototype. Error detection is also different in that, rather than returning errors as exception messages, the system API indicates that an error has been encountered by using an integer return value of positive 1.

 

Due to no exceptions being returned by the API, there is no need for the program to define a monitor group. After setting the proper command string value to the Cmd variable, the program calls the API and checks the return value of the API for positive 1. If the return value is not 1, the program resumes running after the ENDIF statement. If the return value is 1, the program sends the escape message ESC0002, which then ends the program.

 

To compile and run the program, you can use these commands:

 

CRTBNDRPG PGM(SYSTEM0)

CALL PGM(SYSTEM0)

 

If the file SOMEFILE is not currently in your jobs library list, you will receive the escape message ESC0002 with message replacement text of 'CPF3142' (file not found). If the file SOMEFILE does exist but does not contain a member with the name of the user profile you signed on with, you will receive the escape message ESC0002 with message replacement text of 'CPF3141' (member not found). If the file SOMEFILE does exist and contains a member with the name of the user profile you signed on with, your job log will contain message CPC3101, Member XXXXXX file SOMEFILE in YYYYYY cleared, where XXXXXX is the name of your user profile and YYYYYY is the name of the library containing file SOMEFILE.

 

Due to the system API not using exception messages to indicate failures, you will not find the CPF3142 and CPF3141 error messages in your job log as you did with CMDEXC0. And as ESC0001 points you to the job log, the SYSTEM0 demonstration program uses message ESC0002 (which does not refer you to the job log as there's little that's more aggravating than being told to look "over there" and finding nothing there). For this same reason (the lack of information in the job log), SYSTEM0 also sends the CPF exception message ID as replacement text in message ESC0002. For our demonstration program, with only one file being used, just having the CPF message ID should be sufficient for problem determination. A more complex application may need to use additional replacement text variables—for instance, to clearly identify the file being used if multiple files are involved.

 

Note that you do, however, find this completion message in the job log: CPC3101, Member XXXXX file SOMEFILE in YYYYYY cleared. With the system API completion messages, informational messages and the like will be available. It's just the actual escape message that is removed from the job log.

 

Now let's look at what's required to clear the member using the Process Commands (QCAPCMD) API, which is documented here.

Using the Process Commands (QCAPCMD) API with Escape Messages

h dftactgrp(*no)                                                 

                                                                 

dRunCmd           pr                  extpgm('QCAPCMD')          

d SourceCmd                  65535    const options(*varsize)    

d LenSrcCmd                     10i 0 const                      

d CtlBlk                     65535    const options(*varsize)    

d LenCtlBlk                     10i 0 const                      

d CtlBlkFmt                      8    const                      

d ChgCmd                         1    options(*varsize)          

d LenAvlChgCmd                  10i 0 const                      

d LenRtnChgCmd                  10i 0                            

d QUSEC                               likeds(QUSEC)              

                                                                 

dSndMsg           pr                  extpgm('QSYS/QMHSNDPM')    

d MsgID                          7    const                      

d QualMsgF                      20    const                      

d MsgDta                     65535    const options(*varsize)   

d LenMsgDta                     10i 0 const                      

d MsgType                       10    const                     

d CSE                        65535    const options(*varsize)   

d CSECtr                        10i 0 const                     

d MsgKey                         4                               

d QUSEC                               likeds(QUSEC)             

d LenCSE                        10i 0 const options(*nopass)    

d CSEQual                       20    const options(*nopass)    

d DSPWaitTime                   10i 0 const options(*nopass)    

d CSEType                       10    const options(*nopass)    

d CCSID                         10i 0 const options(*nopass)    

                                                                

 /copy qsysinc/qrpglesrc,qcapcmd                                

 /copy qsysinc/qrpglesrc,qusec                                  

                                                                

dPSDS            sds           429    qualified                 

d JobUsr                254    263                            

                                                              

dCmd              s            512                            

dMsgKey           s              4                            

dNotUsedChr       s              1                            

dNotUsedInt       s             10i 0                         

                                                              

 /free                                                        

                                                               

  QUSBPRV = 0;                                                

                                                              

  QCAP0100 = *loval; // initialize input structure to nulls   

  QCACMDPT = 0;      // Run command                           

  QCABCSDH = '0';    // Ignore DBCS                           

  QCAPA = '0';       // Do not prompt command                 

  QCACMDSS = '0';    // User i5/OS syntax                     

                                                               

  monitor;                                                    

     Cmd = 'CLRPFM FILE(SOMEFILE) MBR(' + PSDS.JobUsr + ')';  

     RunCmd(Cmd :%len(%trim(Cmd)) :QCAP0100 :%size(QCAP0100)  

            :'CPOP0100' :NotUsedChr :0 :NotUsedInt :QUSEC);   

  on-error;                                                   

     SndMsg('ESC0001' :'OURMSGS   *LIBL' :' ' :0              

            :'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);         

  endmon;                                                      

                                                              

  // Do further processing                                    

                                                              

  *inlr = *on;                                                 

  return;                                                     

                                                              

 /end-free                                                    

 

The program source, named PCMD0, is again quite similar to the earlier program CMDEXC0 in terms of providing prototypes for the called APIs and declaring variables. These are the differences:

  • Quite a few more parameters for the prototype of QCAPCMD due to significantly more function being available (though we will not be using these additional features)
  • The inclusion of QSYSINC member QCAPCMD from QRPGLESRC to define the Options control block parameter of the API
  • The new variables NotUsedChr and NotUsedInt, which are not used but must be defined as they are prototyped as outputs of the QCAPCMD API

The actual running of the command, and related error detection, is essentially the same as with CMDEXC0. There is additional setup in order to initialize the various options available in the Options control block structure, which are being set to simply run a command, but the approach is the same.

 

To compile and run the program, you can use these commands:

 

CRTBNDRPG PGM(PCMD0)

CALL PGM(PCMD0)

 

If the file SOMEFILE is not currently in your jobs library list, you will receive the escape message ESC0001 and in your job log find CPF3142, File SOMEFILE in library *LIBL not found. If the file SOMEFILE does exist but does not contain a member with the name of the user profile you signed on with, you will receive the escape message ESC0001 and in your job log find CPF3141, Member XXXXXX not found, where XXXXXX is the name of your user profile. If the file SOMEFILE does exist and contains a member with the name of the user profile you signed on with, your job log will contain CPC3101, Member XXXXXX file SOMEFILE in YYYYYY cleared, where YYYYYY is the name of the library containing file SOMEFILE. This is the same output as you experienced with CMDEXC0.

 

The QCAPCMD API can however also provide the same output as the SYSTEM0 demonstration program, as shown in the following program.

Using the Process Commands (QCAPCMD) API Without Escape Messages

h dftactgrp(*no)                                                 

                                                                  

dRunCmd           pr                  extpgm('QCAPCMD')          

d SourceCmd                  65535    const options(*varsize)    

d LenSrcCmd                     10i 0 const                      

d CtlBlk                     65535    const options(*varsize)    

d LenCtlBlk                     10i 0 const                      

d CtlBlkFmt                      8    const                      

d ChgCmd                         1    options(*varsize)           

d LenAvlChgCmd                  10i 0 const                      

d LenRtnChgCmd                  10i 0                            

d QUSEC                               likeds(QUSEC)              

                                                                  

dSndMsg           pr                  extpgm('QSYS/QMHSNDPM')    

d MsgID                          7    const                      

d QualMsgF                      20    const                      

d MsgDta                     65535    const options(*varsize)

d LenMsgDta                     10i 0 const                  

d MsgType                       10    const                  

d CSE                        65535    const options(*varsize)

d CSECtr                        10i 0 const                  

d MsgKey                         4                           

d QUSEC                               likeds(QUSEC)          

d LenCSE                        10i 0 const options(*nopass) 

d CSEQual                       20    const options(*nopass) 

d DSPWaitTime                   10i 0 const options(*nopass) 

d CSEType                       10    const options(*nopass) 

d CCSID                         10i 0 const options(*nopass) 

                                                              

 /copy qsysinc/qrpglesrc,qcapcmd                             

 /copy qsysinc/qrpglesrc,qusec                               

                                                             

dPSDS            sds           429    qualified               

d JobUsr                254    263                              

                                                                

dCmd              s            512                              

dMsgKey           s              4                               

dNotUsedChr       s              1                              

dNotUsedInt       s             10i 0                           

                                                                

 /free                                                           

                                                                

  QUSBPRV = %size(QUSEC);                                       

                                                                

  QCAP0100 = *loval; // initialize input structure to nulls     

  QCACMDPT = 0;      // Run command                             

  QCABCSDH = '0';    // Ignore DBCS                             

  QCAPA = '0';       // Do not prompt command                   

  QCACMDSS = '0';    // User i5/OS syntax                       

                                                                

  Cmd = 'CLRPFM FILE(SOMEFILE) MBR(' + PSDS.JobUsr + ')';      

  RunCmd(Cmd :%len(%trim(Cmd)) :QCAP0100 :%size(QCAP0100)      

         :'CPOP0100' :NotUsedChr :0 :NotUsedInt :QUSEC);       

  if QUSBAVL > 0;                                              

     SndMsg('ESC0002' :'OURMSGS   *LIBL' :QUSEI :%size(QUSEI)  

            :'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);          

  endif;                                                        

                                                               

  // Do further processing                                     

                                                               

  *inlr = *on;                                                 

  return;                                                      

                                                               

 /end-free                                                     

 

This program source, named PCMD1, is the same as PCMD0 with the following exceptions:

  • The Bytes provided field of the API error code structure is set to a non-zero value to turn off exceptions from the QCAPCMD API.
  • The monitor, on-error, and endmon statements are replaced by a test for the Bytes available field of the API error code structure being non-zero.
  • The escape message being sent is changed to ESC0002 with the Exception ID field of the API error code structure being used to set the message replacement text variable of ESC0002.

To compile and run the program, you can use these commands:

 

CRTBNDRPG PGM(PCMD1)

CALL PGM(PCMD1)

 

If the file SOMEFILE is not currently in your jobs library list, you will receive the escape message ESC0002 with message replacement text of 'CPF3142' (file not found). If the file SOMEFILE does exist but does not contain a member with the name of the user profile you signed on with, you will receive the escape message ESC0002 with message replacement text of 'CPF3141' (member not found). If the file SOMEFILE does exist and contains a member with the name of the user profile you signed on with, your job log will contain message CPC3101, Member XXXXXX file SOMEFILE in YYYYYY cleared, where XXXXXX is the name of your user profile and YYYYYY is the name of the library containing file SOMEFILE. This is the same output as you experienced with SYSTEM0.

 

By making very minor changes, namely the setting of the Error code Bytes provided field, you can control whether or not exception messages appear in the job log when using QCAPCMD. We will take advantage of this in future articles when we look at how to have the application program recover from errors such as file and/or member not found—and not leave "errors" in the job log when the application program is handling them (a pet peeve of mine that can impact the amount of time needed for problem determination).

Questions?

In the meantime, if you have any API questions, send them to me at This email address is being protected from spambots. You need JavaScript enabled to view it.. I'll see what I can do about answering your burning questions in future columns.

 

 

 

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$0.00 Raised:
$