/*---------------------------------------------------------------------+
| COPYRIGHT SAS INSTITUTE INC., 1990, SAS CAMPUS DRIVE, CARY, NC 27513 |
| |
| S A S / C S A M P L E |
| |
| NAME: CICLISTN |
| LANGUAGE: C |
| PURPOSE: Demonstrate a CICS TCP/IP listener that STARTS a CICS |
| TRANSID, CICTSERV, within CICS. Refer to the SAS/C |
| Library Reference Vol 2, Third Edition, Release 6.00 |
| for additional details on socket API function calls. |
| MVS - |
| COMPILE, LINK: SUBMIT prefix.SAMPLE.AUX(CICLISTN) |
| where "prefix" is the installation defined high-level- |
| qualifier for the SAS/C product. |
| EXECUTE: Refer to the SAS/C CICS User's Guide, Second Edition, |
| Release 6.00 for details on defining and starting a |
| SAS/C application in CICS. |
| NOTES: CICLISTN assumes a working TCP/IP environment on both |
| the local and remote hosts, refer to MISC NOTES. |
| TSO - |
| COMPILE: LCCCP/LC370 CLISTs |
| NOTES: RENT and EXTNAME are required LC370 "options" |
| LINK: CLK370 CLIST |
| EXECUTE: N/A |
| NOTES: CICLISTN assumes a working TCP/IP environment on both |
| the local and remote hosts. Refer to MISC NOTES: |
| CMS - You may Translate, Compile and CLINK SAS/C programs under |
| CMS, but the resulting object MUST be link-edited on MVS. |
| Refer to the SAS/C Compiler and Library User's Guide, |
| Fourth Edition, Release 6.00 for additional information. |
| MISC NOTES: CICLISTN needs to be customized for the local TCP/IP |
| environment. The local information required by |
| CICLISTN is: |
| LISTENPORT - defines the port CICLISTN will do a |
| listen(). Default is port 3001. |
| MAXSOCKETS - defines the maximum number of sockets |
| for the select() socket array. |
| MISC NOTES: To terminate CICLISTN, use the SAS/C Sample, CICLQUIT, |
| and initiate the CICLQUIT program w/in CICS. |
+---------------------------------------------------------------------*/
#define LISTENPORT 3001
#define QUITSTRING "CICLQUIT"
#define MAXSOCKETS 50 /* how many sockets may be open at one time */
/*-------------------------------------------------------------------+
º Includes for socket calls. NOTE: SAS/C programs prior to Version º
º 6.00 require the #define __IBM_TCPIP. º
+-------------------------------------------------------------------*/
#define __IBM_TCPIP 1
#include <sys/types.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <lcstring.h>
#include <packed.h>
#define TRUE 1
#define FALSE 0
#define MAXREQLEN 50 /* max length of CICS request, in chars */
/*-------------------------------------------------------------------+
º Macros for converting EBCDIC <-> ASCII. º
+-------------------------------------------------------------------*/
#define convFromAscii(str) {cp=(str); while(*cp = ntohcs(*cp)) cp++;}
#define convToAscii(str) {cp=(str); while(*cp = htoncs(*cp)) cp++;}
int setupSocket(void);
int acceptRequest(void);
int closeSockets(int);
void Quit(void);
char *cp; /* char pointer for EBCDIC<->ASCII macros */
int listensock, /* socket on which we listen for requests */
numsockets, /* number of sockets opened by listener */
highsocket, /* highest socket number */
holdrequests; /* flag: listener cannot handle any more */
/* requests right now (all sockets used) */
fd_set pending, /* list of sockets waiting for transactions */
taken; /* list of sockets which have been taken */
struct socklist /* linked list of sockets given to pending */
{ /* CICS transactions */
int sock;
struct socklist *next;
} *translist;
struct clientid myid, /* clientid of listener for TCPSOCKET_PARM */
giveid; /* generic clientid used for givesocket() */
void main()
{
int ns, /* number of sockets with action */
cicserr; /* error condition from CICS commands */
char cicsdummy; /* dummy data area for CICS commands */
fd_set incoming; /* bound socket description for select() */
struct timeval timeout;
/*----------------------------------------------------------------+
º Check for existence of CICLQUIT temporary storage queue. If it º
º does exist (QIDERR is not returned), then get rid of it so that º
º the listener is not halted by a leftover "quit" command. º
+----------------------------------------------------------------*/
EXEC CICS READQ TS QUEUE("CICLQUIT") INTO(&cicsdummy)
LENGTH(1) RESP(cicserr);
if (cicserr != DFHRESP(QIDERR))
EXEC CICS DELETEQ TS QUEUE("CICLQUIT");
/*----------------------------------------------------------------+
º Set up a socket, initialize list of pending transactions to º
º none, and set timeout for select() call. º
+----------------------------------------------------------------*/
if (setupSocket() == -1)
exit(EXIT_FAILURE);
FD_ZERO(&pending);
holdrequests = FALSE;
timeout.tv_sec = 60;
timeout.tv_usec = 0;
while (TRUE)
{
do {
/*----------------------------------------------------------+
º Check for existence of CICLQUIT temporary storage queue. º
º If it does exist (QIDERR is not returned), then quit. º
+----------------------------------------------------------*/
EXEC CICS READQ TS QUEUE("CICLQUIT") INTO(&cicsdummy)
LENGTH(1) RESP(cicserr);
if (cicserr != DFHRESP(QIDERR))
{
EXEC CICS DELETEQ TS QUEUE("CICLQUIT");
Quit();
}
/*----------------------------------------------------------+
º Check for action on sockets given to pending transactions,º
º as well as incoming socket (unless all sockets are used). º
+----------------------------------------------------------*/
FD_ZERO(&incoming);
if ((!holdrequests) && (numsockets < MAXSOCKETS))
FD_SET(listensock, &incoming);
taken = pending;
ns = select(highsocket+1, &incoming, NULL, &taken, &timeout);
} while (ns == 0);
if (ns == -1)
{
perror("select() call failed");
Quit();
}
/*-------------------------------------------------------------+
º If there was action on listensock, then handle the incoming º
º request. º
+-------------------------------------------------------------*/
if (FD_ISSET(listensock, &incoming))
{
ns--;
acceptRequest();
}
/*-------------------------------------------------------------+
º If there was action on any other sockets, then release the º
º sockets of those transactions which have taken them. º
+-------------------------------------------------------------*/
if (ns > 0)
closeSockets(ns);
}
}
/*-------------------------------------------------------------------+
º Open a socket and set it up to accept requests. Return -1 if error.º
+-------------------------------------------------------------------*/
int setupSocket()
{
struct sockaddr_in sa_serv; /* socket address for server */
/*----------------------------------------------------------------+
º Get a socket of the proper type. º
+----------------------------------------------------------------*/
listensock = socket(AF_INET, SOCK_STREAM, 0);
if (listensock == -1)
{
perror("socket() call failed in setupSocket()");
return -1;
}
numsockets = 1;
/*----------------------------------------------------------------+
º Get information about listener to pass to transactions. º
º Clientid used for givesocket() should be the same but with a º
º subtaskname of all blanks. º
+----------------------------------------------------------------*/
if (getclientid(AF_INET, &myid) == -1)
{
perror("getclientid() call failed in setupSocket()");
close(listensock);
return -1;
}
giveid = myid;
memset(giveid.subtaskname, ' ', 8);
/*----------------------------------------------------------------+
º Prepare a socket address for the server. º
+----------------------------------------------------------------*/
memset(&sa_serv, '\0', sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = htonl(INADDR_ANY);
sa_serv.sin_port = htons(LISTENPORT);
/*----------------------------------------------------------------+
º Bind our socket to the desired address so clients may reach us. º
+----------------------------------------------------------------*/
if (bind(listensock, &sa_serv, sizeof(sa_serv)) == -1)
{
perror("bind() call failed in setupSocket()");
close(listensock);
return -1;
}
/*----------------------------------------------------------------+
º Set up a queue for incoming CICS requests. SOMAXCONN is º
º maximum size of queue. º
+----------------------------------------------------------------*/
listen(listensock, SOMAXCONN);
highsocket = listensock;
return 0;
}
/*-------------------------------------------------------------------+
º A request has been detected--accept it, decode it, and issue º
º EXEC CICS START or EXEC CICS WRITEQ as appropriate. º
º Return -1 if error. º
+-------------------------------------------------------------------*/
int acceptRequest()
{
int reqtype, /* type of request: 1=START,2=WRITEQ*/
numchars, /* number of chars read from socket */
numseg, /* number of segments in request */
reqsock, /* socket for individual requests */
cicserr; /* error condition from CICS command*/
char req[MAXREQLEN+1], /* buffer for reading in request */
*thisseg, *nextseg, /* pointers used to break up request*/
seg[4][MAXREQLEN+1], /* segments of request */
packedint[4], /* interval in packed decimal form */
*leftover, /* chars left over after interval */
errstring[MAXREQLEN+80];/* error message sent to client */
struct socklist *new; /* new node in list of open sockets */
double interval; /* intermediate form of interval */
struct TCPSOCKET_PARM /* parameters passed to CICS trans */
{ /* (cics_socket_parm) */
int give_take_socket;
char lstn_name[8];
char lstn_subname[8];
char client_in_data[36];
struct sockaddr_in sockaddr_in_parm;
} csp;
short csp_len = (short)sizeof(csp);
struct sockaddr_in sa_client;/* socket address of client */
int sa_len = sizeof(sa_client);
/*----------------------------------------------------------------+
º Divert incoming request to its own socket. If accept() call º
º fails, then all sockets are being used, so hold off requests. º
+----------------------------------------------------------------*/
reqsock = accept(listensock, &sa_client, &sa_len);
if (reqsock == -1)
{
perror("accept() failed in acceptRequest()");
holdrequests = TRUE;
return -1;
}
/*----------------------------------------------------------------+
º Read CICS request from socket and convert from ASCII to EBCDIC. º
+----------------------------------------------------------------*/
/* Read CICS request. */
numchars = read(reqsock, req, MAXREQLEN);
req[numchars] = '\0';
convFromAscii(req);
/*----------------------------------------------------------------+
º Separate request into segments: transid, data, type, interval. º
º Segments are separated by commas. Force transid and type º
º segments to uppercase. º
+----------------------------------------------------------------*/
numseg = 0;
nextseg = thisseg = req;
while ((nextseg != NULL) && (numseg < 4))
{
/*-------------------------------------------------------------+
º Look for next comma and pluck off what's before it. º
+-------------------------------------------------------------*/
if ((nextseg = strchr(thisseg, (int) ',')) != NULL)
*nextseg = '\0';
strcpy(seg[numseg], thisseg);
thisseg = nextseg+1;
numseg++;
}
strupr(seg[0]);
strupr(seg[2]);
/*----------------------------------------------------------------+
º Check segments for validity. º
+----------------------------------------------------------------*/
if (numseg < 1)
{
strcpy(errstring, "CICS listener: empty request received.\n");
convToAscii(errstring);
write(reqsock, errstring, strlen(errstring));
close(reqsock);
return -1;
}
if ((seg[0][0] == '\0') || (strlen(seg[0]) > 4))
{
sprintf(errstring, "CICS listener: invalid transid/TDQ '%s' "
"requested.\n", seg[0]);
convToAscii(errstring);
write(reqsock, errstring, strlen(errstring));
close(reqsock);
return -1;
}
/*----------------------------------------------------------------+
º Fill in TCPSOCKET_PARM for RECEIVEing CICS transaction. º
+----------------------------------------------------------------*/
/* FD_TO_IBM_TCPIP is only required for releases prior to 6.00 */
csp.give_take_socket = FD_TO_IBM_TCPIP(reqsock);
memcpy(csp.lstn_name, myid.name, 8);
memcpy(csp.lstn_subname, myid.subtaskname, 8);
memset(csp.client_in_data, '\0', 36);
if (numseg > 1)
strcpy(csp.client_in_data, seg[1]);
csp.sockaddr_in_parm = sa_client;
if ((numseg < 3) || (seg[2][0] == '\0'))
{
/*-------------------------------------------------------------+
º Type of request unspecified, so just givesocket() to and º
º START transaction. º
+-------------------------------------------------------------*/
reqtype = 1;
givesocket(reqsock, &giveid);
EXEC CICS START TRANSID(seg[0]) FROM(&csp) LENGTH(csp_len)
RESP(cicserr);
}
else if (strcmp(seg[2], "IC") == 0)
{
/*-------------------------------------------------------------+
º Interval Control specified, so interpret interval segment andº
º START transaction accordingly. If interval segment is blank,º
º assume an interval of 0; otherwise, convert interval from º
º string form to numerical form and check it for validity. º
º Finally, convert interval from numerical form to packed º
º decimal form and START transaction. º
+-------------------------------------------------------------*/
reqtype = 1;
if ((numseg < 4) || (*seg[3] == '\0'))
interval = 0.0;
else
{
interval = strtod(seg[3], &leftover);
if ((*leftover != '\0') || (interval < 0.0)
ºº (interval > 999999.0))
{
sprintf(errstring, "CICS listener: could not interpret "
"interval '%s'.\n", seg[3]);
convToAscii(errstring);
write(reqsock, errstring, strlen(errstring));
close(reqsock);
return -1;
}
}
pdset(&packedint, interval, 0, 0.0);
givesocket(reqsock, &giveid);
EXEC CICS START TRANSID(seg[0]) FROM(&csp) LENGTH(csp_len)
INTERVAL(packedint) RESP(cicserr);
}
else if (strcmp(seg[2], "TD") == 0)
{
/*-------------------------------------------------------------+
º Transient Data specified, so just write the TCPSOCKET_PARM º
º data structure to the transient data queue specified in º
º segment 0. º
+-------------------------------------------------------------*/
reqtype = 2;
EXEC CICS WRITEQ TD QUEUE(seg[0]) FROM(&csp) LENGTH(csp_len)
RESP(cicserr);
if (cicserr == DFHRESP(NORMAL))
{
close(reqsock);
return 0;
}
}
else
{
/*-------------------------------------------------------------+
º Invalid type specified, so send an error message to client. º
+-------------------------------------------------------------*/
sprintf(errstring, "CICS listener: invalid type '%s' specified "
"in request.\n", seg[2]);
convToAscii(errstring);
write(reqsock, errstring, strlen(errstring));
close(reqsock);
return -1;
}
/*----------------------------------------------------------------+
º Check for CICS errors and report them as appropriate. º
º Unfortunately, we can't write START errors to the socket, º
º because we already gave the socket away... so write them to º
º stderr. No matter what, close the socket and return -1. º
+----------------------------------------------------------------*/
if (cicserr != DFHRESP(NORMAL))
{
if (reqtype == 1) /* START error */
switch (cicserr)
{
case DFHRESP(INVREQ):
fprintf(stderr,"Invalid interval '%s' specified while "
"STARTING transaction '%s'.\n",seg[3],seg[0]);
break;
case DFHRESP(IOERR):
fprintf(stderr, "I/O error while STARTing transaction "
"'%s'.\n", seg[0]);
break;
case DFHRESP(NOTAUTH):
fprintf(stderr, "Resource security check failed while "
"STARTing transaction '%s'.\n", seg[0]);
break;
case DFHRESP(TRANSIDERR):
fprintf(stderr, "Transid '%s' not found in PCT.\n",
seg[0]);
break;
default:
fprintf(stderr, "Error #%d while STARTing transaction "
"'%s'.\n", cicserr, seg[0]);
break;
}
else /* WRITEQ error */
{
switch (cicserr)
{
case DFHRESP(DISABLED):
sprintf(errstring, "Requested TDQ '%s' has been "
"disabled.\n", seg[0]);
break;
case DFHRESP(IOERR):
sprintf(errstring, "I/O error while writing to TDQ "
"'%s'.\n", seg[0]);
break;
case DFHRESP(LENGERR):
sprintf(errstring, "Invalid LENGTH specified while "
"writing to TDQ '%s'.\n", seg[0]);
break;
case DFHRESP(NOSPACE):
sprintf(errstring, "Requested intrapartition queue "
"'%s' is full.\n", seg[0]);
break;
case DFHRESP(NOTAUTH):
sprintf(errstring, "Resource security check failed "
"while writing to TDQ '%s'.\n", seg[0]);
break;
case DFHRESP(NOTOPEN):
sprintf(errstring, "Requested TDQ '%s' is closed.\n",
seg[0]);
break;
case DFHRESP(QIDERR):
sprintf(errstring, "Requested TDQ '%s' not found.\n",
seg[0]);
break;
default:
sprintf(errstring, "Error #%d while writing to TDQ "
"'%s'.\n", cicserr, seg[0]);
break;
}
write(reqsock, errstring, strlen(errstring));
}
close(reqsock);
return -1;
}
/*----------------------------------------------------------------+
º Add new CICS transaction to lists of pending transactions. º
+----------------------------------------------------------------*/
new = (struct socklist *)malloc(sizeof(struct socklist));
if (new == NULL)
{
perror("malloc() failed in acceptRequest()");
close(reqsock);
return -1;
}
new->sock = reqsock;
new->next = translist;
translist = new;
FD_SET(reqsock, &pending);
if (reqsock > highsocket)
highsocket = reqsock;
numsockets++;
return 0;
}
/*-------------------------------------------------------------------+
º Close sockets taken by CICS transactions. Return -1 if error. º
+-------------------------------------------------------------------*/
int closeSockets(nt)
int nt; /* number of sockets taken by CICS transactions */
{
struct socklist *transptr, /* list of socket descriptors of */
*next, *prev; /* pending CICS transactions */
int nr = nt; /* number of sockets left to close */
prev = transptr = translist; /* point to first socket in list */
while ((nr > 0) && (transptr != NULL))
{
/*-------------------------------------------------------------+
º If this socket was taken by its transaction, then close the º
º socket, remove it from our lists, and signal that another º
º socket is free for use. º
+-------------------------------------------------------------*/
if (FD_ISSET(transptr->sock, &taken))
{
close(transptr->sock);
nr--;
numsockets--;
holdrequests = FALSE;
FD_CLR(transptr->sock, &pending);
next = transptr->next;
if (transptr == translist)
translist = next;
else
prev->next = next;
free((char *)transptr);
transptr = next;
}
else
{
prev = transptr;
transptr = transptr->next;
}
}
return 0;
}
/*-------------------------------------------------------------------+
º Close all sockets and quit. º
+-------------------------------------------------------------------*/
void Quit()
{
struct socklist *transptr, *next;
next = translist;
while (next != NULL)
{
transptr = next;
next = transptr->next;
close(transptr->sock);
free((char *)transptr);
}
close(listensock);
EXEC CICS RETURN;
exit(EXIT_SUCCESS);
}
|