Not for humans, but for search engines z/OS Unix messages

WARNING: terminal is not fully functional Press RETURN to continue

After I installed zopen:less I got this message.

Action: use zopen install ncurses

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1019)

urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1019)

I was doing Python pip install and got these messages. I had to bodge a certificate package from Linux. See here.

How hard can it be to copy a directory on z/OS

My home directory had filled up, and I wanted to copy my files to a file system with more space. This took me much longer than I expected.

Philippe Richard said you should take a look at copytree (in /samples). Nice tool to clone/copy directories. can be run as a unix script or under TSO REXX.

copy using cp

I used cp -R olddir newdir but not all of the files were copied.

tar complained

I used tar -cf old /u/colin/* but got

FSUM7218 tar: /u/colin/env/lib/python3.12/….pyc: name too long

I got pax wrong (1)

I used

pax -w – -f /u/tmp/zowet/archive /u/colin/*

The gave message

FSUMF319 File tag exists but pax cannot store the tag information using this format/option(s) for file /u/colin/env/lib…json.py

I needed the -x os390 option

I got pax wrong (2)

pax -w -x os390 -f /u/tmp/zowet/archive /u/colin/*

The backup worked fine. Unfortunately when I restored from the archive it overwrote the same directory – because I had explicitly coded the fully qualified name /u/colin.

I got pax wrong (3)

cd /u/colin
pax -w -x os390 -f /u/tmp/zowet/archive .

The files are relative to the directory (.) when the files are restored they are also relative to the directory

cd /u/tmp/zowet/colin
pax -ppx -rvf /u/tmp/zowet/archive

This command unpaxes the files from /u/tmp/zowet/archive into the current directory, and prints out the names (-v) as it does so.

The names were displayed, like

./bsdel.sh

so you can see the file name is a relative, rather than fully qualified.

It looked like it worked – but the files belonged to the userid that created them from the pax file.

I got pax right

pax -ppox -rvf ../archive

The -p option specifies which file characteristics to restore

  • o Preserves the user ID and group ID.
  • p Preserves the file mode: access permissions (without modification by umask), set-user-ID bit, set-group-ID bit, and sticky bit.
  • x Preserves extended attributes

I’m sure there are other methods to do the copy.

Submitting jobs from Rexx in Unix Services

As part of using Slickedit and VScode to work on z/OS I wanted to be able to submit JCL from these tools and look at the output. With a bit of glue code it was pretty easy.

SlickEdit and VScode can use SSH to issue commands in Unix on z/OS.

Submitting the JCL

There are several ways of doing it

  • Using TSO SUBMIT to submit a file. It was not easy to tailor the file. Also you do not get back the Jobid, so you cannot easily retrieve the output.
  • Submitting from Rexx variables to the internal reader. This was OK.You do not get back the Jobid.
  • Using the Rexx submit command.

Submit to the internal reader

You do not get back the job id, so you do not know which is your job in the spool.

/* REXX */
"ALLOC FI(INTRDR) SYSOUT(A) WRITER(INTRDR) REUSE"
JOB.1="//COLIN JOB CLASS=A"
JOB.2="//TEST1 EXEC PGM=IEFBR14,REGION=0M"
JOB.3="//"
"EXECIO * DISKW INTRDR (FINIS STEM JOB."
"FREE FI(INTRDR)"
EXIT

Using the Rexx submit

This worked, and was easy. See the documentation.

For example

mysub.rexx

/* rexx */ 
parse arg input
say "*************************"input
rc=isfcalls('ON')
rc= syscalls('ON')
JOB.0=4
JOB.1="//COLINJ2 JOB "
JOB.2="//S1 EXEC PGM=IEFBR14"
JOB.3="//*"
JOB.4="//"
jobid = submit("JOB.")
say "submitted " jobid

You can tailor the JCL depending on your input parameter. For example invoke a JCL procedure, or explicitly build the JCL. For example pass the name of a member of C code in a PDS, and compile that member.

Retrieving the output

SDSF has some great facilities to retrieve the output. SDSF “owns” the prefix ISF. It uses the isf prefix to identify its variables.

If you use SDSF from a terminal you would use:

  • the STatus command to display the jobs you are interested in
    • You can filter what jobs you want displayed, for example COLIN*, or JOB98765,
  • Select the specific jobid
  • Use the ? line command to display the job output
  • Select the output file and browse it.

I use the Jobid because this should be unique. If you use job name, it is not unique (I have 20 jobs in the spool for the COLINCC job to compile a C program).

Selecting the ST display

isffilter = "JOBID = "jobid  /* isf filter=... */                                                               

/* Access the ST panel */
Address SDSF "ISFEXEC 'ST' ()"
lrc=rc

if lrc<>0 then do error handling.

The above code says filter on the specified jobid. You could specify ISFOWNER, and ISFPREFIX. See the documentation.

If you have filtered by jobname, for example COLINCC, there could be multiple instances, so you need to select the one(s) you are interested in, so you might just a well use the JobId from the start.

If the ISFEXEC ST command was successful, it returns variables: The count of rows, and details on each row. See the list of available columns/field names.

  • isfrows the number of rows
  • JobId.n is the job at the nth row
  • RETCODE.n this could be a number such as 0000 or JCL ERROR
  • PhaseName.n such as “AWAITING OUTPUT” or “EXECUTING”
  • Token.n uniquely identifies the job, file or spool output.
do i=1 to isfrows  /* Loop for all rows */ 
if ( JobId.i = jobid) then /* pick the ones of interest */
do 5 /* loop up to 5 times - while executing */
say "PhaseName" PhaseName.i
say "RETCODE" RETCODE.i
if RETCODE.i ="JCL ERROR" then
do
call "./readspool" TOKEN.i "JESYSMSG.JES2. JESMSGLG.JES2. JESJCL.JES2 "
leave
end
if PhaseName.i ="AWAITING OUTPUT" then /* it completed */
do
call "./readspool" TOKEN.i rest /* Rest is the list of output files */
leave
end
/* if PhaseName.i ="EXECUTING" then */
call sleep 5 /* wait 5 seconds before retry */
end /* do 5 */
end /* For all rows */


To display files of interest… call the external Rexx readspool ( the Rexx program is called readspool without a suffix).

readspool – display the contents of the spool file

Process each data set, if it matches the passed names, then display the contents. In my program a names is DDNAME, StepName and ProcStep concatenation with ‘.’.

SDSD Job Dataset Display panel has columns

COMMAND INPUT ===>              
NP DDNAME StepName ProcStep
JESJCL JES2
JESMSGLG JES2
JESYSMSG JES2
SYSCPRT COMPILE COMPILE
SYSOUT COMPILE COMPILE
SYSPRINT COMPILE BIND
SYSPRINT RUN

To display the JESYSMSG output use “JESYSMSG.JES2.” to look at the output from the C bind use “SYSOUT.COMPILE.BIND”

The token to uniquely identify the job is passed. Use the SDSF parm “?” to get access to the data sets.

/* rexx */ 
rc=isfcalls('ON')
rc= syscalls('ON')
parse arg token rest
Address SDSF "ISFACT ST TOKEN('"token"') PARM(NP ?)( prefix jds_)"
lrc=rc
if lrc<>0 then do error handling.

This gets you into the Job Data Set panel. There are many fields you can use relating to each data set. The (prefix jds_) puts jds_ before each variable so it does not overwrite existing symbols. So to reference the DDNAME.n you specify jds_DDNAME.n

Each data set will have

  • TOKEN.n a token to uniquely identify it.
  • STEPN.n the job step name
  • PROCS.n the procedure step name
/* go through each spool record and see if it in the list we were passed */ 
do jx=1 to jds_DDNAME.0
/* build up the list of matching names */
which = jds_DDNAME.jx||"."||jds_STEPN.jx ||"."||jds_PROCS.jx
if wordpos(which,rest) > 0 then /* was it passed in ? */
do
Address SDSF "ISFACT ST TOKEN('"jds_TOKEN.jx"') PARM(NP SA)"
lrc=rc
if lrc<>0 then do error handling.

/* Read the records from the data set and list them. */
/* The ddname for each allocated data set will be in */
/* the isfddname stem. Since the SA action was done */
/* from JDS, only one data set will be allocated. */
do kx=1 to isfddname.0
ADDRESS MVS "EXECIO * DISKR" isfddname.kx "(STEM line. FINIS"
Say "==="Which"===="
do lx = 1 to line.0
say line.lx
end
Say " "
end
end
return 0

The EXECIO reads from the DDName, into the stem variable line., and the data is then written to the terminal.

Note: When running the script in Unix, it needed ADDRESS MVS “EXECIO * DISKR”. Without it I got

FSUM7332 syntax error: got (, expecting Newline

Which usually indicates a code page problem.

Example output

The command

./mysub.rexx

submitted a simple IEFBR14 job. It produced

*************************                                                                                    
submitted JOB07229
PhaseName AWAITING OUTPUT
RETCODE CC 0000
===JESMSGLG.JES2.====
1 J E S 2 J O B L O G -- S Y S T E M S 0 W 1 -- N O D E
0
07.24.52 JOB07229 ---- FRIDAY, 25 APR 2025 ----
07.24.52 JOB07229 IRR010I USERID COLIN IS ASSIGNED TO THIS JOB.
07.24.52 JOB07229 ICH70001I COLIN LAST ACCESS AT 07:24:13 ON FRIDAY, APRIL 25, 2025
07.24.52 JOB07229 $HASP373 COLINJ2 STARTED - INIT 1 - CLASS A - SYS S0W1
07.24.52 JOB07229 IEF403I COLINJ2 - STARTED - TIME=07.24.52
07.24.52 JOB07229 - -----TIMINGS (MINS.)----
-
07.24.52 JOB07229 -STEPNAME PROCSTEP RC EXCP CONN TCB SRB
S
07.24.52 JOB07229 -S1 00 6 0 .00 .00 .0
0
07.24.52 JOB07229 IEF404I COLINJ2 - ENDED - TIME=07.24.52
07.24.52 JOB07229 -COLINJ2 ENDED. NAME- TOTAL TCB CPU TIME= .00
07.24.52 JOB07229 $HASP395 COLINJ2 ENDED - RC=0000
0------ JES2 JOB STATISTICS ------
- 25 APR 2025 JOB EXECUTION DATE
- 4 CARDS READ
- 41 SYSOUT PRINT RECORDS
- 0 SYSOUT PUNCH RECORDS
- 6 SYSOUT SPOOL KBYTES
- 0.00 MINUTES EXECUTION TIME

With thanks to Rob Scott and Dave Crayford for opening my eyes as to how easy it is, and for help in the basics. A lot of code was generated from SDSF using the RGEN facility.

What now?

Having displayed the job output, you could include a step to delete the job.

If you cannot open a data set, amrc may help

I had a C program which opened a dataset and read from it. I enhanced it, by adding comments and other stuff, and after lunch it failed to open

I undid all my changes, and it still it failed to open! Weird.

I got message

  • EDC5061I
    • An error occurred when attempting to define a file to the system. (errno2=0xC00B0403)
    • Programmer response : Check the __amrc structure for more information. See z/OS XL C/C++ Programming Guide for more information on the __amrc structure.
  • C00B0403:
    • The filename argument passed to fopen() or freopen() specified dsname syntax. Allocation of a ddname for the dsname was attempted, but failed.
    • Programmer response: Failure information returned from SVC 99 was recorded in the AMRC structure. Use the information there to determine the cause of the failure.

This feels like the unhelpful messages Ive seen. “An error has occurred – we know what the error is – but we wont tell you” type messages.

To find the reason I had to add some code to my program.

 file =  fopen(fileName, mode     ); 
 __amrc_type save_amrc; 
 memcpy(&save_amrc,__amrc,sizeof(__amrc)); 
 printf("AMRC __svc99_info %hd error %hd\n",
         save_amrc.__code.__alloc.__svc99_info, 
         save_amrc.__code.__alloc.__svc99_error); 
                                                                                    

and it printed

AMRC __svc99_info 0 528

The DYNALLOC (dynamic allocation) which uses SVC 99 to allocate data sets, has a section Interpreting error reason codes from DYNALLOC. The meaning of 528 is Requested data set unavailable. The data set is allocated to another job and its usage attribute conflicts with this request.

And true enough, in one of the ISPF sessions in one of my TSO userid I was editing the file.

It looks like

printf(“__errno2 = %08x\n”, __errno2());

Would print the same information.

Thoughts

It appears that you cannot tell fopen to open it for read even if it has a write lock on it.

For DYNALLOC, if the request worked, these fields may have garbage in them – as I got undocumented values.

It would be nice if the developer of the fopen code produced messages like

EDC5061I: An error occurred when attempting to define a file to the system. (errno2=0xC00B0403) (AMRC=0x00000210)

Then it would be more obvious!

How do I assemble a program in Unix System Services using the c complier?

My preferred way of assembling some code is to use JCL. The next best way is to use the “as – Use the HLASM assembler to produce object files“. (For example “as -a -d xxxx.o xxxx.s 1>a”) Unfortunately I needed to use the”C” compiler xlc, because I wanted to compile a file for a Python External command.

The basic syntax is

xlc “-Wa,LIST” zonsole.s 1>a 2>b

Where

  • -Wa specifies these are options for the assembler source
  • LIST says generate a listing. By default it does not generate a listing, only the error file.
  • zonsole.s is my assembler source program
  • 1>a says put the listing into file a
  • 2>b says put the error file into file b

At the top of the listing file is

No Overriding ASMAOPT Parameters
Overriding Parameters- NODECK,OBJECT,TERM,NOLIST,NOESD,NORLD,NOXREF,FLAG(4),LIST
No Process Statements

** ASMA425N Option conflict in invocation parameters. LIST overrides an earlier setting.

The options in the -Wa are described here

How do I use SFTP to ftp to a z/OS data set?

You cannot do it directly, you have to do a two step process.

Sending files to z/OS using SFTP

SFTP can send files to Unix Services Subsystem. It cannot send to data sets.

sftp colin@10.99.88.77

You can use commands like

  • cd to change directory on the remote system
  • lcd change directory on the local system
  • put
  • get
  • chmod
  • chown
  • exit

There is no command “bin” nor “quote…”.

Getting from a Unix Services file to a dataset.

You can use the cp command.

To copy a binary file to a dataset

For example as if you were using FTP with BIN; quote site cyl pri=1 sec=1 recfm=fb blksize=3200 lrecl=80; put mp1b.load.xmit ‘COLIN.MP1B.LOAD.XMIT’)

cp -W “seqparms=’RECFM=FB,SPACE=(500,100),LRECL=80,BLKSIZE=3200′” mp1b.load.xmit “//’COLIN.MP1B.LOAD.XMIT'”

Where the seqparms are in upper case. If they are in mixed case you get

FSUM6258 cannot open file “… “: EDC5121I Invalid argument.

I could then do TSO RECEIVE INDSN(‘COLIN.MP1B.LOAD.XMIT’).

To copy a text file to a data set

If you use SFTP to copy a text file to Unix Services, it gets sent in bin, and, on z/OS, looks like

ñà ÈÇÁ ËøÁÄÑÃÑÁÀ….

You can tag a file so Unix Services knows it is an ASCII file, using

chtag -tc ISO8859-1 aaa

This makes the file editable from Unix Services, but you cannot just use cp to copy and create a dataset, as above.

You can convert it from ASCII to EBCDIC using

iconv -f ISO8859-1 -t IBM-037 ascii_file  > ebcdic-file

Then use

cp -W “seqparms=’RECFM=VB,SPACE=(CYL,(1,1)),LRECL=800,BLKSIZE=8000′” ebcdic-file “//’COLIN.EBCDFILE'”

Oh p*x, I’ve lost my changes

I have been using pax to backup the files in my Unix Services directory and needed to restore a file so I could compare it with the last version (and work out why my updates didn’t work). Unfortunately I managed to overwrite my latest version instead of creating a copy.
I backed up my directory using

pax -W “seqparms=’space=(cyl,(10,10))'” -wzvf “//’COLIN.PAX.PYMQI2′” -x os390 /u/tmp/pymqi2/

This created a data set COLIN.PAX.PYMQI2 with the give space parameters, and os390 format.
(If you do not want all the files listed, use –wzf)

To list the contents of this file use

pax -f “//’COLIN.PAX.PYMQI2′”

To display a subset of the files use

pax -f “//’COLIN.PAX.PYMQI2′” /u/tmp/pymqi2/code

which gave

/u/tmp/pymqi2/code/
/u/tmp/pymqi2/code/pymqi/
/u/tmp/pymqi2/code/pymqi/__init__.py
/u/tmp/pymqi2/code/pymqi/old__init__.old
/u/tmp/pymqi2/code/pymqi/aa

And provide more information using the -v option

drwxrwxrwx 1 COLIN    1000      0 Jan 22 17:04 /u/tmp/pymqi2/code/
drwxr-xr-x 1 COLIN    1000      0 Feb 11 13:10 /u/tmp/pymqi2/code/pymqi/
-rw-r--r-- 1 OMVSKERN 1000 133011 Feb 22 13:15 /u/tmp/pymqi2/code/pymqi/init.py
-rw-r----- 1 COLIN    1000 119592 Feb  3 12:59 /u/tmp/pymqi2/code/pymqi/old__init__.old
-rwx------ 1 OMVSKERN 1000 119565 Jan 22 16:43 /u/tmp/pymqi2/code/pymqi/aa

The whoops

To restore an individual file and overwrite the original I used the -r option.

pax -rf “//’COLIN.PAX.PYMQI2′” /u/tmp/pymqi2/pymqi/__init__.py

I was expecting the file to be restored relative to the directory I was in; No – because I had backed up the files using an absolute path it restored the file to the same place, and so it overwrote my changes to the file. I had changed to a temporary directory, but I had not realised how the command worked.

There are several ways of doing it properly.

Restore with rename

pax -rf “//’COLIN.PAX.PYMQI2′” -i /u/tmp/pymqi2/pymqi/__init__.py

The -i option means rename.

I ran the command and it prompted me to rename it

Rename “/u/tmp/pymqi2/pymqi/__init__.py” as…

/tmp/oldinit.py

Set “do not overwrite”

I could also have used the -k option which prevents the overwriting of existing files.

Rename on restore

I could also have used the rename

pax -rf “//’COLIN.PAX.PYMQI2′” -s#/u/tmp/pymqi2/pymqi#/tmp/# /u/tmp/pymqi2/pymqi/__init__.py

Where the -s#/u/tmp/pymqi2/pymqi#/tmp/# / says use the regular expression to change /u/tmp/pymqi2/pymqi to /tmp and so restore it to a different place. Note: The more obvious -s/abc/xyz/, where / is used as the delimiter, would not work, as there is a ‘/’ in the file path.

All of the above

I could have use all of the options -i -k -s…. .

A better way to backup.

I had specified an absolute directory /u/tmp/pymi2/. If I was in this directory when I did the backup I could have used

pax … -x os390 .

Where the . at the end means from this directory, and so backup a relative directory.

If I list the files I get

pax -f “//’COLIN.PAX.PYMQI2A'” ./aa
./aa

And now if I restore the file…

pax -rf “//’COLIN.PAX.PYMQI2A'” ./aa

It restored the file into my working directory /tmp/aa .

So out of all the good ways of backing up and restoring – I chose the worst one. It only took me about 2 hours to remake all the changes I had lost.