MVS TOOLS AND TRICKS OF THE TRADE March 1997 Sam Golob MVS Systems Programmer sbgolob@cbttape.org Sam Golob is a Senior Systems Programmer. HARNESSING MACRO POWER - PART 1 Early in my career, before I felt comfortable programming in Assembler Language, I used to be very frustrated by some IBM pronouncements, to the effect that they've provided a programming interface to some system service, in the form of a macro. "You can't harness the macro unless you write a program", I used to moan to myself. Since I was then uncomfortable writing my own program, I started looking around for someone else's program. With access to the CBT MVS Utilities Tape, a large free software collection that is now orderable through NaSPA, I usually could find someone else's code which took advantage of the service. Then I could either use that program as is, or tailor it for my own needs. This process is looked upon disdainfully by the old timers, who would always try and write the complete code themselves. I have a different view, being of a little later vintage. First, if you have a working coded example, you don't have to repeat the little bugs and mistakes yourself. (This has the disadvantage, that you often never discover what they were in the first place.) Second, by carefully studying the other person's work, you can quickly find out how that macro interface can be used in a program, so you can easily write your own similar programs later. Third, you can save quite a bit of debugging time, and get the code into production quickly. Today, we'll talk about a few useful examples of this process. Hopefully, you'll get some good ideas, which you can then use in your work. A by-product of this study, is that you'll either find, or develop, some helpful utility programs that will benefit your shop and yourselves later. A RACF EXAMPLE If your shop has a security package other than RACF, don't turn yourself off at this point. You can probably write a similar program to fit the other security packages as well. If you do write such a program, the "whole world" will benefit if you'll send the code to the proprietor of one of the free software collections, such as the CBT Tape itself. You may find out the address of the proprietor by calling the NaSPA office. The problem is as follows: A Systems Programmer would like to interrogate RACF (in batch or TSO) to find out if a specific user has access to a specific resource, and what that access is. Sometimes this information is not easily obtainable from RACF's ISPF interface. For example we have the following case: Our specific user is connected to several groups, and each group has some level of access to the resource. It is not easy to know, from the RACF ISPF display directly, what our specific user's actual maximum access level is. Each of the groups might have a different access level to the resource. We want to know positively, and right now, the highest level of access that our user has, to that resource. A related problem is familiar to EDP Auditors, but a Systems Programmer might want to know this information as well. We want to know if a specific user has UPDATE access or higher, to any system linklist libraries, LPA list libraries, or APF authorized libraries. We can list out all such libraries in our system, and we want to query the security package if our specific user has the power to update any of them. RACF's ISPF interface is almost helpless in this situation. There is a system macro interface provided by IBM, which can give this information. This is the RACHECK macro. However, the RACHECK macro is useless unless it is put into a program. On the CBT MVS Tape, File 106, there is a program which harnesses the power of the RACHECK macro service, to easily give us the information we want. How does the program (called RACCESS) work? The program takes card-image input for each security interrogation request. Output is sent to a normal print line. Input is read and output is written via simple QSAM GET and PUT. In this simple application, the input cards are not parsed, but the input has to be in fixed columns, and is broken into fields. Userid, resource class, and profile name are in fixed fields, and once the input card is read, these fields are copied into the appropriate input fields for the RACHECK macro. See Figure 1 for details on how the RACHECK macro is set up. The outputs from the RACHECK macro are formatted (eventually) into a print line. There is a little more to it. RACHECK is quite inflexible, so our program has to invoke it repeatedly in the following way: There are various "degrees of access" possible within RACF. No access to the resource is called "NONE". Then, in increasing order, are READ, UPDATE, CONTROL, and finally ALTER, which is full access for creating and deleting the resource. An invocation of RACHECK has to specify exactly which degree of access we are querying for. Thus, our program first must invoke RACHECK to check for ALTER access. Then it must invoke RACHECK again (if ALTER access is not granted), to see whether CONTROL access is granted. If CONTROL hasn't been granted, then another invocation of RACHECK specifically asks if UPDATE is granted. If UPDATE isn't there, then another RACHECK is done for READ. Finally, if READ isn't granted, NONE is assumed and is reported. With the first return of a "YES", an appropriate output line is formatted and written. See Figure 2 for a sample input to and output from the RACCESS program. The main result we have here is a practical utility which takes normal, easily controllable input, runs it into a macro service facility, and formats convenient, readable output. An assembler macro service has thus been converted into a very usable tool. Just to complete the picture, I'd like to show how this utility can be used to solve the EDP Auditor problem mentioned above. We want to show if a particular userid has UPDATE or higher access to linklist, LPA list, and APF authorized libraries. We can copy the LNKLSTxx, LPALSTxx and PROGxx members of SYS1.PARMLIB to our own card image dataset, and edit them to create a combined list of dataset names, starting in column 17 of the cards. Then, columns 1 to 8 can be filled in (left justified and padded with blanks) with the USERID name to be tested. Columns 9 to 16 can be filled in with the resource class name of "DATASET". Thus, the input deck of cards is complete. This deck is run into our program, and a corresponding output list is produced, which gives the actual level of access that the user has, to each library. If any of these accesses are UPDATE, CONTROL, or ALTER, these can be easily spotted in the program's print output. To check a different USERID, just do a global change for column 1 in the entire input deck. The problem has been solved. File 106 from the CBT Tape includes a member which is a sample CLIST to invoke the RACCESS program in the foreground. Output is displayed on the screen, from an input card-image deck in a dataset. Since the RACCESS program produces conventional QSAM SYSPRINT output, there is no trouble displaying this when the program is invoked in the foreground under TSO. One further note. Under some conditions, for example if the RACF Audit option is turned on for datasets, running the RACCESS program with its many invocations of the RACHECK macro, can produce unwanted RACF console messages and SMF Type 80 Records for each RACHECK invocation. This can be gotten around by coding the LOG=NONE parameter in the RACHECK macro, and running the program authorized. To do so, the RACCESS program has to be linkedited with SETCODE AC(1) and run out of an authorized library. Most Systems Programmers have the facilities to run their own authorized programs. A TSO EXAMPLE In the newer "TSO/E Customization" manuals, an IBM macro service is described, which interfaces with the USERID records of the SYS1.BRODCAST dataset. The macro is called IKJIFRIF, and you have to write a program in the form of a TSO Command Processor, to use the service. I'll show you what the service is for, and how I easily wrote a simple TSO Command program to interface to it. The SYS1.BRODCAST dataset performs a service for TSO users at an installation. This is to hold system messages that are destined to be sent to a certain TSO user, so that the messages can be displayed when that user logs on to TSO later. The TSO SEND or Operator SEND commands are used to create the messages. If the user is currently logged on, then the message is sent directly to that user's terminal. If the user is not logged on, then the message is written to a record in the SYS1.BRODCAST dataset, and chained to that user's string of pending message records. The beginning and end of this chain is pointed to by a USERID record in the SYS1.BRODCAST dataset. (We are assuming that individual TSO Userlogs are not being used.) When the TSO user logs on, LOGON processing invokes the LISTBC program, which displays the user's messages at the terminal, and deletes all messages on the user's chain. If the user never logs on, all of the messages in that user's chain are piled up, and the chain grows longer and longer as more messages are sent to that user. Suppose a user leaves the company. Then if production job messages continue to be sent to that user, it is possible for the SYS1.BRODCAST dataset, being a direct access, formatted dataset, to fill up. IBM does not provide a means of displaying and deleting a different user's SYS1.BRODCAST messages. You can only delete your own. However, IBM does provide the IKJIFRIF macro interface to delete that user's USERID record and all the attached messages. The IKJIFRIF macro can also be used to add a new USERID record to the SYS1.BRODCAST dataset, so that after the particular userid and its messages have been deleted from SYS1.BRODCAST, the same userid can be reinstated later, with none of the old messages attached to it. This can be used as one means of clearing an arbitrary user's unwanted messages out of SYS1.BRODCAST. The only obstacle is that a program must be written to harness the power of the IKJIFRIF macro. In the TSO/E Customization manual, the usage of the IKJIFRIF macro is explained. You have to invoke the macro as part of a TSO Command Processor program that has a parsing facility for a parameter. TSO Command Processors are programs that are designed to run under TSO as a command. A TSO Command Processor program assumes that Register 1 points to a control block called the CPPL, or Command Processor Parameter List. The CPPL is filled in when the program is invoked under TSO as a command. Thus, the 4 fullwords of the CPPL, which point to the "command buffer" and 3 TSO control blocks, have to be accessible to our program. In other words, a program which invokes IKJIFRIF has to start by copying all four data area pointers to program storage. In order for IKJIFRIF to be invoked, you then have to tell IKJIFRIF where to find the 3 TSO control blocks, whose pointers you got from the CPPL. Then you tell IKJIFRIF if you want to ADD or DELETE a userid record in SYS1.BRODCAST. Finally, the TSO command has to parse the command buffer somehow, to retrieve the name of a userid to tell IKJIFRIF which name to work with. I have coded such programs, which are part of my SYS1.BRODCAST handling package that is on File 247 of the CBT Tape. They are called BCMUSADD for adding a userid record, and BCMUSDEL for deleting a userid record. All they do is take a TSO Command Processor shell, and "wrap it around" an invocation of the IKJIFRIF macro, to ADD or DELETE the particular userid record which was specified by the command parm. IKJIFRIF does the rest. It is really very simple. For an example of invoking IKJIFRIF, see Figure 3. I hope that I have whetted your appetite to look for other macros to make utilities out of. Any macro which takes some input, and which invokes a service to return a result, is a candidate for such treatment. Next time, I hope to elaborate on more details of carrying out this process. Meanwhile, good luck and good hunting. * * * * * * * * * * * * * * * * * * * * * * * Figure 1. Invocation of the RACHECK macro from within the RACCESS program. If the CLASS is "DATASET", then this invocation is done. If not, then the VOLSER parameter is left out. Please notice that the OUTDSN, VOLSER, RACCLASL, and OUTUID are data names which contain information that came either from the input cards or from the result (in the case of the VOLSER name) of an invocation of the LOCATE macro within the program. ALTER is hard-coded. With other invocations of RACHECK in this program, CONTROL, UPDATE, or READ are hard-coded instead of ALTER, in the ATTR keyword. RACHECK ENTITY=(OUTDSN),ATTR=ALTER,LOG=NONE, X VOLSER=VOLSER,CLASS=RACCLASL, X RELEASE=1.8,USERID=OUTUID * * * * * * * * * * * * * * * * * * * * * * * Figure 2. Input to the RACCESS program and output from the program. Note that the System Programmer ID can alter SYS1.LINKLIB and it can use the Logon Procedure called SYSTMPRC, whereas the two user level ID's can only read the system libraries, and they cannot use the System Programmer's Logon Procedure. Input (on 80-column card-image records) USER01 DATASET SYS1.LINKLIB USER01 DATASET SYS1.LPALIB USER01 TSOPROC SYSTMPRC USER02 DATASET SYS1.LINKLIB USER02 DATASET SYS1.LPALIB USER02 TSOPROC SYSTMPRC SYSPG1 DATASET SYS1.LINKLIB SYSPG1 TSOPROC SYSTMPRC Output (on a print line) USERID CLASS RESOURCE NAME ACCESS LEVEL USER01 DATASET SYS1.LINKLIB READ USER01 DATASET SYS1.LPALIB READ USER01 TSOPROC SYSTMPRC NO ACCESS USER02 DATASET SYS1.LINKLIB READ USER02 DATASET SYS1.LPALIB READ USER02 TSOPROC SYSTMPRC NO ACCESS SYSPG1 DATASET SYS1.LINKLIB ALTER SYSPG1 TSOPROC SYSTMPRC READ * * * * * * * * * * * * * * * * * * * * * * * Figure 3. Execution of the IKJIFRIF macro to either delete or add a userid record in the SYS1.BRODCAST dataset. The ECT, PSCB, and UPT are TSO control blocks which are passed to this program when it is executed as a TSO command. For full information on the IKJIFRIF macro, see the TSO/E Customization manual. This is the execute form of the macro. Label ADDIDL contains the list form. ADDIDL IKJIFRIF MF=L DODEL IKJIFRIF ECT=SAVEECT,PSCB=SAVEPSCB,UPT=SAVEUPT,DEL=DELADR, X RETCODE=IKJIFRET,MF=(E,ADDIDL) DOADD IKJIFRIF ECT=SAVEECT,PSCB=SAVEPSCB,UPT=SAVEUPT,ADD=ADDADR, X RETCODE=IKJIFRET,MF=(E,ADDIDL)