Improving on IBM's CHGOBJOWN command.
Transferring ownership of a business can re-quire lawyers, a lot of paperwork and a substantial transfer of funds between the two parties. When you sell your car, the transaction is somewhat simpler, although it still requires you to file a document with the lo-cal Department of Motor Vehicles, even if you're giving the car away.
All AS/400 objects have an owner too, and OS/400 provides a command to transfer the ownership from one user to another. Generally speaking, the owner of an object is the user who created the object. This presents no problems until one of two things happens:
The user is deleted from the system because that person no longer works on the computer (or with the company). Although the Delete User Profile (DLTUSRPRF) command lets you transfer ownership of all objects owned by the user being deleted, you can easily forget to take this option and miss your only chance to change owners.
The object is saved on tape and restored on another system which doesn't have that user profile.
Changing the Owner
IBM gave us the Change Object Owner (CHGOBJOWN) command. It works, but it has the disadvantage of not allowing generic names; you must run the CHGOBJOWN command once for each object whose ownership is to be changed. As you can imagine, it can become quite tedious (and prone to error) if dozens or hundreds of objects are involved.
IBM's QUSRTOOL library gave us the Change Library Owner (CHGLIBOWN) command. It is a definite improvement over CHGOBJ-OWN because it allows you to change the ownership of an entire library (both the library object and all the objects contained in it). But you cannot limit by generic object name or object type what objects within the library are to be changed; CHGLIB-OWN always assumes that you want to change the ownership of all objects in the library.
The CHGOWN Command
Let me introduce the Change Ownership (CHGOWN) command. CHGOWN (1) has four parameters:
Let me introduce the Change Ownership (CHGOWN) command. CHGOWN (Figure 1) has four parameters:
OBJ: The qualified name of the object or objects whose ownership you want to change. You can enter a specific object name, a generic name, or *ALL. The library portion of the qualified name defaults to *CURLIB.
OBJTYPE: The type of object, such as *PGM for program or *FILE for files. You can also specify *ALL for all object types.
NEWOWN: The new owner's profile name. You can enter any user profile name or special value *PROMPT.
CUROWNAUT: What authority to give to the current owner. It defaults to *REVOKE, which means that the current owner's private authority to the object is revoked. You can change this value to *SAME if you want the current owner to keep the same private authority to the object.
Even if you specify CUROWN-AUT(*REVOKE), the old owner may still be authorized to the objects whose owner you're changing if the old owner belongs to a group profile who is authorized, or if an authorization list specifically authorizes either the user profile or the group profile.
Examples of Use
To give you an idea of the power of CHGOWN, consider the examples given in this section:
CHGOWN OBJ(SSLIB/SS100) + OBJTYPE(*PGM) + NEWOWN(QPGMR)
This works just like CHGOBJ-OWN, changing owner to QPGMR for program SS100 in library SSLIB. The old owner loses all authorities to the program.
CHGOWN OBJ(SSLIB/*ALL) + OBJTYPE(*ALL) + NEWOWN(FRED) + CUROWNAUT(*SAME)
This second example works like CHGLIBOWN, changing all objects in library SSLIB so that FRED owns them. The old owner keeps his authorities to the objects now owned by FRED.
CHGOWN OBJ(SSLIB/SS1*) + OBJTYPE(*ALL) + NEWOWN(FRANNIE)
Here CHGOWN will change ownership to FRANNIE for all objects in SSLIB whose name begins with SS1. The previous owner's authority to these objects is revoked.
CHGOWN OBJ(ACCTG/CR*) + OBJTYPE(*PGM) + NEWOWN(QPGMR)
This example changes the owner of all programs in library ACCTG with names beginning with CR, so they're owned by QPGMR. Again, the previous owner's authority is revoked.
CHGOWN OBJ(ACCTG/*ALL) + OBJTYPE(*FILE) + NEWOWN(BETTY)
When this command finishes executing, BETTY will own all files in library ACCTG; their previous owners will have no private authorities to those files.
How About NEWOWN(*PROMPT)?
Remember that parameter NEW-OWN can also have special value *PROMPT. When you request NEWOWN(*PROMPT), the CHG-OWN command will prompt you for the new owner for each object. To change the owner of the object being prompted for, type a user profile name and press Enter. If you wish to skip the object, press F12; CHGOWN will simply go on to the next object. To terminate the entire sequence, press F3.
Because NEWOWN(*PROMPT) requires user intervention, it cannot be run in batch mode. To see NEWOWN(*PROMPT) in action, suppose you need to change the owner of all data areas in your UTILITY library. Some data areas are to be owned by ROMEO, some by JULIET. The data areas are named in a random fashion such that you cannot use generic names. NEWOWN(*PROMPT) takes care of this situation:
CHGOWN OBJ(UTILITY/*ALL) + OBJTYPE(*DTAARA) + NEWOWN(*PROMPT)
CHGOWN will stop at each data area in UTILITY, displaying the CHGOBJOWN command prompter with every parameter already filled out except the new owner's name. At each stop, you type either ROMEO or JULIET and press Enter. Optionally, you can change the value of the CUROWNAUT parameter from *REVOKE to *SAME or vice versa. CHGOWN continues, prompting for the next data area, and so on.
How It Works
The CHGOWN command (1) is rather simple. The only tricky part is to be sure to list all object types in the OBJTYPE parameter's VALUES clause. If IBM creates new object types in the future, you'll have to add them to the VALUES clause.
The CHGOWN command (Figure 1) is rather simple. The only tricky part is to be sure to list all object types in the OBJTYPE parameter's VALUES clause. If IBM creates new object types in the future, you'll have to add them to the VALUES clause.
Program OWN001CL (2) performs some preliminary validation, such as rejecting NEW-OWN(*PROMPT) if running in batch, an invalid library name in the OBJ parameter and an invalid user profile name in NEWOWN if *PROMPT not specified.
Program OWN001CL (Figure 2) performs some preliminary validation, such as rejecting NEW-OWN(*PROMPT) if running in batch, an invalid library name in the OBJ parameter and an invalid user profile name in NEWOWN if *PROMPT not specified.
At tag GENERIC, it uses QCLSCAN to determine if the object name contains an asterisk. If it does, either *ALL or generic was specified; this means that OWN001CL will have to get the object names from an outfile generated by the Display Object Description (DSPOBJD) command. Incidentally, that's why the program declares file QADSPOBJ-that's the model outfile for DSP-OBJD. If the outfile is used, logical variable &LOOP is set to true.
If looping is not necessary (because a specific object name was entered in the OBJ parameter), the object name is validated and control is transferred to tag SPECIFIC, where the standard CHGOBJOWN command takes care of the object's ownership.
Otherwise, DSPOBJD creates the outfile in QTEMP. Since the DCLF command declared the QSYS file, we need to override QADSPOBJ to QTEMP-hence the Override Data-base File (OVRDBF) command that follows. Then OWN001CL enters the loop, reading one record from the outfile until all records are processed.
Processing changes slightly depending on the value of the NEWOWN parameter. If *PROMPT is specified, the CHGOBJOWN command is prompted. If *PROMPT is not specified, CHGOBJOWN is immediately executed. Let's examine the first case in greater detail.
First, the question mark before the CHGOBJOWN command forces the system to invoke the command prompter for CHGOBJOWN. Notice that parameters OBJ and OBJTYPE have the characters ?* before the keyword; this instructs the system to present the parameter values, but does not allow the user to change them. The CUROWNAUT parameter, however, has the characters ?? before the keyword, instructing the system to present the parameter value and allow the user to change it.
If the user presses F3 or F12 when he gets the command prompter, *ESCAPE message CPF6801 is issued. OWN001CL traps it. We want to end the program if the user pressed F3, or simply skip the current object if the user pressed F12. To distinguish between F3 and F12, V2R1 provides message data in CPF6801 which we can compare with the MONMSG's CMPDTA command. This technique won't work in machines that are running under V1R3 or earlier releases of OS/400.
As you can see, the first MONMSG traps CPF6801 and compares the message data against F3. If it's equal, the EXEC parameter directs the program to end execution. If the user pressed F12, however, the comparison against F3 fails, so OWN-001CL continues to the next MONMSG. The second MONMSG works similarly, comparing against F12. If the user pressed F12, the program branches to tag CONTINUE.
OS/400 security is important. Keeping AS/400 objects assigned to their rightful owner isn't just a matter of jurisdiction on a certain property; it also establishes authorities to the object since the system gives the owner *ALL authority to the objects he owns. With the CHGOWN command, security doesn't have to be hard work. Maintaining up-to-date, accurate object ownership just became a whole lot easier.
Under New Ownership? The CHGOWN command
Figure 1 Command CHGOWN
CHGOWN: CMD PROMPT('Change Ownership') PARM KWD(OBJ) TYPE(Q1) MIN(1) PROMPT('Object name') PARM KWD(OBJTYPE) TYPE(*CHAR) LEN(8) RSTD(*YES) + DFT(*ALL) VALUES(*ALL *ALRTBL *AUTL *CFGL + *CHTFMT *CLD *CLS *CMD *COSD *CSPMAP + *CSPTBL *CTLD *DEVD *DOC *DTAARA *DTADCT + *DTAQ *EDTD *FCT *FILE *FLR *FNTRSC + *FORMDF *GSS *JOBD *JOBQ *JRN *JRNRCV + *LIB *LIND *MENU *MODD *MSGF *MSGQ *OUTQ + *OVL *PAGSEG *PDG *PGM *PNLGRP *PRDAVL + *PRDDFN *PRDLOD *QMFORM *QMQRY *QRYDFN + *RCT *SBSD *SCHIDX *SPADCT *SSND *S36 + *TBL *USRIDX *USRPRF *USRQ *USRSPC) + EXPR(*YES) PROMPT('Object type') PARM KWD(NEWOWN) TYPE(*NAME) LEN(10) DFT(*PROMPT) + SPCVAL((*PROMPT)) EXPR(*YES) PROMPT('New + owner') PARM KWD(CUROWNAUT) TYPE(*CHAR) LEN(7) RSTD(*YES) + DFT(*REVOKE) VALUES(*REVOKE *SAME) + EXPR(*YES) PROMPT('Current owner authority') Q1: QUAL TYPE(*GENERIC) LEN(10) SPCVAL((*ALL)) MIN(1) + EXPR(*YES) QUAL TYPE(*NAME) LEN(10) DFT(*CURLIB) + SPCVAL((*CURLIB)) EXPR(*YES) + PROMPT('Library')
Under New Ownership? The CHGOWN command
Figure 2 CL program OWN001CLOWN001CL: + PGM PARM(&QOBJ &OBJTYPE &NEWOWN &CUROWNAUT) DCL VAR(&CUROWNAUT) TYPE(*CHAR) LEN(7) DCL VAR(&JOBTYPE) TYPE(*CHAR) LEN(1) DCL VAR(&LOOP) TYPE(*LGL) LEN(1) DCL VAR(&MSG) TYPE(*CHAR) LEN(80) DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(132) DCL VAR(&MSGF) TYPE(*CHAR) LEN(10) DCL VAR(&MSGFLIB) TYPE(*CHAR) LEN(10) DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) DCL VAR(&NEWOWN) TYPE(*CHAR) LEN(10) DCL VAR(&OBJ) TYPE(*CHAR) LEN(10) DCL VAR(&OBJLIB) TYPE(*CHAR) LEN(10) DCL VAR(&OBJTYPE) TYPE(*CHAR) LEN(8) DCL VAR(&PATLEN) TYPE(*DEC) LEN(3 0) VALUE(1) DCL VAR(&PATTERN) TYPE(*CHAR) LEN(1) VALUE('*') DCL VAR(&QOBJ) TYPE(*CHAR) LEN(20) DCL VAR(&RESULT) TYPE(*DEC) LEN(3 0) DCL VAR(&STRLEN) TYPE(*DEC) LEN(3 0) VALUE(10) DCL VAR(&STRPOS) TYPE(*DEC) LEN(3 0) VALUE(1) DCL VAR(&TRIM) TYPE(*CHAR) LEN(1) VALUE('1') DCL VAR(&WILD) TYPE(*CHAR) LEN(1) VALUE(' ') DCL VAR(&XLATE) TYPE(*CHAR) LEN(1) VALUE('0') DCLF FILE(QADSPOBJ) MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR)) /* Abort if NEWOWN(*PROMPT) requested in batch mode */ RTVJOBA TYPE(&JOBTYPE) IF COND(&JOBTYPE *EQ '0' *AND &NEWOWN *EQ '*PROMPT') THEN(DO) SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) + MSGDTA('NEWOWN(*PROMPT) not allowed in batch mode') + MSGTYPE(*ESCAPE) RETURN ENDDO /* Break qualified name */ CHGVAR VAR(&OBJ) VALUE(%SST(&QOBJ 1 10)) CHGVAR VAR(&OBJLIB) VALUE(%SST(&QOBJ 11 10)) /* Validate library name */ CHKOBJ OBJ(&OBJLIB) OBJTYPE(*LIB) /* If new owner's name is not *PROMPT, verify it */ IF COND(&NEWOWN *NE '*PROMPT') THEN(DO) CHKOBJ OBJ(&NEWOWN) OBJTYPE(*USRPRF) ENDDO /* Perform loop if object name is *ALL or generic* */ GENERIC: + CALL PGM(QCLSCAN) PARM(&OBJ &STRLEN &STRPOS &PATTERN &PATLEN + &XLATE &TRIM &WILD &RESULT) IF COND(&RESULT *GT 0) THEN(CHGVAR VAR(&LOOP) VALUE('1')) ELSE CMD(CHGVAR VAR(&LOOP) VALUE('0')) /* If specific requested, go directly to CHGOBJOWN statement */ IF COND(*NOT &LOOP) THEN(DO) CHKOBJ OBJ(&OBJLIB/&OBJ) OBJTYPE(&OBJTYPE) CHGVAR VAR(&ODOBNM) VALUE(&OBJ) CHGVAR VAR(&ODLBNM) VALUE(&OBJLIB) CHGVAR VAR(&ODOBTP) VALUE(&OBJTYPE) GOTO CMDLBL(SPECIFIC) ENDDO /* Obtain names of all objects that meet selection criteria */ DSPOBJD OBJ(&OBJLIB/&OBJ) OBJTYPE(&OBJTYPE) DETAIL(*FULL) + OUTPUT(*OUTFILE) OUTFILE(QTEMP/QADSPOBJ) OVRDBF FILE(QADSPOBJ) TOFILE(QTEMP/QADSPOBJ) /* Loop through outfile */ LOOP: + RCVF MONMSG MSGID(CPF0864) EXEC(RETURN) SPECIFIC: + IF COND(&NEWOWN *EQ '*PROMPT') THEN(DO) ? CHGOBJOWN ?*OBJ(&ODLBNM/&ODOBNM) ?*OBJTYPE(&ODOBTP) + ??CUROWNAUT(&CUROWNAUT) MONMSG MSGID(CPF6801) CMPDTA(F3) EXEC(RETURN) MONMSG MSGID(CPF6801) CMPDTA(F12) EXEC(GOTO CMDLBL(CONTINUE)) SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA(&ODOBTP *BCAT + &ODLBNM *TCAT '/' *CAT &ODOBNM *TCAT '''s owner changed') + MSGTYPE(*INFO) ENDDO ELSE CMD(DO) SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Processing + object' *BCAT &ODLBNM *TCAT '/' *CAT &ODOBNM *TCAT ', + type' *BCAT &ODOBTP) TOPGMQ(*EXT) MSGTYPE(*STATUS) CHGOBJOWN OBJ(&ODLBNM/&ODOBNM) OBJTYPE(&ODOBTP) + NEWOWN(&NEWOWN) CUROWNAUT(&CUROWNAUT) SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA(&ODOBTP *BCAT + &ODLBNM *TCAT '/' *CAT &ODOBNM *TCAT '''s owner changed + to' *BCAT &NEWOWN) MSGTYPE(*INFO) ENDDO CONTINUE: + IF COND(&LOOP) THEN(GOTO CMDLBL(LOOP)) RETURN /* Forward program messages */ ERROR: + RCVMSG MSGTYPE(*EXCP) MSGDTA(&MSGDTA) MSGID(&MSGID) MSGF(&MSGF) + MSGFLIB(&MSGFLIB) SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) MSGDTA(&MSGDTA) + MSGTYPE(*ESCAPE) ENDPGM