MVS TOOLS AND TRICKS OF THE TRADE October 1997 Sam Golob MVS Systems Programmer P.O. Box 906 Tallman, New York 10982 Sam Golob is a Senior Systems Programmer. WHY NOT RUN A BATCH JOB? Often, we forget our roots. While we're caught up in the latest developments in our industry, we forget that our field has had a rich past. We're not exactly punching cards any more, but the vast potential of running system utilities via a batch job, has yet to be fully tapped and exploited. Today, I'd like to open your eyes to worlds of opportunity, in our current environment, that lay yet unused by most of us. After you've finished reading this article, the concept of running a batch job will have a lot of new meaning. While it seems that most of us have our JCL handy, to run batch jobs for our work, I am under the impression that we don't usually think of this JCL when we are trying to fix something. For example, we have JCL to back up a disk pack with FDR or DFDSS (or whatever utility you use to back up a pack). We use JCL when we want to initialize a pack (because that's how ICKDSF is usually run on an MVS system). Allocating a complicated VSAM file is another case when we run (and save) the JCL. However, if a large collection of datasets (such as DLIBs) has the wrong allocation parameters, such as too few directory blocks, or too small a secondary space allocation, we don't think of running a batch job to fix that situation. I'll talk more about this, later. I always save my old JCL. I worked at one shop for almost seven years, and the JCL collection from there consisted of almost 2300 pds members. My collection from another shop, where I was working for almost five years, amounted to well over 1000 new members. While I was working at the later shop, my collection of JCL from the earlier shop was always online and handy. I also save different copies of the JCL from different runs, and I don't often retype over old JCL if the job is a bit complicated, or a particular case is unique. I make another copy. Why do I do this? And how do I manage to quickly find the exact JCL model that I need? My own JCL philosophy is based on the premise that I am the "doctor", responsible for the "health" and smooth running of the installation's operating systems at all times. If there is a system emergency that requires running a batch job to fix, I do not want to spend a lot of time customizing my "one copy" of ICKDSF or IEBCOPY JCL to adapt to that particular emergency. I'd rather have a copy of JCL handy, which is very close to what I need. Maybe a dataset name is different, and that's all. Then, I'll solve the problem much quicker, and be less prone to making a typing error that could spoil more than it fixes. This accounts for why I prefer to have many IEBCOPY or ICKDSF jobs around, for example. How do I find the right JCL quickly? That's another question. I have two basic techniques for quickly finding the right JCL. The first is my naming system for jobs. I always assume that I'll keep more than one copy of any kind of job. I'll start by naming one prototype JCL member with a normal name, such as IEBCOPY. Afterwards, all the subsequent copies, which are variations of the first copy, will have ordered suffixes, so the next IEBCOPY job will be called IEBCOP01, and the next, IEBCOP02, and so on. Or I might use IEBCOPYA, IEBCOPYB, etc. Then, I use the ISPF technique of pulling partial member lists, to keep things relatively simple and easy to find. However, I have another tool, which is my ace in the hole. For many years, I have used the multi-talented free utility called "PDS", from the CBT MVS Utilities Tape (File 182) or its vendor-supported successor called "STARTOOL" (from Serena in Burlingame, California). The PDS utility supported its own member list system, which allowed for partial ISPF member listing, years before ISPF itself did. In addition, PDS can build its partial member list on the basis of whether a certain data string is found, or not found, in that member. This allows me to find all of the IEBCOPY JCL easily, for example. Suppose I have a large partitioned dataset of JCL, not necessarily my own (so it's not organized with my nice member name convention), and I want to find all of the IEBCOPY jobs and make a partial member list of them only. I simply point PDS to the dataset where I have the JCL, and enter the command: FIND : /PGM=IEBCOPY/ THEN(MEMLIST) This means that PDS should look at all dataset members (the colon ":" means "all members of the dataset"), and choose only members containing the data string "PGM=IEBCOPY". Then it should construct a partial ISPF member list containing only those members in which it found the string. From that PDS member list, I can BROWSE, EDIT, RENAME, or SUBMIT any member. So you see that with this tool, I have no trouble at all, handling a huge JCL dataset with ease. It doesn't even have to be my own JCL. It can be anybody's. That's how I handle batch JCL in general. Now we'll talk about some batch job techniques you may not have heard of. But even if you've heard of them, you probably haven't fully exploited them. SOME BATCH JOB INNOVATIONS - TSO-IN-BATCH Batch jobs have the advantage that they don't tie up your terminal. A batch job runs in its own address space that was started by an initiator, while your TSO terminal session runs in a different address space at the same time. Suppose a TSO task is tedious, and takes a lot of time at the terminal. Often, you can set this task up to run as a batch job instead, and when it's finished, you can examine the result. A batch job which executes TSO commands, uses a technique called "TSO-in-batch". To set up a TSO-in-batch job, you execute PGM=IKJEFT01, which is the normal IBM TSO terminal monitor program. Required DD names are SYSTSPRT (instead of SYSPRINT), SYSTERM, and SYSTSIN (in place of SYSIN). TSO commands are entered as data in the SYSTSIN DD name, or in the PARM field. A sample TSO-in-batch job is shown in Figure 1. Output from a TSO command, which normally goes to a terminal, can go to the SYSTSPRT DD name under certain conditions. There are two main techniques for producing terminal output in a TSO command. These are either from the IBM services TPUT and TGET, or from a different set of services called PUTLINE, GETLINE, and PUTGET. The TPUT service is easier to use in a program, but it has the disadvantage that its terminal output cannot be sent to the SYSTSPRT DD name when the command is run under TSO-in-batch. The PUTLINE service is harder to program, but all the terminal output will appear in the SYSTSPRT DD name under TSO-in-batch, and it can be sent to a dataset for further processing, or to keep for a later time. Now we'll get to the nitty-gritty of this article--the innovations. All of the following examples are planned as prototypes, to get you to start thinking of new things you can do. Given these examples, and some cleverness, you'll expand your techniques to cover lots of new situations in which you can get a job done. Let's start with something relatively simple. Almost any TSO command can be run under TSO-in-batch. If you don't care about the output, even a program that uses the TPUT interface, can be run usefully. I'll give an example. Most IBM-supplied TSO commands have been programmed using the PUTLINE service. However a notorious exception is IBM's TSO RENAME command, which uses the TPUT interface. Nevertheless, if you have a large number of datasets to rename, you can create a card-image dataset with the general format: RENAME dataset.name.a1 dataset.name.b1 RENAME dataset.name.a2 dataset.name.b2 RENAME dataset.name.a3 dataset.name.b3 and point the SYSTSIN DD name in a TSO-in-batch job to this dataset. The TSO command RENAME will do its work, but the SYSTSPRT DD name will not show any output from the command executions. It doesn't matter. Most probably, the datasets will be properly renamed, unless there is an outstanding enqueue on one of the names. You can use ISPF 3.4 or a VTOC listing utility to find out how well the renames worked. This technique is especially good, if you have to rename hundreds, or thousands of datasets at a time, and the datasets are cataloged. Another direction in which TSO-in-batch is supremely useful, is when we're trying to see a display or a report from a TSO command or a CLIST, and the TSO command or CLIST is long-running. To execute the TSO command or CLIST from a terminal, would tie up the terminal for an excessively long time, and would interfere with our other work. Of course, this technique would only be useful for those TSO commands which are programmed using the PUTLINE terminal interface. For example, a system TSO administrator would like to synchronize userids in the SYS1.BRODCAST dataset with the current collection of TSO userids. Issuing the ACCOUNT and SYNC TSO commands from the terminal might take a long time. Instead, this administrator might run a TSO-in-batch job under the (privileged) administrator userid. A lot of terminal time would be saved, and the job's report, with its time and date records, could be archived as proof that the job was done properly. Reports produced by other TSO commands, with longer outputs, can be similarly generated and archived for your records. A third direction in which we can go, is to use non-IBM commands and batch programs. The CBT MVS Tape, an independently produced collection of free software (which can be ordered through NaSPA), contains a huge collection of such TSO commands and batch programs. We shall use some examples from the CBT Tape as starters, to get you to look further at the possibilities in this direction. For example, let's consider Bill Godfrey's CDSCB (change the DSCB) TSO command from File 300 of the CBT Tape. CDSCB is an authorized TSO command, which changes the dataset allocation attributes of a dataset, such as BLKSIZE, LRECL, RECFM, and secondary SPACE. This is done by changing the VTOC entry directly. However, it is safer to use CDSCB than to directly zap the VTOC, because you know that the correct VTOC fields will be changed. CDSCB also has a tremendous advantage because it can be used in batch. Hundreds of datasets can be fixed at the same time. How is this useful in a practical situation? Everyone knows that the dataset allocations of IBM's distributed system datasets, leave much to be desired, especially if the datasets are to be reused later. Often, when IBM distributes a set of DLIBs or TARGET libraries, the secondary space will be 1 track for a small dataset, and if that library is reused, it can quickly run out of extents. A mass APPLY or ACCEPT job can easily fail. Another source of dataset failure is when there aren't enough directory blocks. I'll deal with that later. I can solve the secondary allocation problem for all your DLIBs, in one fell swoop, using CDSCB. You can run a TSO-in-batch job, using CDSCB, by creating a dataset consisting of your list of DLIBs. You might even create this list, using ISPF 3.4 run in batch. (ISPF can be run in batch. So can SDSF. See the appropriate IBM manuals, because I don't have space to discuss that topic here.) The list of DLIB names should be edited and converted into a format similar to the following: CDSCB dlib.name.a ALLOC(TR) SPACE(15) CDSCB dlib.name.b ALLOC(TR) SPACE(15) and so on. This will make sure that secondary space allocation will be in tracks, for each named dataset, and that each secondary extent allocation will be 15 tracks, instead of perhaps, 1 track. For larger datasets, you can set the secondary allocation to be appropriately larger. Running this job, will reset all of the secondary allocations. There is a catch here. CDSCB has to be run authorized. I'll now tell you a fact. Load module IKJTABLS, which contains CSECT IKJEFTE2 that contains the names of authorized TSO commands allowed, will override the global TSO Parmlib settings, provided that it is run from an authorized library as an authorized STEPLIB. You can therefore create your own copy of IKJTABLS, with CDSCB zapped into IKJEFTE2, and put it in your own authorized STEPLIB. If you use that library as a STEPLIB in a TSO-in-batch job, CDSCB will work. See Files 185 and 186 on the CBT Tape for help. This solves the secondary SPACE problem for your set of DLIBs. How about the small directory block allocation problem? Answer: The free PDS command (or the vendor product STARTOOL) contains a subcommand to expand the number of directory blocks in a partitioned dataset, without reallocating the dataset. The format of this PDS subcommand is: FIXPDS EXPANDDIR(nn) where nn is the number of additional directory blocks desired. The command works by figuring out if there are any members located at the beginning data area of the dataset, past the directory area. Then a question is asked: If the dataset would contain nn additional directory blocks, which members' data would get overlaid? These members are then moved to the end of the dataset. Finally, this area at the beginning of the data area, is reformatted as the additional directory blocks. Neat. Additionally, the PDS command does not have to be run authorized. Thus, you can run a TSO-in-batch job to invoke the PDS command as follows: PDS 'dataset.name1' FIXPDS EXPANDDIR(mm) CHANGE 'dataset.name2' FIXPDS EXPANDDIR(nn) and so on for all your DLIBs will small directory allocations. We've run out of space here. But I hope I've stimulated your ingenuity to create some innovative batch jobs. If your installation will run smoother, and your work will take less time, we've accomplished something. Good luck. See you next month. * * * * * * * * * * * * * * * * * * * * * * * * Figure 1. Layout of a TSO-in-Batch Job. Note that the actual TSO commands to be executed, are placed in a list in the SYSTSIN ddname. Only TSO commands which put out their output using the PUTLINE interface (and not the TPUT interface) will show output under TSO-in-Batch. Standard IBM-supplied commands usually use the PUTLINE interface, but not always. //TBATJOB JOB (insert your job card information here) //STEP01 EXEC PGM=IKJEFT01,REGION=4096K,DYNAMNBR=50 //STEPLIB DD DISP=SHR,DSN=your.steplib.name (authorized if needed) //SYSPRINT DD SYSOUT=* //SYSTSPRT DD SYSOUT=* //SYSTERM DD SYSOUT=* //SYSTSIN DD * LISTC LEV(SYS1) ALL LISTC LEV(SYS2) ALL /*