Wednesday, September 16, 2020

Retrieving commands' defaults

qcdrcmdd api to get command definition

I wanted to retrieve the defaults for a number of IBM commands, so I could compare them to those in another IBM i partition. I found an API, QCDRCMDD, would give me a XML string of the command's definition, that I could then write to a file. This file could then be transferred to the other partition and used when comparing the two sets of commands' information.

The output file, OUTFILE, would contain one field, for the XML string, but would need to big enough to contain the longest possible XML string. My guess is that a field of 10,000 characters would be long enough to cover most commands.

01  A          R OUTFILER
02  A            FIELD      10000A

When I was creating the RPG program it made sense to have the API in a subprocedure that could be called multiple times. If I placed all the definitions for the API in the subprocedure it makes the "main line" very simple.

01  **free  
02  ctl-opt option(*nodebugio:*srcstmt) dftactgrp(*no) ;

03  dcl-f OUTFILE usage(*output) ;

04  GetData('WRKJOB    *LIBL     ') ;
05  GetData('DSPJOB    *LIBL     ') ;
06  GetData('CHGJOB    *LIBL     ') ;
07  GetData('WRKSPLF   *LIBL     ') ;
08  GetData('CHGSPLFA  *LIBL     ') ;
09  GetData('WRKACTJOB *LIBL     ') ;
10  GetData('CRTDTAARA *LIBL     ') ;
11  GetData('DSPDTAARA *LIBL     ') ;
12  GetData('CHGDTAARA *LIBL     ') ;
13  GetData('DLTDTAARA *LIBL     ') ;
14  GetData('CRTPF     *LIBL     ') ;
15  GetData('CHGPF     *LIBL     ') ;
16  GetData('DLTF      *LIBL     ') ;
17  GetData('CRTPRTF   *LIBL     ') ;
18  GetData('WRKQRY    *LIBL     ') ;
19  GetData('RUNQRY    *LIBL     ') ;
20  GetData('STRSQL    *LIBL     ') ;
21  GetData('GRTOBJAUT *LIBL     ') ;
22  GetData('EDTOBJAUT *LIBL     ') ;
23  GetData('Not_A_Cmd *LIBL     ') ;

24  *inlr = *on ;

Line 1: This should come as no surprise to you as all the RPG I have written for the past few years has been totally free RPG (RPG for i!).

Line 2: My favorite control options. I need to have the default activation group as this program contains a subprocedure.

Line 3: Definition for the output file I am using. It is defined just for output.

Lines 4 - 23: These are the commands I will be retrieving the definition of. Notice that line 23 contains a command that does not exist.

And onto the code for the subprocedure, GetData, that contains the call to the QCDRCMDD API.

25  dcl-proc GetData ;
26    dcl-pi *n ;
27      Parm char(20) const ;
28    end-pi ;

29    dcl-ds CMDD0100data ;
30      BytesReturned int(10) ;
31      ByteAvailable int(10) ;
32      XMLdata char(10240) ccsid(1208) ;
33    end-ds ;

34    dcl-pr QCDRCMDD extpgm ;
35      *n char(20) const ;                   //Command & library
36      *n int(10) const ;                    //Size of returned DS
37      *n char(8) const ;                    //Destination format name
38      *n char(32767) options(*varsize) ;    //Returned DS
39      *n char(8) const ;                    //Receiver format name
40      *n likeds(QUSEC) options(*varsize) ;  //API error DS
41    end-pr ;

42    /include qsysinc/qrpglesrc,qusec        //API error DS

43    XMLdata = ' ' ;

44    QCDRCMDD(Parm :
45             %size(CMDD0100data) :
46             'DEST0100' :
47             CMDD0100data :
48             'CMDD0100' :
49             QUSEC) ;

50    if (XMLdata = ' ') ;
51      FIELD = '* ERROR * ' + Parm ;
52    else ;
53      FIELD = XMLdata ;
54    endif ;

55    write OUTFILER ;

56    return ;
57  end-proc ;

Lines 26 - 28: When I am defining the procedure interface I do not have to give it name, I use *N instead.

Lines 29 - 33: This data structure is returned by QCDRCMDD. The first two parameters tell me how long the returned XML string is. The third is the XML string. The API returns the XML data in CCSID 1208, therefore, I use the CCSID keyword in the data structure subfield's definition so I can see the data there. I will explain later why I need to do this.

Lines 34 - 41: This is the procedure definition to call the API program. The EXTPGM shows it is a program. I never bother to give the parameters names, therefore, I name them all *N. The parameters that are passed to the program and not returned are all constants, CONST. The two returned parameters are variable in length.

Line 42: I am including the code for the QUSEC standard API data structure from the QSYSINC library. I used it in a LIKEDS definition on line 40.

Line 43: I am clearing the data structure subfield that the XML is moved to. If the API cannot retrieve the command's definition it does not change this subfield. Therefore, I can use this to tell if the API was successful or not.

Lines 44 - 49: This is the call to the API program. I have placed each parameter used by the API on a separate line to make it easier to understand what is going on.

Line 44: This variable contains the command, and the library it is in.

Line 45: The size of the data structure the results will be placed into.

Line 46: There are two different ways the results are returned. By using format DEST0100 informs the API to place the results in the next parameter.

Line 47: The name of the variable the results are placed into.

Line 48: There are two sets of results that can be returned. In this program I want the commands' definition returned.

Line 49: If the API encounters an error the information is returned into the standard API data structure.

Lines 50 - 54: Error checking. If the returned value in the XML subfield is blank then the API was unable to perform, and an error value is moved to the output file's field. If it was successful the XML string is moved to the field.

Line 55: The output file is written to.

Line 56: Return from the subprocedure to the "main line" of the program.

I mentioned previously that I need to use the CCSID(1208) with the XML subfield. If I use debug to see the data in the subfield I see:

EVAL XMLdata
XMLDATA
     ....5...10...15...20...25...30...35...40...45...50
 1  '<QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKJOB" C

If I removed the CCSID keyword from line 32.

32      XMLdata char(10240) ;

The XML would be displayed in CCSID 1208, which in the IBM i I am using looks like:

EVAL XMLdata
XMLDATA
     ....5...10...15...20...25...30...35...40...45...50
 1  '█éÄÀä<ä_À█àèàîÁÊËÑ?>████████ä_À█ä_À+/_Á██ïê.¢|â██ä

What do the results look like? I am not going to display the entire XML statements for all the commands I used, but here is the beginning of each record from the output file.

   FIELD
   -------------------------------------------------------
01 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKJOB" CmdLib
02 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="DSPJOB" CmdLib
03 * ERROR * CHGJOB    *LIBL
04 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKSPLF" CmdLi
05 * ERROR * CHGSPLFA  *LIBL
06 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKACTJOB" Cmd
07 * ERROR * CRTPRTF   *LIBL
08 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="CRTDTAARA" Cmd
09 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="DSPDTAARA" Cmd
10 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="CHGDTAARA" Cmd
11 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="DLTDTAARA" Cmd
12 * ERROR * CRTPF     *LIBL
13 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="DLTF" CmdLib="
14 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKQRY" CmdLib
15 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="RUNQRY" CmdLib
16 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="STRSQL" CmdLib
17 * ERROR * GRTOBJAUT *LIBL
18 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="EDTOBJAUT" Cmd
19 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKJOB" CmdLib
20 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="DSPJOB" CmdLi
21 * ERROR * CHGJOB    *LIBL
22 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKSPLF" CmdL
23 * ERROR * CHGSPLFA  *LIBL
24 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKACTJOB" Cm
25 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="CRTDTAARA" Cm
26 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="DSPDTAARA" Cm
27 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="CHGDTAARA" Cm
28 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="DLTDTAARA" Cm
29 * ERROR * CRTPF     *LIBL
30 * ERROR * CHGPF     *LIBL
31 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="DLTF" CmdLib=
32 * ERROR * CRTPRTF   *LIBL
33 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKQRY" CmdLi
34 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="RUNQRY" CmdLi
35 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="STRSQL" CmdLi
36 * ERROR * GRTOBJAUT *LIBL
37 <QcdCLCmd DTDVersion="1.0"><Cmd CmdName="EDTOBJAUT" Cm

A couple of things I noticed:

  1. When the API tried to execute the invalid command the program errored, therefore, no record was written to the output file
  2. All of the commands that change, create, or grant failed. I hope this is just something due to my authority on the IBM i partition I was using.

I cannot show all the XML data from all of the records written as there is not enough room on this page. But I can show all of the XML data from the first record just as an example:

<QcdCLCmd DTDVersion="1.0"><Cmd CmdName="WRKJOB" C
mdLib="__LIBL" CCSID="37" HlpPnlGrp="QHWCCMD" HlpP
nlGrpLib="__LIBL" HlpID="WRKJOB" MaxPos="2" Prompt
="Work with Job" MsgF="QCPFMSG" MsgFLib="__LIBL" E
xecBatch="YES" ChgCmdExit="NO" RtvCmdExit="NO"><Pa
rm Kwd="JOB" PosNbr="1" KeyParm="NO" Type="QUAL" M
in="0" Max="1" Prompt="Job name" Rstd="NO" Dft="*"
 AlwUnprt="YES" AlwVar="YES" Expr="YES" Full="NO"
DspInput="YES" Choice="*" ><SngVal><Value Val="*"
MapTo="*"/></SngVal><Qual Type="NAME" Min="1" Max=
"1" Len="10" Rstd="NO" AlwUnprt="YES" AlwVar="YES"
 Expr="YES" Full="NO" DspInput="YES" Choice="Name"
 ></Qual><Qual Type="NAME" Min="0" Max="1" Prompt=
"User" Len="10" Rstd="NO" AlwUnprt="YES" AlwVar="Y
ES" Expr="YES" Full="NO" DspInput="YES" Choice="Na
me" ></Qual><Qual Type="CHAR" Min="0" Max="1" Prom
pt="Number" Len="6" Rstd="NO" RangeMinVal="000000"
 RangeMaxVal="999999" AlwUnprt="YES" AlwVar="YES"
Expr="YES" Full="YES" DspInput="YES" Choice="00000
0-999999" ></Qual></Parm><Parm Kwd="OUTPUT" PosNbr
="2" KeyParm="NO" Type="CHAR" Min="0" Max="1" Prom
pt="Output" Len="1" Rstd="YES" Dft="*" AlwUnprt="Y
ES" AlwVar="YES" Expr="YES" Full="NO" DspInput="YE
S" Choice="*, *PRINT" ><SpcVal><Value Val="*" MapT
o="*"/><Value Val="*PRINT" MapTo="L"/></SpcVal></P
arm><Parm Kwd="OPTION" PosNbr="3" KeyParm="NO" Typ
e="INT2" Min="0" Max="1" Prompt="Option" Rstd="YES
" Dft="*SELECT" AlwUnprt="YES" AlwVar="YES" Expr="
YES" Full="NO" DspInput="YES" Choice="*SELECT, *ST
SA, *DFNA..." ><SpcVal><Value Val="*SELECT" MapTo=
"13"/><Value Val="*STSA" MapTo="1"/><Value Val="*D
FNA" MapTo="2"/><Value Val="*RUNA" MapTo="3"/><Val
ue Val="*SPLF" MapTo="4"/><Value Val="*JOBLOG" Map
To="5"/><Value Val="*PGMSTK" MapTo="6"/><Value Val
="*JOBLCK" MapTo="7"/><Value Val="*LIBL" MapTo="8"
/><Value Val="*OPNF" MapTo="9"/><Value Val="*FILOV
R" MapTo="10"/><Value Val="*CMTCTL" MapTo="11"/><V
alue Val="*CMNSTS" MapTo="14"/><Value Val="*ACTGRP
" MapTo="15"/><Value Val="*MUTEX" MapTo="16"/><Val
ue Val="*THREAD" MapTo="17"/><Value Val="*MLBA" Ma
pTo="18"/><Value Val="*ENVVAR" MapTo="19"/><Value
Val="*ALL" MapTo="12"/></SpcVal></Parm><Parm Kwd="
DUPJOBOPT" PosNbr="5" KeyParm="NO" PmtCtl="PMTRQS"
 Type="CHAR" Min="0" Max="1" Prompt="Duplicate job
 option" Len="10" Rstd="YES" Dft="*SELECT" AlwUnpr
t="YES" AlwVar="YES" Expr="YES" Full="NO" DspInput
="YES" Choice="*SELECT, *MSG" ><SpcVal><Value Val=
"*SELECT" MapTo="*SELECT"/><Value Val="*MSG" MapTo
="*MSG"/></SpcVal></Parm><Parm Kwd="SLTTHD" PosNbr
="4" KeyParm="NO" PmtCtl="PMTCTL" Type="CHAR" Min=
"0" Max="20" Prompt="Thread ID to include" Len="8"
 Rstd="NO" Dft="*INITIAL" AlwUnprt="YES" AlwVar="Y
ES" Expr="NO" Full="YES" DspInput="YES" Choice="Ch
aracter value" ><SngVal><Value Val="*ALL" MapTo="*
ALL"/><Value Val="*SELECT" MapTo="*SELECT"/></SngV
al><SpcVal><Value Val="*INITIAL" MapTo="*INITIAL"/
><Value Val="*CURRENT" MapTo="*CURRENT"/></SpcVal>
<PmtCtl CtlKwd="OUTPUT" NbrTrueRel="EQ" NbrTrue="1
" LglRel="AND" ><PmtCtlCond Rel="EQ" CmpVal="L"/><
/PmtCtl><PmtCtl CtlKwd="OPTION" NbrTrueRel="EQ" Nb
rTrue="1" ><PmtCtlCond Rel="EQ" CmpVal="6"/></PmtC
tl></Parm><Dep CtlKwdRel="SPCFD" CtlKwd="SLTTHD" N
brTrueRel="EQ" NbrTrue="0" MsgID="CPD0977"><DepPar
m Kwd="OPTION" Rel="NE" CmpVal="6" /><DepParm Kwd=
"OUTPUT" Rel="NE" CmpVal="L" /></Dep></Cmd></QcdCL
Cmd>

What next?

I am going to copy this file to the other IBM i partition. Run this same program on that partition into a different file. Then I will compare the data within the two files using a hash of the record.

 

You can learn more about the QCDRCMDD API from the IBM website here.

 

This article was written for IBM i 7.4, and should work for some earlier releases too.

 

Addendum

I found a way to get the command defaults string for all commands using a CLOB, read about it here.

5 comments:

  1. I tried your program and the same commands fail for me also and I have sufficient authority to run them. No error reported from API.

    -Matt

    ReplyDelete
    Replies
    1. Wow. I have no idea what rules are used with this API.

      Delete
  2. dear simon, thank you for this smart code template.

    ReplyDelete
  3. Not an authority problem, 10240 is not long enough for those commands, then BytesReturned is 0 and ByteAvailable is what's needed to get something

    ReplyDelete
  4. Simon, i think the answer is in documentation "If the receiver variable is not large enough to hold all of the generated CDML source, no CDML source is returned."

    i made a test with XMLdata char(65535) ccsid(1208); and i have everything.

    -Christian.

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.