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