Admin Alert: Basic Tools for the System i Admin Tool Chest
November 28, 2007 Joe Hertvik
Like it or not, iSeries, i5, and System i administrators usually have to write small Control Language (CL) programs to help them manage their systems. To make this task easier, I frequently use several common programming and IBM-sponsored techniques to perform different functions. This week, I’ll look at three basic programming and administrative tools for CL programming and demonstrate how they can benefit you when creating operational programs.
Tool #1: Sending Status Messages When a User is Running a Program
When you’re writing long-running code that has to be run interactively, it helps to flash status messages at the bottom of the screen to let the user know what’s happening and to reassure him that the program is indeed running. I frequently use the Send Program Message (SNDPGMMSG) command for this task. SNDPGMMSG displays a text message of your choosing on line 24 of the display session where the program is running.
If I want to send a status message during an interactive program run, for example, I would execute the following SNDPGMMSG command at the appropriate place in my CL code.
SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Message text')
By running SNDPGMMSG, whatever text is listed in the Message Data parameter (MSGDTA) will be printed at the bottom of the screen. For this tip, it’s important to code this command so that it always sends a program message with a Message Identifier (MSGID) parameter of CPF9898 from the QCPFMSG message file, as designated in the Message File (MSGF) parameter. CPF9898 is the escape message identifier that i5/OS uses to send messages to a display terminal instead of to a named message queue. The text of the message must always be entered in the Message Data (MSG) parameter. If you don’t want to hard-code your message text but prefer instead to display the contents of a character-based CL variable in the program, you would instead code your SNDPGMMSG command like this.
SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA(&MESSAGE)
In this case, whatever value is present in the variable listed in the MSGDTA parameter will appear as a message on line 24 of your user’s display session.
In addition to using message ID CPF9898 to send a message, you can also code your SNDPGMMSG command to use message ID CPF9897, like this.
SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('Message text')
This command will run exactly the same way as the CPF9898 version of the command runs, with one trivial exception. When you send message CPF9898, i5/OS appends a period character (‘. ‘) to the end of each displayed message. Sending a message by using CPF9897 displays the message at the bottom of the screen without appending a period to the end of the delivered text.
Step #2: Using IBM APIs Instead of Growing Your Own Code
One of the problems with system administrators is that we sometimes start coding homegrown CL code when IBM has already created a perfectly good alternative in the form of a callable System Application Program Interface (System API) program. IBM offers many System APIs for all kinds of common or obscure scenarios, including APIs for the following categories.
There’s a ton of APIs out there, and the biggest problem is figuring out whether there’s an API that does what you want it to do. Luckily, IBM has made the job of finding APIs easier by providing an API finder in the i5/OS Information Center. To perform an API search, go to the i5/OS Information Center for the release that you’re currently running, and type in “API Finder” in the site’s search function. The search results window will display a link that takes you to that version’s API finder, where you can search for APIs by category, by descriptive name, by program name, or by entering only part of the name. So you can save some time when coding by checking the API finder to see if IBM already offers an API that runs the functionality that you would otherwise have to program yourself.
Step #3: Using the Local Data Area for Cross-Program Communication
When calling one program from within another program, the called program may sometimes need to send information back to the calling program. This is where the Local Data Area (*LDA) comes in handy. For each running job, the system creates a localized 1,024-character data area that can be used for any one of the following purposes.
Similar to a job’s QTEMP library, each job’s local data area is unique to that job and it cannot be accessed by another process running inside i5/OS. Inside an individual job, the local data area is always referred to as the *LDA, and it can be written to or referenced from any CL or RPG program. To write to the *LDA in a job step, you could use the Change Data Area (CHGDTAARA) command like this:
CHGDTAARA DTAARA(*LDA (xxxx yyyy)) VALUE('text')
In this example, xxxx is a one- to four-digit number between 1 and 1024 representing the starting position that you want to write the information to in the *LDA. yyyy is another one- to four-digit number that represent the length of the substring that you are writing out to the *LDA. So if I wanted to write the literal ‘Admin Alert’ to positions 1-80 of my job’s LDA, I would execute the following CHGDTAARA command.
CHGDTAARA DTAARA(*LDA (1 80)) VALUE('Admin Alert')
If the literal is shorter than the LDA location that you are writing it to, the CHGDTAARA command will left-justify the literal in that location and then pad out the rest of the location with blanks.
Once the information is written to the job’s *LDA, any program or procedure called in that same job can retrieve information from that *LDA into a program variable. In a CL program (which is what most administrators use to write special function code), you can use the Retrieve Data Area (RTVDTAARA) command to retrieve the contents of an *LDA substring and copy those contents to a CL program variable. A sample RTVDTAARA statement to retrieve *LDA information might look like the following.
RTVDTAARA DTAARA(*LDA (xxxx yyyy)) RTNVAR(&VARIABLE)
Like the CHGDTAARA command, the xxxx and yyyy values are four-digit numbers designating the starting position and the length of the *LDA substring to retrieve. The CL variable for the Returned Value (RTNVAR) parameter designates the program variable that will contain the retrieved substring after the command is run. Following our example, if I want to retrieve positions 1-80 of my *LDA into a CL variable called &LDADTA, I would code that command this way.
RTVDTAARA DTAARA(*LDA (1 80)) RTNVAR(&LDADTA)
While CHGDTAARA and RTVDTAARA are fairly easy to use to pass data between programs or procedures within a job stream, you can also use the *LDA to pass information from a source job to any batch job that the source job submits by using the Submit Job (SBMJOB) command. This is because, by default, SBMJOB copies the contents of the submitting job’s *LDA into its own *LDA as the job is submitted. As a result, you can easily pass *LDA information from a submitting job to a submitted job by performing the following two steps:
The moral here is that the *LDA is a great way to pass information anywhere within an individual job or between an individual job and any other jobs that it submits.
About Our Testing Environment
Configurations described in this article were tested on an i5 550 box running i5/OS V5R3. Most of the commands shown here are also available in earlier versions of the operating system running on System i, iSeries or AS/400 machines. If a command or function is present in earlier versions of the i5/OS or OS/400 operating systems, you may notice some variations in the pre-V5R3 copies of these commands. These differences may be due to command improvements that have occurred from release to release.