TechTip: Take Advantage of the SU CL Command

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

Switch to and back from different user profiles within the same job with ease.

 

In UNIX or Linux, users switch to the super user (aka root) or another user via the su shell command (in a new shell, actually). With proper options, a su command can even preserve the original environment in the newly started shell sessionfor example, the -m option (do not reset environment variables) of the su command of GNU Linux. For an IBM i developer or operator, a CL command with function similar to the UNIX/Linux su shell command would be very handy.

 

In this article, I'll show you a practical utility, the SU CL command, which allows you to switch to and back from different user profiles within the same job. The SU command changes the USRPRF under which the current thread/job runs so that the new USRPRF (replacing the previous USRPRF) becomes one of the sources of authority of the current thread/job. While no other job resources are changed due to a SU command, you can for example still retrieve previously entered commands with function key F9 in the command environment.

How Does the Su Command Work?

The SU command allows an operator or a program to switch to another user profile in a single job. When switching to another user profile, you should pass a user profile (*USRPRF) name and the password of the USRPRF (or optionally one of the following accepted special values: *NOPWD, *NOPWDCHK, or *NOPWDSTS). For example: SU USER(USER_A) PASSWORD(******). When switching back to the previous USRPRF, you can simply issue SU USER(*EXIT).

 

The SU command allows an operator or a program to switch to multiple users one by one and then switch back in the reverse order. To preserve the USRPRF handles, the SU command uses data queue (*DTAQ) object QTEMP/@SUDTAQ of type *LIFO (queue entries are received in a last-in first-out order). In other words, @SUDTAQ works as a "USRPRF handle stack." Figure 1 illustrates the working mechanism of the SU command in brief.

 

 02-1513Junleiflow-small                     

Figure 1: This is how the SU command works.

 

[1], [2] When an operator or a program issues a SU USER(USER_NAME) PASSWORD(******) command, SU saves the current USRPRF handle by first retrieving a USRPRF handle (PH_A) of the current user (USER_A) via the Get Profile Handle (QSYGETPH) API and then pushing PH_A on the USRPRF handle stack), @SUDTAQ, via the Send Data Queue (QSNDDTAQ) API.

[3] Get the USRPRF handle (PH_B) of the target user (USER_B) by invoking the QSYGETPH API with the input user name and password parameters.

[4] Switch to the USER_B by invoking the Set Profile (QWTSETP) API, passing the USRPRF handle PH_B. After QWTSEPT returns successfully, the current job is running under the user profile USER_B -- PH_B.

[5] Release PH_B via the Release Profile Handle (QSYRLSPH) API.

[6] Work under user profile USER_B.

[7] When a SU USER(*EXIT) command is issued, SU pops the user profile handle of USER_A (PH_A) out of the "USRPRF handle stack" via the Receive Data Queue (QRCVDTAQ) API and then invokes QWTSETP to switch back USER_A.

[8] After QWTSEPT returns successfully, the current thread runs under the user profile USER_A again. SU then releases PH_A.

 

Note that the SU command accepts either a password or one of the following special values:

  • *NOPWDThe user requesting the profile handle must have *USE authority to the user profile. A profile handle does not get created for a disabled user profile. A profile handle does not get created for a user profile with an expired password.
  • *NOPWDCHKThe user requesting the profile handle must have *USE authority to the user profile. If the profile is disabled, the user requesting the profile handle must have *ALLOBJ and *SECADM special authorities to get a handle. If the password is expired, the user requesting the profile handle must have *ALLOBJ and *SECADM special authorities to get a handle.
  • *NOPWDSTSThe user requesting the profile handle must have *USE authority to the user profile. A profile handle does not get created for a disabled user profile. If the password is expired, the user requesting the profile handle must have *ALLOBJ and *SECADM special authorities to get a handle.

Source Code of the SU Command

The following is the source of command definition of the SU command su.cl-cmd.

 

             CMD       PROMPT('su')

             PARM       KWD(USER) TYPE(*NAME) LEN(10)   SPCVAL((*EXIT +

                         *EXIT)) MIN(1)   CASE(*MONO) PROMPT('User +

                         name')

             PARM       KWD(PASSWORD) TYPE(*CHAR) LEN(128) +

                         DFT(*NOPWDCHK)   SPCVAL((*NOPWD *NOPWD) +

                         (*NOPWDCHK   *NOPWDCHK) (*NOPWDSTS +

                         *NOPWDSTS)) MIN(0)   CASE(*MIXED) +

                         DSPINPUT(*NO)   INLPMTLEN(10) +

                         PROMPT('Password')

 

The CL version of the CPP of the SU command su.clle is shown as follows:

 

/********************************************************************/

/* @file su.clle                                                  */

/* CL version of the CPP of   the SU command.                           */

/********************************************************************/

 

             PGM       PARM(&TGTUSR &PWD)

 

             DCL       VAR(&TGTUSR) TYPE(*CHAR) LEN(10)

             DCL       VAR(&PWD) TYPE(*CHAR) LEN(128)

             DCL       VAR(&ORG_PH) TYPE(*CHAR) LEN(12)

             DCL       VAR(&TGT_PH) TYPE(*CHAR) LEN(12)

             DCL       VAR(&QNAM) TYPE(*CHAR) LEN(10)   VALUE(@SUDTAQ)

             DCL       VAR(&QLIB) TYPE(*CHAR) LEN(10)   VALUE(QTEMP)

             DCL       VAR(&ENT_LEN) TYPE(*DEC) LEN(5 0)   VALUE(12)

             DCL       VAR(&TIMEOUT) TYPE(*DEC) LEN(5 0)   VALUE(0)

             DCL       VAR(&PWD_LEN) TYPE(*INT) LEN(4)   VALUE(128)

             DCL       VAR(&OFF1) TYPE(*INT) LEN(4)

             DCL       VAR(&OFF2) TYPE(*INT) LEN(4)

             DCL       VAR(&CCSID) TYPE(*INT) LEN(4)   VALUE(0)

             DCL       VAR(&WHERE) TYPE(*PTR)

             DCL       VAR(&PWD_PTR) TYPE(*PTR)

             DCL       VAR(&NULL) TYPE(*PTR)

             DCL       VAR(&EC) TYPE(*CHAR) LEN(16) +

                           VALUE(X'00000000000000000000000000000000')

 

             IF         COND(&TGTUSR *NE '*EXIT') THEN(GOTO   +

                         CMDLBL(SWAP_TO))

             ELSE       CMD(GOTO CMDLBL(SWAP_BACK))

 

SWAP_TO:     CHKOBJ       OBJ(&QLIB/&QNAM) OBJTYPE(*DTAQ)

             MONMSG     MSGID(CPF9801) EXEC(DO)

             CRTDTAQ   DTAQ(QTEMP/@SUDTAQ) MAXLEN(12) SEQ(*LIFO)   +

                         AUT(*CHANGE)

             ENDDO     /* Create the PH stack in case it isn't   +

                         already exists */

 

             CALL       PGM(QSYGETPH) PARM('*CURRENT ' ' ' &ORG_PH)

             CALL       PGM(QSNDDTAQ) PARM(&QNAM &QLIB   &ENT_LEN +

                         &ORG_PH) /*   Save current PH */

 

             /* Get the PH of target user */

             IF         COND(%SST(&PWD 1 6) *EQ   '*NOPWD') THEN(CALL +

                         PGM(QSYGETPH)   PARM(&TGTUSR &PWD &TGT_PH))

             ELSE       CMD(DO)

             CALLPRC   PRC('_MEMCHR') PARM((&PWD *BYREF) ('   ' +

                         *BYVAL)   (&PWD_LEN *BYVAL)) RTNVAL(&WHERE)

             IF         COND(&WHERE *NE &NULL)   THEN(DO)

             CHGVAR     VAR(&PWD_PTR) VALUE(%ADDR(&PWD))

             CHGVAR     VAR(&OFF2) VALUE(%OFS(&WHERE))

             CHGVAR     VAR(&OFF1) VALUE(%OFS(&PWD_PTR))

             CHGVAR     VAR(&PWD_LEN) VALUE(&OFF2 -   &OFF1)

             ENDDO     /* If &where == *NULL */

             CALL       PGM(QSYGETPH) PARM(&TGTUSR   &PWD &TGT_PH &EC +

                         &PWD_LEN   &CCSID)

             ENDDO     /* Else block         */

 

             CALL       PGM(QWTSETP) PARM(&TGT_PH) /*   Switch to +

                          target user */

             CALL       PGM(QSYRLSPH) PARM(&TGT_PH) /*   Release PH */

             GOTO       CMDLBL(BYE)

 

SWAP_BACK:   CALL       PGM(QRCVDTAQ)   PARM(&QNAM &QLIB &ENT_LEN +

                         &ORG_PH   &TIMEOUT) /* Pop previous PH out +

                         of the PH stack */

             IF         COND(&ENT_LEN *EQ 0)   THEN(SNDPGMMSG +

                         MSGID(CPF9898)   MSGF(QSYS/QCPFMSG) +

                         MSGDTA('Exit to   where? :p') MSGTYPE(*ESCAPE))

             ELSE       CMD(DO)

             CALL       PGM(QWTSETP) PARM(&ORG_PH) /*   Switch back to +

                         original user */

             CALL       PGM(QSYRLSPH) PARM(&ORG_PH) /*   Release PH */

             ENDDO

 

BYE:         ENDPGM

 

The following is the OPM MI version of the CPP of the SU command su.emi (needs to be compiled via mic).

 

/**

* @file su.emi

*

* CPP of the SU command.

*/

 

dcl spcptr @tgt-user   parm       ;

dcl dd tgt-user char(10)   bas(@tgt-user) ;

dcl spcptr @pwd parm           ;

dcl dd pwd char(128)   bas(@pwd) ;

 

dcl ol pl-main(

      @tgt-user, @pwd

) parm ext                     ;

entry *(pl-main) ext           ;

brk   "MORNING"                   ;

       /* Check target USRPRF parameter */

       cmpbla(b) tgt-user, *EXIT / eq(=+3) ;

       calli swap-to, *, @swap-to         ;

       b =+2                               ;

:

       calli swap-back, *, @swap-back ; /* SU USER(*EXIT) */

:      

brk "SEEU"                     ;

       rtx *                   ;

 

/* Routine: swap-to */

dcl insptr @swap-to   auto       ;

entry swap-to int               ;

dcl sysptr @sudtaq   auto         ;

dcl dd rt char(34)   auto         ;

       dcl dd * char(2) def(rt) pos(1)   init(x"0A01")   ;

       dcl dd * char(30) def(rt) pos(3)   init(" ")   ;

       dcl dd * char(2) def(rt) pos(33)   init(x"0000")   ;

 

       /* Resolve QTEMP/@SUDTAQ */

       cpybla rt(3:30), qrcvdtaq?q       ;

       setip @on-2201, crt-su-dtaq       ;

       rslvsp @sudtaq, rt, @pco?qtemp, * ;

       b so-boring                       ;

crt-su-dtaq:

       cpyblap cl-cmd,

         "CRTDTAQ DTAQ(QTEMP/@SUDTAQ)   MAXLEN(12) SEQ(*LIFO) AUT(*CHANGE)",

         " "                   ;

       triml pkd-cmd-len, cl-cmd, "   " ;

       callx pco?sept(qcmdexc-entry),

         al-qcmdexc, *         ;

so-boring:

       /* Save current profile handle for   swapping back */

       cpybla user, cur-user   ;

       setspp @ph, org-ph     ;

       callx pco?sept(qsygetph-entry),

         al-qsygetph-short, * ; /* When   specify *CURRENT for parm user, */

                               /* QSYGETPH expects 3-4 parms           */

       cpynv qrcvdtaq?len, 12 ;

       setspp @qrcvdtaq?msg, org-ph ;

       callx pco?sept(qsnddtaq-entry),

         al-qsnddtaq, *       ;

brk "MEMO"                           ;

       /* Get the profile handle of the   target USRPRF */

       cpybla user, tgt-user   ;

       setspp @ph, ph         ;

       cmpbla(b) pwd, "*NOPWD" /   eq(spec-pwd-value) ;

       triml pwd-len, pwd, " " ;

       callx pco?sept(qsygetph-entry),

         al-qsygetph, *      ;

       b =+2                   ;

spec-pwd-value:

       callx pco?sept(qsygetph-entry),

         al-qsygetph-short, * ; /* When   specify a special value for parm */

                                 /* PASSWORD,   QSYGETPH expects 3-4 parms */

:

        /* Swap to target USRPRF */

       callx pco?sept(qwtsetp-entry),

         al-qwtsetp, *         ;

       callx pco?sept(qsyrlsph-entry),

         al-qsyrlsph, *       ; /* Release profile handle of   tgt-user */

end-swap-to:  

brk "TO"                      ;

       b @swap-to             ;

 

/* Routine: swap-back */

dcl insptr @swap-back   auto     ;

entry swap-back int             ;

       /* Resolve QTEMP/@SUDTAQ */

       cpybla rt(3:10), qrcvdtaq?q       ;

       setip @on-2201, end-swap-back     ;

       rslvsp @sudtaq, rt, @pco?qtemp, * ;

 

       /* Dequeue previous profile handle   from @SUDTAQ */

       setspp @qrcvdtaq?msg, org-ph ;

       callx pco?sept(qrcvdtaq-entry),

         al-qrcvdtaq, *       ;

       cmpnv(b) qrcvdtaq?len, 0 /   neq(end-deq) ;

       cpybla msg,

         "Exit to where? :p"   ;

       setspp @sndimdmsg?text,

         msg                   ;

       triml sndimdmsg?textl,

         msg, " "             ;

       cpybla sndimdmsg?msgtype,

         "*ESCAPE"             ;

       calli sndimdmsg, *, @sndimdmsg ;

       b end-swap-back                     ;

end-deq:

       /* Set profile handle to previous PH   */

       setspp @ph, org-ph     ;

       callx pco?sept(qwtsetp-entry),

         al-qwtsetp, *         ;

       callx pco?sept(qsyrlsph-entry),

         al-qsyrlsph, *       ;

 

end-swap-back:

       b @swap-back           ;

 

/* Exception handlers */

dcl excm excd-2201   excid(h'2201') bp(on-2201) imd ;

dcl insptr @on-2201   auto                ;

on-2201:

       cpybla msg,

         "Data queue @sudtaq does not   exist" ;

       cpybla msg(12:7), qrcvdtaq?q         ;

       setspp @sndimdmsg?text,

         msg                   ;

       triml sndimdmsg?textl,

         msg, " "              ;

       cpybla sndimdmsg?msgtype,

         "*DIAG"                     ;

       calli sndimdmsg, *, @sndimdmsg ;

brk "2201"                     ;

       b @on-2201             ;

 

/* QCMDEXC */

dcl con qcmdexc-entry   bin(2) unsgnd init(h'7C5')   ;

dcl dd pkd-cmd-len   pkd(15,5) auto               ;

dcl spcptr @pkd-cmd-len   auto init(pkd-cmd-len)   ;

dcl dd cl-cmd char(64)   auto                     ;

dcl spcptr @cmd-str auto   init(cl-cmd)           ;

dcl ol al-qcmdexc   (@cmd-str, @pkd-cmd-len) arg   ;

 

/* QSYGETPH */

dcl con qsygetph-entry   bin(2) unsgnd init(h'1305') ;

dcl dd user char(10)   auto                         ;

dcl spcptr @user auto   init(user)                   ;

dcl dd ph char(12)   auto                           ;

dcl dd org-ph char(12)   auto                        ;

       /* Profile handle of original USRPRF   */

dcl spcptr @ph auto                               ;

dcl dd ec-size bin(4) auto   init(0)                 ;

dcl spcptr @ec auto   init(ec-size)                 ;

dcl dd pwd-len bin(4) auto                         ;

dcl spcptr @pwd-len auto   init(pwd-len)             ;

dcl dd ccsid bin(4) auto   init(-1)                 ;

       /* determine CCSID according to the   current password level (QPWDLVL) */

dcl spcptr @ccsid auto   init(ccsid)                ;

dcl ol al-qsygetph (

       @user, @pwd, @ph, @ec, @pwd-len,   @ccsid

) arg                                     ;

dcl ol al-qsygetph-short (

       @user, @pwd, @ph

) arg                                     ;

dcl con cur-user char(10)   init("*CURRENT")         ;

 

/* QSYRLSPH */

dcl con qsyrlsph-entry   bin(2) unsgnd init(h'130B') ;

dcl ol al-qsyrlsph (@ph)   arg                       ;

 

/* QWTSETP */

dcl con qwtsetp-entry   bin(2) unsgnd init(h'1350') ;

dcl ol al-qwtsetp (@ph)   arg                     ;

 

/* QRCVDTAQ */

dcl con qrcvdtaq-entry   bin(2) unsgnd init(h'B51') ;

dcl dd qrcvdtaq?q char(10)   auto init("@SUDTAQ")   ;

dcl spcptr @qrcvdtaq?q auto   init(qrcvdtaq?q)     ;

dcl dd qrcvdtaq?lib   char(10) auto init("QTEMP")     ;

dcl spcptr @qrcvdtaq?lib   auto init(qrcvdtaq?lib) ;

dcl dd qrcvdtaq?len   pkd(5,0) auto init(p'12')     ;

dcl spcptr @qrcvdtaq?len   auto init(qrcvdtaq?len) ;

dcl spcptr @qrcvdtaq?msg   auto                     ;

dcl dd qrcvdtaq?waittime   pkd(5,0) auto init(p'0') ;

       /* Dequeue @SUDTAQ without waiting */

dcl spcptr   @qrcvdtaq?waittime auto init(qrcvdtaq?waittime) ;

dcl ol al-qrcvdtaq(

       @qrcvdtaq?q,

       @qrcvdtaq?lib,

       @qrcvdtaq?len,

       @qrcvdtaq?msg,

       @qrcvdtaq?waittime

) arg                           ;

 

/* QSNDDTAQ */

dcl con qsnddtaq-entry   bin(2) unsgnd init(h'B52') ;

dcl ol al-qsnddtaq(

       @qrcvdtaq?q,

       @qrcvdtaq?lib,

       @qrcvdtaq?len,

       @qrcvdtaq?msg

) arg                           ;

 

 

dcl con *EXIT char(10)   init("*EXIT") ;

dcl dd msg char(64) auto   init(" ")   ;

dcl dd flag bin(2) auto   init(0) ;

 

/* Includes */

/include   "sept.emi"             ;

/include   "sndimdmsg.emi"       ;

 

pend                           ;

 

Source files included by su.emi are available here: sept.emi, sndimdmsg.emi.

 

For your convenience, the C version of the CPP of SU is available here: su.c.

Let's Try It!

Imagine that you're developing a defect-tracking application. A physical file called BUGS is expected to store all reported defects. Three kinds of users of this defect-tracking application have different authorities to PF BUGS:

  • The administrator (identified by USRPRF ADMIN)User profile ADMIN owns the PF BUGS and hence has full authorities to BUGS.
  • Testers whose group profile is TESTERTesters are responsible for reporting defects to managers and programmers, who are expected to solve all detected defects. USRPRF TESTER has add and read authorities to PF BUGS but is prohibited from modifying or deleting BUGS records.
  • Programmers whose group profile is PGMRUSRPRF PGM has read and update authorities to PF BUGS.

 

The public and private authorities to different users are set by ADMIN as shown:

 

GRTOBJAUT OBJ(BUGS)   OBJTYPE(*FILE) USER(*PUBLIC) AUT(*EXCLUDE)

/* Revoke *PUBLIC authorities to BUGS */

 

GRTOBJAUT OBJ(BUGS)   OBJTYPE(*FILE) USER(TESTER) AUT(*USE)

GRTOBJAUT OBJ(BUGS)   OBJTYPE(*FILE) USER(TESTER) AUT(*ADD)

/* Allow TESETER to read BUGS or add   records to it */

 

GRTOBJAUT OBJ(BUGS)   OBJTYPE(*FILE) USER(PGMR) AUT(*CHANGE)

RVKOBJAUT OBJ(BUGS)   OBJTYPE(*FILE) USER(PGMR) AUT(*DLT)

/* Allow PGMR to read BUGS or update   records in it */

 

The following example steps demonstrate operations on PF BUGS under three different USRPRFs within a single job:

 

CLRPFM BUGS /* Under USRPRF ADMIN */

SU TESTER.

   STRQSH CMD('db2 "INSERT INTO BUGS   (DID, DDATE, DDESC)

     VALUES(''A01'', ''2013-01-01'',   ''Today is too cold.'')"')

     /* Under USRPRF TESTER */

   SU PGMR

   STRQSH CMD('db2 "UPDATE BUGS SET   DDESC=TRIM(DDESC) CONCAT ' - Solved'"')

       /* Under USRPRF PGMR */

   SU *EXIT

RUNQRY *N BUGS /* Under USRPRF TESTER */

SU *EXIT

 

Note that a DSPJOB OPTION(*STSA) can be used to check the current user profile of the job.

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$0.00 Raised:
$