Running z/OS REXX from Persistent TSO Address Space w/Zowe CLI
Previously we shared how to run z/OS REXX faster with Zowe CLI. One technique described there involved using a persistent (longer running) TSO address space. However, specific details on how to do this were skipped.
Below we’ll look at the steps used to keep a persistent TSO address space to quickly run REXX via something like Zowe Explorer:
Overview
We’ll rely on a script, keep-tso.mjs
, (source below) which does three primary things:
- runs a
zowe tso start address-space
command - executes any TSO environment initialization commands (e.g. dynamic allocation)
- periodically pings the TSO address space to keep it alive
keep-tso.mjs
You can copy this script in a project and keep it in source control:
import { execSync } from 'child_process';
import { writeFileSync } from 'fs';
import { readFileSync } from 'fs';
const KEY_FILE = 'key.txt';
const CMD_FILE = 'cmds.txt';
const MAX_FAIL = 5;
const WAIT_TIME = 1000 * 60 * 5; // 5 minutes (in milliseconds) - milliseconds * seconds * minutes
function start() {
console.log(`Starting TSO...`);
const key = execSync(`zowe tso start as --sko`).toString().trim();
writeFileSync(KEY_FILE, key);
commands().forEach(cmd => {
console.log(`running '${cmd}'`)
send(key, cmd);
});
console.log(`... key was ${key}`);
return key;
}
function ping(key) {
try {
console.log(execSync(`zowe tso ping as ${key}`).toString().trim());
return true;
} catch (e) {
return false;
}
}
function stop(key) {
try {
console.log(execSync(`zowe tso stop as ${key}`).toString().trim());
return true;
} catch (e) {
return false;
}
}
function read() {
try {
return readFileSync(KEY_FILE).toString().trim();
} catch (e) {
}
return `fakekey`;
}
function commands() {
try {
return readFileSync(CMD_FILE).toString().trim().split(/\r?\n/g);
} catch (e) {
}
return [];
}
function send(key, cmd) {
try {
console.log(execSync(`zowe tso send as ${key} --data "${cmd}"`).toString().trim());
} catch (e) {
// do nothing
}
}
function keep() {
let failed = 0;
let key = read();
if (!ping(key)) {
failed++;
if (failed > MAX_FAIL) {
console.log(`failed too many times`);
process.exit(1);
}
key = start();
}
}
// stop(read()); // debug
keep();
setInterval(() => {
keep();
}, WAIT_TIME);
cmds.txt
If you need any one-time initialization commands to run at TSO address space start time, you can create an adjacent file, cmds.txt
, and if found, keep-tso.mjs
runs the TSO commands found there (to do things like dynamic allocation).
For example:
allocate file(ops$opsy) dummy
allocate file(myexec) dsn('opsmvs.kelda16.ops.dev.rexx') shr
concat file(sysexec myexec)
allocate file(isplog) dummy
In this case, a data set, OPSMVS.KELDA16.OPS.DEV.REXX
, is allocated to a SYSEXEC
DD.
Running the Script
Ideally, you’ll run the script in a dedicated terminal window (instead of something like the VS Code terminal which tends to crash more often than not):
Note that when the script runs, if firsts attempts to ping a previous TSO address space. If the ping fails, it starts a new instance (which is why there is an error message issued in this gif).
When the script runs, it also writes a file, key.txt
. This file should be in your .gitignore
.
oi.bat
Lastly, you can use Windows batch files (as an example) to recreate a mainframe experience. Here we use a file oi.bat
with these contents:
@echo off
set /P TSO_KEY=<key.txt
zowe tso send as %TSO_KEY% --data "oi %1"
This allows you to run oi
from your workstation terminal, similar to how you might run oi
from a TSO interface: