TechTip: Implement Your Own DLTOBJ Command via the Undocumented QLIDLOBJ API

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

Wouldn't it be nice to have just one delete command for all object types?

 

As an IBM i developer, you have numerous object-related commands that accept a qualified object name and a valid object type to identify the target object(s) to work with—for example, WRKOBJ OBJ(XLIB/XFILE) OBJTYPE(*FILE), ALCOBJ OBJ(XLIB/XPGM) OBJTYPE(*PGM), DSPOBJD OBJ(XLIB/XSPC) OBJTYPE(*USRSPC), etc. However, you do not have a Delete Object (DLTOBJ) command. Instead, you have a bunch of object type?specific delete object commands, such as Delete Program (DLTPGM), Delete File (DLTF), and so on.

Therefore, it's hard to design a program that is expected to delete objects of different types via a single delete command. For example, a CL program doing such stuff might look like this:

DCL &OBJLIB *CHAR 10

DCL &OBJNAM *CHAR 10

DCL &OBJTYP *CHAR 7

IF COND(&OBJTYP *EQ '*PGM') THEN(DLTPGM PGM(&OBJLIB/&OBJNAM))

IF COND(&OBJTYP *EQ '*FILE') THEN(DLTF FILE(&OBJLIB/&OBJNAM))

/* ... ... */

/* Endless IF/THEN statements ... */

To solve this problem, obviously you can write a CL program like the above-shown example, filled with lines of IF/THEN statements. However, a much simpler solution has been discussed several times in the Midrange-L and RPG400-L mailing lists. That solution is the undocumented API QLIDLOBJ. The following post from Walden H. Leverich gave a brief introduction to the QLIDLOBJ API:

RE: Object manipulation API documentation

   Subject: RE: Object manipulation API documentation

   From: "Walden H. Leverich" <waldenl@xxxxxxxxxxxxxxx>;

   Date: Mon, 12 Mar 2001 12:48:25 -0500

QLIDLOBJ is the delete object "API", however it is in the system domain so you can't use it without tweaking your program. The nice thing about the QLIDLOBJ program is it takes the object type as a parameter so I've created a DLTOBJ command here that takes the object and type and deletes it. Much nicer than a giant IF statement that call the correct DLTxxx command based on type.

-Walden

PS. We tweaked.

In a post in September 2000 (Re: Recycle Bin in AS/400?), Peter Dow pointed out: …there are 81 DLT* commands, many of which call the same command processing program, QLIDLOBJ.

Simon Coulter provided the necessary parameter format of the QLIDLOBJ API in a post in November, 2001:

However, since that program is not an exposed API (as I recall it accepts 2 parameters: a char(20) for qualified object and a CHAR(10) for object type without the asterisk, but since it is a system domain program it is a little difficult to invoke) most sites have written a DLTOBJ command which performs a function similar to your requested ADDDSKSPC command.

The approach I'd like to introduce is slightly different from Walden's:

  • Create a copy of the QLIDLOBJ API, and change the copy to the user domain (instead of QLIDLOBJ itself).
  • Wrap a Delete Object (DLTOBJ) command over the copy of the QLIDLOBJ API.

Determine Which Commands Use QLIDLOBJ as Their Command Processing Program

In order to build a DLTOBJ command, it's necessary to know which delete object commands are backed by the QLIDLOBJ API. External object types supported by different releases of IBM i may differ slightly, and so do the delete object commands. To know exactly which delete object commands use QLIDLOBJ as their Command Processing Program (CPP), you can consider the following approach. Start a QShell session, and issue the following command:

ls /qsys.lib/DLT*.CMD | \

while read CMD; \

do OBJNAME=$(basename   $CMD); \

OBJNAME=${OBJNAME%.*}; \

system "dspcmd   QSYS/$OBJNAME" | \

if grep 'Program to process   command.*QLIDLOBJ' > /dev/null; \

then echo $OBJNAME; \

fi; \

done

Output of the above-shown QShell command on a VRM540 IBM i might look like this (a total of 67 delete object commands using QLIDLOBJ as their CPP):

DLTALRTBL

DLTAUTL

DLTBNDDIR

DLTCFGL

DLTCLD

DLTCLS

DLTCMD

DLTCNNL

DLTCOSD

DLTCRQD

DLTCSI

DLTCTLD

DLTDEVD

DLTDTAARA

DLTDTAQ

DLTEDTD

DLTF

DLTFNTRSC

DLTFNTTBL

DLTFORMDF

DLTFTR

DLTGSS

DLTIGCDCT

DLTIGCSRT

DLTIMGCLG

DLTIPXD

DLTJOBD

DLTJOBQ

DLTJRN

DLTJRNRCV

DLTLIND

DLTMEDDFN

DLTMGTCOL

DLTMOD

DLTMODD

DLTMSGF

DLTMSGQ

DLTNODGRP

DLTNODL

DLTNTBD

DLTNWID

DLTNWSCFG

DLTNWSD

DLTOUTQ

DLTOVL

DLTPAGDFN

DLTPAGSEG

DLTPDFMAP

DLTPDG

DLTPGM

DLTPNLGRP

DLTPSFCFG

DLTQMFORM

DLTQMQRY

DLTQRY

DLTSBSD

DLTSCHIDX

DLTSPADCT

DLTSQLPKG

DLTSRVPGM

DLTTBL

DLTTIMZON

DLTUSRIDX

DLTUSRQ

DLTUSRSPC

DLTVLDL

DLTWSCST

With this information, now you can create your DLTOBJ command within a few minutes.

The Delete Object (DLTOBJ) Command

The following example source of the DLTOBJ command is extracted from dltobj.cl-cmd.

             CMD       PROMPT('Delete Object (DLTOBJ)')

             PARM       KWD(OBJ) TYPE(Q_OBJ) MIN(1)   PROMPT('Object')

             PARM       KWD(OBJTYPE) TYPE(*CHAR) LEN(7)   RSTD(*YES) +

                         SPCVAL((*ALRTBL   ALRTBL) (*AUTL AUTL) +

                         (*BNDDIR BNDDIR)   (*CFGL CFGL) (*CLD CLD) +

                         (*CLS CLS) (*CMD   CMD) (*CNNL CNNL) (*COSD +

                         COSD) (*CRQD CRQD)   (*CSI CSI) (*CTLD +

                         CTLD) (*DEVD DEVD)   (*DTAARA DTAARA) +

                         (*DTAQ DTAQ) (*EDTD   EDTD) (*FILE FILE) +

                         (*FNTRSC FNTRSC)   (*FNTTBL FNTTBL) +

                         (*FORMDF FORMDF)   (*FTR FTR) (*GSS GSS) +

                         (*IGCDCT IGCDCT)   (*IGCSRT IGCSRT) +

                         (*IMGCLG IMGCLG)   (*IPXD IPXD) (*JOBD +

                         JOBD) (*JOBQ JOBQ)   (*JRN JRN) (*JRNRCV +

                         JRNRCV) (*LIND   LIND) (*MEDDFN MEDDFN) +

                         (*MGTCOL MGTCOL)   (*MODULE MODULE) (*MODD +

                         MODD) (*MSGF MSGF)   (*MSGQ MSGQ) (*NODGRP +

                         NODGRP) (*NODL   NODL) (*NTBD NTBD) (*NWID +

                         NWID) (*NWSCFG   NWSCFG) (*NWSD NWSD) +

                          (*OUTQ OUTQ) (*OVL OVL) (*PAGDFN   PAGDFN) +

                         (*PAGSEG PAGSEG)   (*PDFMAP PDFMAP) (*PDG +

                         PDG) (*PGM PGM)   (*PNLGRP PNLGRP) (*PSFCFG +

                         PSFCFG) (*QMFORM   QMFORM) (*QMQRY QMQRY) +

                         (*QRYDFN QRYDFN)   (*SBSD SBSD) (*SCHIDX +

                         SCHIDX) (*SPADCT   SPADCT) (*SQLPKG SQLPKG) +

                         (*SRVPGM SRVPGM)   (*TBL TBL) (*TIMZON +

                         TIMZON) (*USRIDX   USRIDX) (*USRQ USRQ) +

                         (*USRSPC USRSPC)   (*VLDL VLDL) (*WSCST +

                         WSCST)) MIN(1)   PROMPT('Object type')

Q_OBJ:       QUAL       TYPE(*GENERIC)   LEN(10)

             QUAL       TYPE(*NAME) LEN(10) DFT(*LIBL) +

                        SPCVAL((*CURLIB) (*LIBL))   PROMPT('Library')

Create a User Domain Copy of the QLIDLOBJ API as the CPP of the DLTOBJ Command

As Walden stated in his post, the QLIDLOBJ API is in the System Domain and hence cannot be invoked directly from user domain programs at security level 40 or above. One possible method to overcome this limit is to change QLIDLOBJ's domain attribute from System Domain to User Domain. However, to minimize the possible effect of this change, I recommend creating a duplicate program in the user domain for QLIDLOBJ as the CPP of the DLTOBJ command. Note that to create your own copy of the QLIDLOBJ API (say a program called DLTOBJ), you will need sufficient authorities. Also you should set proper authorities on the duplicated DLTOBJ program so that it can be used by target operators or programs.

The domain attribute of an MI object is stored in its MI object header, which is composed of a 32-byte base segment header and a 224-byte Encapsulated Program Architecture (EPA) header. By investigating the MI object header and conducting proper experiments, you can easily find the domain bit(s) at your specific IBM i release. For example:

  • At VRM520, the domain bits are bytes 6–7 in the 32-byte base segment header. A value of hex 8000 means System Domain, and a value of hex 0001 means User Domain.
  • At VRM540, the domain bit is bit 7 of the ATT1 field in the 224-byte EPA header. The ATT1 field is a 1-byte field at the beginning of the EPA header (i.e., at offset hex 20 from the beginning of the MI object header). A value 1 of EPA.ATT1 bit 7 means System Domain, and a value of 0 means User Domain.

After locating the domain bit(s), you can change the domain attribute of your copy of QLIDLOBJ (the DLTOBJ program) via the System Service Tools (SST).

Note: Patching program objects via the SST could be dangerous to your system. Please take the underlying risks into consideration before actually changing the domain attribute of your duplicated DLTOBJ program via the SST!

Finally, compile your DLTOBJ command like this:

CRTCMD CMD(XLIB/DLTOBJ) PGM(XLIB/DLTOBJ) SRCFILE(...)

Alternatively, if you're unwilling to patch a program object via the SST or if your target IBM i runs under security level 30 (or below?!), you can use the QLIDLOBJ API directly:

CRTCMD CMD(XLIB/DLTOBJ) PGM(QSYS/QLIDLOBJ) SRCFILE(...)

Test Your DLTOBJ Command

Now, are you ready to test your newly created DLTOBJ command? Imagine that you want to remove all objects created at a specific date from a library called BLDLIB. You could code a couple of simple CL programs like the following:

A290 (a290.clp)

             PGM       PARM(&CRTDAT)

             DCL       VAR(&CRTDAT) TYPE(*CHAR) LEN(6)

             DSPOBJD   OBJ(BLDLIB/*ALL) OBJTYPE(*ALL)   DETAIL(*BASIC) +

                         OUTPUT(*OUTFILE)   OUTFILE(OBJD) /* +

                         Retrieve object   description information */

             OVRDBF     FILE(OBJD) SHARE(*YES)

             OPNQRYF   FILE((OBJD)) QRYSLT('ODCDAT *EQ "'   *CAT +

                         &CRTDAT *CAT   '"') /* Select objects to +

                         delete by creation   date */

             CALL       PGM(A291) /* Call A291 to actually   delete +

                         selected objects */

             CLOF       OPNID(OBJD)

             DLTOVR     FILE(OBJD)

A291 (a291.clp)

             DCLF       FILE(OBJD)

READ:         RCVF       RCDFMT(QLIDOBJD)

             MONMSG     MSGID(CPF0864) EXEC(GOTO CMDLBL(BYE))

             DLTOBJ     OBJ(&ODLBNM/&ODOBNM)   OBJTYPE(&ODOBTP) /* +

                         Delete an object   via the DLTOBJ command */

             GOTO       CMDLBL(READ)

BYE:         ENDPGM

Compile the CL programs, and then call A290 like this:

CALL A290 '010813' /* Remove all objects created at 2013-01-08 from BLDLIB. */

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$0.00 Raised:
$