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.
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); \
system "dspcmd QSYS/$OBJNAME" | \
if grep 'Program to process command.*QLIDLOBJ' > /dev/null; \
then echo $OBJNAME; \
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):
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:
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 */
READ: RCVF RCDFMT(QLIDOBJD)
MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(BYE))
DLTOBJ OBJ(&ODLBNM/&ODOBNM) OBJTYPE(&ODOBTP) /* +
Delete an object via the DLTOBJ command */
Compile the CL programs, and then call A290 like this:
CALL A290 '010813' /* Remove all objects created at 2013-01-08 from BLDLIB. */