/*---------------------------------------------------------------------+
|                Copyright (c) 1995, SAS Institute Inc.                |
|                  Unpublished - All Rights Reserved                   |
|                      S A S / C   S A M P L E                         |
|                                                                      |
|         NAME: TCPTSERV                                               |
|     LANGUAGE: C                                                      |
|      PURPOSE: Demonstrate a TCP Server, attached by TCPLISTN. The    |
|               "sub-task" will issue a takesocket(), and write to the |
|               remote client application.                             |
|   MVS -                                                              |
|      COMPILE, LINK: SUBMIT prefix.SAMPLE.AUX(TCPTSERV)               |
|               where "prefix" is the installation defined high-level- |
|               qualifier for the SAS/C product.                       |
|      EXECUTE: N/A - TCPTSERV is "attached" by TCPLISTN               |
|        NOTES: TCPTSERV 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: N/A - TCPTSERV is "attached" by TCPLISTN               |
|        NOTES: TCPTSERV assumes a working TCP/IP environment on both  |
|               the local and remote hosts. Refer to MISC NOTES:       |
|   CMS -                                                              |
|      COMPILE: LC370 EXEC - options ENXREF EXTNAME RENT               |
|         LINK: CLINK EXEC                                             |
|      EXECUTE: TCPTSERV                                               |
|        NOTES: TCPTSERV assumes a working TCP/IP environment on both  |
|               the local and remote hosts. Refer to MISC NOTES:       |
|        NOTES: TCPTSERV may be executed stand-alone. In this          |
|               environment, customize TIMEPORT. A client application  |
|               using TCPTSERV in this instance will only need to do a |
|               socket(), connect(), recv() and close(). The TCPCLNT   |
|               may be modified to test TCPTSERV independently.        |
|   MISC NOTES: Use TCPCLNT to test TCPLISTN and TCPTSERV              |
|   MISC NOTES: TCPTSERV 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.                              |
+---------------------------------------------------------------------*/

#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <options.h>
#if  __SASC__ == 550
   #include <fcntl.h>
#else
   #include <unistd.h>
#endif

int GetSocket(int *cs);

#define TIMEPORT 0   /* default listen() port if NOT attached */
#define TCP_SOCK_FAILED   -1    /* generic return for failed request */

main(int argc, void **argv)
{
   int cs=0;
   int i;

   /* Variables used to obtain the time.                  */
   char *p;
   int len;
   time_t t;

   /* Loop count */
   int n_times;

   /* Buffer for outgoing time string.                    */
   char outbuf[128];

   /* If there were any arguments passed, echo them  */
   if (argc >1)
      {
      for (i=0; i<argc; i++)
         printf("argv[%d] = %s\n", i, argv[i]);
      }

   /*
    * Get a socket.  If TIMESRV was attached by the LISTENER, then
    * GetSocket() calls takesocket().  Otherwise, GetSocket() will
    * open a new socket, bind, listen and accept before returning.
    */
   if (GetSocket(&cs) == -1)
      exit(EXIT_FAILURE);

   /* Now, get the time and write it out to the client */
   n_times = 2;
   while (n_times--)
   {
      /* Send the time to the client. clients           */
      /*  expect the string to be in ASCII.             */
      time(&t);               /* Machine readable time. */
      p = ctime(&t);          /* Human readable time.   */

      /* Convert to ASCII if necessary. */
      for (len=0; len < sizeof(outbuf) && p[len] ; len++)
         outbuf[len] = htoncs(p[len]);

      if (write(cs, outbuf, len ) == -1)
      {
         perror("write() failed");
         return EXIT_FAILURE;
      }
   }
   close(cs);
   return EXIT_SUCCESS;  /* Avoid compilation warnings. */
}


/*--------------------------------------------------------------
 * Get the Socket either from a calling Main task of via a
 * Socket,bind, sequence.
 */
int GetSocket(int *cs)
   {

   /* struct for clientid information                          */
   struct clientid clientid;
   char *buffer;

   /* Socket addresses for server and client. */
   struct sockaddr_in sa_serv;
   struct sockaddr_in sa_clnt;
   int sa_len;

   int i;  /* loop count */
   int rc=0;
   int s;  /* Working socket                                          */

   /*
    * If The Environment variable TCP_SOCK is not Null, we were attached
    * by the LISTENER and need to call takesocket to communicate.
    * Otherwise, we need to open a socket and accept a request on that.
    */
   buffer = getenv("TCP_SOCK");
   if (buffer != NULL)
      {
      /*
       * Build a clientid struct for takesocket
       */
      memset(&clientid, 0, sizeof(clientid)); /* zero out client id */
      s = atoi(buffer);
      buffer = getenv("TCP_DOMAIN");
      clientid.domain = atoi(buffer);
      buffer = getenv("TCP_NAME");
      memcpy(clientid.name, buffer, 8);

      /*Pad out the program name.                                    */
      for (i = 0; i < sizeof(clientid.name); i++)
         {
          if  (clientid.name[i] < ' ')
               clientid.name[i] = ' ';
          else clientid.name[i] = toupper(clientid.name[i]);
         }

      buffer = getenv("TCP_SUBTASKNAME");
      memcpy(clientid.subtaskname, buffer, 8);

      /*Pad out the Subtask name.                                   */
      for (i = 0; i < sizeof(clientid.subtaskname); i++)
         {
         if  (clientid.subtaskname[i] < ' ')
              clientid.subtaskname[i] = ' ';
         }

      printf("info from ENV vars: "
             "Domain = <%d>, Socket# <%d> Name = <%.8s>, Task = <%.8s>\n",
             clientid.domain, s, clientid.name, clientid.subtaskname);

      /* Take the passed client socket; takesocket will return a local   */
      /*  socket number enabling us to write to the client               */

      *cs = takesocket(&clientid, s);

      /* Check takesocket rc */
      if (*cs == -1)
         {
         perror("takesock() call failed");
         rc = TCP_SOCK_FAILED;
         }
      }
   else
      {
      /*
       * Since this instance is standalone (not invoked by LISTENER,
       *   get a stream socket to communicate.
       */
      s = socket(AF_INET, SOCK_STREAM, 0);
      if (s == -1)
         {
         perror("timesvr - socket() failed");
         return (TCP_SOCK_FAILED);
         }

      /* 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 = INADDR_ANY;
      sa_serv.sin_port = TIMEPORT;

      /* Bind our socket to the desired address. Now clients*/
      /*   specifying this address will reach this server.  */
      if (bind(s, &sa_serv, sizeof(sa_serv)) == -1)
         {
         perror("timesrv - bind() failed");
         return (TCP_SOCK_FAILED);
         }

      /* Find out what port was choosen and report it.      */
      sa_len = sizeof(sa_serv);
      if (getsockname(s, &sa_serv, &sa_len) == -1)
         {
         perror("timesrv - getsockname() failed");
         return (TCP_SOCK_FAILED);
         }
      printf("TIMESRV server port is: %d\n",
                     (int) ntohs(sa_serv.sin_port));

      /* Set up a queue for incoming connection requests.   */
      listen(s, SOMAXCONN);

      /* Accept a new request. Ask for client's address  */
      /*   so that we can print it if there is an error. */
      sa_len = sizeof(sa_clnt);
      *cs = accept(s, &sa_clnt, &sa_len);
      if (*cs==-1)
         {
         perror("timesrv - accept() failed");
         return (TCP_SOCK_FAILED);
         }
      }

   return(rc);
}