w w w . P r o v a t o S y s . c o m
M i c h a e l   C a t a l a n i
9 0 1 . 5 8 1 . 8 7 9 1

 

               


The system() C Api

The system() api allows us to execute i5OS commands from within our RPG applications. The system() api performs the same task as QCMDEXC, but is easier to work with. The system() api is in the "C" library, so you will need to bind to QC2LE binding directory in your application when you use this api.  

Here's the prototype for the system api. 

 
        // * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        //
        //  Command_System - Execute A System Command
        //
        //   Parameter 1: String that contains the command to process
        //
        //   Returns 0-No Error  1-Null CommandString  2-CPF Error
        //
        // * * * * * * * * * * * * * * * * * * * * * * * * * * * *

     d Command_System...
     d                 pr            10i 0 ExtProc('system')
     d  CommandString                  *   value options(*String)
                                                                                      

With the system() api, we only need to pass the command string. This makes executing a command within RPG as easy as:

System_Command( CommandString );

That's all there is to it, and it looks very neat and clean. Let's use it to quickly delay a job 10 seconds:

System_Command( 'DLYJOB DLY(10)' );


Notice how it compares to the QCMDEXC prototype. 

 
 D Command_System...
 D                 pr                  ExtPgm('QCMDEXC')      
 D  CommandString               200a   const                  
 D  Length                       15p 5 const                                                                                                                
With the QCMDEXC program, we have to build a command string, then get the length of the command string and pass that length as a parameter. So a call to the QCMDEXC prototype would look like:

callp Command_System( %trim(CommandString) : %len(%trim(CommandString)) );

Clearly there's more code, more built in functions involved, and it just looks more cryptic. Here's the same code to delay the job 10 seconds:

 callp Command_Sys('DLYJOB DLY(10)': 14 );

Notice how we have to hard code a length in order to pull this off. If you ever need to change this line of code to 100 seconds, you better remember to up the length field to 15, or else the command would fail due to the right parenthesis missing from the command string. To get away from hard coding the length, we would have to move the command string into a variable first, then pass the length o the variable, such as this snippet of code does:

CommandString = DLYJOB DLY(10);
callp Command_Sys(%trim(CommandString): %len(%trim(ComandString)));

So it's easy to see the the code required for the QCmdExc method is remarkably more cryptic and isn't nearly as pretty as the system() api.

In addition to looking and feeling cleaner, the system() api  can return a number to us to determine if an error occured.

0-No Error
1-Null Command String
2-Command Failed

You will only receive a "1" error if the command string is NULL.  As long as you put something in the CommandString field, you should not encounter this error.

Normally, you will be returned a zero, which means no error, or a two, which means that the command failed for some reason. While it's nice to know that the command failed, what would be nicer is to know the exact error message that was encountered so that we can deal with it within our program.  There's an easy way to find this out, by simply adding the _EXCP_MSGID import, which is highlighted below in green.

        // * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        //
        //  System_Command - Execute A System Command
        //
        //   Parameter 1: String that contains the command to process
        //
        //   Imports the Error Message
        //
        //   Returns 0-No Error  1-Null CommandString  2-CPF Error
        //
        //?* * * * * * * * * * * * * * * * * * * * * * * * * * * *

     d Command_System...
     d                 pr            10i 0 ExtProc('system')
     d  CommandString                  *   value options(*String)

     d CPF_Error       s              7a   Import('_EXCP_MSGID')                                                                                       

That's all there is to it. Now we can more effectively monitor for messages we encounter. If we are returned a "2", then we can check the value of the CPF_Error field and get the error message id.

A very common error message you will encounter is CPF0006, which means that there is something wrong with the command string.  For instance, we build a command string that looks like:

DLYJOB DLY10)
instead of
DLYJOB DLY(10)

Since the first command string is missing the left parenthesis, the command string is invalid, and the system() api would return us a "2" error code.  The CPF_Error field would contain "CPF0006".

Although the system() api is nice, you can exploit more of its power by using it to create functions for each of the i5/OS commands you want to execute.  Click Here to jump directly to the section where I show how to do this.

It should be noted that although the system() command is a very nice feature, you should use an api instead of a command when possible. For example, we used the DLYJOB command for this example.  But there are two "C library api's that do a better and cleaner job of delays; sleep and usleep.  So be careful that you dont replicate a system command by using the system() api when there is a better api alternative to doing the same task.