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 (c) 1995, SAS Institute Inc.                |
|                  Unpublished - All Rights Reserved                   |
|                      S A S / C   S A M P L E                         |
|                                                                      |
|         NAME: TCPLISTN                                               |
|     LANGUAGE: C                                                      |
|      PURPOSE: Demonstrate a TCP listener application that "attaches" |
|               a TCP Server, TCPTSERV, on MVS. TCPLISTN details the   |
|               following socket calls:                                |
|               socket(), bind(), listen(), accept(), read(),          |
|               givesocket(), takesocket(), select() and close().      |
|   MVS -                                                              |
|      COMPILE, LINK: SUBMIT prefix.SAMPLE.AUX(TCPLISTN)               |
|      EXECUTE: prefix.SAMPLE.AUX(TCPLSGO)                             |
|               where "prefix" is the installation defined high-level- |
|               qualifier for the SAS/C product.                       |
|        NOTES: TCPLISTN assumes a working TCP/IP environment on both  |
|               the local and remote hosts, refer to MISC NOTES.       |
|   TSO -                                                              |
|      COMPILE: LC370 CLIST - options ENXREF EXTNAME RENT              |
|         LINK: CLK370 CLIST                                           |
|      EXECUTE: call 'your.local.load(TCPLISTN)'                       |
|        NOTES: TCPLISTN assumes a working TCP/IP environment on both  |
|               the local and remote hosts. Refer to MISC NOTES:       |
|   CMS - N/A - Multi-tasking is NOT supported w/in CMS                |
|   MISC NOTES: TCPLISTN needs to be customized for the local TCP/IP   |
|               environment. The local information required by         |
|               TCPLISTN is:                                           |
|               LISTENPORT - defines the port TCPLISTN will do a       |
|                            listen(). Default is port 13.             |
|               QUITSTRING - used as a method to terminate TCPLISTN.   |
|                            TCPLISTN will read the message from the   |
|                            client application, if the message is:    |
|                            QUITSTRING, then TCPLISTN terminates.     |
|               MAXSOCKETS - defines the maximum number of sockets     |
|                            for the select() socket array.            |
|   MISC NOTES: Use TCPCLNT to test the TCPLISTN listener.             |
|   MISC NOTES: TCPLISTN uses the following environment variables:     |
|               TCP_SOCK   - Socket descriptor passed to TCPTSERV      |
|               TCP_DOMAIN - AF_INET = 2, passed to TCPTSERV           |
|               TCP_NAME   - Listener Jobname, passed to TCPTSERV      |
|               TCP_SUBTASKNAME - taskname from getclientid() passed   |
|                            to TCPTSERV.  On the givesocket() call,   |
|                            sub-taskname is set to: '        '        |
|   MISC NOTES: Telnet may be used to terminate the TCPLISTN.  Telnet  |
|               to the HOST and PORT for TCPLISTN, and then enter the  |
|               QUITSTRING as defined below.                           |
|                                                                      |
+---------------------------------------------------------------------*/

#define LISTENPORT 13   /* Set default listen port                    */
#define QUITSTRING "LSNRQUIT" /* If the recv() message is QUITSTRING  */
                              /* terminate TCPLISTN                   */
#define MAXSOCKETS 50   /* max sockets opened at once                 */

/*-------------------------------------------------------------------+
| Includes for socket calls.                                         |
+-------------------------------------------------------------------*/
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#if  __SASC__ == 550
   #include <fcntl.h>
#else
   #include <unistd.h>
#endif
/*-------------------------------------------------------------------+
| Include for ATTACH.                                                |
+-------------------------------------------------------------------*/
#include <ostask.h>
/*-------------------------------------------------------------------+
| Miscellaneous includes.                                            |
+-------------------------------------------------------------------*/
#include <lclib.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <lcstring.h>

#define TRUE 1
#define FALSE 0
#define MAXREQLEN 256   /* max length of 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);

struct Argument {
   short len;
   char parms[1000];
};

struct socklist {       /* linked list of socket file descriptors   */
   int sock;
   int  domain;         /*  so that it can issue takesocket()       */
   char name[8];        /*  so that it can issue takesocket()       */
   char subtaskname[8];
   char pgmname[8];
   void *subtcb;        /* Handle for ATTACHed program              */
   void *plist[1];      /* pointer to argument structure for ATTACH */
   struct socklist *next;
} *servlist;

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 to be taken      */
       taken;           /* list of sockets which have been taken    */
struct clientid myid,   /* clientid of listener for argument struct */
                giveid; /* generic clientid used for givesocket()   */

void main()
{
   int ns;              /* number of sockets with action            */
   fd_set incoming;     /* bound socket description for select()    */
   struct timeval timeout;

   /*----------------------------------------------------------------+
   | Set up a socket, initialize list of pending servers 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 action on sockets given to ATTACHed programs,   |
         | 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 programs 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        */
   int sa_len = sizeof(sa_serv);
   char hostname[72];

   /*----------------------------------------------------------------+
   | 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 ATTACHed servers.     |
   | 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.  We will request the   |
   | port number LISTENPORT, unless LISTENPORT is 0, in which case   |
   | we are assigned the next available port number.                 |
   +----------------------------------------------------------------*/
   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;
   }

   /*----------------------------------------------------------------+
   | Print listener information.                                     |
   +----------------------------------------------------------------*/
   if (getsockname(listensock, &sa_serv, &sa_len) == -1)
   {
      perror("getsockname() failed in setupSocket()");
      close(listensock);
      return -1;
   }

   if (gethostname(hostname, sizeof(hostname)))
   {
      perror("gethostname() failed in setupSocket()");
      close(listensock);
      return -1;
   }
   printf("Listener is on Host:  %.64s   Port#:  %d.\n",
           hostname,  (int) ntohs(sa_serv.sin_port));
   printf("Request server name '" QUITSTRING "' to quit.\n");

   /*----------------------------------------------------------------+
   | Set up a queue for incoming requests.  SOMAXCONN is maximum     |
   | size of queue.                                                  |
   +----------------------------------------------------------------*/
   listen(listensock, SOMAXCONN);
   highsocket = listensock;
   return 0;
}


/*-------------------------------------------------------------------+
| A request has been detected--accept it, givesocket(), and ATTACH   |
| program.  Return -1 if error.                                      |
+-------------------------------------------------------------------*/
int acceptRequest()
{
   int numchars,                /* number of chars read from socket */
       rc,                      /* rc from MVS ATTACH function      */
       pos,                     /* index variable                   */
       reqsock,                 /* request socket                   */
       i;                       /* index variable                   */
   char req[MAXREQLEN+1],       /* buffer for reading in request    */
        errstring[MAXREQLEN+80];/* error message sent to client     */
   struct socklist *new;        /* new node in list of open sockets */
   struct sockaddr_in sa_client;/* socket address of client         */
   int sa_len = sizeof(sa_client);
   char Env_buff[256];
   struct Argument *args;
   char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
                 "abcdefghijklmnopqrstuvwxyz" \
                 "0123456789@$#";

   /*----------------------------------------------------------------+
   | Allocate space for a new addtion to the socklist                |
   +----------------------------------------------------------------*/
   new  = (struct socklist *)malloc(sizeof(struct socklist));
   if (new == NULL)
      {
      perror("malloc() for new failed in acceptRequest()");
      return -1;
      }
   memset(new, 0, sizeof(struct socklist));

   args = (struct Argument *)malloc(sizeof(struct Argument));
   if (args == NULL)
      {
      perror("malloc() for args failed in acceptRequest()");
      return -1;
      }
   memset(args, 0, sizeof(struct Argument));

   /*----------------------------------------------------------------+
   | 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 request from socket and convert from ASCII to EBCDIC.      |
   +----------------------------------------------------------------*/
   numchars = read(reqsock, req, MAXREQLEN);
   if (numchars < 0)
     {
      perror("socket read() failed in acceptRequest()");
      close(reqsock);
      return -1;
     }
   req[numchars] = '\0';
   convFromAscii(req);

   /*----------------------------------------------------------------+
   | Check request for validity (a pretty lax check, admittedly).    |
   +----------------------------------------------------------------*/
   if (req[0] == '\0')
   {
      strcpy(errstring, "MVS listener: empty request received.\n");
      convToAscii(errstring);
      write(reqsock, errstring, strlen(errstring));
      close(reqsock);
      return -1;
   }

   /*----------------------------------------------------------------+
   | Set up the Env vars string for subtaskname, name, sock, etc.    |
   +----------------------------------------------------------------*/
   new->domain = AF_INET;
   memcpy(new->name, myid.name, 8);
   memcpy(new->subtaskname, myid.subtaskname, 8);
   sprintf(Env_buff, " =TCP_SOCK=%d =TCP_DOMAIN=%d " \
          "=TCP_NAME=%s =TCP_SUBTASKNAME=%s",
           reqsock, new->domain, new->name, new->subtaskname);

   /*----------------------------------------------------------------+
   | Separate the command from the parmameters                       |
   +----------------------------------------------------------------*/
   pos = strspn(req, valid);
   req[pos] = '\0';
   strcpy(new->pgmname, req);
   strcpy(args->parms, (req+pos+1));

   /*----------------------------------------------------------------+
   | Clean up the Program name.                                      |
   +----------------------------------------------------------------*/
   for (i = 0; i < sizeof(new->pgmname); i++)
      {
       if  (new->pgmname[i] <  ' ')
            new->pgmname[i]  = ' ';
       else new->pgmname[i] = toupper(new->pgmname[i]);
      }

   /*----------------------------------------------------------------+
   | Check for QUIT request.                                         |
   +----------------------------------------------------------------*/
   if (strcmp(new->pgmname, QUITSTRING) == 0)
   {
      close(reqsock);
      Quit();
   }

   /*----------------------------------------------------------------+
   | Combine the Program parms and the environment vars and assign   |
   | to the plist pointer in new.                                    |
   +----------------------------------------------------------------*/
   strcat(args->parms, Env_buff);
   args->len = strlen(args->parms);
   for (i = 0; i < args->len; i++)
      {
       if  (args->parms[i] <  ' ')
            args->parms[i]  = ' ';
      }

   new->plist [0] = (void *) (0x80000000 | (unsigned) args);

   /*----------------------------------------------------------------+
   | Give the socket and then attach the new subtask.                |
   +----------------------------------------------------------------*/
   givesocket(reqsock, &giveid);
   rc = ATTACH(&(new->subtcb), _Aep, new->pgmname, _Aparam,
               new->plist, _Aend);

   /*----------------------------------------------------------------+
   | Check return code from ATTACH.                                  |
   +----------------------------------------------------------------*/
   if (rc != 0)
   {
      fprintf(stderr, "ATTACH for server '%s' failed with rc = %d.\n",
              new->pgmname, rc);
      close(reqsock);
      return -1;
   }

   /*----------------------------------------------------------------+
   | Add this server to lists of pending programs.                   |
   +----------------------------------------------------------------*/
   new->sock = reqsock;
   new->next = servlist;
   servlist = new;
   FD_SET(reqsock, &pending);
   if (reqsock > highsocket)
      highsocket = reqsock;
   numsockets++;
   return 0;
}


/*-------------------------------------------------------------------+
| Close sockets taken by ATTACHed servers.  Return -1 if error.      |
+-------------------------------------------------------------------*/
int closeSockets(nt)
int nt;         /* number of sockets taken by ATTACHed servers      */
{
   struct socklist *servptr,    /* list of file descriptors of      */
                 *next, *prev;  /*  sockets given to servers        */
   int nr = nt;                 /* number of sockets left to close  */

   prev = servptr = servlist;   /* point to first socket in list    */
   while ((nr > 0) && (servptr != NULL))
   {
      /*-------------------------------------------------------------+
      | If this socket was taken by an ATTACHed server, then close   |
      | the socket, remove it from our lists, and signal that another|
      | socket is free for use.                                      |
      +-------------------------------------------------------------*/
      if (FD_ISSET(servptr->sock, &taken))
      {
         close(servptr->sock);
         nr--;
         numsockets--;
         holdrequests = FALSE;
         FD_CLR(servptr->sock, &pending);
         next = servptr->next;
         if (servptr == servlist)
            servlist = next;
         else
            prev->next = next;
         free((char *) servptr->plist[0]);
         free((char *) servptr);
         servptr = next;
      }
      else
      {
         prev = servptr;
         servptr = servptr->next;
      }
   }
   return 0;
}


/*-------------------------------------------------------------------+
| Close all sockets and quit.                                        |
+-------------------------------------------------------------------*/
void Quit()
{
   struct socklist *servptr, *next;

   next = servlist;
   while (next != NULL)
   {
      servptr = next;
      next = servptr->next;
      close(servptr->sock);
      free((char *) servptr->plist[0]);
      free((char *) servptr);
   }
   close(listensock);
   exit(EXIT_SUCCESS);
}


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