www.sas.com > Service and Support > Technical Support
 
Technical Support SAS - The power to know(tm)
  TS Home | Intro to Services | News and Info | Contact TS | Site Map | FAQ | Feedback


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

Copyright (c) 2000 SAS Institute Inc. All Rights Reserved.
Terms of Use & Legal Information | Privacy Statement