True, VMFTP must run in the VMTFTP environment, not the COMMAND (or CMS, or
XEDIT, etc.) environment.
I usually write a "whatever EXEC", that does some setup work, and which then
calls a rexx "whatever VMFTP" macro. I suppose it could all be done in one
EXEC, just by changing the addressing environment to VMFTP, but I started that
pattern a long time ago.
The following is a console snippet from running the FTPFMZOS EXEC (which calls
FTPFMZOS VMFTP). It FTPs a file from one of our z/OS systems with no fuss or
muss. I do have a "NETRC DATA A0" file with my z/OS ID and password therein.
If there was no "NETRC DATA" file, it would prompt for the password.
---<snip>---
ftpfmzos @02663 'SYS1.PROCLIB(AVM)' AVM PROC A (OLDD
331 Send password please.
250 The working directory "SYS1.PROCLIB" is a partitioned data set
Target output location...
Label Vdev M Stat Cylinders Used Files Ownerid Addr Rdev Volser LblType
M2W191 0191 A R/W 150 CYL 63% 2320 = M2WALTER 0191 09F6 VMPP09 CMS1
200 Port request OK.
125 Sending data set SYS1.PROCLIB(AVM) FIXrecfm 80
250 Transfer completed successfully.
162 bytes transferred in 0.016 seconds. Transfer rate 10.12 Kbytes/sec.
Ready;
---<snip>---
The FTPFMZOS EXEC and VMFTP macro are pasted below my signature as examples of
using VMFTP.
Mike Walter
Aon Corporation
The opinions expressed herein are mine alone, not my employer's.
/* Prolog; See Epilog for additional information ********************
* Exec Name - FTPFMZOS EXEC *
* Unit Support - MF S&D *
* Status - Version 1, Release 2.0 *
********************************************************************/
address COMMAND
parse source xos xct xfn xft xfm xcmd xenvir .
parse upper arg parms 1 operands '(' options ')' parmrest
If parms='?' | parms='' then Signal Explain
parse var parms w1 w2 rest
parse var w2 w2c1 2 restw2
parse value reverse(restw2) with w2clast 2 .
If w2c1="'" & w2clast="'" then
Do /* For: FTPFMZOS @nnnnn 'gdg.dsn(0)' ofn.oft.ofm (options */
parse var rest oprest '(' options ')' parmrest
operands=w1 w2 oprest
End
'VMFTP' xfn '(NOPSWD PARM' parms
Call Exit rc
/********************************************************************/
/* Sub-Routines below this point */
/********************************************************************/
Exit:
parse arg exitrc .
If verify(exitrc,'-0123456789')=0 then Exit exitrc
else Exit 999999
Error:
say '+++ "ERROR" error routine entered in:' xfn xft xfm', rc='rc
say '+++ from line:' sigl', which reads:'
say '+++'sourceline(sigl)
Call Exit 20
Syntax:
say '+++ "SYNTAX" error routine entered in:' xfn xft xfm', rc='rc
say '+++ from line:' sigl', which reads:'
say '+++'sourceline(sigl)
Call Exit 20
NoValue:
say '+++ "NoValue" error routine entered in:' xfn xft xfm', rc='rc
say '+++ from line:' sigl', which reads:'
say '+++'sourceline(sigl)
say '+++ Variable with no value is:' condition('Description') Call Exit 24
Explain:
address 'COMMAND'
'PIPE (NAME Explain)' ,
'| <' xfn xft xfm ,
'| INSIDE /ExplainBegin;/ /ExplainEnd;/' ,
'| PREFACE STRLITERAL /'xfn xft xfm 'help.../' ,
'| CONSOLE'
Call Exit 0
/*
ExplainBegin;
FTPFMZOS opens an FTP session with the hard-coded remote FTP server, and 'GET's
a sequential, or partitioned (PDS) dataset (one or all members).
>-FTPFMSOZ--@mvs_userid--+-dsn-----------+------------------------------
>>
+-pds(mbr_name)-+
+-pds(*)--------+
+-FTPXFER-+
>>--+-outfn----+--+-outft---+--+-outfm-+--(--+---------+--+---------+--<
>>--+-outfn----+--+-outft---+--+-outfm-+>
+-=--------+ +-=-------+ +-REPlace-+ +-OLDDate-+
+-*--------+ +-*-------+
Operands:
=========
dsn A z/OS dataset name as would be entered from TSO (i.e.
prefixed with your TSO userid unless enclosed in quotes).
mbr_name A single member to be copied from a given PDS.
* ALL members will be copied from the PDS.
outfn Output filename.
= The '=' and '*' indicate to use the defaults listed below.
* For a sequential dataset, the output filename defaults
to the first index of the dataset (before the 1st '.').
DEFAULT: for a sequential dataset, the first index (the
string before the first '.').
DEFAULT: for a "PDS", the member name (even if '*' was
given to copy all members).
outft Output filetype.
= The '=' and '*' are "place holders" should you not want to
* provide specific filetype, but want provide a filemode.
DEFAULT='FTPXFER'
outfm Output filemode at which to write the file(s).
DEFAULT='A'.
Options:
========
REPlace Causes the file localfile to be overwritten if the file
already exists. If the localfile already exists and you
do not use the REPLACE option, the GET subcommand does
does not overwrite the existing file and displays a
message that a file with that name already exists.
OLDDate Sets the date and time of the gotten file as:
- For sequential datasets, date set to LAST REFERENCE
date, time set to 00:00:00
- For PDS members, date and time set to LAST UPDATE
date and time as stored in the PDS by ISPF.
Examples:
=========
ftpfmzos @nnnnn 'app.T.sequential' app sequential a ( repl
ftpfmzos @nnnnn 'some.bigpds(ambr)' = * q ( replace
ftpfmzos @nnnnn 'some.bigpds(*)' * * q ( replace
ftpfmzos @nnnnn 'some.gdg(0)' ( replace
ftpfmzos @nnnnn 'some.gdg(-1)'
ftpfmzos @nnnnn my.tso.file
Hint: If you receive a message that "@nnnnn.dsn" was not found, and the dsn
indeed is not your own, you will need to place single-quotes around the mvsdsn
in the command.
FTPFMZOS EXEC calls an FTPFMZOS VMFTP macro, which is processed by the VMFTP
MODULE. VMFTP is a public-domain utility to script FTP processing.
ExplainEnd; */
/* Epilog ***********************************************************
* Function - See 'Explain:' subroutine above. *
* Component of - FTP at Hewitt Associates *
* Command format- See 'Explain:' subroutine above. *
* Called by - Users or EXECs. *
* Dependencies - VMFTP MODULE *
* (from http://ukcc.uky.edu/~tools.1997/) *
* - FTPFMZOS VMFTP (macro) *
* Program Lang. - CMS REXX *
* Date Written - 20060105 *
* Author - Michael R. Walter *
* Changed | By | Description of Change *
* ---------+----+------------------------------------------------- *
* 20070402 mrw - Properly handle GDGs in quotes *
* *
********************************************************************/
/* Prolog; See Epilog for additional information ********************
* Exec Name - FTPFMZOS VMFTP *
* Unit Support - MF S&D *
* Status - Version 1, Release 1.0 *
********************************************************************/
address 'COMMAND'
parse source xos xct xfn xft xfm xcmd xenvir .
/* ------------------------------------------------------------- */
/* Since the DSN may be a PDS containing parens, the standard */
/* parse upper arg operands '(' options ')' parmrest will fail. */
/* ------------------------------------------------------------- */
parse upper arg parms 1 mvsid dsn rest /* Get dsn, even if pds */
parse var rest operands '(' options ')' parmrest /* Get rest */
operands=mvsid dsn operands /* Build std operands */
parse var operands mvsid dsn outfn outft outfm error
hi='1DE8'x /* 3270 Hilite Char */
lo='1D60'x /* 3270 Default Char */
Signal ON Syntax
Signal ON NoValue
/* Signal ON ERROR */
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
remotesys='MVS-SYSB' /* <<--- Update for local stds !! */
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
If mvsid='?' | mvsid='' then Signal Explain
If error<>'' then
Do
say xfn'; Extraneous operand(s) found: "'error'"'
say 'For more help, enter: VMFTP' xfn '(PARM ?'
Call Exit 20
End
parse upper var options wkopts
?olddate=0
replace=''
Do while wkopts<>''
parse var wkopts opt wkopts /* Get next opt */
Select
When abbrev('OLDDATE',opt,4) then ?olddate=1
When abbrev('REPLACE',opt,3) then replace='( REPLACE'
Otherwise say 'Unsupported option ignored: "'opt'".'
End
End
'STATE NETRC DATA A'
?netrc=(rc=0)
'STATE QOWNER EXEC *'
?qowner=(rc=0)
address COMMAND ,
'PIPE COMMAND QUERY DISK' outfm , /* Target an SFS dir? */
'| TAKE LAST 1' ,
'| PICK W2 == /DIR/' , /* Yes */
'| COUNT LINES' ,
'| VAR ?outdir' /* Set binary true|false flag */
/* ------------------------------------------------------------- */
address 'VMFTP' /* The default VMFTP macro environment */
/* ------------------------------------------------------------- */
/* "Emit" sub-routine syntax: */
/* */
/* >>--command string--+---------+--+---------+-->< */
/* +-,OK_RCs-+ +-,'TYPE'-+ */
If ?netrc then
Do
Call Emit 'OPEN' remotesys,230
End
Else
Do
say 'Enter MVS Password at the prompt.'
Call Emit 'OPEN' remotesys,220,'TYPE'
say 'If you have trouble entering your password, enter: QUIT'
Call Emit mvsid,230,'TYPE'
End
/* Prepare for File Transfer */
Call Emit 'TYPE E',200 /* Type= EBCDIC */
Call Emit 'MODE S',200 /* Mode= Streaming */
/* ------------------------------------------------------------- */
/* Get DCB specs, date/time, etc. */
/* ------------------------------------------------------------- */
parse var dsn listdsn '('mbr')' quote? /* Handle PDS or GDG */
listdsn=listdsn||quote? /* Handle 'SOME.GDG(0)' */
Call Emit 'LIST' listdsn,250 /* Get DSORG, last update dt/tm */
/* Sample LIST command "output." stem results...
200 Port request OK.
125 List started OK
Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname
PRD288 3390 2006/01/05 2 135 FB 80 27920 PS
'OS.P.CCT.PCTNEW.CSECT.G
6252V00'
250 List completed successfully.
*/
address 'COMMAND' ,
'PIPE (NAME ParseDsorgDateTime)' ,
'| STEM output.' ,
'| STRIP LEADING' ,
'| INSIDE /Volume/ /250/' , /* Keep DCB */
'| TAKE 1' ,
'| VAR listdsninfo'
parse var listdsninfo . . RefDate . . recfm lrecl . dsorg .
parse var RefDate yyyy'/'mm'/'dd
RefDate=mm'/'dd'/'yyyy /* DMSPLU fmt */
/* ------------------------------------------------------------- */
/* Could it be a GDG? */
/* ------------------------------------------------------------- */
Call Emit 'LIST' listdsn,250 /* Get DSORG, last update dt/tm */
/* Sample LIST command "output." stem results...
200 Port request OK.
125 List started OK
Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname
GDG 'OS.P.CCT.PCTNEW.CSECT'
250 List completed successfully.
*/
address 'COMMAND' ,
'PIPE (NAME ParseDsorgDateTime)' ,
'| STEM output.' ,
'| STRIP LEADING' ,
'| INSIDE /Volume/ /250/' , /* Keep DCB */
'| TAKE 1' ,
'| VAR listdsninfo'
parse var listdsninfo w1 w2 rest
?gdg=(w1='GDG')
/* ------------------------------------------------------------- */
/* Handle different DSorgs. */
/* ------------------------------------------------------------- */
?pds=(dsorg='PO')
?gdg=(dsorg='GDG')
If \?pds then
Select
When outfn='' then parse var dsn outfn "."
When outfn='*' then parse var dsn outfn '.'
When outfn='=' then parse var dsn outfn '.'
Otherwise nop
End
outfn=strip(outfn,"L","'")
Select
When outft='' then outft='FTPXFER'
When outft='*' then outft='FTPXFER'
When outft='=' then outft='FTPXFER'
Otherwise nop
End
If outfm='' then outfm='A'
If ?pds
then Call PDS
else Call SequentialDSN
Call Emit 'QUIT'
address 'COMMAND' /* Done with VMFTP environment */
Call Exit 0
/********************************************************************/
/* Sub-Routines below this point */
/********************************************************************/
Emit:
cmd = arg(1)
okrcs = arg(2)
disp = arg(3)
parse var cmd subcmd subcmdargs
trace o
cmd /* Issue FTP subcommand */
src=rc /* Save subcmd rc */
trace
If src=-3 then
Do
If subcmd='open' then
Do
say hi
say 'Connection failed to:' subcmdargs
parse upper var subcmdargs . '-'targetsys'.' .
say 'Check "System Status" to see if "'targetsys'"' ,
'is available.' lo
End
Else
Do
say hi
say '"'cmd'" failed with rc-3.'
say 'This usually indicates a failure of the network' ,
'connection.'
End
Call Exit src
End /* rc=-3 */
If disp='TYPE' then
If subcmd='put' /* Don't bother them with SITE/Port msgs */
then address 'COMMAND' ,
'PIPE (NAME StripSiteAndPort)' ,
'| STEM output.' ,
'| STRNLOCATE /200 SITE command was accepted/' ,
'| STRNLOCATE /200 Port request OK./' ,
'| CONSOLE'
else
address 'COMMAND' 'PIPE STEM output. | CONSOLE'
If okrcs='' then Return
If wordpos(src,okrcs)>0 then Return
address 'COMMAND' /* This FTP session is toast anyway */
say hi
say xfn'; FTP Command: "'cmd'" rc='src 'not expected.'
say output.0 'lines returned:'
'PIPE STEM output. | CONSOLE'
say lo
If cmd='GET' & rc=226 then
say '+++ Output filemode "'outfm'" is full.'
Call Exit src
Return /* Doc only */
PDS:
Call Emit 'CWD' listdsn ,250,'TYPE' /* Set, /show DSN */
Call Emit 'DIR',250 /* Get mbr list */
/* Sample DIR command "output." stem results...
200 Port request OK.
125 List started OK
Name VV.MM Created Changed Size Init Mod Id
mbrname1 01.45 1994/08/22 2003/01/30 09:01 147 100 0 mvsid1
mbrnamen 01.07 1993/08/25 2003/01/10 18:12 136 136 0 mvsid1
250 List completed successfully.
*/
Call TargetDisplay
parse var dsn . '('mbr')' .
address 'COMMAND' ,
'PIPE (NAME KeepAllMbrDtls)' ,
'| STEM output.' ,
'| STRIP LEADING' ,
'| INSIDE /Name/ /250/' , /* Keep only mbrs */
'| STEM dir.'
If mbr<>'' & mbr<>'*' then
Do /* Just get the single, specified member */
If outfn='' | outfn='=' then outfn=mbr
address 'COMMAND' 'STATEW' outfn outft outfm
If rc=0 & replace='' then
Do
say hi'Output file "'outfn outft outfm'" already' ,
'exists. Use REPLACE option.'lo
Call Exit 28
End
Call Emit 'GET' mbr outfn'.'outft'.'outfm ,
replace,129 230 250,'TYPE' /* Get or errmsg */
If ?olddate then
Do
address 'COMMAND' ,
'PIPE (NAME PickOneMbrDtls)' ,
'| STEM dir.' ,
'| PICK WORD1 == /'mbr'/' ,
'| TAKE 1' ,
'| VAR pdsdtl'
parse var pdsdtl mbr vvmm crtdt chgdt chgtm ,
recs init mod id .
parse var chgdt yyyy'/'mm'/'dd .
address 'COMMAND' ,
'DMSPLU' outfn outft outfm ,
mm'/'dd'/'yyyy chgtm':00' /* Set OLDDATE */
End /* Olddate */
End /* Just get the single, specified member */
Else
Do /* Get ALL members from specified DSN */
Do ix=1 to dir.0 /* All members */
parse var dir.ix mbr vvmm crtdt chgdt chgtm ,
recs init mod id .
address 'COMMAND' 'STATEW' mbr outft outfm
If rc=0 & replace='' then
Do
say hi'Output file "'mbr outft outfm'" already' ,
'exists. Use REPLACE option.'lo
Call Exit 28
End
If (ix%25)=(ix/25) | (ix=1) then /* P.U.F. */
say 'GETting member' ix 'of' dir.0 'members.'
Call Emit 'GET' mbr mbr'.'outft'.'outfm ,
replace ,129 230 250
If ?olddate then
Do
parse var chgdt yyyy'/'mm'/'dd .
address 'COMMAND' ,
'DMSPLU' mbr outft outfm ,
mm'/'dd'/'yyyy chgtm':00' /*Set OLDDATE*/
End /* Olddate */
End /* All members */
say ix-1 'members retrieved.'
End /* Get ALL members from specified DSN */
Return
SequentialDSN:
Call Emit 'LIST' dsn,250,'TYPE' /* Set/show DSN */
address 'COMMAND' 'STATEW' outfn outft outfm
If rc=0 & replace='' then
Do
say hi'Output file "'outfn outft outfm'" already' ,
'exists. Use REPLACE option.'lo
Call Exit 28
End
Call TargetDisplay
Call Emit 'GET' dsn outfn'.'outft'.'outfm ,
replace,129 230 250,'TYPE'
If ?olddate then
Do /* Olddate */
address 'COMMAND' ,
'DMSPLU' outfn outft outfm ,
RefDate '00:00:00' /* Set RefDate as OLDDATE */
say hi
say 'Warning: OLDDATE for sequential dataset has been set' ,
'to'
say ' the MOST RECENT REFERENCE DATE ('RefDate')' ,
'and TIME=00:00:00.'
say 'The z/OS FTP server does not return the most recent' ,
'dataset UPDATE date and time.' lo
End /* Olddate */
Return
TargetDisplay:
say
say 'Target output location...'
If ?qowner then
address COMMAND 'EXEC QOWNER' outfm /* Best info */
Else
Do /* Some info */
If ?dir then address COMMAND 'QUERY ACCESSED' outfm
else address COMMAND 'QUERY DISK' outfm
End
say
Return
Exit:
parse arg exitrc .
If verify(exitrc,'-0123456789')=0 then Exit exitrc
else Exit 999999
Error:
say '+++ "ERROR" error routine entered in:' xfn xft xfm', rc='rc
say '+++ from line:' sigl', which reads:'
say '+++'sourceline(sigl)
Call Exit 20
Syntax:
say '+++ "SYNTAX" error routine entered in:' xfn xft xfm', rc='rc
say '+++ from line:' sigl', which reads:'
say '+++'sourceline(sigl)
Call Exit 20
NoValue:
say '+++ "NoValue" error routine entered in:' xfn xft xfm', rc='rc
say '+++ from line:' sigl', which reads:'
say '+++'sourceline(sigl)
say '+++ Variable with no value is:' condition('Description') Call Exit 24
Explain:
address 'COMMAND'
'PIPE (NAME Explain)' ,
'| <' xfn xft xfm ,
'| INSIDE /ExplainBegin;/ /ExplainEnd;/' ,
'| PREFACE STRLITERAL /'xfn xft xfm 'help.../' ,
'| CONSOLE'
Call Exit 0
/*
ExplainBegin;
For help with FTPFMZOS VMFTP, enter: EXEC FTPFMZOS ?
ExplaineEnd; */
/* This help can be displayed once VMFTP is installed by entering:
vmftp ftpfmzos (parm ?
*/
/* Epilog ***********************************************************
* Function - See 'Explain:' subroutine above. *
* Component of - System Programmers Toolbox. *
* Command format- See 'Explain:' subroutine above. *
* Called by - Users or EXECs. *
* Dependencies - VMFTP MODULE *
* (http://zvm.sru.edu/~DOWNLOAD/LOAD ) *
* Optional: QOWNER EXEC *
* Program Lang. - CMS REXX *
* Date Written - 20060105 *
* Author - Michael R. Walter *
* Changed | By | Description of Change *
* ---------+----+------------------------------------------------- *
* 20060113 mrw - Correct LIST to use 'listdsn' *
* 20090722 mrw - Accept any filemode number for NETRC DATA until *
* we can begn using certificates. *
* 20110826 mrw - Provide alternative to "QOWNER EXEC". *
* *
********************************************************************/
-----Original Message-----
From: CMSTSO Pipelines Discussion List [mailto:[email protected]] On
Behalf Of John P. Hartmann
Sent: Friday, August 26, 2011 10:20 AM
To: [email protected]
Subject: Re: FTP user exit, ultimately a PIPE stage?
Mike, it's been a while. Could well be VMFTP. It had problems running
under COMMAND, as you can well imagine. I never used it other than
make a test case to ensure it worked.