/*---------------------------------------------------------------------+ | 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 #include #include #include #include #include #if __SASC__ == 550 #include #else #include #endif int setupSocket(void); /* initializes a socket descriptor */ #endif /* TCPIP_SYSTEM */ /*-------------------------------------------------------------------+ | Include for ATTACH. | +-------------------------------------------------------------------*/ #include /*-------------------------------------------------------------------+ | Miscellaneous includes. | +-------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include /*-------------------------------------------------------------------+ | 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; }