A Multi-tasking Internet Daemon in REXX/MVS
Earl Hodil, Open Software Technologies (firstname.lastname@example.org)
In part one, we looked at the general architecture of an Internet daemon, and we examined the code and JCL required to implement the daemon's main task. In this part we'll see how the sub-job, TELNS, works.
TELNS, shown below, accepts the main job's client ID and the given socket descriptor as arguments. These are used in the TAKESOCKET call to gain control of the connection. TAKESOCKET returns a new socket descriptor, "s", and notifies, via the aforementioned EXCEPTION event, the RXINETD program that it can now reclaim the socket. TELNS also uses SETSOCKOPT to tell TCP/IP that it would like all inbound data to be converted from ASCII to EBCDIC, and all outbound data to be converted from EBCDIC to ASCII.
After this point, the programming is quite simple and completely variable. In this example we start a simple lock-step conversation with the client. The server says something, and then the client says something. When either party determines that it is time to quit, they simply close the socket.
GetLine and PutLine.
TELNS uses 2 subroutines to read from and write to the socket. These are PUTLINE and GETLINE. GETLINE, shown below, buffers bytes of inbound data into a buffer named INBUF. It scans INBUF for a carriage return/line feed pair in order to break out lines of data. This is required because stream sockets do not define record boundaries. The definition of data boundaries is left to the application. By convention, many application protocols use the CRLF to delimit discrete bits of information. The SMTP (email) and HTTP (web) protocols are good examples of this.
Note that GETLINE neatly handles all of the cases you can encounter:
PUTLINE (shown below) performs the reverse function. It appends a CRLF to the end of each passed line of data and buffers the lines in a variable called OUTBUF. The buffer is sent whenever either the End-Of-Message argument is passed, or when the buffer reaches a pre-determined size.
Strictly speaking, the TELNS application does not need the PUTLINE function since it doesn't use buffering. However, in other applications, where you may need to send a page or more of data in response to a client command, PUTLINE will *significantly* improve your application's performance.
I hope that some of you out there in MVS land can put this technology to good use. Already several customers of mine are using it in real applications. If you'd like a copy of a "beefed-up" RXINETD (and a couple of example sub-jobs), just send me a note at email@example.com. The code is FREE and not copyrighted. Do with it what you will. I am hopeful that you will make it better!
Along the way, several variations on this scheme have developed. Most prominent is the substitution of started tasks for both the main job and sub-jobs. Instead of writing JCL to an internal reader, these applications use ADDRESS CONSOLE -- or some ISV software method -- to issue MVS START commands. And, of course, the JCL is kept in a procedure library like 'SYS1.PROCLIB'.
In another variation, the sub-job prompts the client for user ID and password. The sub-job then constructs JCL for a sub-sub-job that includes the user ID and password on the job card. Next it gives the socket to the sub-sub-job, submits the JCL (using the ddname/internal reader trick), and terminates. When the sub-sub-job starts, it runs under the specific authority of the client's user. All without a single line of APF-authorized code!
One twist I'd like to see tried is the use of BatchPipes/MVS. I don't have it on my system (yet), but a cursory reading of the manuals leads me to believe that several "hot" job steps could be created and passed connection request indications via "piped" ddnames. This would provide two nice improvements to the architecture. First, it would move all of the action into one address space. And second, it would reduce the time it takes to hand a connection from the main task to the subtask. If someone out there tackles this, please let me (and everyone else) know. I am *very* interested in this subject.