KICKS

A transaction processing system for CMS & TSO

KICKS KooKbooK recipe

The 251 project – Scripting 3270 apps with open source tools ( I )

Fire up TSO in a script !

This is the first in a series of KooKbooK recipes detailing my successful effort to demonstrate over 250 KICKS users sharing VSAM files in the MVS Turnkey under Hercules, with sub-second response time.

This recipe will focus on scripting a 3270 application using free open source tools.

Environment

If you establish a similar environment you can replicate my results. While the 251 project was originally completed and documented in 2009, the following reflects the environment for the 2014 update to this recipe, using newer version of KICKS and the other components. The updated environment is:

Installation verification:

With all the parts installed make sure you can start the TK3update using a C3270 console. Then startup 3 more C3270 sessions and logon the HERC01, HERC02, and HERC03. Type KICKS on at least one of them and make sure it starts up.

Open a terminal window in Fedora. Make sure EXPECT is installed by entering expect . You should see a prompt like

expect 1.1>

Press ^D to return to the shell, and ^D again to close the terminal windows. At this point all the software is installed and you know it works manually. Now let’s automate!

First - a little knowledge

C3270 and EXPECT are open source packages that come with substantial documentation (man pages).

C3270’s web page is http://x3270.bgp.nu

C3270 has a scripting cousin called S3270 but I won’t be discussing it, nor any of the “inside” scripting features of C3270. I’ve used EXPECT to drive C3270 strictly from the “outside”.

EXPECT’s web page is http://expect.nist.gov

EXPECT is also the subject of an O’Reilly book Exploring Expect I strongly recommend. You may be able to find a pdf of that book on the web, I’ve even seen one that says you can use it free if you can’t afford to buy the book, but I’m not sure it or any of the other pdfs are legit. If you can’t afford the twenty odd bucks Amazon wants for a copy a used bookstore is probably a good bet as the book has been out more than 15 years.

You should definitely read at least the first three chapters (introduction …, tcl …, getting started…) of Exploring Expect before you get involved with modifying the scripts I’ll describe.

Next - a little code

Here is an EXPECT script to logon to TSO, do “something”, then logoff.
#!/usr/bin/expect -f
# Using expect & c3270 to logon to tso, run an application, then logoff
# Note this script does not 'handle' all possible exception cases...
# set the userid, password, and terminal id
set who "herc01" ;# tso userid
set what "" ;# tso password
set where "0C0" ;# CUU for terminal
# suppress terminal output to our screen
log_user 1 ;# if you want to see what it does
# spawn c3270
spawn c3270 -model 3279-2 -once 0$where@localhost:3270
# send a CLEAR (wakes up hung screen, puts netsol on it)
sleep 1; send "\001"; send "c"
# see if we got a netsol screen
set timeout 30
expect {
"Logon ===>" { set which netsol }
"INPUT NOT RECOGNIZED" { set which netsol }
timeout { set which timeout }
}
# if timeout die
if { $which == "timeout" } {
send_user "$where not at netsol \n"
exit
}
# at netsol so logon
sleep 1; send "logon $who"; send "\015"
while { $which != "ready" } {
# see what happened to the logon rqst
expect {
"ENTER CURRENT PASSWORD" { set which needpass }
"READY" { set which ready }
"KEY TO CONTINUE" { set which fsibanner }
timeout { set which timeout }
}
# if timeout die
if { $which == "timeout" } {
send_user "$who $where logon failed\n"
exit
}
# tso wants password so give it
if { $which == "needpass"} {
sleep 1; send "$what"; send "\015"
}
# press enter if fsi banner
if { $which == "fsibanner"} {
sleep 1; send "\015"
}
} ;# end of 'while'
# at tso ready prompt, so go to rpf
sleep 1; send "rpf"; send "\015"
# at rpf screen so quit rpf
expect "===>" { sleep 1; send "x"; send "\015" }
# at tso ready again, now logoff
expect "READY" { sleep 1; send "logoff"; send "\015" }
# back at netsol screen so do ^] to get to c3270 menu
expect "===>" { sleep 1; send "\035" }
# at c3270 menu so quit
expect "c3270>" { sleep 1; send "quit\n"; }

You should be able to cut/paste this into your terminal window, run it, and watch it do its thing. For example:

cat >logtso.exp
…paste here…
^d
.expect -f logtso.exp

So how does it work? We’ll go thru it line by line

#!/usr/bin/expect -f

Is the typical shell-bang (aka hash-bang, aka …).

The next few lines starting with # are comments. Comments continue thru the end of the line (usually); their exact syntax might seem strange if you aren’t familiar with TCL. See Exploring Expect p24-25.

Next we have a group of lines that initialize some variables

# set the userid, password, and terminal id
set who "herc01" ;# tso userid
set what "" ;# tso password
set where "0C0" ;# CUU for terminal

This is initializing the variables who, what, and where to characters “herc01”, “”, and “0C0” respectively. Set is the EXPECT assignment operator. EXPECT variables are always character.  The values do not have to be quoted (so for example you could say “set who here01”) unless they contain white space, or as in the 2nd set above, you want to set a variable to null. Numbers are not usually quoted since they can’t contain white space.

Multiple commands may be placed on one line by separating them with semicolons, comments following commands on a line must be preceded by a semicolon – hence the ;# syntax above.

As you will see, in most EXPECT statements if you are obtaining the variable’s value you will precede the variable name by a $, and if you are setting its value you will not use the $.

Next we see an expect command that causes the output of spawned commands to be shown with EXPECT standard output.

# suppress terminal output to our screen
log_user 1 ;# if you want to see what it does

The command looks like it is setting the log_user variable to 1, but what it’s really doing is executing the log_user command with an argument of 1. See Exploring Expect p175 for more detail on log_user.

Next the C3270 program is launched with arguments described in its man page.

# spawn c3270
spawn c3270 -model 3279-2 -once 0$where@localhost:3270

The spawn command is used to launch a program EXPECT will control. That is, when EXPECT “sends” data it becomes keyboard characters to C3270, and data that C3270 wants to display on a user screen becomes keyboard characters to expect! Of course it’s more complicated than that: Exploring Expect has a whole chapter called “spawn” and actually most of the book relates to it. It is, along with the expect command (of the EXPECT program) the core of the program.

Note use of the where variable ($where) to specify which of the Hercules 3270’s to connect to, in this case 00C0, which matches one of the 3270’s in the Hercules configuration file (conf/tk3upd.conf).

After C3270 is spawned the script sends a CLEAR.

# send a CLEAR (wakes up hung screen, puts netsol on it)
sleep 1; send "\001"; send "c"

The sleep 1 command causes a delay of 1 second, so that any initial interaction between C3270 and Hercules has a chance to settle down. Also note the semicolons between the commands.

After sending CLEAR the expect command is used to see what kind of response resulted.

# see if we got a netsol screen
set timeout 30
expect {
"Logon ===>" { set which netsol }
"INPUT NOT RECOGNIZED" { set which netsol }
timeout { set which timeout }
}

“timeout” is a special variable. It tells the following expect command(s) how long to wait (in seconds) to match patterns to input. The expect command matches patterns (“Logon…”, “INPUT NOT…”, etc) to actions (set which netsol, etc). In this context “timeout” is also special, it means that the time specified in the “timeout” variable expired without matching any of the other patterns. When any pattern (including “timeout”) matches the action is taken and the script continues with the command following the expect.

So we sent a CLEAR and then waited to see if we got input that looked like it came from the Network Solicitor. If so we set a variable to indicate, otherwise we set the variable to say a timeout occurred. The next few lines of code act on this result.

# if timeout die
if { $which == "timeout" } {
send_user "$where not at netsol \n"
exit
}

Pretty obvious.

And

# at netsol so logon
sleep 1; send "logon $who"; send "\015"
while { $which != "ready" } {
# see what happened to the logon rqst
expect {
"ENTER CURRENT PASSWORD" { set which needpass }
"READY" { set which ready }
"KEY TO CONTINUE" { set which fsibanner }
timeout { set which timeout }
}
# if timeout die
if { $which == "timeout" } {
send_user "$who $where logon failed\n"
exit
}
# tso wants password so give it
if { $which == "needpass"} {
sleep 1; send "$what"; send "\015"
}
# press enter if fsi banner
if { $which == "fsibanner"} {
sleep 1; send "\015"
}
} ;# end of “while”

When we know the network solicitor is there we send a logon command with the logon id, then go into a loop waiting for a TSO READY prompt. The loop has an expect statement and code to act on what’s found. Of course if READY is ever found the loop ends and the script continues. If an expect timeout happens that’s a logon failure. If a password request is seen then the password is sent and the loop goes back to looking for READY. Another possibility is the FSI banner. If that’s seen an “enter” is sent and the loop goes back to looking for READY.

It is worth reiterating that an expect timeout doesn’t mean there was no new output. It means there was no new output that matched what we were looking for. Plainly there are LOTS of possibilities the above expect statement does not cover. Reconnect? Bad password? TSO down? They are all lumped together under timeout, which the script reports as logon failed.

Assuming READY was found the next step is to “do something”. In this case that means getting in, then out of RPF. Going in -

# at tso ready prompt, so go to rpf
sleep 1; send "rpf"; send "\015"

Waiting ‘till in, then - Going out –

# at rpf screen so quit rpf
expect "===>" { sleep 1; send "x"; send "\015" }

Kind of a giant hole in the above logic. Suppose a “timeout” happens? First off the x will never get sent, and secondly we’ll silently fall thru to the next statement in the code. After something like this stuff can start going south in mysterious ways. But have faith! All will be well! Trust me!

The rest of the script is in a similar vein.

# at tso ready again, now logoff
expect "READY" { sleep 1; send "logoff"; send "\015" }
# back at netsol screen so do ^] to get to c3270 menu
expect "===>" { sleep 1; send "\035" }
# at c3270 menu so quit
expect "c3270>" { sleep 1; send "quit\n"; }

At the READY prompt we send logoff. At the netsol screen we send an escape, get into C3270 command mode, then send C3270 the “quit” command. And that’s also the end of the EXPECT script.

 

That’s enough code for today. Next time we’ll see how to drive several sessions at once and get think time simulation and response time recording.


Copyright © Mike Noel, 2008-2014; last updated 12/30/2014 for KICKS 1.5.0