/*---------------------------------------------------------------------+ | 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); }