/*---------------------------------------------------------------------+
|                Copyright (c) 1995, SAS Institute Inc.                |
|                  Unpublished - All Rights Reserved                   |
|                      S A S / C   S A M P L E                         |
|                                                                      |
|         NAME: ECBMAIN                                                |
|     LANGUAGE: C                                                      |
|      PURPOSE: Demonstrate the use of various SAS/C MVS Multi-tasking |
|               functions, including: ECB processing, the ATTACH()     |
|               function, selectecb(), ecbpause() and the WAIT1 macro. |
|               ECBMAIN attaches a sub-task, ECBTASK (a C MAIN),       |
|               passing a standard C parameter list.                   |
|               Refer to the "sub_attach()" function for concerns      |
|               within a multi-tasking environment.                    |
|               Additional details on the functions may be found in    |
|               SAS/C Library Reference, Third Edition, Release 6.00.  |
|   MVS -                                                              |
|      COMPILE, LINK, EXECUTE: SUBMIT prefix.SAMPLE.AUX(ECBMAINJ)      |
|               where "prefix" is the installation defined high-level- |
|               qualifier for the SAS/C product.                       |
|   TSO -                                                              |
|      COMPILE: LC370 CLIST - options EXTNAME                          |
|         LINK: CLK370 CLIST                                           |
|      EXECUTE: CALL 'your.local.load(ECBMAIN)'                        |
|        NOTES: ECBTASK should be compiled and linked prior to         |
|               executing ECBMAIN.                                     |
|        NOTES: Issue a: ALLOC F(SYSOTASK) DS(your.stdout.dataset) MOD |
|               prior to calling ECBMAIN, where "your.stdout.dataset"  |
|               DCB values are: RECFM=VBA LRECL=133 BLOCKSIZE=23476    |
|   CMS - N/A - multi-tasking is not supported in a CMS environment    |
|                                                                      |
|   MISC NOTES: Systems without a TCP/IP stack should comment out the  |
|               "#define TCPIP_SYSTEM" statement below.                |
|   MISC NOTES: The ATTACHed load module ECBTASK is itself a C main    |
|               program, therefore a new C environment will be created |
|               for the subtask. SAS/C requires that a multitasking    |
|               application be structured such that each task uses a   |
|               separate C environment. For this reason, address       |
|               space contention for: stdin, stdout, and stderr        |
|               may occur. Refer to the SAS/C Library Reference,       |
|               I/O Functions chapter, and the SAS/C Compiler Library  |
|               and User's Guide, Run-Time Argument Processing chapter,|
|               for how to manage and/or redirect stdin, stdout and    |
|               stderr.                                                |
|                                                                      |
+---------------------------------------------------------------------*/
#define TCPIP_SYSTEM       /* comment the #define if a TCPIP stack is */
                           /* not available on this system.           */
#if defined(TCPIP_SYSTEM)
   /*----------------------------------------------------------------+
   | Includes for socket calls.                                      |
   +----------------------------------------------------------------*/
   #include <sys/types.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

   int setupSocket(void); /* initializes a socket descriptor        */
#endif  /* TCPIP_SYSTEM */

/*-------------------------------------------------------------------+
| Include for ATTACH.                                                |
+-------------------------------------------------------------------*/
#include <ostask.h>
/*-------------------------------------------------------------------+
| Miscellaneous includes.                                            |
+-------------------------------------------------------------------*/
#include <ctype.h>
#include <lclib.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <lcstring.h>
#include <lcsignal.h>


/*-------------------------------------------------------------------+
| sub_attach() is used to attach a sub-task. Parmlist is:            |
| int - used to determine the parameter list passed to attached task |
| unsigned * - ecb address to be posted when the sub-task terminates |
| void ** - address of the tcb for the sub-task, used in DETACH()    |
+-------------------------------------------------------------------*/
int sub_attach(int, unsigned *, void **);

main()
{
int attach_rc;
void *subtcb = 0;    /* sub-task tcb, used for DETACH            */
time_t cur_time;     /* time_t stucture for current time function*/

#if defined(TCPIP_SYSTEM)
   int listensock;      /* socket on which we listen for requests   */
   int select_posted = 0; /* set when selectecb() has been posted   */
   int ns = 0;          /* number of sockets with action            */
   fd_set incoming;     /* bound socket description for selectecb() */
   struct timeval timeout;  /* timeeval stucture for selectecb()    */
#endif

/*-------------------------------------------------------------------+
| set up mainECB to contain a single ECB to be posted when the       |
| attached sub-task terminates.                                      |
+-------------------------------------------------------------------*/
unsigned ECB = 0;
struct _ecblist mainECB = { 1 , 0 };
mainECB.ecbarr = &ECB;

#if defined(TCPIP_SYSTEM)
   /*----------------------------------------------------------------+
   | Set up a socket, to prepare for sub_attach() and selectecb()    |
   +----------------------------------------------------------------*/
   listensock = setupSocket();
   if (listensock == -1)
       exit(EXIT_FAILURE);

   /*----------------------------------------------------------------+
   | attach the sub-task and call selectecb()                        |
   +----------------------------------------------------------------*/
   time(&cur_time);
   printf("calling sub_attach():      %s", ctime(&cur_time));

   attach_rc = sub_attach(0, &ECB, &subtcb);
   if (attach_rc == -1)
      {
      printf("sub_attach failed\n");
      close(listensock);
      exit(EXIT_FAILURE);
      }

   /*----------------------------------------------------------------+
   | set timeout values, file descriptor array                       |
   +----------------------------------------------------------------*/
   timeout.tv_sec = 3;        /* set timeout value to 3 seconds     */
   timeout.tv_usec = 0;
   FD_ZERO(&incoming);
   FD_SET(listensock,&incoming); /* set socket descriptor for       */
                                 /* demonstration purposes only     */

   /*----------------------------------------------------------------+
   | selectecb() may return after a time-out, or when posted by an   |
   | event. In this case, time-out is 3 seconds. When selectecb()    |
   | returns test the ecb, and take the appropirate action. Either   |
   | set "select_posted" or re-enter selectecb()                     |
   +----------------------------------------------------------------*/
   while ( select_posted != 1 )
      {
      time(&cur_time);
      printf("calling selectecb():       %s",ctime(&cur_time));

      ns = selectecb(1, &incoming, NULL, NULL, &timeout, &mainECB, 1);
      if (ns == -1)
         {
         perror("selectecb() call failed");
         close(listensock);
         exit(EXIT_FAILURE);
         }
      time(&cur_time);
      printf("returned from selectecb(): %s",ctime(&cur_time));

      if ( ECB & 0x40000000)
         {
         select_posted = 1;
         printf("selectecb() was posted by sub-task termination\n\n");
         }
      else
         printf("selectecb() returned because of a timeout\n\n");
      }
   /*----------------------------------------------------------------+
   | cleanup: detach sub-task, close socket, and reset ecb.          |
   +----------------------------------------------------------------*/
   DETACH(&subtcb,NOSTAE);
   close(listensock);
   ECB = 0;
#endif  /* defined TCPIP_SYSTEM   */

/*----------------------------------------------------------------+
| attach the sub-task and call ecbpause()                         |
+----------------------------------------------------------------*/
time(&cur_time);
printf("calling sub_attach():     %s", ctime(&cur_time));

attach_rc = sub_attach(1, &ECB, &subtcb);
if (attach_rc == -1)
   {
   printf("sub_attach failed\n");
   exit(EXIT_FAILURE);
   }

/*----------------------------------------------------------------+
| call ecbpause()                                                 |
+----------------------------------------------------------------*/
time(&cur_time);
printf("calling ecbpause():       %s",ctime(&cur_time));

/*----------------------------------------------------------------+
| NOTE: ecbpause() is a replacement for sigpause(). The ecbpause()|
|       function allows an application to wait for either a POST  |
|       of an OS ECB or a C signal. Refer to the SAS/C Library    |
|       Reference for a complete description of the sigpause()    |
|       and ecbpause() functions.                                 |
+----------------------------------------------------------------*/
ecbpause( 0, 1, &mainECB);

time(&cur_time);
printf("returned from ecbpause(): %s\n",ctime(&cur_time));

/*----------------------------------------------------------------+
| cleanup: detach sub-task, and reset ecb.                        |
+----------------------------------------------------------------*/
DETACH(&subtcb,NOSTAE);
ECB = 0;


/*----------------------------------------------------------------+
| attach the sub-task and call WAIT1() macro                      |
+----------------------------------------------------------------*/
time(&cur_time);
printf("calling sub_attach():  %s",ctime(&cur_time));

attach_rc = sub_attach(2, &ECB, &subtcb);
if (attach_rc == -1)
   {
   printf("sub_attach failed\n");
   exit(EXIT_FAILURE);
   }

/*----------------------------------------------------------------+
| invoke WAIT1() macro                                            |
+----------------------------------------------------------------*/
time(&cur_time);
printf("calling WAIT1():       %s",ctime(&cur_time));

/*----------------------------------------------------------------+
| NOTE: WAIT1() allows and application to wait for only a POST    |
|       of an OS ECB.                                             |
+----------------------------------------------------------------*/
WAIT1(&ECB);

time(&cur_time);
printf("returned from WAIT1(): %s\n",ctime(&cur_time));

/*----------------------------------------------------------------+
| cleanup: detach sub-task, and reset ecb.                        |
+----------------------------------------------------------------*/
DETACH(&subtcb,NOSTAE);
ECB = 0;

return (EXIT_SUCCESS);
}


#if defined(TCPIP_SYSTEM)
/*-------------------------------------------------------------------+
| Open a socket and set it up to accept requests. Return -1 if error.|
+-------------------------------------------------------------------*/
int setupSocket()
{
int listensock;
struct sockaddr_in sa_serv;

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

   /*----------------------------------------------------------------+
   | Bind our socket to the desired address so clients may reach us. |
   +----------------------------------------------------------------*/
   memset(&sa_serv,'\0',sizeof(struct sockaddr_in));
   sa_serv.sin_family = AF_INET;
   sa_serv.sin_addr.s_addr = htonl(INADDR_ANY);
   sa_serv.sin_port = 0;

   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 requests.  SOMAXCONN is maximum     |
   | size of queue.                                                  |
   +----------------------------------------------------------------*/
   if (listen(listensock, SOMAXCONN) == -1)
      {
      perror("listen() call failed in setupSocket()");
      close(listensock);
      return -1;
      }
   return listensock;
}
#endif /* TCPIP_SYSTEM */


/*----------------------------------------------------------------+
| sub_attach() attaches a sub-task which will post the sub_ecb    |
| passed by the main() function. sub_ecb will be posted at        |
| sub-task termination.                                           |
+----------------------------------------------------------------*/
int sub_attach(int sub_int, unsigned *sub_ecb, void **subtcb)
{
int rc;

/*----------------------------------------------------------------+
| care must be taken when passing automatic variables to an       |
| ATTACHed task. When sub_attach() returns, the storage           |
| associated with an automatic variable is freed. If the attached |
| task addresses these variables, unpredictable results may occur.|
| For this reason, plist[] and argument are defined as "static".  |
+----------------------------------------------------------------*/
static void *plist[1];
struct argument_s
   {
   short len;
   char parms[100];
   };
static struct argument_s argument;

/*----------------------------------------------------------------+
| argument.parms[] consists of a parameter list passed to C MAIN. |
| The parameter list is equivalent to: PARM='parms'  in BATCH, or |
| within TSO, equivalent to "parms" in: CALL 'pgmname' 'parms'    |
| In a multi-tasking environment, contention may occur for stdin, |
| stdout, and stderr. For this reason stdout for the subtask is   |
| redirected to the DDname SYSOTASK using the standard C notation |
| ">filename" in the parameters.                                  |
+----------------------------------------------------------------*/
switch(sub_int)
   {
   case 0: strcpy(argument.parms,">SYSOTASK selectecb() call "); break;
   case 1: strcpy(argument.parms,">SYSOTASK ecbpause() call "); break;
   case 2: strcpy(argument.parms,">SYSOTASK WAIT1() call"); break;
   default:
      printf("invalid sub_int value: %d passed to sub_attach, exiting\n",
              sub_int);
      return(-1);
   }

argument.len = strlen(argument.parms);
plist[0] = (void *) (0x80000000 | (unsigned) &argument);


/*----------------------------------------------------------------+
| call ATTACH()                                                   |
+----------------------------------------------------------------*/
rc = ATTACH(subtcb, _Aep, "ECBTASK", _Aecb, sub_ecb, _Aparam,
              plist, _Aend);

/*----------------------------------------------------------------+
| Check return code from ATTACH.                                  |
+----------------------------------------------------------------*/
if (rc != 0)
   {
   printf("ATTACH for ECBTASK failed with rc = %d.\n",rc);
   return -1;
   }
return 0;
}