If you have more than one AS/400, you know the problem. You write and test your new program, but it needs to be on a system other than the one you compiled it on. Depending on the sophistication of your network, you can use SNADS to send the program to the machine that needs it. Or, even worse, if you don't use SNADS, the only way to get it there is on tape.
Even with SNADS, you have to save the program into a save file, send the save file to the other machine, receive the save file, and restore the objects. And then you do it again the next time you have to distribute objects. Doesn't this sound like something the system should be doing for you?
The system should do it, but IBM doesn't give you a way. The Send Network Objects (SNDNETOBJ) command (see 1) is the easier way you've been hoping for. Just like the OS/400 commands Send Network Spooled File (SNDNETSPLF) and Send Network Message (SNDNETMSG), SNDNETOBJ uses SNADS as the communications medium between the machines. And since SNADS uses Advanced Peer-to-Peer Networking (APPN), there are many different ways of connecting the machines, from slow-moving dial-up to screamingly fast Fiber Distributed Data Interface (FDDI). You can check "Getting Started with SNADS," MC, May 1994, for details on how to configure SNADS. If you already use the manual method of saving objects into save files and using Send Network File (SNDNETF), then your network configuration is ready to go.
The system should do it, but IBM doesn't give you a way. The Send Network Objects (SNDNETOBJ) command (see Figure 1) is the easier way you've been hoping for. Just like the OS/400 commands Send Network Spooled File (SNDNETSPLF) and Send Network Message (SNDNETMSG), SNDNETOBJ uses SNADS as the communications medium between the machines. And since SNADS uses Advanced Peer-to-Peer Networking (APPN), there are many different ways of connecting the machines, from slow-moving dial-up to screamingly fast Fiber Distributed Data Interface (FDDI). You can check "Getting Started with SNADS," MC, May 1994, for details on how to configure SNADS. If you already use the manual method of saving objects into save files and using Send Network File (SNDNETF), then your network configuration is ready to go.
OS/400 has built-in support for receiving objects sent with SNDNETSPLF and SNDNETMSG. However, there's no built-in support for receiving objects sent with SNDNETOBJ. We have to add a never ending job that runs the Receive Network Objects (RCVNETOBJ) command to receive and restore objects sent with SNDNETOBJ.
SNDNETOBJ has a total of eight parameters (see 2), four of which are straight from the SAVOBJ command: OBJ, LIB, OBJTYPE, and TGTRLS. They work just like parameters on the Save Object (SAVOBJ) command, except that only OBJ lets you specify more than one value. There's no reason the LIB and OBJTYPE parameters couldn't allow a list like OBJ, except for publishing size restrictions. The RSTLIB and OPTION parameters are borrowed from the Restore Object (RSTOBJ) command and work the same way. OPTION lets you prevent the overlay of objects that already exist on the target system by specifying *NEW. The default of *ALL will restore all objects, even if they are already in the restore library.
SNDNETOBJ has a total of eight parameters (see Figure 2), four of which are straight from the SAVOBJ command: OBJ, LIB, OBJTYPE, and TGTRLS. They work just like parameters on the Save Object (SAVOBJ) command, except that only OBJ lets you specify more than one value. There's no reason the LIB and OBJTYPE parameters couldn't allow a list like OBJ, except for publishing size restrictions. The RSTLIB and OPTION parameters are borrowed from the Restore Object (RSTOBJ) command and work the same way. OPTION lets you prevent the overlay of objects that already exist on the target system by specifying *NEW. The default of *ALL will restore all objects, even if they are already in the restore library.
The new parameters here are SYSTEM and PARTIAL. SYSTEM lets you specify the destination of the objects. By specifying *YES on the PARTIAL parameter, you tell the command to send all the objects it can save, even if you list objects that don't exist. The default of *NO will send the objects only if they all exist.
To send some objects, you specify the objects you want sent with SNDNETOBJ, and the rest is taken care of for you. When it's done, you get a message telling you of the success or failure of the restore.
The programs used by SNDNETOBJ automate the steps you use when sending objects manually. NET001CL is the command processing program for SNDNETOBJ and, as shown in 3, saves the objects into a save file. But because it can't remember which object to restore on the other machine as you can, it saves that information in a data area. The data area is saved into a second save file with the save file containing the objects. This ensures that the whole package arrives together on the destination machine. With the objects and data area together in a save file, they are sent to the other machine with SNDNETF.
The programs used by SNDNETOBJ automate the steps you use when sending objects manually. NET001CL is the command processing program for SNDNETOBJ and, as shown in Figure 3, saves the objects into a save file. But because it can't remember which object to restore on the other machine as you can, it saves that information in a data area. The data area is saved into a second save file with the save file containing the objects. This ensures that the whole package arrives together on the destination machine. With the objects and data area together in a save file, they are sent to the other machine with SNDNETF.
Much of the program is concerned with fixing strings and error handling. The OBJ parameter must have a blank inserted between each object name before it can be added to the string passed to QCMDEXC. The string &CMD must be truncated because of the way CL allocates *CHAR variables. Because this program is meant as a user interface, it can use a generic error handling routine that forwards unexpected errors to give the user feedback. Expected errors are ignored.
Whenever a file sent with SNDNETF arrives, the system sends a message CPI8050 (File &5 member &6 number &7 received from user &1 &2.) to the message queue of the recipient. The RCVNETOBJ command (shown in 4) has a command processing program (CPP), NET002CL (see 5), that uses the message data in this message to receive the save file with the Receive Network File (RCVNETF) command. Once received, the package that NET001CL built can be opened. The data area has information on the objects, library, and restore library parameters to be used on the RSTOBJ command.
Whenever a file sent with SNDNETF arrives, the system sends a message CPI8050 (File &5 member &6 number &7 received from user &1 &2.) to the message queue of the recipient. The RCVNETOBJ command (shown in Figure 4) has a command processing program (CPP), NET002CL (see Figure 5), that uses the message data in this message to receive the save file with the Receive Network File (RCVNETF) command. Once received, the package that NET001CL built can be opened. The data area has information on the objects, library, and restore library parameters to be used on the RSTOBJ command.
Again, error handling and string manipulation make up much of the code. However, no general error handling routine is used, because this program is not meant to be a user interface program. It is intended to be submitted when the system is started and then must handle all the objects being sent to it. If it used a general error handling routine that forwarded errors it couldn't handle, it might not run for long. Instead, it handles what it can, forwards a message about the success or failure of the RSTOBJ command to the sender, and, if it can't cope with what happens, lets CL send a message to QSYSOPR.
If you've looked at the code for the programs, you might have noticed references to the user profile RCVNETOBJ and the SNADS user ID RCVNETOB. These will need to be set up on any machine that can receive objects with the following commands, substituting your system's name for System in the Add Directory Entry (ADDDIRE) command:
CRTUSRPRF USRPRF(RCVNETOBJ) + PASSWORD(*NONE) STATUS(*DISABLED) + TEXT('Profile for receiving network objects') ADDDIRE USRID(RCVNETOB System) + USRD('Profile for receiving network objects') + USER(RCVNETOBJ)
Since the message queue for a user profile is created only the first time it is used to sign on and this user will never sign on, you need to execute these commands:
CRTMSGQ MSGQ(QUSRSYS/RCVNETOBJ) CHGOBJOWN OBJ(QUSRSYS/RCVNETOBJ) + OBJTYPE(*MSGQ) NEWOWN(RCVNETOBJ)
So that the system is always ready to receive objects, you will want to add the following command to the startup program specified in the QSTRUPPGM system value:
SBMJOB CMD(RCVNETOBJ) JOB(RCVNETOBJ) + JOBQ(QSYSNOMAX)
If the RCVNETOBJ job ends for any reason or isn't started by the startup program, you can submit it at any time.
There are many enhancements that can be made to this utility. As I hinted at before, the LIB and OBJTYPE parameters could be made to work more like the ones on the SAVOBJ command. One thing to keep in mind is that SAVOBJ allows only one library to be specified when using save files. But since we're saving one save file into another already, you could create one save file for each library and then save all of them into the same save file.
Other improvements could make this a mini distribution system. Things like archive libraries, sending source members, and timed restores would do this.
But if you have more than two AS/400s, the first thing you will want to add is support for a list of destination machines. Otherwise, you would have to execute SNDNETOBJ once for each machine. Isn't that what we're trying to avoid in the first place?
Martin Pluth is a senior technical editor for Midrange Computing. He can be reached by E-mail at
Automate Object Distribution with Send Network Objects
Figure 1: The SNDNETOBJ Command Prompt
Automate Object Distribution with Send Network Objects
Figure 2: The SNDNETOBJ Command Source
/*===============================================================*/ /* To compile: */ /* */ /* CRTCMD CMD(XXX/SNDNETOBJ) PGM(XXX/NET001CL) + */ /* SRCFILE(XXX/QCMDSRC) */ /* */ /*===============================================================*/ CMD PROMPT('Send Network Objects') PARM KWD(OBJ) TYPE(*GENERIC) MIN(1) MAX(50) + PROMPT('Objects') PARM KWD(LIB) TYPE(*NAME) MIN(1) PROMPT('Library') PARM KWD(OBJTYPE) TYPE(*CHAR) LEN(7) MIN(1) + PROMPT('Object type') PARM KWD(SYSTEM) TYPE(*NAME) LEN(8) MIN(1) + PROMPT('System') PARM KWD(RSTLIB) TYPE(*NAME) DFT(*SAVLIB) + SPCVAL((*SAVLIB)) PROMPT('Restore to + Library') PARM KWD(OPTION) TYPE(*CHAR) LEN(5) RSTD(*YES) + DFT(*ALL) VALUES(*ALL *NEW *OLD *FREE) + PROMPT('Option') PARM KWD(PARTIAL) TYPE(*CHAR) LEN(4) RSTD(*YES) + DFT(*NO) VALUES(*NO *YES) PROMPT('Allow + partial save') PARM KWD(TGTRLS) TYPE(*CHAR) LEN(8) RSTD(*YES) + DFT(*CURRENT) VALUES(*PRV *CURRENT) + PROMPT('Target Release')
Automate Object Distribution with Send Network Objects
Figure 3: The SNDNETOBJ Command Processing Program, NET001CL
/*===============================================================*/ /* To compile: */ /* */ /* CRTCLPGM PGM(XXX/NET001CL) SRCFILE(XXX/QCLSRC) */ /* */ /*===============================================================*/ NET001CL: PGM PARM(&OBJ &LIB &OBJTYPE &SYSTEM &RSTLIB + &OPTION &PARTIAL &TGTRLS) DCL VAR(&OBJ) TYPE(*CHAR) LEN(502) DCL VAR(&LIB) TYPE(*CHAR) LEN(10) DCL VAR(&OBJTYPE) TYPE(*CHAR) LEN(7) DCL VAR(&SYSTEM) TYPE(*CHAR) LEN(8) DCL VAR(&RSTLIB) TYPE(*CHAR) LEN(10) DCL VAR(&OPTION) TYPE(*CHAR) LEN(5) DCL VAR(&PARTIAL) TYPE(*CHAR) LEN(4) DCL VAR(&TGTRLS) TYPE(*CHAR) LEN(8) DCL VAR(&OBJ1) TYPE(*CHAR) LEN(549) DCL VAR(&OBJTEMP) TYPE(*CHAR) LEN(2) DCL VAR(&OBJ#) TYPE(*DEC) LEN(3 0) DCL VAR(&OBJPOS) TYPE(*DEC) LEN(3 0) DCL VAR(&CMD) TYPE(*CHAR) LEN(1024) DCL VAR(&CMDLEN) TYPE(*DEC) LEN(15 5) VALUE(1025) DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(256) MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR)) CRTSAVF FILE(QTEMP/SNDNETOBJ) MONMSG MSGID(CPF7302) /* Save file exists */ CRTSAVF FILE(QTEMP/SNDNETOBJ2) MONMSG MSGID(CPF7302) /* Save file exists */ /* Put a blank between the object names */ CHGVAR VAR(&OBJTEMP) VALUE(%SST(&OBJ 1 2)) CHGVAR VAR(&OBJ#) VALUE(%BIN(&OBJTEMP)) CHGVAR VAR(&OBJPOS) VALUE(3) CHGVAR VAR(&OBJ1) VALUE(%SST(&OBJ &OBJPOS 10)) GOTO CMDLBL(DECREMENT) APPEND: CHGVAR VAR(&OBJ1) VALUE(&OBJ1 *BCAT %SST(&OBJ + &OBJPOS 10)) DECREMENT: CHGVAR VAR(&OBJ#) VALUE(&OBJ# - 1) CHGVAR VAR(&OBJPOS) VALUE(&OBJPOS + 10) IF COND(&OBJ# *NE 0) THEN(GOTO CMDLBL(APPEND)) /* Build a command string because of the variable number of objects */ CHGVAR VAR(&CMD) VALUE('SAVOBJ OBJ(' *CAT &OBJ1 + *TCAT ') LIB(' *CAT &LIB *TCAT ') + OBJTYPE(' *CAT &OBJTYPE *TCAT ') TGTRLS(' + *CAT &TGTRLS *TCAT ') DEV(*SAVF) + SAVF(QTEMP/SNDNETOBJ) CLEAR(*ALL) + UPDHST(*NO) DTACPR(*YES)') /* Calculate the length of the command */ CALCLEN: CHGVAR VAR(&CMDLEN) VALUE(&CMDLEN - 1) IF COND(%SST(&CMD &CMDLEN 1) *EQ ' ') THEN(GOTO + CMDLBL(CALCLEN)) CALL PGM(QCMDEXC) PARM(&CMD &CMDLEN) MONMSG MSGID(CPF3701) EXEC(DO) /* Some objects not + saved */ IF COND(&PARTIAL *EQ '*NO') THEN(GOTO + CMDLBL(ERROR)) RCVMSG MSGTYPE(*EXCP) MSGDTA(&MSGDTA) MSGID(&MSGID) SNDPGMMSG MSGID(&MSGID) MSGF(QCPFMSG) MSGDTA(&MSGDTA) + MSGTYPE(*COMP) ENDDO /* Save some things for the receiving side to use */ CRTDTAARA DTAARA(QTEMP/SNDNETOBJ) TYPE(*CHAR) LEN(581) MONMSG MSGID(CPF1023) /* Data Area exists */ CHGDTAARA DTAARA(QTEMP/SNDNETOBJ) VALUE(&OBJ1) CHGDTAARA DTAARA(QTEMP/SNDNETOBJ (550 10)) VALUE(&LIB) CHGDTAARA DTAARA(QTEMP/SNDNETOBJ (560 10)) VALUE(&RSTLIB) CHGDTAARA DTAARA(QTEMP/SNDNETOBJ (570 7)) VALUE(&OBJTYPE) CHGDTAARA DTAARA(QTEMP/SNDNETOBJ (577 5)) VALUE(&OPTION) SAVOBJ OBJ(SNDNETOBJ) LIB(QTEMP) DEV(*SAVF) + OBJTYPE(*DTAARA *FILE) + SAVF(QTEMP/SNDNETOBJ2) TGTRLS(&TGTRLS) + CLEAR(*ALL) DTACPR(*YES) SNDNETF FILE(QTEMP/SNDNETOBJ2) TOUSRID((RCVNETOB + &SYSTEM)) GOTO CMDLBL(ENDPGM) ERROR: RCVMSG MSGTYPE(*EXCP) MSGDTA(&MSGDTA) MSGID(&MSGID) SNDPGMMSG MSGID(&MSGID) MSGF(QCPFMSG) MSGDTA(&MSGDTA) + MSGTYPE(*ESCAPE) ENDPGM: ENDPGM
Automate Object Distribution with Send Network Objects
Figure 4: The RCVNETOBJ Command
/*===============================================================*/ /* To compile: */ /* */ /* CRTCMD CMD(XXX/RCVNETOBJ) PGM(XXX/NET002CL) + */ /* SRCFILE(XXX/QCMDSRC) */ /* */ /*===============================================================*/ CMD PROMPT('Receive Network Objects')
Automate Object Distribution with Send Network Objects
Figure 5: The RCVNETOBJ Command Processing Program, NET002CL
/*===============================================================*/ /* To compile: */ /* */ /* CRTCLPGM PGM(XXX/NET002CL) SRCFILE(XXX/QCLSRC) + */ /* USRPRF(*OWNER) */ /* */ /* This program should be owned by a profile with *ALLOBJ */ /* special authority so that it can receive from all profiles */ /*===============================================================*/ NET002CL: PGM DCL VAR(&OBJ) TYPE(*CHAR) LEN(549) DCL VAR(&LIB) TYPE(*CHAR) LEN(10) DCL VAR(&OBJTYPE) TYPE(*CHAR) LEN(7) DCL VAR(&SYSTEM) TYPE(*CHAR) LEN(8) DCL VAR(&RSTLIB) TYPE(*CHAR) LEN(10) DCL VAR(&OPTION) TYPE(*CHAR) LEN(5) DCL VAR(&TGTRLS) TYPE(*CHAR) LEN(8) DCL VAR(&MSGQ) TYPE(*CHAR) LEN(10) DCL VAR(&MSGQLIB) TYPE(*CHAR) LEN(10) DCL VAR(&CMD) TYPE(*CHAR) LEN(1024) DCL VAR(&CMDLEN) TYPE(*DEC) LEN(15 5) DCL VAR(&MSG) TYPE(*CHAR) LEN(256) DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(256) CRTSAVF FILE(QTEMP/SNDNETOBJ2) MONMSG MSGID(CPF7302) /* Save file exists */ RTVUSRPRF USRPRF(RCVNETOBJ) MSGQ(&MSGQ) MSGQLIB(&MSGQLIB) RCVMSG: RCVMSG MSGQ(&MSGQLIB/&MSGQ) MSGTYPE(*INFO) + WAIT(*MAX) MSGDTA(&MSGDTA) MSGID(&MSGID) IF COND(&MSGID *NE 'CPI8050') THEN(GOTO + CMDLBL(RCVMSG)) CLRSAVF FILE(QTEMP/SNDNETOBJ2) RCVNETF FROMFILE(%SST(&MSGDTA 33 10)) + TOFILE(QTEMP/SNDNETOBJ2) NBR(%SST(&MSGDTA + 43 6)) USER(RCVNETOBJ) MONMSG MSGID(CPF8060) EXEC(GOTO CMDLBL(RCVMSG)) /* + No files compare */ DLTDTAARA DTAARA(QTEMP/SNDNETOBJ) MONMSG MSGID(CPF2105) /* Data area not found */ DLTF FILE(QTEMP/SNDNETOBJ) MONMSG MSGID(CPF2105) /* File not found */ RSTOBJ OBJ(SNDNETOBJ) SAVLIB(QTEMP) DEV(*SAVF) + SAVF(QTEMP/SNDNETOBJ2) RTVDTAARA DTAARA(QTEMP/SNDNETOBJ (1 549)) RTNVAR(&OBJ) RTVDTAARA DTAARA(QTEMP/SNDNETOBJ (550 10)) RTNVAR(&LIB) RTVDTAARA DTAARA(QTEMP/SNDNETOBJ (560 10)) + RTNVAR(&RSTLIB) RTVDTAARA DTAARA(QTEMP/SNDNETOBJ (570 7)) + RTNVAR(&OBJTYPE) RTVDTAARA DTAARA(QTEMP/SNDNETOBJ (577 5)) + RTNVAR(&OPTION) /* Build a command string because of the variable number of objects */ CHGVAR VAR(&CMD) VALUE('RSTOBJ OBJ(' *CAT &OBJ + *TCAT ') SAVLIB(' *CAT &LIB *TCAT ') + OBJTYPE(' *CAT &OBJTYPE *TCAT ') + RSTLIB(' *CAT &RSTLIB *TCAT ') OPTION(' + *CAT &OPTION *TCAT ') DEV(*SAVF) + MBROPT(*ALL) SAVF(QTEMP/SNDNETOBJ) + ALWOBJDIF(*ALL)') /* Calculate the length of the command */ CHGVAR VAR(&CMDLEN) VALUE(1025) CALCLEN: CHGVAR VAR(&CMDLEN) VALUE(&CMDLEN - 1) IF COND(%SST(&CMD &CMDLEN 1) *EQ ' ') THEN(GOTO + CMDLBL(CALCLEN)) CALL PGM(QCMDEXC) PARM(&CMD &CMDLEN) MONMSG MSGID(CPF0000) RCVMSG MSGTYPE(*LAST) RMV(*NO) MSG(&MSG) SNDNETMSG MSG(&MSG) TOUSRID((%SST(&MSGDTA 1 8) + %SST(&MSGDTA 9 8))) GOTO CMDLBL(RCVMSG) ENDPGM: ENDPGM
LATEST COMMENTS
MC Press Online