Copyright 1987-2016 by Kevin G. Barkes All rights reserved. This article may be duplicated or redistributed provided no alterations of any kind are made to this file. This edition of DCL Dialogue is sponsored by Networking Dynamics, developers and marketers of productivity software for OpenVMS systems. Contact our website www.networkingdynamics.com to download free demos of our software and see how you will save time, money and raise productivity! Be sure to mention DCL Dialogue! DCL DIALOGUE Originally published July, 1987 A UIC Utility By Kevin G. Barkes The DCL command files featured this month complete the trio of command procedure sets which use "stacks" for saving and restoring various environmental items. UPUSH.COM and its associated utilities permit "stacking" UICs, a useful tool for system managers and other privileged users who have a need to switch user identification codes for various purposes. For a complete explanation of the stack concept, readers are directed to last month's column and the January 1987 issue. These UIC procedures are based on the same principle, the use of global symbols to push and pop UICs on a DCL- maintained "stack". There are a few quirks in these command files which require explanation. In order for a process to modify its UIC, the change mode to kernel (CMKRNL) privilege must be available. Alternately, the process must have SETPRV, which permits any process privilege to be enabled, including CMKRNL. The procedures verify the authorization of the privileges by using the F$GETJPI and F$PRIVILEGE lexical functions. As shown in the UPUSH.COM procedure, the string containing the list of privileges which the process may legally enable is saved in the symbol AUTH_PRIV; the size of the string of authorized privileges is stored in the symbol AUTH_SIZE. AUTH_SIZE is used in conjunction with the F$LOCATE lexical to test for the presence of a target string within AUTH_PRIV. First, F$PRIVILEGE is used to determine if the calling process currently has CMKRNL enabled. If this test fails, the F$LOCATE lexical is used to scan AUTH_PRIV to see if CMKRNL can be turned on. Since it's common for users to employ SETPRV to enable potentially dangerous privileges only when necessary, that privilege is the next target of F$LOCATE. Should both tests fail, the procedure exits with an "insufficient privilege" message. Otherwise, CMKRNL is enabled, the UIC changed, and the old UIC is stored in the stack. If CMKRNL had to be enabled to execute the procedure, it is disabled prior to procedure termination. The caveats dealing with excessive use of DCL symbol space which were noted in the previously-published default directory and privilege procedures are also valid here. However, the symbols containing UICs are generally far smaller than their protection and default counterparts. Last month's reader-suggested enhancements and modifications are similarly valid. ******** Beginning next month, we'll be featuring versatile procedures submitted by readers. You're more than welcome to join the growing number of DCL Dialogue participants, To speed things along, please try adhere to the following suggestions: - The procedure should work. This may seem to be an inane requirement, but you'd be surprised at the number of submissions we get that are D.O.A. If the procedure is particularly intriguing, we may try debugging it to get it to operate, but generally we return the file with a request the author correct the problem. - The procedure should do something. By that, I mean it should serve some useful purpose, or present a fresh approach to an old problem. Procedures which demonstrate iterative symbol substitution are great but of limited interest, unless they also perform some other function. - The procedure should be brief. Due to space limitations, extremely large command files can't be easily integrated into DCL Dialogue. If your procedure isn't a good fit here, it may be used as a separate feature. - Don't reinvent the wheel. Needless to say, we're not really looking for default directory switchers right now. - Don't delay. It would appear great minds think alike. We frequently receive two or three virtually identical procedures within a few days of each other. As a rule, we use the first bug-free file we get. - Don't be anonymous. Make certain you include your return address. It's a good idea to put it in the procedure itself, so readers can contact you directly for comments or for additional information. - Be patient. Allow four to six weeks for a response to your submission. I do respond to ALL letters. If selected, it may take a while for your procedure to appear, due to lead time and the number of programs ahead of you in the "queue". - Be open to suggestion. We may request a few changes in your procedure to make it more suitable for publication. Please be open-minded; the modifications are for your benefit as well. --- Reader Tony Carter dialed into ARIS to note the INQUIRE bug mentioned in the May column has been fixed. Input to INQUIRE made with the terminal set to NOECHO no longer appears in the RECALL buffer. I originally learned of the problem via system dispatches, and confirmed the problem on an earlier version of VMS. DEC obviously was on the stick with this one. Thanks to DEC for plugging a potential security hole, and to Tony for bringing us all up to date. --- PROGRAM 1 $! UPUSH.COM $! "Pushes" UIC onto stack. $! Disable error processing $ SET NOON $! If no UIC is specified, print help message $ IF P1 .NES. "" THEN GOTO CHECKUIC $ WRITE SYS$OUTPUT "Usage: UPUSH [group,member]" $ EXIT $ CHECKUIC: $! Save the original UIC, and initialize symbols: $ ORIGINAL_UIC = F$GETJPI("","UIC") $ CURRENT_KERNEL = 0 $ AUTH_KERNEL = 0 $ AUTH_SETPRV = 0 $ AUTH_SIZE = F$LENGTH(F$GETJPI("","AUTHPRIV")) $ AUTH_PRIV = F$GETJPI("","AUTHPRIV") $! See if we have or can set CMKRNL or SETPRV privileges: $ IF F$PRIVILEGE("CMKRNL") THEN CURRENT_KERNEL = 1 $ IF CURRENT_KERNEL THEN GOTO SET_UIC $ IF F$LOCATE("CMKRNL",AUTH_PRIV) .NE. AUTH_SIZE - THEN AUTH_KERNEL = 1 $ IF F$LOCATE("SETPRV",AUTH_PRIV) .NE. AUTH_SIZE - THEN AUTH_SETPRV = 1 $ IF AUTH_KERNEL .OR. AUTH_SETPRV THEN GOTO SET_PRIVS $ COPY/NOLOG SYS$INPUT: SYS$OUTPUT: You do not have the privileges required to use this command. $ EXIT $ SET_PRIVS: $ SET PROCESS/PRIVILEGE = CMKRNL $ SET_UIC: $ SET UIC 'p1' $ IF $STATUS THEN GOTO FIX_STACK $ WRITE SYS$OUTPUT "Error setting UIC to ''p1'." $ GOTO EXIT $ FIX_STACK: $! Initialize the stack if necessary: $ IF F$TYPE(U_NUM) .EQS. "" THEN U_NUM == 0 $! Increment the stack $ U_NUM == U_NUM + 1 $! "Push" the old uic on the stack $ UIC_'U_NUM' == ORIGINAL_UIC $ EXIT: $ WRITE SYS$OUTPUT "UIC currently ",F$GETJPI("","UIC"),"." $! Restore old privilege: $ IF .NOT. CURRENT_KERNEL THEN SET PROCESS/PRIVILEGE=NOCMKRNL $ EXIT PROGRAM 2 $! UPOP.COM $! "Pops" UIC from stack $! Disable error processing $ SET NOON $! Exit if nothing to pop; otherwise, pop $ IF F$TYPE(U_NUM) .EQS. "" THEN U_NUM == 0 $ IF F$TYPE(UIC_'U_NUM') .NES. "" THEN GOTO DO_POP $ NO_POP: $ WRITE SYS$OUTPUT "No PUSH to POP! Stack pointer: ",U_NUM $ EXIT $! "Pop" the last uic from the "stack" $ DO_POP: $ CURRENT_KERNEL = 0 $ AUTH_KERNEL = 0 $ AUTH_SETPRV = 0 $ AUTH_SIZE = F$LENGTH(F$GETJPI("","AUTHPRIV")) $ AUTH_PRIV = F$GETJPI("","AUTHPRIV") $! See if we have or can set CMKRNL or SETPRV privileges: $ IF F$PRIVILEGE("CMKRNL") THEN CURRENT_KERNEL = 1 $ IF CURRENT_KERNEL THEN GOTO POP_UIC $ IF F$LOCATE("CMKRNL",AUTH_PRIV) .NE. AUTH_SIZE - THEN AUTH_KERNEL = 1 $ IF F$LOCATE("SETPRV",AUTH_PRIV) .NE. AUTH_SIZE - THEN AUTH_SETPRV = 1 $ IF AUTH_KERNEL .OR. AUTH_SETPRV THEN GOTO SET_PRIVS $ COPY/NOLOG SYS$INPUT: SYS$OUTPUT: You do not have the privileges required to use this command. $ EXIT $ SET_PRIVS: $ SET PROCESS/PRIVILEGE = CMKRNL $ POP_UIC: $ SET UIC &UIC_'U_NUM' $ IF .NOT $STATUS THEN GOTO DO_ERROR $! Decrement "stack" $ U_NUM == U_NUM - 1 $ WRITE SYS$OUTPUT "UIC changed to ",F$GETJPI("","UIC") $ GOTO EXIT $! Handle errors: $ DO_ERROR: $ WRITE SYS$OUTPUT "Invalid UIC ",UIC_'U_NUM' $ WRITE SYS$OUTPUT "UIC remains ",F$GETJPI("","UIC") $ U_NUM == U_NUM - 1 $ IF F$INTEGER(U_NUM) .EQ. 0 THEN GOTO EXIT $ WRITE SYS$OUTPUT "Next item on stack: ",U_NUM,". ",UIC_'U_NUM' $ EXIT: $ IF .NOT. CURRENT_KERNEL THEN SET PROCESS/PRIVILEGE=NOCMKRNL $ EXIT PROGRAM 3 $! USWAP.COM $! "Swaps" current UIC with one in the UIC stack $! Disable error processing $ SET NOON $! Exit if nothing to "swap" $ IF F$TYPE(U_NUM) .EQS. "" THEN U_NUM == 0 $ IF F$TYPE(UIC_'U_NUM') .NES. "" THEN GOTO DO_SWAP $ NO_SWAP: $ WRITE SYS$OUTPUT "Nothing to SWAP! Stack pointer: ",U_NUM $ EXIT $ DO_SWAP: $ IF P1 .EQS. "" THEN P1 = U_NUM $ IF P1 .GT. 0 .AND. P1 .LE. U_NUM THEN GOTO DO_SWITCH $ WRITE SYS$OUTPUT "SWAP out of range!" $ EXIT $ DO_SWITCH: $ CURRENT_KERNEL = 0 $ AUTH_KERNEL = 0 $ AUTH_SETPRV = 0 $ AUTH_SIZE = F$LENGTH(F$GETJPI("","AUTHPRIV")) $ AUTH_PRIV = F$GETJPI("","AUTHPRIV") $! See if we have or can set CMKRNL or SETPRV privileges: $ IF F$PRIVILEGE("CMKRNL") THEN CURRENT_KERNEL = 1 $ IF CURRENT_KERNEL THEN GOTO SWAP_UIC $ IF F$LOCATE("CMKRNL",AUTH_PRIV) .NE. AUTH_SIZE - THEN AUTH_KERNEL = 1 $ IF F$LOCATE("SETPRV",AUTH_PRIV) .NE. AUTH_SIZE - THEN AUTH_SETPRV = 1 $ IF AUTH_KERNEL .OR. AUTH_SETPRV THEN GOTO SET_PRIVS $ COPY/NOLOG SYS$INPUT: SYS$OUTPUT: You do not have the privileges required to use this command. $ EXIT $ SET_PRIVS: $ SET PROCESS/PRIVILEGE = CMKRNL $ SWAP_UIC: $ ORIGINAL_UIC = F$GETJPI("","UIC") $ SET UIC &UIC_'P1' $ IF .NOT $STATUS THEN GOTO DO_ERROR $ UIC_'P1' == ORIGINAL_UIC $ WRITE SYS$OUTPUT "UIC changed to ",F$GETJPI("","UIC") $ GOTO EXIT $! Handle errors: $ DO_ERROR: $ WRITE SYS$OUTPUT "Invalid UIC ",UIC_'P1' $ SET UIC 'ORIGINAL_UIC' $ WRITE SYS$OUTPUT "UIC remains ",F$GETJPI("","UIC") $ EXIT: $ IF .NOT. CURRENT_KERNEL THEN SET PROCESS/PRIVILEGE=NOCMKRNL $ EXIT PROGRAM 4 $! UDIRS.COM $! Displays contents of the UIC "stack" $! Disable error processing $ SET NOON $! Exit if nothing to display $ IF F$TYPE(U_NUM) .EQS. "" THEN U_NUM == 0 $ IF F$INTEGER(U_NUM) .GT. 0 THEN GOTO DO_UIC $ NO_UIC: $ WRITE SYS$OUTPUT "Nothing in stack." $ WRITE SYS$OUTPUT "Current UIC: ",F$GETJPI("","UIC") $ EXIT $! Display "stack" $ DO_UIC: $ L_NUM = U_NUM $ WRITE SYS$OUTPUT "Current UIC ",F$GETJPI("","UIC") $ DO_LOOP: $ IF F$TYPE(UIC_'L_NUM') .EQS. "" THEN GOTO DEC_COUNTER $ WRITE SYS$OUTPUT "(''L_NUM'.) ",UIC_'L_NUM' $! Decrement counter $ DEC_COUNTER: $ L_NUM = L_NUM - 1 $ IF L_NUM .EQ. 0 THEN EXIT $ GOTO DO_LOOP $ EXIT ---------- Kevin G. Barkes is an independent consultant. He publishes the KGB Report newsletter, operates the www.kgbreport.com website, lurks on comp.os.vms, and can be reached at kgbarkes@gmail.com.