Can I easily make an ISPF edit line command macro?

The key word in the title is easily. If you search for “Working with an edit line command table”, the documentation gives you instructions on how to create a table, then add your definition, create your macro, and then use it. When you were expecting to do this invisibly for the end user, it is a lot of work.

A command line rexx program can process line commands. I created a command line program so when I use it, and use S or SS..SS line commands, the macro can extract the tagged lines, and so do things with the tagged data.

The command line Rexx

/* REXX */ 
address isredit
'MACRO (a) NOPROCESS '
'process range S '
IF RC = 0
THEN DO
"(first) = LINENUM .ZFRANGE"
"(last) = LINENUM .ZLRANGE"
data = ""
/* build up a long string from the tagged lines */
do i = first to last
"(l) = LINE (i)"
data = data || strip(l) || " "
end
l = data
address ispexec "edit dataset('COLIN.$$TEMP$$') macro(l2) parm(l)"
END

Where the lines of interest are

  • ‘MACRO (a) NOPROCESS ‘
    • Usually when a command line macro is processed, ISPF processes any outstanding line commands (such as D) before processing any commands in the rexx exec. The NOPROCESS says – hold off doing this until instructed.
    • a is the data passed in
  • ‘process range S ‘
    • This locates any line commands beginning with S, for a single line, or SS…SS for multiple lins.
  • if rc = 0 then do
  • “(first) = LINENUM .ZFRANGE”
    • get the line number of the First in the RANGE
  • “(last) = LINENUM .ZLRANGE”
    • get the line number of the Last in the Range.
  • data = “”
  • do i = first to last /* process the data build up the string */
    • “(l) = LINE (i)”
    • data = data || strip(l) || ” “
  • end
  • ldata = data
  • address ispexec “edit dataset(‘COLIN.$$TEMP$$’) macro(l2) parm(ldata)”
    • Invoke edit on a file – invoke macro l2, and pass the data in the rexx variable ldata
  • END

If the “process range” gives a non zero return code, set an error message

else do 
zedsmsg = 'You need to specify line prefix s|ss ss'
zedlmsg = 'You need to specify line prefix s|ss ss long message'
address ispexec 'SETMSG MSG(ISRZ001)'
end

This sets a Short message (zedSmsg) which is display at the top right of the display, and a long message (zedLmsg) if the user presses PF1 on the short message.

Macro L2

This macro is processed when the edit dataset(..) macro(l2) command is issued

/* REXX */ 
address isredit
'MACRO (a) '

say "Passed data" a
...

Linux: Why are my cursor keys not working?

I created a new userid, and when I use the cursor keys in a command window it does not work as expected and I get commands like ]]?a

Solution

sudo chsh -s /bin/bash myid

If you use the command adduser you can specify the shell at create time.

Once you have done this you need to logoff and logon again (or just start a new window).

You should also have a .profile script for when you logon. My .profile has

# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# see /usr/share/doc/bash/examples/startup-files for examples.
# the files are located in the bash-doc package.

# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
#umask 022

# if running bash
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi

# set PATH so it includes user's private bin directories
PATH="$HOME/bin:$HOME/.local/bin:$PATH"


JVM_ARGS="-Dcom.sun.management.jmxremote.port=9081 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false "
# export MQCHLLIB=/var/mqm/qmgrs/QMA/@ipcc
export MQSSLKEYR=/var/mqm/qmgrs/QMB/ssl/key
export SWT_GTK3=0
export SWT_WEBKIT2=0
export CLASSPATH="${JAVA_HOME}/lib/tools.jar:${CLASSPATH}"
export LESS="-I -R"

This . profile invokes .bashrc (if it exists)

My file has

####### added for zPDT #######200725095805
export PATH=/usr/z1090/bin:$PATH
export LD_LIBRARY_PATH=/usr/z1090/bin:$LD_LIBRARY_PATH
export MANPATH=/usr/z1090/man:$MANPATH
ulimit -c unlimited
ulimit -d unlimited
####### end of added for zPDT #######

export LESS="-I -R "
export HISTTIMEFORMAT="%T "
export HISTFILESIZE=5000

Get your granny to read it before you distribute it

I have spend a day trying to understand an electricity bill. There is online help called “how to understand your electricity bill”. It is a good example of how not to provide help, and people should learn from this.

When your write documents, get someone else outside of your area to review the document and make sure the documents are useful. My father was great, when I was eleven years old I had to write down how to make a pot of tea. My father would point out I had not checked/put any water in the kettle etc.

Some of the problems I found with the electricity bill help information were:

  • A PDF file showing the bill and help information, this had been created from a web page. I searched for a word, it was found but not displayed! In the PDF document it was white text on a white background – and so was invisible. If you copied the page to the clipboard and pasted it into a document, the text was visible.
  • In the pdf document, the instructions “move your mouse over the line to get more information” only applied to the web page, not the pdf page, but it was displayed in the pdf.
  • In the web page “how to understand your electricity bill” it gave an example bill, with a pop-up beside each line of interest. I thought this would be useful – but no. On the topic “Agreed Availability Charge”, the pop up said “Agreed Availability Charge”. Great – this adds no value, and does not explain, nor give me a link to what the Agreed Availability Charge actually is.
  • In the “Power information summary” section, it gives values like HH, MD, RE REAP, and only explains some of them. REAP is “reactive power”, what is this? I guess HH is Half Hourly readings. (Reactive power represents the electrical power that flows back and forth between the phase conductors and the neutral conductor of a three-phase network, but does not perform any mechanical work – this didn’t help me either.)
  • There is a helpful(?) video of someone who said to reduce the cost of your electricity, consider reducing the the availability cap/limit. Our value is 69, but does not give any units, nor how to compare it with you current usage.

The moral of this story is – get someone who is not an expert to try out your information.

RACF – I cannot delete a certificate!

I made a mistake creating a certificate, and could not delete it, because I got a message

IRRD109I The certificate cannot be added. Profile … is already defined.

In my JCL I had

RACDCERT GENCERT  -                                           
CERTAUTH -
SUBJECTSDN(CN('DocZosCADSA')-
O('COLIN') -
OU('CA')) -
NOTAFTER( DATE(2027-07-02 ))-
KEYUSAGE( CERTSIGN ) -
DSA
SIZE(1024) -
WITHLABEL('DocZosCADSA')

This created a certificate

I was missing the – after DSA, so the DSA, SIZE(1024) and importantly the WITHLABEL() was missing.

When I tried to recreate this certificate I got message

IRRD109I The certificate cannot be added. Profile 00.CN=DocZosCADSA.OU=CA.O=COLIN is already defined.

My problem was, what do I delete ?

The following command listed all of the certificate owned by certauth.

RACDCERT certauth LIST

I searched for CN=DocZosCADSA and it found. (Use the CN=… not the whole string)

Label: LABEL00000002 
...
Subject's Name:
>CN=DocZosCADSA.OU=CA.O=COLIN<
...

Note the label.

The command

RACDCERT CERTAUTH DELETE(LABEL('LABEL00000002'))

Deleted the certificate in error – problem solved.

Note: For a personal certificate the reported certificate was like

02AB.CN=SSCA256.OU=CA.O=SSS.C=GB

If you use the list command the certificate sequence number 02AB is on a different line to the remainder of the label.

Setting up a JES2 output NJE TCPIP node as a client using AT-TLS

This is part of some work I did to configure AT-TLS for a JES2 TCPIP node to another system.

I didn’t have a remote system to connect to, but I had a Python TLS server which the NJE node could connect to (and then end), which demonstrated the TLS connection.

The JES2 definition

The address of the remote end, running the Python TLS server was 10.1.0.2.

$ADDSOCKET(LAPTOP),IPADDR=10.1.0.2,LINE=3,NETSRV=1,NODE=50,PORT=2175,SECURE=NO 

Starting the NJE node

$SN,SOCKET=LAPTOP

The AT-TLS definitions

This definition acts as a client to a remote server, so AT-TLS needs to be configured as a AT-TLS client.

TTLSRule CPJES2OUT 
{
RemoteAddr 10.1.0.2
RemotePortRange 2175
Direction Output
TTLSGroupAction
{
TTLSEnabled On
}
TTLSEnvironmentAction
{
HandshakeRole Client
TTLSEnvironmentAdvancedParms
{
# clientAuthType needs to be required or Passthru
ClientAuthType PassThru
TLSv1 Off
TLSv1.1 Off
TLSv1.2 On
# TLSv1.3 On
}
TTLSKeyringParms AZFKeyringParms
{
Keyring start1/TN3270
}

TTLSConnectionAction
{
TTLSCipherParmsRef AZFCipherParms
TTLSConnectionAdvancedParms
{
# ServerCertificateLabel is for a server connection
# ServerCertificateLabel RSA2048
CertificateLabel RSA2048
# ApplicationControlled OFF
}
}
}

AZFCipherParms

I put common definitions into their own section, for example

TTLSCipherParms AZFCipherParms 
{
V3CipherSuites TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
V3CipherSuites TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
V3CipherSuites TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
V3CipherSuites TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
V3CipherSuites TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
V3CipherSuites TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
V3CipherSuites TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
V3CipherSuites TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
# TLSv1.3
V3CipherSuites TLS_CHACHA20_POLY1305_SHA256
}

Using TLSv1.3

You need TTLSEnvironmentAdvancedParms to contain

TTLSEnvironmentAdvancedParms 
{
TLSv1.1 Off
TLSv1.2 On
TLSv1.3 On
}

and at least one TLSV1.3 cipher spec.

TTLSCipherParms AZFCipherParms 
{
# TLSv1.2
V3CipherSuites TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
V3CipherSuites TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
V3CipherSuites TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
V3CipherSuites TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
V3CipherSuites TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
V3CipherSuites TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
V3CipherSuites TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
# TLSv1.3
V3CipherSuites TLS_CHACHA20_POLY1305_SHA256
# TLSv1.2
V3CipherSuites4Char TLS_CHACHA20_POLY1305_SHA256
V3CipherSuites4Char TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
V3CipherSuites4Char TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
V3CipherSuites4Char TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 C02
}

such as TLS_CHACHA20_POLY1305_SHA256

See Cipher suite definitions and search for 1301 (TLS_AES_128_GCM_SHA256) ,1302 (TLS_AES_256_GCM_SHA384) ,1303(TLS_CHACHA20_POLY1305_SHA256).
There is a column called TLSv1.3 (but it is hard to find). There are two tables, you need to use the second table to find what version of TLS the cipher specs provide.

Python server

The code below acted as a remote TLS server for the handshake.

import socket
import ssl
import struct
import pprint

HOST= ''
PORT = 2175

cafile="/home/colinpaice/ssl/ssl2/jun24/docca256.pem"
certfile="/home/colinpaice/ssl/ssl2/jun24/docec521june.pem"
keyfile="/home/colinpaice/ssl/ssl2/jun24/docec521june.key.pem"
certpassword = None

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.maximum_version = ssl.TLSVersion.TLSv1_3
context.load_cert_chain(certfile, keyfile)
context.load_verify_locations(cafile=cafile)

context.verify_mode = ssl. CERT_REQUIRED
getciphers = context.get_ciphers()
#for gc in getciphers:
# print("get cipher",gc)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
sock.bind((HOST, PORT))
sock.listen(1)
with context.wrap_socket(sock, server_side=True) as ssock:
conn, addr = ssock.accept()
cert = conn.getpeercert()
pprint.pprint(cert)
v = conn.version()
print("version",v)
c = conn.cipher()
print("ciphers",c)
sock.close

When this ran, and the z/OS NJE node connected to it ($SN,SOCKET=LAPTOP), the output was

{'issuer': ((('organizationName', 'COLIN'),),
(('organizationalUnitName', 'CA'),),
(('commonName', 'DocZosCA'),)),
'notAfter': 'Jun 17 23:59:59 2025 GMT',
'notBefore': 'Jun 17 00:00:00 2024 GMT',
'serialNumber': '07',
'subject': ((('organizationName', 'RSA2048'),),
(('organizationalUnitName', 'SSS'),),
(('commonName', '10.1.1.2'),)),
'subjectAltName': (('IP Address', '10.1.1.2'),),
'version': 3}
version TLSv1.3
ciphers ('TLS_CHACHA20_POLY1305_SHA256', 'TLSv1.3', 256)

Showing the certificate, the level of TLS and the cipher spec used.

The messages on the z/OS console were

$SN,SOCKET=LAPTOP                                                        
$HASP000 OK
IAZ0543I NETSRV1 TCP/IP connection with IP Addr: 10.1.0.2 Port: 2175
Initiated
IAZ0543I NETSRV1 TCP/IP connection with IP Addr: 10.1.0.2 Port: 2175
Successful
IAZ0543I NETSRV1 TCP/IP connection with IP Addr: ::ffff:10.1.0.2 Port:
2175 ended due to TCP/IP error, rc: 1121

Setting up JES2 input NJE node (server) and AT-TLS

I got this working in response to a question about AT-TLS and JES2.

You need to configure the port and IP address of the destination node using AT-TLS.

I created the socket definitions

$ADDSOCKET(TLS),NODE=1,IPADDR=10.1.1.2,NETSRV=1,PORT=2275

Before you start

Get a working JES2 NJE, and AT-TLS environment. It makes it difficult to get the AT-TLS configured as well as getting NJE to work.

JES2 NJE needs a Netserver (NETSRV) to do the TCP/IP communication.

When you configure AT-TLS this intercepts the traffic to the IP address and port and does the TLS magic. This means you need a different netserver, and a tls specific port, and a TLS specific socket. It looks like the default TLS port is 2252. The doc says

SECURE=OPTIONAL|REQUIRED|USE_SOCKET
Indicates whether the NETSERV should accept only connection requests with a secure protocol in use such as TLS/SSL. When SECURE=REQUIRED is speci®edQ the NETSERV rejects all connection requests that do not specify a secure protocol is to be used for the connection. When SECURE=OPTIONAL is speciedQ the NETSERV allows connections with or without a secure protocol in use.
The default, USE_SOCKET, inherits the SECURE setting from the SOCKET statement associated with the NETSERV. If the SOCKET says SECURE=YES, then processing is the same as specifying
SECURE=REQUIRED on the NETSERV.
To specify that the NETSERV should use NJENET-SSL (2252) as the PORT it is listening on and the default port for outgoing connections, but not require all connections to use TLS/SSL, you must specify SOCKET SECURE=YES on the socket that is associated with the NETSERV and set the NETSERV to SECURE=OPTIONAL.

I do not understand this because AT-TLS will try to do a TLS handshake and fail if the session is not a TLS session.

It feels like the easiest way is to have a netserver just for TLS with its own port. I may be wrong.

In my PAGENT configuration, I took a working TLSrule and created

TTLSRule CPJES2IN 
{
LocalAddr ALL
RemoteAddr ALL
LocalPortRange 2252
Direction Inbound
Priority 255
TTLSGroupActionRef AZFGroupAction1
TTLSEnvironmentActionRef AZFEnvAction1
TTLSConnectionActionRef AZFConnAction1
}

This is for the inbound traffic on port 2252.

I defined the JES2 node

$TSOCKET(TLS),NODE=1,IPADDR=10.1.1.2,NETSRV=1,PORT=2252 

with the matching port=2252

I assigned this socket to netsrv1, and started it

$TNETSRV1,SOCKET=TLS
$SNETSRV1

I used a Python nje client to connect to z/OS. I used a modified version of the python NJE client, where I defined a certfile, keyfile and cafile.

I used

nje = njelib.NJE("N50","S0W1")
nje.set_debuglevel(1)
nje.setTLS is colin added code
#nje.setTLS(certfile="/home/colinpaice/ssl/ssl2/jun24/docec521june.pem",
# keyfile="/home/colinpaice/ssl/ssl2/jun24/docec521june.key.pem",
# cafile="/home/colinpaice/ssl/ssl2/jun24/docca256.pem")
connected = nje.session(host="10.1.1.2",port=2252,timeout=1)

Where the JES2 system is called S0W1, the node used is N50.

The z/OS IP address is 10.1.1.2, and the port is 2252.

There were no helpful messages to say the session was using TLS. I used Wireshark on the connection, and AT-TLS trace to check the TLS calls.

If I used a non TLS connection to the z/OS node I got

EZD1287I TTLS Error RC: 5003 Data Decryption    
LOCAL: ::FFFF:10.1.1.2..2252
REMOTE: ::FFFF:10.1.0.2..41288
JOBNAME: JES2S001 RULE: CPJES2IN

showing the AT-TLS definition was CPJES2IN.

RC 5003 will occur when the AT-TLS process is expecting an TLS message but receives a clear-text message – so no TLS request coming in.

Setting up JES2 NJE using TCP/IP

I was trying to test TLS and JES2 NJE, and needed to get JES2 NJE working. I did not have remote system to use, so I used Python NJE, I also used openssl s_server to act as a server – just for the connection.

For more information on setting up JES 2 NJE with TLS see:

Setting up NJE on JES2

You can use static (defined in the JES2PARM member) or define them dynamically using commands.

The bits you need

TCP/IP work is done in a net server NETSRV task. You can define more than one of these to allow you to partition the work.

The net server needs a SOCKET definition. This socket definition needs the IP address on the local system, and the port used to connect to the socket code. If you let it default to the local IP address, it may not pick the IP address you want to use.

You need a NODE definition for the remote end.

You need a TCP/IP LINE definition for the connection to the remote system.

You need a SOCKET for the remote connection, giving the IP address of the remote end, the port to be used at the remote end, the LINE definition to be used, and the NODE to be used.

These have to be started before they can be used.

I had firewall problems on my Linux server, where it was not forwarding packets to the remote system. Once I fixed this, the connection was easy.

Static definition

The address of my z/OS is 10.1.1.2. The address of the remote end is 10.1.0.2

In the JES2 parmlib members I added

NODE(2)     NAME=LAPTOP    
SOCKET(LOC) NODE=1,IPADDR=10.1.1.2,netsrv=1,PORT=175
NETSRV(1) SOCKET=LOC
SOCKET(LAPTOP) NODE=50,IPADDR=10.1.0.2,LINE=2,NETSRV=1,port=22
LINE(2) UNIT=TCP

Dynamic definitions

I used the following operator commands to define the resources, rather than define them statically

$ADDSOCKET(LOC),NODE=1,IPADDR=10.1.1.2,netsrv=1,PORT=175
$Addnetsrv(1),socket=LOC
$addline(2),unit=tcp
$ADDSOCKET(LAPTOP),IPADDR=10.1.0.2,line=2,netsrv=1,node=50

You need to use a statically defined NODE.

Starting them up

I then issued

  • $SNetsrv1 This starts an address space with name JES2S001.
  • $SLNE2 to start the line
  • $Sn,socket=LAPTOP

Other useful commands

  • $DNETSRV1
  • $DNetsrv1,sessions this gave output like
    • $HASP898 NETSRV1 SESSIONS=(LNE2/LAPTOP/S6)
  • $DNetsrv1,socket this displays which socket the net server is using.
  • $DSOCKET to display all sockets
  • $DSOCKET(LAPTOP4)
  • $TSOCKET(LOC),SECURE=YES,PORT=2275