MVS TOOLS AND TRICKS OF THE TRADE July 1995 Sam Golob MVS Systems Programmer sbgolob@cbttape.org Sam Golob is a Senior Systems Programmer EXPLORING SYS1.BRODCAST This month, we shall begin a tour of an "ancient" IBM data structure, the SYS1.BRODCAST dataset which is part of TSO. The TSO broadcast dataset has to do with holding messages for TSO users. Besides providing messaging service for active users, TSO also supplies the capability of preserving messages to inactive users until those users will logon to the system later. In the meantime, their yet undelivered messages have to be kept someplace. That place is the SYS1.BRODCAST dataset. In later versions of TSO/E, there is an option of allocating individualized user logs for holding the TSO user messages which have been deferred. However, even when user logging is active, the SYS1.BRODCAST dataset is still needed for public notices and for messages to users who haven't allocated their own logs yet. In our discussion, we won't dwell on the user logs, since the central issue of deferred TSO messaging is still primarily the structure of the SYS1.BRODCAST dataset. The SYS1.BRODCAST dataset also contains non-deletable messages that are sent to all TSO users when they either logon, or issue the LISTBC command. These global messages, which are more accurately called "notices", are displayed to all TSO users unless they set a special flag at logon time, not to receive them. The global notices are kept in different types of records in SYS1.BRODCAST than the user-directed messages. We shall talk about this in more detail later. To my knowledge, TSO messages have their origin either in the TSO SEND command, or from the SEND subcommand of the OPERATOR TSO command. Even when you put NOTIFY messages in your JCL, a TSO SEND command is generated. This will be readily apparent if you look at your system's SYSLOG and watch what happens when jobs end. SEND tries to route each message directly to the TSO userid, and will fail if the user is not logged on. However if you use the extra "LOGON" subparameter of SEND, a logged on recipient will get the message immediately as before, while all other recipient id's have their messages stored in the broadcast dataset, waiting until they logon to TSO later. Three principal programs open the SYS1.BRODCAST dataset. They are: SEND, LISTBC, and SYNC. I'll quickly explain the functions of these three programs: SEND originates messages, between either TSO users or consoles. LISTBC displays and deletes all messages in the broadcast dataset that are addressed to a particular user. LISTBC is also called during TSO logon processing, to display and delete all of that user's saved-up messages at logon time. SYNC clears all user messages out of the broadcast dataset. In addition, SYNC creates and initializes all TSO userid entries in the broadcast dataset from the userids defined to either SYS1.UADS or the RACF database. The rest of our talk will describe the functions of these three programs, as they relate to the internal structure of the broadcast dataset. BROADCAST DATASET INTERNALS. Let's give a quick overview of the SYS1.BRODCAST dataset's internal structure. The broadcast dataset is a fixed unblocked keyed dataset with a one byte key, and 129 bytes of data in each record. There are seven types of records, indicated by the value in the key. They are termed types 0 thru 5 (Hex '00' thru '05') plus the X'FF' records for messages that have been deleted. See Figure 1 for an overview of the purpose of each record type. It is difficult to ISPF browse the complete SYS1.BRODCAST dataset, because the last character of each record gets left out. To accurately look at the SYS1.BRODCAST dataset, I use the REVIEW TSO command, a very powerful public-domain full screen browser. REVIEW can be obtained from File 134 of the CBT MVS Utilities Tape, which can be ordered through the NaSPA office. The Fullscreen ZAP program from the same file, is also useful for examining each broadcast record in detail. You can picture the structure of the broadcast dataset (odd as it is) in such a way that it makes logical sense. See Figure 2 for an overview. There is a general header record, called a type 4. Next, bear in mind that SYS1.BRODCAST keeps two kinds of messages: the global notices for all userids, and the individual messages intended for single TSO userids. Each type has a different part of the broadcast dataset set aside for its use. The global non-deletable notices have a section reserved near the beginning, with a sub-section for directory records (type 0) and another sub-section for the notice records (type 2) themselves. Then the user message section follows. This also has two parts: the userid sub-section containing entries for all TSO userids eligible to receive deferred messages, and the message sub-section, containing lists of messages chained from each userid record that has messages pending. This classification of "sections" is not anything official. In fact, different types of broadcast records can be in mixed-up order as long as all the pointers point correctly. However, thinking of SYS1.BRODCAST as divided into sections rather than into record types makes your picture of its overall structure easier to grasp. It pays to say a few words about the chaining mechanism. The broadcast dataset is unblocked. Therefore logical records are also physical. All physical broadcast records can be found by a relative record method of direct access, starting from the top of the dataset. In other words, one can directly refer to the 1362nd record off the beginning of the dataset, for example. This would be record 552 in hex. In this system, we are counting the first record (the header) as record zero, and record X'552' would be the 1362nd record after the header, or the 1363rd record from the top. The header record (type 4) contains a relative record pointer to the first userid record (type 1). And all subsequent userid records are then chained off each other until they end. Every userid record has room for nine userid entries. Each userid entry consists of 13 bytes: a 7-byte userid field followed by followed by two three-byte binary numeric fields. If there are no messages pending for that user, both numeric fields are set to binary zeros. If there are messages pending, the first three-byte field contains the hexadecimal relative record address of the first message (type 3), while the second field contains the relative record address of the last message in the message chain for that user. Thus, if there is only one message for that user, both numbers are equal and non-zero. The last three bytes in each message record contain a relative record pointer to the next message record in the chain. If we are at the last message for this user, the last three bytes in the message record will contain hex zeros. Deleted messages have a key of X'FF', and must contain the Record Number of that record, the "R" from the TTR value, in the first data byte. This system is not too complicated, once you get used to it. Take a good look at Figure 2, which should help you to grasp what is going on. MANAGING SYS1.BRODCAST. As we mentioned before, the three IBM commands: SEND, LISTBC, and SYNC affect the SYS1.BRODCAST dataset. However, for our practical purposes as system doctors, we have to treat the subject of how to solve messaging problems, and we have to know what tools we can use. The most common problem occurs when SYS1.BRODCAST fills up with undelivered messages. No new messages can be deferred for logged off users. What can we do? IBM says we can't do much, and they provide one tool: the SYNC command, or the SYNC subcommand of the ACCOUNT command. SYNC takes the big bludgeon and says: "Your broadcast dataset is full, initialize the entire user message section!" Even if only one dead user is accumulating 4000 messages, you have to clean the whole thing out, according to IBM. With our methods, we will be able to stave off this occurrence. However, the SYNC command performs another useful function, and there are occasions when we need to use it. When we define a new TSO userid, that id cannot receive deferred messages until a userid entry is defined in SYS1.BRODCAST. This is because all deferred messages are chained off the userid entry, and when there is no entry, there can be no messages stored. SYNC initializes the entire user record section of SYS1.BRODCAST, either from all userids defined in SYS1.UADS, or from those in the RACF (or other security system) database, or from both of these. Thus, if you've added any new userids, you could use SYNC to let SYS1.BRODCAST know about them. However, if you have RACF and you've defined a new userid with a TSO segment, you'll get a userid entry created in SYS1.BRODCAST the first time you try to send a deferred message to the new id. RACF seems to take care of it. IBM has now provided an official interface for managing new userid entries. This interface is through a macro, IKJIFRIF, which accesses the SYS1.BRODCAST interface routine IKJIFR00. Details about using IKJIFRIF are described in the TSO/E Customization manual. Through IKJIFRIF, we can write a TSO command processor to add, delete, or rename userid entries in SYS1.BRODCAST. To my knowledge, sample code is not provided by IBM but I myself hope to write such a command processor soon. It looks to be quite easy, and I hope some of our readers will try it. Getting back to our problem of draining unwanted messages that have accumulated for users who will never logon, one answer lies in cleverly harnessing the power of the LISTBC command. LISTBC is an authorized command which (by default) lists broadcast notices, followed by all the deferred messages which have accumulated for a single userid. LISTBC is usually invoked by TSO logon processing, which is why you get all of your old messages when you logon. After LISTBC has displayed an id's deferred messages, it deletes them all. You can (normally) only do this for your own id, and not for other ids. However, we system doctors have to try and "tickle" LISTBC to work on other people's messages. My friend Bob Campanella came up with a clever idea that works. We can write a program that will do the following: First it links to LISTBC to clean out your own messages. This will zero the message pointers of your own id's SYS1.BRODCAST userid entry. Then your program copies the pointers from the other id's userid entry into yours, and zeroes his pointers out. Now, your userid entry has the pointers to his messages. Finally your program links to LISTBC again. This will display and clean out the other id's messages. Quite neat. Another approach would be to point LISTBC to think that the other id is your id. This also works, and I have written code to prove it. A third approach is to write a program that will avoid LISTBC altogether, and deal directly with the internals of SYS1.BRODCAST. This third strategy has the disadvantage that it depends on your current knowledge of the broadcast dataset internals, whereas LISTBC is an officially maintained IBM program that may be adjusted by IBM for future changes. I must also mention that there are programs on the CBT MVS Tape which will display which userids have messages waiting. The BRODSCAN program from File 136 of the CBT Tape will show you all this information. And the BRDANL program from Tim Vanderwall, on File 141, will actually display pending messages without deleting them afterwards. I hope I have gotten your thinking juices started. Good luck to all of you. We'll see you next month. * * * * * * * * * * * * * * * * * * * * * * * Figure 1. OVERVIEW OF SYS1.BRODCAST RECORD TYPES. The following is a summary of the record types in the SYS1.BRODCAST dataset. The five macros mentioned below can also be found on File 179 of the CBT MVS Tape. Type 0: Contains relative record index pointers to NOTICE records (type 2). 25 pointers per record. Mapped by IKJZT302 from PVTMACS (TSO optional materials). Type 1: Contains TSO userid entries (9 per record) which point to relative record numbers of the starting and ending messages in that userid's chain. Both pointers are zero if there are no messages for that userid. Each userid entry (13 bytes) is mapped by macro IKJZT304 from PVTMACS. Type 2: Broadcast NOTICES record for permanent notices to all TSO users. Mapped by macro IKJZT303. Type 3: User message record for individual user mail messages to a particular TSO user. Mapped by macro IKJZT305. Type 4: SYS1.BRODCAST header record. Mapped by macro IKJZT301. Type 5: Pointer to current first re-usable deleted record (type FF). This is probably for SEND to know where it can start writing the next user mail message. No mapping macro. First data byte: hex zeros, reserved. Next three bytes: a binary relative record number pointing to a type FF record. The rest of the record contains binary zeros. Type FF: Contains a deleted user message record. In a type FF record the first data byte must contain the Record Number in Hex of that record on the track. This is the "R" from the TTR of the record. If that byte of the type FF record does not contain this value, the SEND program will have trouble, and SYS1.BRODCAST may appear full when it really isn't. * * * * * * * * * * * * * * * * * * * * * * * Figure 2. Below is a pictorial view of the broadcast which should help one to be able to navigate it successfully. (This diagram is courtesy of Jim Marshall.) S Y S 1 . B R O D C A S T =============================================================== = = = *--*--------*---------*----------------------------*---* = = *04* * R1USPTR * 'SYS1.BRODCAST DATA SET' * * = = *--*--------*-----+---*----------------------------*---* = = + = = ++++++++++++++++++ = = + 1ST USER MAIL DIRECTORY RECORD (EACH USERID = = + ENTRY CONSISTS OF A 13 BYTE AREA; THE USERID, = = + NEXT RBA POINTER AND ENDING RBA POINTER. = = + = = *--*-------*---*----* /// *------*---*----*--*-----* = = *01* USERID*RBA*ERBA* /// *USERID*RBA*ERBA*7F* RBA * = = *--*-------*---*----* /// *------*---*----*--*--+--* = = + = = +++++++++++++++++++++++++++++++ //// +++++++++++ = = + = = + LAST USER MAIL DIRECTORY RECORD = = + = = + NOTE: '000' ENDS THE CHAIN OF X'01' RECORDS. = = *--*-------*---*----* /// *------*---*----*--*-----* = = *01* USERID*RBA*ERBA* /// *USERID*RBA*ERBA*7F* 000 * = = *--*-------*---*----* /// *------*-+-*-+--*--*--+--* = = + + = = +++++++++++++++++++++++++++++++++++ +++++++++++++++++ = = + + = = + 1ST MESSAGE RECORD FOR USER + = = *--*-----------------------------------------*-----* + = = *03* * RBA * + = = *--*-----------------------------------------*--+--* + = = + + = = ++++++++++++++++++++++++++ //// ++++++++++++++++ + = = + + = = +++++++++++++++++++++++++++++++++++++++++++++++++++++++ = = ++ LAST MESSAGE RECORD FOR USER = = ++ NOTE: '000' ENDS THE CHAIN OF X'03' RECORDS. = = *--*-----------------------------------------*-----* = = *03* * 000 * = = *--*-----------------------------------------*-----* = = = = FREE SPACE = = *--*-----------------------------------------------* = = *FF* * = = *--*-----------------------------------------------* = = = ===============================================================