Who writes the commands that IBM forgot to write?
Every now and then, I think that there are functions that IBM simply forgot to provide. One example is the inability to do a command like DSPPGM for all programs in a given library. I recently had a need for exactly this functionality. It seemed like a perfect opportunity to create a new utility. In this tip, I'll show you the GETPGMINFO utility.
The GETPGMINFO Command
The GETPGMINFO command is what will allow us to display program information for all programs in a library. This command accepts four parameters as shown below:
GETPGMINFO PGMNAME(library/program)
OUTTYPE(output-type)
OUTFILE(output-library/output-file)
OUTMBR(output-member)
The PGMNAME parameter is used to supply the qualified library/program for the command. Note that *ALL can be used for the program name to show all programs in the supplied library. The OUTTYPE parameter is used to identify where the output should be sent. Valid options are * to send the output to the screen, *PRINT to generate a printer file with the results, and *OUTFILE to send the output to the physical file/member identified on the OUTFILE and OUTMBR parameters.
The Code
The key to this utility is the QCLRPGMI API. This API allows us to retrieve the same type of program information that can be displayed using the DSPPGM command. The difference here is that instead of being restricted to a single program, our command will call this API multiple times and send the output to a printer file, a physical file, or the display. The code for this tip is made up of a command, a CL program, an RPGLE program, and a physical file. The source for the CL program is shown below.
PGM PARM(&QUALPGM &OUTTYPE &OUTFILE &OUTMBR)
DCL VAR(&QUALPGM) TYPE(*CHAR) LEN(20)
DCL VAR(&OUTTYPE) TYPE(*CHAR) LEN(8)
DCL VAR(&OUTFILE) TYPE(*CHAR) LEN(20)
DCL VAR(&OUTMBR) TYPE(*CHAR) LEN(10)
DCL VAR(&OUTFLE) TYPE(*CHAR) LEN(10)
DCL VAR(&OUTLIB) TYPE(*CHAR) LEN(10)
DCL VAR(&PGMNAME) TYPE(*CHAR) LEN(10)
DCL VAR(&PGMLIBR) TYPE(*CHAR) LEN(10)
CHGVAR VAR(&PGMNAME) VALUE(%SST(&QUALPGM 1 10))
CHGVAR VAR(&PGMLIBR) VALUE(%SST(&QUALPGM 11 10))
DSPOBJD OBJ(&PGMLIBR/&PGMNAME) OBJTYPE(*PGM) +
OUTPUT(*OUTFILE) OUTFILE(QTEMP/PGMS)
IF COND(&OUTTYPE *EQ '*OUTFILE') THEN(DO)
CHGVAR VAR(&OUTFLE) VALUE(%SST(&OUTFILE 1 10))
CHGVAR VAR(&OUTLIB) VALUE(%SST(&OUTFILE 11 10))
ENDDO
ELSE CMD(DO)
CHGVAR VAR(&OUTFLE) VALUE('PGMINFO')
CHGVAR VAR(&OUTLIB) VALUE('QTEMP')
ENDDO
CRTDUPOBJ OBJ(PGMINFO) FROMLIB(*LIBL) OBJTYPE(*FILE) +
TOLIB(&OUTLIB) NEWOBJ(&OUTFLE)
MONMSG MSGID(CPD2104 CPF2130) EXEC(CLRPFM +
FILE(&OUTLIB/&OUTFLE))
CHGPF FILE(&OUTLIB/&OUTFLE) MAXMBRS(*NOMAX)
ADDPFM FILE(&OUTLIB/&OUTFLE) MBR(&OUTMBR)
MONMSG MSGID(CPF5812 CPF7306) EXEC(CLRPFM +
FILE(&OUTLIB/&OUTFLE) MBR(&OUTMBR))
OVRDBF FILE(PGMINFO) TOFILE(&OUTLIB/&OUTFLE) +
MBR(&OUTMBR) OVRSCOPE(*JOB)
OVRDBF FILE(QADSPOBJ) TOFILE(QTEMP/PGMS) +
OVRSCOPE(*JOB)
CALL PGM(GETPGMINFO)
IF COND(&OUTTYPE *EQ '*PRINT') THEN(DO)
RUNQRY QRYFILE((QTEMP/PGMINFO *FIRST)) +
OUTTYPE(*PRINTER)
ENDDO
IF COND(&OUTTYPE *EQ '*DISPLAY') THEN(DO)
RUNQRY QRYFILE((QTEMP/PGMINFO *FIRST)) +
OUTTYPE(*DISPLAY)
ENDDO
ENDPGM
This CL program performs all of the control functions for the command. It first generates a list of program objects matching what was supplied on the PGMNAME parameter. Note that this value can be a single program, a wildcard, or *ALL. Next, the program determines if it needs to create an output file based on the output type identified on the OUTTYPE parameter. Next, the GETPGMINFO RPGLE is used to extract the program information. This program reads through the output of the DSPOBJD command, which uses the format of the IBM file QADSPOBJ. That command was used to extract a list of program objects within the CL program described above. After that, our RPGLE program calls the IBM QCLRPGMI API to extract program information. This API returns a data structure containing the same information that is displayed by the DSPPGM command. The resulting data is written to the PGMINFO file. The layout for that file is shown in the table below.
File Layout for the File PGMINFO |
|||
Field Name |
Type |
Length |
Description |
PINNAME |
Char |
10 |
Program Name |
PINLIBRARY |
Char |
10 |
Program Library |
PINOWNER |
Char |
10 |
Object Owner |
PINATTR |
Char |
10 |
Program Attribute (type) |
PINCRTDTTM |
Char |
13 |
Date/Time Created |
PINSRCFILE |
Char |
10 |
Source File |
PINSRCFLLB |
Char |
10 |
Source Library |
PINSRCMBR |
Char |
10 |
Source Member |
PINSRCUPDT |
Char |
13 |
Source Update Date/Time |
PINOBSRVBL |
Char |
1 |
Observable Flag |
PINUSRPROP |
Char |
6 |
User Profile |
PINUSEADAU |
Char |
1 |
Use Adopted Authority |
A few notes on the values in this table: ILE programs do not show any of the source information because of the fact that they are created from a module. The link exists between the source and the module, not the source and the program. The RPGLE program GETPGMINFO is used to repeatedly call the QCLRPGMI API for each program in the list generated by the DSPOBJD command in our CL program.
//------------------------------------------------------------------------
// Program: GetPgmInfo
//
// Description: Retrieve program information using the QCLRPGMI API
//
// Copyright 2012 Mike Faust
//------------------------------------------------------------------------
FQADSPOBJ if e DISK
Fpgminfo o a e DISK
dpep pr EXTPGM('GETPGMINFO')
d parpgm 10a
d parlibr 10a
dpep pi
d parpgm 10a
d parlibr 10a
dRtvPgmInfo pr ExtPgm('QCLRPGMI')
d OutputData 32767a Options(*varsize)
d Len 10i 0 Const
d Format 8a Const
d PgmName 20a Const
d ErrorCode LikeDS(ErrorDS)
dPGMI0100 ds Qualified
d BytesReturned 10i 0
d BytesAvailable 10i 0
d Name 10a
d Library 10a
d Owner 10a
d Attribute 10a
d CrtDateTime 13a
d SrcFile 10a
d SrcFileLib 10a
d SrcMbr 10a
d SrcUpdDateTime 13a
d Observable 1a
d UsrPrfOption 1a
d UseAdpAut 1a
d LogCmd 1a
d AlwRTVCLSRC 1a
d FixDec 1a
d Text 50a
d PgmType 1a
d TeraspaceEnabled...
d 1a
d 58a
d MinParms 10i 0
d MaxParms 10i 0
d Size 10i 0
d AssocSpcSize 10i 0
d StaticStgSize 10i 0
d AutoStgSize 10i 0
d MICount 10i 0
d MIODTCount 10i 0
d State 1a
d CompilerID 14a
d OSCanRunOn 6a
d SrtSeqTbl 10a
d SrtSeqTblLib 10a
d LangID 10a
d Domain 1a
d ConvRequired 1a
d 20a
d Optimized 1a
d PagingPool 1a
d UpdPASA 1a
d ClrPASA 1a
d PagingAmt 1a
d 18a
d PEPMod 10a
d PEPModLib 10a
d ActGrpAttr 30a
d ObservInfoCompressed...
d 1a
d RuntimeInfoCompressed...
d 1a
d OSCrtOn 6a
d SharedActGrp 1a
d AlwUpd 1a
d CCSID 10i 0
d ModCount 10i 0
d SrvPgmCount 10i 0
d CopyrightCount 10i 0
d UnresolveRefCount...
d 10i 0
d OSCrtFor 6a
d AlwStaticReinit...
d 1a
d CrtDta 1a
d AlwBndSrvPgmLibUpd...
d 1a
d ProfilingData 10a
d TeraspaceEnabledModules...
d 1a
d StgMdl 1a
d 87a
dErrorDS ds Qualified
d BytesProvided 10i 0 Inz(%Size(ErrorDS))
d BytesAvailable 10i 0
d MsgID 7a
d 1a
d Text 500a Varying
dwrkQualPgm s 20a
/free
read QLIDOBJD;
dow not %eof();
wrkQualPgm = ODOBNM + ODLBNM;
RtvPgmInfo (pgmi0100: 540: 'PGMI0100': wrkQualPgm: ErrorDS);
if ErrorDS.BytesAvailable = 0;
clear pgminfor;
pinNAME = PGMI0100.Name;
pinLIBRARY = PGMI0100.Library;
pinOWNER = PGMI0100.Owner;
pinATTR = PGMI0100.Attribute;
pinCRTDTTM = PGMI0100.CrtDateTime;
pinSRCFILE = PGMI0100.SrcFile;
pinSRCFLLB = PGMI0100.SrcFileLib;
pinSRCMBR = PGMI0100.SrcMbr;
pinSRCUPDT = PGMI0100.SrcUpdDateTime;
pinOBSRVBL = PGMI0100.Observable;
select;
when PGMI0100.UsrPrfOption = 'U';
pinUSRPROP = '*USER';
when PGMI0100.UsrPrfOption = 'O';
pinUSRPROP = '*OWNER';
endsl;
pinUSEADAU = PGMI0100.UseAdpAut;
write pgminfor;
endif;
read QLIDOBJD;
enddo;
*inlr = *on;
return;
As you can see, the code here simply loops through the file, calling QCLRPGMI and writing the results to the PGMINFO file. That file is then used to generate either the printed report, physical file, or display output. Figure 1 below shows a sample of the output as generated using *DISPLAY output type.
Figure 1: This is the output generated by the GETPGMINFO command.
This was generated for all programs in the QSYS library. Below is the command that was called to generate this sample:
GETPGMINFO PGMNAME(QSYS/*ALL) OUTTYPE(*DISPLAY)
The "Why"
The reasons why you might want this command are numerous. It may simply be that you want to identify all of the programs in a given library that are owned by a specific user. Possibly, you might want to find every program that uses adopted authority. Whatever your reason, I hope this utility helps to make your job a little easier.
LATEST COMMENTS
MC Press Online