My Unix Services shell is yucky – how do I change it?

You can get into Unix Services from TSO OMVS, or you can use SSH to get directly into a Unix Services session (without access to ISPF). I was interested in using SSH. At first glance it works just like a Linux command window.

My first experience was the keys did not behave as I expected, and how can I change this.

Why do I care which shell I am using?

On Linux I can type the first part of a file name, press the tab key, and it either completes the name of the file (if there was only one) or lists the names of the possible matching files. This is known as autocomplete.

I tried this in an SSH session on z/OS, and this did not work. I was using the default shell, which does not have this support. I needed to use the bash shell or the zsh shell. Note the zsh was developed on Unix and ported to z/OS, it is not a z/OS special.

How do I use a different shell?

The first question is, “is the shell available on my system?”, to which the answer is maybe.

  • z/OS ships with the default shell in /bin/sh.
  • You can get a free version of bash for z/OS. My ADCD based system does not have Bash pre-installed. I believe you can get it for free from Rocket.
  • My ADCD system has zsh installed (but not configured).

Your default shell

This is set in the in your RACF OMVS segment PROGRAM field . To display it, use

TSO LU COLIN OMVS

This gave me

OMVS INFORMATION
----------------
UID= 0000990021
HOME= /u/colin
PROGRAM= /bin/sh
...

Once you logon to your shell various profiles may be executed if they exist for example for the default shell

  • /etc/profile and ~/.profile where ~ is your default home directory (see HOME= in the output of the LU command, above;

Typically the profile script will set the environment variable SHELL.
I put the following in my ~/.profile so my SSH sessions get the zsh shell.

#!/bin/sh 

if [[ -z "$SSH_CLIENT" ]]
then
# dummy
xxx="$SSH_CLIENT"
else
# SSH_CLIENT has a value ... so an SSH terminal
#cho "using the zsh shell"
zsh="/bin/zsh"
echo "shell $SHELL zsh $zsh"
if [[ $SHELL != $zsh ]]
then
echo "using the zsh shell"
export SHELL="$zsh"
exec "$zsh"
fi
fi


zsh is documented in the UNIX System Services Command Reference. This is a big book, so I extracted the section of zsh using

pdftk USSCommand_v3r1.pdf cat 899-1071 output zsh.pdf

zsh education

I found https://thevaluable.dev/zsh-completion-guide-examples/ was a good source of information about zsh.

If zsh has been installed properly you should have an environment variable $ZDOTDIR defined. If this is missing then $HOME is used instead.

There are files

  • $ZDOTDIR/.zshenv
  • $ZDOTDIR/.zprofile
  • $ZDOTDIR/.zshrc
  • $ZDOTDIR/.zlogin
  • $ZDOTDIR/.zlogout

Which should contain configuration information

There are comment on the IBM community about the poor documention of zsh on z/OS, an how you can get started. It gives hints on setting up colours etc.

The IBM documentation says

  • Commands are first read from /etc/zshenv; this cannot be overridden. …
  • Commands are then read from $ZDOTDIR/.zshenv.
    • If the shell is a login shell, commands are read from /etc/zprofile and then $ZDOTDIR/.zprofile.
    • Then, if the shell is interactive, commands are read from /etc/zshrc and then $ZDOTDIR/.zshrc.
    • Finally, if the shell is a login shell, /etc/zlogin and $ZDOTDIR/.zlogin are read.
  • When a login shell exits, the files $ZDOTDIR/.zlogout and then /etc/zlogout are read. This happens with either an explicit exit via the exit or logout commands, or an implicit exit by reading end-of-file from the terminal

If you start with the system shell, then invoke the zsf shell, the PATH and LIBPATH statements etc will be inherited from the system shell.
If you go directly to the zsh you’ll need to set up a profile based on the system profile. ( or just invoke the existing profile).

You may want to set up a profile to set the command prompt for example the default is

$LOGNAME:$PWD:

You can set it with

export prompt='%n:%/'

to give

COLIN:/u/colin

Changing what keys do … getting the delete key to work as expected

This took a while to understand see Linux mapping the keyboard and on z/OS SSH.

Installing useful commands

I installed zopen. zopen provides lots of Unix-like packages on z/OS. Its home page says

The zopen community is here to provide popular Open Source tools and to encourage z/OS Open Source tools development. We currently host 200+ z/OS Open Source projects and we’re looking for more!

See installing zopen. I then installed the less command , and openssl. See installing packages.

Oh s**T, I cannot boot Ubuntu – I’ve lost my gnome

The problem

I installed some software on Ubuntu, which I had not realised was very back level. It prompted and said

The following packages will be REMOVED:
fuse3 gnome-remote-desktop gnome-shell-extension-desktop-icons-ng gvfs-fuse nautilus
ntfs-3g shfs ubuntu-desktop-minimal ubuntu-session xdg-desktop-portal xdg-desktop-portal-gnome xdg-desktop-portal-gtk

Do you want to continue? [Y/n]

I blindly replied y – as I always do.
The next IPL failed to start, and left me in a command window with strange messages.

The cure

I rebooted, and selected a kernel with recovery mode.
This displayed a menu with

resume   Resume normal boot
...
network Enable networking
root Drop into root shell mode
...

I selected network. This gave a window with

Continuing will remount your / filesystem in read/write mode.  Do you wish to continue?

I clicked on <yes> .

Some status messages were displayed at the bottom of the screen, and it returned to the original menu.

At the menu I selected root. This gave me a command window with access to the network.

The magic command to restore Gnome was

sudo apt install gnome-desktop

This installed various packages.

I then issued

sudo shutdown now -r

and my Ubuntu came up as if nothing had happened.

Lessons learned

After I had recovered my Ubuntu, I remember doing this recovery before. Unfortunately I had written the recovery instructions in a file under my userid, but I could not find them during recovery. The wise thing to do is print off the instructions and keep a copy in my desk – or blog them as I’ve just done.

zopen: installing packages

zopen provides lots of Unix-like packages on z/OS. Its home page says

The zopen community is here to provide popular Open Source tools and to encourage z/OS Open Source tools development. We currently host 200+ z/OS Open Source projects and we’re looking for more!

I wanted to install some packages to make my SSH shell on z/OS look like the Linux shell I know and love. This was easy, but not entirely straight forward.

You can list all the packages available using

zopen query –list

Install less

less pages through the content of a file.

I used

zopen install less

Once I had installed it and tried to use it, I got

WARNING: terminal is not fully functional
Press RETURN to continue 

I had to install ncurses, a programming library for creating textual user interfaces (TUIs) that work across a wide variety of terminals.

zopen install ncurses

and it worked.

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.

Which certificates does Python install (PIP) use on z/OS?

Using the Python pip install … command I was getting error message

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)

On Discord, someone said the ca-certificates package seems to be missing on z/OS, I give a possible solution below.

How I fixed it see Upload the certificate from Linux.

I used Wireshark to monitor the web sites being used, and z/OS could not validate the certificate sent to it. It had a signing chain of 3 Certificate Authorities.
I tried capturing the certificates using openssl s_client…, but they didn’t work.

There is a pip option –trusted-host github.com –trusted-host 20.26.156.215 which says ignore the certificate validation from the specified sites. This did not work.

The pip command worked on Linux, so it was clearly a problem with certificates on z/OS.

I had zopen installed on my z/OS, and could issue the command

openssl s_client -connect github.com:443

This gave

subject=CN=github.com                                                                                                        
issuer=C=GB, ST=Greater Manchester, L=Salford, O=..., CN=...Secure Server CA          
---                                                                                                                          
No client certificate CA names sent                                                                                          
Peer signing digest: SHA256                                                                                                  
Peer signature type: ecdsa_secp256r1_sha256                                                                                  
Peer Temp Key: X25519, 253 bits                                                                                              
---                                                                                                                          
SSL handshake has read 3480 bytes and written 1605 bytes                                                                     
Verification error: unable to get local issuer certificate                                                                    

This was much quicker than trying to wait for Pip to process the request.

Where does Python expect the certificates to be?

I executed a small Python program to display the paths used

COLIN:/u/colin: >python
Python 3.12.3 ....on zos
Type "help", "copyright", "credits" or "license" for more information.

import _ssl
print(_ssl.get_default_verify_paths())
quit()

This gave

(‘SSL_CERT_FILE’, ‘/usr/local/ssl/cert.pem’, ‘SSL_CERT_DIR’, ‘/usr/local/ssl/certs’)

This was unexpected because I have openssl certificates in /usr/ssl/certs.

Upload the certificate from Linux

The Linux command

sudo apt reinstall ca-certificates

downloads the latest ca certificates into /etc/ssl/certs/ca-certificates.crt

I uploaded this to z/OS into /usr/local/ssl/cert.pem, for the Python code.

echo “aa” | openssl s_client -connect github.com:443 -verifyCAfile /etc/ssl/certs/ca-certificates.crt

worked. The certificate was verified.

I also uploaded it to /etc/ssl/certs/ca-certificates.crt for Python.

The openssl documentation

The openssl documentation discusses the location of the certificate store. The environment variable OPENSSLDIR locates where the certificate is stored, and how to download trusted certificates in a single file. Specifying OPENSSLDIR did not help.

Linux mapping the keyboard and on z/OS SSH

I wanted to configure the keyboard in zsh and bash on z/OS so the delete key (top right of the keyboard) deleted the current character. (Backspace deletes the previous character) I could not find any good documentation on how to do it.

I found some bash for Linux documentation and have based this blog post on that. I’ve given the command I used, then sections on the background of how it works.

Note the binding of key to action depends on the shell. The shells zsf and bash support binding. shell does/may not.

Having written the original blog post, I found some additional useful information which will make the document clearer.

How to set the delete key to delete

With my Bash shell, I initially had to do it in stages:

  • Type bind “ that’s bind space double quote
  • Control+V, delete key to insert the value of the key. This made it bind “^[[3~
  • Type “:delete-char with no space after the :
  • It now looks like bind “^[[3~”:delete-char
  • Press enter
  • The delete key should delete the character under the cursor

The command bind -p |grep delete gave

"\C-h": backward-delete-char
"\C-?": backward-delete-char
"\C-d": delete-char
"\M-[3~": delete-char
"\\": delete-char
# delete-char-o

Which shows that Ctrl+d and \\ also deletes the current character.

In a shell (in ASCII) this is (in ISPF edit with hex on)

bind '" ∇3~":delete-char' 
6666222153723666676266672222
29E4072BB3E2A45C545D38127000

Where the incantation is x1b5b337e.

What does the mapping mean?

It took me many hours of looking for a good description of what the key mapping is.

The Wikipedia page ANSI escape codes, was very helpful and clear.

  • You can press the “a” key – and “a” is displayed in the command window.
  • You can press shift “a” – and “A” is displayed in the command window
  • You can press Control (Ctrl) “a” – and this may do something
  • You can press Meta “the Windows” key “a” and this may do something
  • You can press Alt + “a” and it may do something.
  • You can press combinations of those keys, such as Ctrl+shift + “a” and this may do something.

The operating system may intercept the key and not pass it to the window. For example on my Linux, Ctrl+Alt+Delete is logout.

In the command window the shell code will intercept a key press combination and take an action, for example move the cursor left, clear the screen, delete word, or just the the character.

You can change the mapping using the bind or bindkeys command for bash and zsf shells.

You can find the code for a key combination by using ctrl+v. For example with the left cursor key

Ctrl+v, < gives ^[[D

How to decode ^[[D ?

There are several formats.

  • <esc>[ number(;modifier)~
  • <esc>[ letters

The string starts with an escape sequence. ^[ This can be written as \e ,\033,  0x1b, or ^[. Where \0 is octal 33, which is hex 1B, (and decimal 27). When specifying a key sequence any of the values can be used. I mentioned above the incantation x1b5b337e.

Because there is no trailing ~ we need to lookup the “[D” in the Wikipedia page section xterm sequences. This has

<esc>[D     - Left 

So for ^[[D we have <esc>, Left key pressed

For ^[[3;5~ we have

  • ^[ is escape
  • [3 isDelete key
  • ; is a modifier
  • 5 is Control + shift
  • ~ end of escape

so the key presses was Control + Delete key (top right on my keyboard). Shift seems to be ignored!

Another popular key is ^M which is Carriage Return – (and process any data on the line) and move the cursor to column 0.

\C is the Ctrl key, \M is the meta key. For keyboards without the Meta key this is displayed as Escape \e.

Keys like \M-OD comes from

Keypad mode
"\M-OD":backward-char
"\M-OC":forward-char
"\M-OA":previous-history
"\M-OB":next-history

This information is not well documented.

What is key mapping

On Linux Ubuntu the control-right arrow combination moves the cursor right one word. The control-left arrow moves the cursor left one word. Control-L clears the screen, but leaves the command line intact.

You can list the possible actions

bind - l

This gave

...
arrow-key-prefix
backward-byte
backward-char
backward-delete-char
backward-kill-line
backward-kill-word
...

Get a list of functions and what keys use them

With upper case -P

bind -P |grep backward

This gave

backward-byte is not bound to any keys
backward-char can be found on "\C-b", "\eOD", "\e[D".
backward-delete-char can be found on "\C-h", "\C-?".

Where does \eOD come from ? See here.

Get a list of keys and their functions in bind format

with lower case -p

bind -p |grep backward

gave

"\eOD": backward-char
"\e[D": backward-char
"\C-h": backward-delete-char
"\C-?": backward-delete-char
...

where

  • Control-h moves the cursor left one character and deletes the character
  • \e is the escape key. The alt key is usually mapped to the escape key by terminal emulators.

This output is slightly untrue. If there is no blank after the : you can use bind….

If you have a blank after the : you need to enclose it in single quotes.

bind ‘”\C-h”: backward-delete-char’

to set the key.

What is the code for a key press combination?

The Linux command Ctrl-V (verbatim insert) followed by a key, gives the mapping for that key.

Ctrl+V LeftArrow
^[[D

Ctrl+V Ctrl+Leftarrrow
^[[1;5D

Where ^[ means the escape key and [D is OD.

^[[3~ is escape Delete key.

Verbatim insert gives the code of the key which was pressed. This works on z/OS if you have the Bash or zsf shell installed.

What keys are mapped?

In the zsh shell you can issue

bindkey -L

(With bash you can use the bind command).

This gives output like

bindkey "^E" end-of-line
...
bindkey "^L" clear-screen

Where ^ is the ctrl key.

If you type (on Linux) man bash , and locate Readline Command Names if lists the function names and what they mean.

The bash command bind -l lists all of the functions

$ bind -q backward-char
backward-char can be invoked via "\C-b", "\M-OD", "\M-[D".

It gets very confusing

A sequence can be created in different formats. For example many commands begin with the Escape (or Meta key). This can be written as \e ,\033,  0x1b, or ^[. Where \0 is octal 33 which is hex 1B, ( or decimal 27). It is confusing when you display information with different commands.

Where does \eOD (or \M-OD)come from?

It was a challenge to find this information.

In the Linux terminfo documentation it says

The codes sent by the left arrow, right arrow, up arrow, down arrow, and home keys can be given as kcub1, kcuf1, kcuu1, kcud1, and khome respectively. If there are function keys such as f0, f1, …, f10, the codes they send can be given as kf0, kf1, …, kf10. If these keys have labels other than the default f0 through f10, the labels can be given as lf0, lf1, …, lf10.

The linux command infocmp

infocmp|grep kcu 

gave

kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,

so this all says the left arrow key maps to backward character. – Phew

Problems

If there was a space after the : I got

readline: delete-char: no key sequence terminator

from the bind command.

Putting the command into my .profile file didn’t work because of the wrong character set.

I could put the command into a shell script, and invoke it – but I could not get it to work from the .profile.

zopen: Installing

My z/OS did not have access to the internet, so it could not the default commands to download zopen from the internet.

The instructions here are pretty clear, use that document and this document together.

I created a ZFS with space 6000 MB and mounted it as /u/zopen. (I think you need about 1700 MB to install it, so mine was over allocated).

I used https://github.com/zopencommunity/metaport/releases/tag/DEV_metaport_3853.

This gives a curl statement curl -o meta-new_installer.20251030_160947.zos.pax.Z… which is very long and has the untar commands at the tail end.

I used the command

curl -o meta-new_installer.20251030_160947.zos.pax.Z -L https://github.com/zopencommunity/metaport/releases/download/DEV_metaport_3853/meta-new_installer.20251030_160947.zos.pax.Z 

to download the paxed file to my Linux machine.

Upload it in binary to z/OS. The paxed file need about 105 MB on z/OS.

You need a userid with write access to /u/zopen.

cd /u/zopen
pax -rf meta-new_installer.20251030_160947.zos.pax.Z
cd meta-new_installer 
. ./.env
zopen init

It reported

During this operation, up to 291.162M of disk space will be required.
After this operation, 206.518M of disk space will be used.

After the install the command df -P /u/zopen showed it was 24% ( of 6000 MB) – and 2871046 512 byte blocks ( = 1401 MB). So using a file system with 1700 MB should be enough space to install it.

I edited my .profile file to add

 . /u/zopen//etc/zopen-config --override-zos-tools 

Internet access

I now have access from my z/OS to the internet, but do not have DNS set up. See How to get out to the internet. SNAT, DNAT and MASQUERADE and Problems getting out of z/OS to the outside world, unknown host.
I configured my /etc/hosts to include

#IPAddress             Hostname   alias 
151.101.128.223        pypi.org    pip 
151.101.192.223        pypi.org    pip 
151.101.192.223        files.pythonhosted.org   pipfiles 
20.26.156.215          github.com 
151.101.128.81         bbc.co.uk 
151.101.1.91           curl.se 
185.199.110.133        raw.githubusercontent.com 
185.199.110.133        release-assets.githubusercontent.com 

and restarted the resolver.

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.

How to get out to the internet. SNAT, DNAT and MASQUERADE

This follows on from concepts explained in If we all have the same IP addresses how does the internet work?

The high level problems

I have a Linux server machine connected to my laptop via Ethernet. How can I change the destination of where data flows?

I want to be able to say

  • Any traffic coming in over Ethernet for IP address 98.76.54.32, route it to the server on my other laptop with address 10.0.0.6. This is changing the Destination Address of a packet., or DNAT: (changing the) Destination Network Address Table to a specific address.
  • I want to be able to send stuff sent over Ethernet with an internal IP address, and route it to external servers. In effect I want to make the data from my server machine be sent to the internet, with the IP address of my laptop. This is changing the Source address of a packet, or SNAT: (changing the) Source Network Address Table to a specific address.

The home address of my z/OS system was 10.1.1.2. My Linux machine has IP address 192.168.1.139.

Kindergarden concepts of a router

Traffic comes in to a router. There are rules which control how traffic is routed, for example this address range should go down the Ethernet connection, anything else (the default) goes over the wireless connection.

Below the surface

I picture the router as 3 boxes in a row. Before – router – after.

  • Before: You can specify rules to be applied before the data gets to the routing code. This allows you to change information in the packet header, such as destination, or port address. The rule type for this are called PREROUTING. The packet then flows into…
  • The router: This decides where each packet goes. The packet the flows into…
  • After: You can change the packets before it gets send down the interface. This rule type is POSTROUTING.

Changing the destination

The command on my Linux laptop

iptables -t nat -A PREROUTING -p tcp --dport 1122 -j DNAT --to-destination 10.0.0.6:3344

Send all TCP traffic destined for port 1122 to the machine with IP address 10.0.0.6, and change the port to 3344.

It is PREROUTING, meaning that make the change before any routing decisions are made.

Changing the source – getting the data to the outside world

The following command on my Linux laptop

iface=wlxcc641aee92c5
sudo iptables -t nat -A POSTROUTING -s 10.1.1.2 -o $iface -j SNAT --to-source 192.168.1.139

tells Linux to take any traffic from 10.1.1.2, send it over the interface wlxcc641aee92c5 and change the Source Network Address Translation (SNAT) so it looks like it came from 192.168.1.139 ( my wireless interface).

This is POSTROUTING because the routing decision has already been made, and the data is ready to be sent over the interface(eg wireless).

This is fine as long as you know the IP address of your interface (192.168.1.139). If your router has DHCP, the Linux may get a different address every time. In this case you can use

iface=wlxcc641aee92c5
sudo iptables -t nat -A POSTROUTING -s 10.1.1.2 -o wlxcc641aee92c5 -j MASQUERADE

which I believe says for the specified source address 10.1.1.2 and use the address of the -o output device.

You might just use the MASQUERADE option every time as it is easier to type.

If you want to specify all traffic (or just want it to work) you can omit the -s

iface=wlxcc641aee92c5
sudo iptables -t nat -A POSTROUTING -o $iface -j MASQUERADE

There are some good examples here.

Problems getting out of z/OS to the outside world, unknown host

I was in OMVS trying to install some software, but it could not find and use the IP address of the server. My current /etc/hosts file is here.

What’s the problem?

I issued

ping pypi.org      

and got

EZZ3111I Unknown host 'pypi.org'                     

There are two solutions

Capture an (IP address) resolver trace

I issued the command

export RESOLVER_TRACE=~/trace 

and reran the command.
This gave

 Resolver Trace Initialization Complete -> 2025/11/26 10:53:47.591994 

res_init Resolver values:
Setup file warning messages = No
CTRACE TRACERES option = No
Global Tcp/Ip Dataset = ADCD.Z31B.TCPPARMS(GBLTDATA)
Default Tcp/Ip Dataset = ADCD.Z31B.TCPPARMS(GBLTDATA)
Local Tcp/Ip Dataset = /etc/resolv.conf
Translation Table = TCPIP.STANDARD.TCPXLBIN
UserId/JobName = COLIN
...
res_init Succeeded
res_init Started: 2025/11/26 10:53:47.650509
res_init Ended: 2025/11/26 10:53:47.650537
***************************************************************************
GetAddrInfo Started: 2025/11/26 10:53:47.650677
GetAddrinfo Invoked with following inputs:
Host Name: pypi.org
No Service operand specified
Hints parameter supplied with settings:
ai_family = 0, ai_flags = 0x00000062
ai_protocol = 0, ai_socktype = 0
No NameServers specified, no DNS activity
GetAddrInfo Opening Socket for IOCTLs
BPX1SOC: RetVal = 0, RC = 0, Reason = 0x00000000, Type=IPv4
BPX1IOC: RetVal = 0, RC = 0, Reason = 0x00000000
GetAddrInfo Opened Socket 0x00000004
GetAddrInfo Only IPv4 Interfaces Exist
GetAddrInfo Searching Local Tables for IPv4 Address
Global IpNodes Dataset = ADCD.Z31B.TCPPARMS(ZPDTIPN1)
Default IpNodes Dataset = ADCD.Z31B.TCPPARMS(ZPDTIPN1)
Search order = CommonSearch
SITETABLE from globalipnodes ADCD.Z31B.TCPPARMS(ZPDTIPN1)
- Lookup for pypi.org
GetAddrInfo Closing IOCTL Socket 0x00000004
BPX1CLO: RetVal = 0, RC = 0, Reason = 0x00000000
GetAddrInfo Failed: RetVal = -1, RC = 1, Reason = 0x78AE1004
GetAddrInfo Ended: 2025/11/26 10:53:47.664992

Where 0x78AE1004 is The GETADDRINFO call failed because the host name cannot be found in DNS, or in the z/OS host configuration files (/etc/hosts or hlq.HOSTS.ADDRINFO).

I think this message is not very helpful. It was not found in sitetable

Default IpNodes Dataset = ADCD.Z31B.TCPPARMS(ZPDTIPN1) 

Use a Dynamic Name Server

You can tell TCPIP to go to a Name Server to look up the address in the internet.

The JCL for my RESOLVER started task has

//* 
//* TCPIP RESOLVER - COLINS
//*
//RESOLVER PROC PARMS=CTRACE(CTIRES00)
//*
//EZBREINI EXEC PGM=EZBREINI,REGION=0M,TIME=1440,
// PARM=('&PARMS',
// 'ENVAR("RESOLVER_TRACE=/var/log/resolver"/')
//SETUP DD DISP=SHR,DSN=COLIN.TCPPARMS(GBLRESOL),FREE=CLOSE
//SYSTCPT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//*

The //SETUP member GBLRESOL has

  DEFAULTTCPIPDATA('COLIN.TCPPARMS(GBLTDATA)') 
GLOBALTCPIPDATA(/etc/resolv.conf)
;
...

File /etc/resolv.conf has

nameserver 8.8.8.8 
nameserver 1.1.1.1

I enabled a resolver trace and pinged a new website.

The trace includes

**************************************************************************
GetAddrInfo Started: 2025/12/16 17:00:54.153989
GetAddrinfo Invoked with following inputs:
Host Name: hmrc.co.uk
...
res_search(hmrc.co.uk, C_IN, T_A)
res_search Host Alias Search found no alias
res_querydomain(hmrc.co.uk., , C_IN, T_A)
res_querydomain resolving name: hmrc.co.uk.
res_query(hmrc.co.uk., C_IN, T_A)
Querying resolver cache for hmrc.co.uk.
EZBRECFR: RetVal = 0, RC = 0, Reason = 0x00000000
No cache information was available
...
* * * * * Beginning of Message * * * * *
...
Number of Question RRs: 1
Question 1:
hmrc.co.uk
...
* * * * * End of Message * * * * *
res_send Name Server Capabilities
Monitoring intervals used = 5
Name server 8.8.8.8
...
Name server 1.1.1.1
...
res_send Sending query to Name Server 8.8.8.8
...
res_send received data via UDP. Message received:
* * * * * Beginning of Message * * * * *
...
Question 1:
hmrc.co.uk
...
Number of Answer RRs: 1
Answer 1:
hmrc.co.uk
...
TTL: 3600 (0 days, 1 hours, 0 minutes, 0 seconds)
195.171.114.178
* * * * * End of Message * * * * *
...
Attempting to cache results for hmrc.co.uk.
EZBRECAR: RetVal = 0, RC = 0, Reason = 0x00000000
Cache information was saved

The TTL from the DNS server says

 TTL:  3600 (0 days, 1 hours, 0 minutes, 0 seconds) 

When there was another request to the site within this time, the trace had

GetAddrInfo Started: 2025/12/16 17:01:09.426161 
GetAddrinfo Invoked with following inputs:
Host Name: hmrc.co.uk
...
Querying resolver cache for hmrc.co.uk.
EZBRECFR: RetVal = 0, RC = 0, Reason = 0x00000000
Cache data from 8.8.8.8 was retrieved
...
GetAddrInfo Succeeded: IP Address(es) found:
IP Address(1) is 195.171.114.178

showing the value from the DNS was retrieved from the local cache.

Explicitly specify the name to IP address mapping

If you do not want to use the DNS you can specify your own name to IP address mapping.

I reconfigured my RESOLVER started task to have

Global IpNodes Dataset  = /etc/hosts 
Default IpNodes Dataset = COLIN.TCPPARMS(ZPDTIPN1) 

and edited /etc/hosts to include

#IPAddress             Hostname   alias 
151.101.128.223        pypi.org    pip 

and the ping pypi.org worked

The trace file now had

 Global IpNodes Dataset  = /etc/hosts 
Default IpNodes Dataset = COLIN.TCPPARMS(ZPDTIPN1)
Search order = CommonSearch
SITETABLE from globalipnodes /etc/hosts
- Lookup for pypi.org
ADDRTABLE from globalipnodes /etc/hosts
- Lookup for 151.101.128.223
GetAddrInfo Returning Zero as Port Number
GetAddrInfo Built 1 Addrinfos
GetAddrInfo Closing IOCTL Socket 0x00000004
BPX1CLO: RetVal = 0, RC = 0, Reason = 0x00000000
GetAddrInfo Succeeded: IP Address(es) found:

When I changed this file to have multiple examples for the pypi.org

#IPAddress             Hostname   alias 
151.101.128.223 pypi.org pip
151.101.192.223 pypi.org pip

the trace file had

GetAddrinfo Invoked with following inputs: 
Host Name: pypi.org
...
SITETABLE from globalipnodes /etc/hosts
- Lookup for pypi.org
ADDRTABLE from globalipnodes /etc/hosts
- Lookup for 151.101.128.223
GetAddrInfo Returning Zero as Port Number
GetAddrInfo Built 2 Addrinfos
GetAddrInfo Closing IOCTL Socket 0x00000004
BPX1CLO: RetVal = 0, RC = 0, Reason = 0x00000000
GetAddrInfo Succeeded: IP Address(es) found:
IP Address(1) is 151.101.128.223
IP Address(2) is 151.101.192.223

and it returned both of them to the caller

My current /etc/hosts

After doing some Pip work to install products, my /etc/hosts file is now

#IPAddress             Hostname   alias 
151.101.128.223        pypi.org    pip 
151.101.192.223        pypi.org    pip 
151.101.192.223        files.pythonhosted.org   pipfiles 
20.26.156.215          github.com 
151.101.128.81         bbc.co.uk 
151.101.1.91           curl.se 
185.199.110.133        raw.githubusercontent.com 
185.199.110.133        release-assets.githubusercontent.com 
169.63.188.167         downloads.pyaitoolkit.ibm.net 

I created this list by resolver_trace to find the hostnames which failed, then adding them to the file along with their addresses using nslookup name.