Advanced Program-to-Program Communication/Virtual Machine (APPC/VM) Functions

Introduction

This chapter covers the SAS/C APPC/VM functions. These are a set of SAS/C Library functions that invoke the VM/SP APPC/VM Assembler programming interface. These functions are defined according to Release 5 of VM/SP. These functions also work with Release 6 of VM/SP and Release 1 of VM/ESA. For background information on the VM/SP APPC/VM Assembler programming interface, refer to the appropriate IBM documentation for your VM system. For a complete description of how the SAS/C Library supports asynchronous signals and the use of signal-related SAS/C functions, refer to Chapter 5, "Signal-Handling Functions," in SAS/C Library Reference, Volume 1 .

Note: SAS/C does not provide its own interface to MVS APPC because the standard APPC interface on MVS can be called directly from SAS/C programs. See Application Development: Writing Transaction Programs for APPC/MVS (GC28-1121) for details.

The following table shows the APPC/VM functions as implemented by the library.

Table 6.1 APPC/VM Functions


   SAS/C Function   Purpose               APPC/VM Function

   appcconn          Request that a path   Invoke APPC/VM
                     be established        CONNECT function

   appcrecv          Accept a message      Invoke APPC/VM
                                           RECEIVE function

   appcscnf          Send a confirmation   Invoke APPC/VM
                     request               SENDCNF function

   appcscfd          Send a confirmation   Invoke APPC/VM
                     response              SENDCNFD function

   appcsdta          Send data             Invoke APPC/VM
                                           SENDDATA function

   appcserr          Send an error         Invoke APPC/VM
                     message               SENDERR function

   appcsreq          Send a request        Invoke APPC/VM
                                           SENDREQ function

   appcsevr          Break a path          Invoke APPC/VM
                                           SEVER function

 

APPC/VM Parameter Lists and External Interrupt Data Formats

The header file for the APPC/VM functions is <cmsappc.h>. Four types of functions are defined in <cmsappc.h> that cover all APPC/VM parameter lists and external interrupt data formats. The types are ident_parms, struct appc_conn_plist, struct appc_send_plist, and appc_conn_data. These types are defined on the following pages.

As indicated by the comments, <cmsappc.h> parameter list fields correspond to fields in the parameter lists for each individual APPC/VM function. For more information on these fields, see the IBM documentation mentioned in Introduction .

ident_parms

The ident_parms structure defines the user data field when connecting to *IDENT.
 typedef struct ident_parms {
    char   name[8];                  /* name of resource or gateway    */
    char   fcode;                    /* function code                  */
  #define MANAGE_RESOURCE 0x01       /* Identify a resource/gateway.   */
  #define REVOKE_RESOURCE 0x02       /* Revoke a resource/gateway.     */
    char   flag;                     /* various flags                  */
  #define GLOBAL_RESOURCE 0x80       /* Resource should be global.     */
  #define ALLOW_SECURITY_NONE 0x40   /* Allow SECURITY(NONE) connects. */
  #define REVOKE_GLOBAL   0x80       /* Revoke global/gateway resource.*/
    char   rcode;                    /* return code from IUCV SEVER    */
    char   ntype;                    /* resource/gateway flag          */
  #define RESOURCE_ID     0x00       /* Name is resource id.           */
  #define GATEWAY_ID      0x01       /* Name is gateway id.            */
    int    UNUSED;                   /* unused                         */
 } ident_parms;
 

appc_conn_plist

The appc_conn_plist structure defines the input and output parameter lists for the APPC CONNECT function data for the APPC connection complete external interrupt.
 struct appc_conn_plist {
    short pathid;                    /* IPPATHID                       */
    char flags1;                     /* IPFLAGS1                       */
    union {
       char rcode;                   /* IPRCODE                        */
       char type;                    /* IPTYPE                         */
    } ip1;
    short code;                      /* IPCODE                         */
    union {
       char whatrc;                  /* IPWHATRC                       */
       char flags2;                  /* IPFLAGS2                       */
    } ip2;
    char sendop;                     /* IPSENDOP                       */
    char vmid[8];                    /* IPVMID                         */
    char resid[8];                   /* IPRESID                        */
    int UNUSED_1;
    struct _adlen bf2;               /* IPBFADR2/IPBFLN2F              */
    int UNUSED_2;
    double _d;                       /* not used - forces alignment    */
 };
 

appc_conn_data

The appc_conn_data structure describes external interrupt data for the IUCV connection complete external interrupt. The appc_conn_data type is declared as follows:
 typedef struct appc_conn_plist appc_conn_data;
 

appc_send_plist

The appc_send_plist structure defines the input and output parameter lists for the APPC SENDCNF, SENCFND, SENDDATA, SENDREQ, RECEIVE, and SEVER functions.
 struct appc_send_plist {
    short pathid;                    /* IPPATHID                       */
    char flags1;                     /* IPFLAGS1                       */
    union {
       char rcode;                   /* IPRCODE                        */
       char type;                    /* IPTYPE                         */
    } ip1;
    short code;                      /* IPCODE                         */
    union {
       char whatrc;                  /* IPWHATRC                       */
       char flags2;                  /* IPFLAGS2                       */
    } ip2;
    char sendop;                     /* IPSENDOP                       */
    unsigned audit;                  /* IPAUDIT (byte 4 not meaningful)*/
    struct _adlen bf1;               /* IPBFADR1/IPBFLN1F              */
    int UNUSED_1[2];
    struct _adlen bf2;               /* IPBFADR2/IPBFLN2F              */
    int UNUSED_2;
    double _d;                       /* (not used - forces alignment)  */
 };
 
The values for the interrupt subtypes, as defined for the structure variable ip.type, are shown in the following table.

Table 6.2 APPC/VM Functions


   Subtype                   Value

   APPC_CONNECTION_PENDING    0x81

   APPC_CONNECTION_COMPLETE   0x82

   APPC_SEVER_INTERRUPT       0x83

   APPC_FUNCTION_COMPLETE     0x87

   APPC_SENDREQ_INTERRUPT     0x88

   APPC_INCOMING_MSG          0x89

 

Function Descriptions

Descriptions of the APPC/VM functions follow. Each description includes a synopsis, description, discussion of return values and portability issues, and an example. Also errors, cautions, diagnostics, implementation details, and usage notes are included where appropriate. A comprehensive example of APPC/VM communication as implemented by the SAS/C Library and Guidelines and Cautions follows the function descriptions.

appcconn -- Perform an APPC/VM CONNECT

SYNOPSIS

 #include <cmsappc.h>

 int appcconn(const char *name, struct appc_conn_plist *conn_parmlst,
              void *resv);
 

DESCRIPTION

The appcconn function invokes the APPC/VM CONNECT function. name matches the name of a previously invoked iucvset function. resv is reserved and should be set to NULL.

RETURN VALUE

The appcconn function returns 0 if the function call is successful. If neither signal nor sigaction has been called, appcconn returns -1. If the CMSIUCV macro returns a nonzero return code, appcconn returns that value. If an IUCV error occurs, appcconn returns 1, and the value of IPRCODE is available in conn_parmlst->ip.rcode.

CAUTION

You must call iucvset before calling appcconn.

EXAMPLE

 #include <cmsappc.h>
 #include <lcstring.h>

 int rc;
 struct appcc_conn_plist path;
 .
 .
 .

 rc = appcconn("APPCSEND",&conn, NULL);
 

RELATED FUNCTIONS

iucvset

appc -- Perform APPC/VM Functions

SYNOPSIS

 #include <cmsappc.h>

 extern int appcrecv(struct appc_send_plist *send_p);
 extern int appcscnf(struct appc_send_plist *send_p);
 extern int appcscfd(struct appc_send_plist *send_p);
 extern int appcsdta(struct appc_send_plist *send_p);
 extern int appcserr(struct appc_send_plist *send_p);
 extern int appcsreq(struct appc_send_plist *send_p);
 

DESCRIPTION

Each of these functions performs an APPC/VM function. The function names and APPC/VM functions are associated as follows:



  SAS/C Function   APPC/VM Function

   appcrecv          RECEIVE

   appcscnf          SENDCNF

   appcscfd          SENDCNFD

   appcsdta          SENDDATA

   appcserr          SENDERR

   appcsreq          SENDREQ

 

RETURN VALUE

All of the functions return the condition code set by the APPC/VM macro instruction.

CAUTION

You must establish an IUCV signal handler with either signal or sigaction and initialize IUCV communications with iucvset before invoking any of these functions.

IMPLEMENTATION

The appc functions invoke the APPC/VM Assembler interface.

EXAMPLE

See The APPCSEND Program and The APPCSERV Program .

RELATED FUNCTIONS

iucvset

appcsevr -- Perform an APPC/VM SEVER

SYNOPSIS

 #include <cmsappc.h>

 int appcsevr(const char *name, struct appc_send_plist *send_p,
              const char *code);
 

DESCRIPTION

The appcsevr function invokes the APPC/VM SEVER function. name is the name passed to a previously invoked appcconn function; send_p is the pointer to an APCC/VM parameter list, and code 6 is either "ALL" or "ONE". Refer to iucvsevr for more information on the code parameter.

RETURN VALUE

The appcsevr function returns 0 if the function call is successful. If either signal or sigaction has not been called, appcsevr returns -1. If the CMSIUCV macro returns a nonzero return code, appcsevr returns that value. If an IUCV error occurs, appcsevr returns 1, and the value of IPRCODE is available in send_p->ip.rcode.

CAUTION

You must call iucvset before calling appcsevr.

IMPLEMENTATION

The appcsevr function invokes the APPC/VM SEVER function.

EXAMPLE

 #include <cmsappc.h>

 short pathid;           /* PATHID to be severed */

 path.pathid = pathid;
 rc = appcsevr("SENDER",&path,"ONE");
 

APPC/VM Communication Example Programs

The following example programs illustrate how the APPC/VM interface can be used to communicate with a global resource manager program. The user program is named APPCSEND. The global resource manager program is named APPCSERV. These programs assume that the global resource is within the same TSAF collection.

Start APPCSERV first. When started, the APPCSEND program prompts for a message to be sent to APPCSERV. APPCSERV displays the message and prompts for a reply message. The reply, in turn, is sent back to APPCSEND, which displays the reply and prompts for another message. This cycle continues until APPCSEND gets a null input string.

The APPCSEND Program

This program has two functions: main, which contains the bulk of the program code, and appctrap, the SIGIUCV signal-handler function. The main function initializes IUCV signal processing and requests a connection to the target resource APPCSERV. If the request is granted, APPCSEND prompts the user for a message string and sends the message to APPCSERV. It then waits for a reply that, when received, is displayed on the terminal. If a null message is read, the path to APPCSEND is severed. The appctrap accepts the initial CONNECTION COMPLETE interrupt from APPCSERV. This function also handles the FUNCTION COMPLETE and SEVER INTERRUPT external interrupts.

Here is the APPCSEND program:

 #include <lcsignal.h>
 #include <lcstring.h>
 #include <cmsappc.h>
 #include <lcio.h>

 void appctrap(void);                /* Declare SIGIUCV handler.       */

                                     /* Set up stdin amparms.          */
 char *_stdiamp = "prompt=What message do I send?\n, eof=";
 static int rep_len = 0;
 static short pathid = 0;
 static short ident_pathid = 0;

 main()
 {
    struct appc_conn_plist conn;  /* APPC plist for CONNECT, SEVER     */
    struct appc_send_plist send;  /* APPC plist for SENDDATA, SENDCNFD */
    int maxconns,                 /* maximum number of IUCV connec-    */
                                  /* tions for this virtual machine    */
        rc;                       /* return code from APPC functions   */
    struct senddata {
       unsigned short length;     /* length of data to send            */
       char message[120+2];       /* message buffer                    */
    } logrec, reply;              /* log record and reply buffers      */

       /* Initialize IUCV signal processing and establish a handler.   */
       /* Block IUCV signals until we're ready to handle them.         */
    signal(SIGIUCV,&appctrap);
    sigblock(1 << (SIGIUCV-1));

       /* Identify this program as APPCSEND.                           */
    if ((rc = iucvset("APPCSEND",&maxconns)) != 0) {
       printf("Return code from iucvset was %d\n", rc);
       exit(4);
    }
    printf("Maximum IUCV connections: %d\n", maxconns);

       /* Fill in the APPC "conn" parameter list with the target       */
       /* resource id, "APPCSERV".                                     */

       /* Note that the resource name in IPRESID must be eight bytes   */
       /* long, padded on the right with blanks if necessary.          */
    memset(&conn, 0, sizeof(conn));
    conn.ip2.flags2 = IPLVLCF + IPMAPPED;
    memcpy(conn.resid, "APPCSERV",8);

       /* Request an APPC CONNECT to the resource named in the         */
       /* parameter list.  Check for an IUCV error (return code != 0)  */
       /* and print the APPC error code.                               */
    rc = appcconn("APPCSEND", &conn, 0);
    if (rc != 0) {
       printf("Return code from appcconn was %d\n", rc);
       if (rc == 1)
          printf("IPRCODE = x'%X'\n", conn.ip1.rcode);
       exit(8);
    }

       /* Now we're ready.  The first interrupt we will receive is the */
       /* CONNECTION COMPLETE interrupt that occurs when APPCSERV      */
       /* issues an APPC ACCEPT.                                       */
    sigpause(0);

       /* Initialize the SEND parameter list with the pathid and       */
       /* buffer addresses, and the length of the reply buffer.        */
    memset(&send, 0, sizeof(send));
    send.pathid = pathid;
    send.sendop = IPSNDRCV;
    send.bf1.adr  = (char *) &logrec;
    send.bf2.adr  = (char *) &reply;
    send.bf2.ln  = sizeof(reply);

       /* Prompt for input messages and send until EOF.                */
    fgets(logrec.message, sizeof(logrec.message), stdin);
    while (!feof(stdin)) {

          /* Put message length in the SEND parameter list.            */
       logrec.length = strlen(logrec.message) + 1;
       send.bf1.ln = logrec.length;

          /* Send message.  Wait (via sigpause) for reply. Upon        */
          /* receipt of reply, 'rep_len' contains the number of        */
          /* unused bytes in the reply buffer.  Print the reply        */
          /* and prompt for a new message.                             */
       appcsdta(&send);
       sigpause(0);
       rep_len = reply.length - sizeof(reply.length);
       printf("APPCSERV replies "%.*s"\n", rep_len,
               reply.message);
       fgets(logrec.message, sizeof(logrec.message), stdin);
    }

       /* We're ready to quit.  Ask the server if it's OK to sever.    */
       /* If so, then sever.                                           */

    memset(&send, 0, sizeof(send));
    send.pathid = pathid;
    send.ip2.flags2 = IPWAIT;       /* Specify WAIT=YES.               */
    send.sendop = IPCNFSEV;

    if ((rc = appcsdta(&send)) != 2) {
       printf("Return code from appcsdta = %d\n", rc);
       if (rc == 1)
          printf("IPRCODE = x'%X'\n", send.ip1.rcode);
       exit(12);
    }

    memset(&conn, 0, sizeof(conn));
    conn.pathid = pathid;
    conn.ip2.flags2 = IPWAIT;      /* Specify WAIT=YES.                */
    conn.sendop = IPSNORM;

    if ((rc = appcsevr("APPCSEND", &conn,"ONE")) != 2) {
       printf("Return code from appcsever = %d\n",rc);
       if (rc == 1)
          printf("IPRCODE = x'%X'\n", conn.ip1.rcode);
       exit(16);
    }

       /* Terminate APPC communications for APPCSEND.                  */
    iucvclr("APPCSEND");
    exit(0);
 }

 #pragma eject

 /* The SIGIUCV signal handler.  Signals are blocked until return.     */

 void appctrap(void)
 {
    appc_conn_data *XID;             /* Pointer to external interrupt  */
                                     /* data returned by siginfo.      */
    int rc;

       /* Get a pointer to the external interrupt data.  Use the       */
       /* interrupt type to determine what to do.                      */
    XID = (appc_conn_data *) siginfo();
    switch (XID->ip1.type) {
       case APPC_CONNECTION_COMPLETE:
          pathid = XID->pathid;      /* Save the pathid.               */
          break;

       case APPC_FUNCTION_COMPLETE:
                                     /* See if it's for CONFIRM.       */
          if (XID->pathid == pathid && XID->ip2.whatrc == IPSNDCNF) {
             struct appc_send_plist *send =
                (struct appc_send_plist *)XID;
             send->sendop = IPCNFRMD;
             rc = appcscfd(send);
             if (rc != 2) {
                printf("Error %d confirming request to recv\n", rc);
                printf("IPRCODE = x'%X'\} n", XID->ip1.rcode);
                exit(20);
             }
          }
          break;

       case APPC_SEVER_INTERRUPT:    /* Handle unexpected termination  */
                                     /* of APPCSERV.                   */
          puts("Unexpected SEVER!");
          exit(24);

       default:                      /* Handle unexpected signal type. */
          printf("Unexpected interrupt type x'%X'\n",XID->ip1.type);
          fflush(stdout);
          abort();
    }

       /* Re-establish this function as the SIGIUCV signal handler.    */
    signal(SIGIUCV, &appctrap);
    return;
 }
 

The APPCSERV Program

The APPCSERV program contains three functions. The main function initializes IUCV signal processing and connects to the *IDENT system service. It then waits for connection requests. As long as some program is connected, it processes interrupts. The appctrap function is the IUCV signal handler that processes incoming external interrupts. Generally these are CONNECTION PENDING interrupts, which are requests from APPCSEND to connect, and incoming message interrupts, which are messages from APPCSEND. This function can also handle the SEVER interrupts that occur if *IDENT or APPCSEND sever the path and the single CONNECTION COMPLETE interrupt that indicates the connection to *IDENT is complete. Finally, rcvrply receives an incoming message, collects a reply from the user, and sends the reply to APPCSEND.

Here is the APPSERV program:

 #include <lcsignal.h>
 #include <lcstring.h>
 #include <lcio.h>
 #include <cmsappc.h>
 #include <stdlib.h>

 void appctrap(void);                /* Declare SIGIUCV signal handler.*/
                                     /* Establish stdin prompt string. */
 char *_stdiamp = "prompt=What do I reply to SENDER?\n, eof=";
 int connects = 0;                   /* number of connections made     */
 static short ident_pathid = 0;      /* used for *IDENT                */

 /* Declare internal functions.                                        */

 static void rcvrply(struct appc_send_plist *);

 main()
 {
    int maxconns;               /* number of IUCV connections allowed  */
    int rc;                     /* return code from functions          */

    struct iucv_path_plist conn;     /* IUCV CONNECT parameter list    */
    ident_parms   ident;             /* *IDENT parameters              */

       /* Initialize IUCV signal processing and establish a handler.   */
       /* Block IUCV signals until we're ready to handle them.         */
    signal(SIGIUCV, &appctrap);
    sigblock(1 << (SIGIUCV-1));

       /* Identify this program as APPCSERV.                           */
    if ((rc = iucvset("APPCSERV", &maxconns)) != 0) {
       printf("Return code from iucvset APPCSERV was %d\n",rc);
       exit(4);
    }
    printf("Maximum IUCV connections: %d\n",maxconns);

       /* Declare another name for connecting to *IDENT.               */
    if ((rc = iucvset("IDENTHAN", &maxconns)) != 0) {
       printf("Return code from iucvset IDENTHAN was %d\n", rc);
       exit(8);
    }

       /* Create an *IDENT parameter list.                             */
    memset(&ident, 0, sizeof(ident));
    memcpy(ident.name, "APPCSERV", 8);
    ident.fcode = MANAGE_RESOURCE;
    ident.flag  = ALLOW_SECURITY_NONE;
    ident.ntype = RESOURCE_ID;

       /* Stow "*IDENT" in IPVMID and copy *IDENT parms to CONNECT     */
       /* plist. Execute an IUCV CONNECT to *IDENT.                    */
    memset(&conn, 0, sizeof(conn));
    memcpy(conn.vmid, "*IDENT  ", 8);
    memcpy(conn.pgm, &ident, sizeof(ident));

    rc = iucvconn("IDENTHAN", &conn);
    if (rc != 0) {
       printf("Return code from iucvconn IDENTHAN was %d\n", rc);
       if (rc == 1)
          printf("IPRCODE = x'%X'\n",conn.ip.rcode);
       exit(12);
    }
    ident_pathid = conn.pathid;      /* Save IDENTHAN pathid.          */
    sigpause(0);                     /* Wait for CONNECTION COMPLETE   */
                                     /* from *IDENT.                   */

       /* Wait for initial CONNECTION PENDING from APPCSEND.           */
    sigpause(0);

       /* As long as some SENDER is connected, wait for incoming       */
       /* messages.                                                    */
    while (connects > 0) sigpause(0);

       /* All paths have been terminated.  Terminate IUCV processing.  */
    iucvclr("APPCSERV");
    iucvclr("IDENTHAN");
    exit(0);
 }

 #pragma eject

 /* SIGIUCV signal handler.  Signals are blocked until return.         */

 void appctrap(void)
 {
    struct appc_send_plist recv;     /* used to receive allocate       */
    struct appc_send_plist *send;    /* APPC SEVER plist pointer       */
    appc_conn_data *XID;             /* Pointer to external interrupt  */
                                     /* data returned by siginfo.      */
    int rc;                          /* return code from iucvacc       */
    ident_parms ident;               /* so we can test RCODE           */
    char  *allocate_data;            /* ptr to allocate data           */

       /* Get a pointer to the external interrupt data.  Use the       */
       /* interrupt type to determine what to do.                      */
    XID = (appc_conn_data *) siginfo();
    switch (XID->ip1.type) {
       case APPC_CONNECTION_PENDING: /* Retrieve allocation data.      */
          allocate_data = malloc(XID->bf2.ln);
          memset(&recv, 0, sizeof(recv));
          recv.pathid = XID->pathid;
          recv.ip2.flags2 = IPWAIT;
          recv.bf1.adr = allocate_data;
          recv.bf1.ln = XID->bf2.ln;
          rc = appcrecv(&recv);
          if (rc != 2) {
             printf("Error %d receiving allocation data\n", rc);
             printf("IPRCODE = x'%X'\n", recv.ip1.rcode);
             printf("IPAUDIT = %x\n", recv.audit);
             exit(16);
          }
          XID->ip1.type = 0;         /* Accept the connection       */
          XID->ip2.flags2 = 0;
          if ((rc = iucvacc("APPCSERV", (struct iucv_path_plist *)
               XID)) != 2) {
             printf("Return code from iucvacc = %d\n", rc);
             if (rc == 1)
                printf("IPRCODE = x'%X'\n", XID->ip1.rcode);
             exit(20);
          }
          connects++;                /* Keep track of the number of    */
          break;                     /* connections made.              */
       case APPC_INCOMING_MSG:       /* Call message handler.          */
          rcvrply((struct appc_send_plist *)XID);
          break;
       case PATH_SEVERED:            /* *IDENT decided to stop.       */
          if (XID->pathid == ident_pathid) {
             memcpy(&ident, XID->resid, sizeof(ident));
             if (ident.rcode != 0x00) {
                printf("*IDENT path severed.rcode = %x\n",
                        ident.rcode);
                exit(24);
             }
          }
          else {                    /* An IUCV sever?  Really?        */
             printf("Unexpected sever.IPPATHID = %d\n", XID->pathid);
             exit(28);
          }
          break;
       case CONNECTION_COMPLETE:    /* *IDENT connection complete     */
          if (XID->pathid == ident_pathid)
             puts("Connection to *IDENT complete\n");
          else {                    /* Some other connection?         */
             printf("Unexpected connection complete.Path = %d\n",
                     XID->pathid);
             exit(32);
          }
          break;
       case APPC_SEVER_INTERRUPT:    /* APPCSEND decided to stop.     */
          send = (struct appc_send_plist *) XID;
          send->ip2.flags2 = IPWAIT;
          send->sendop = IPSNORM;
          send->bf1.adr = NULL;
          send->bf1.ln = 0;
          if ((rc = appcsevr("APPCSERV", send, "ONE")) != 2) {
             printf("Return code from appcsevr = %d\n", rc);
             printf("IPRCODE is x'%X'\n", send->ip1.rcode);
             exit(36);
          }
          connects--;                /* Update number of connections.  */
          break;
       default:                      /* Handle other interrupt types.  */
          printf("Unexpected interrupt type %d\n",
             XID->ip1.type);
          fflush(stdout);
          abort();
    }

 
       /* Reestablish this function as the SIGIUCV signal handler.     */
    signal(SIGIUCV, appctrap);
    return;
 }

 #pragma eject

 /* function to issue APPC RECEIVE and print the message               */

 static void rcvrply(struct appc_send_plist *int_data)
 {
    int msg_len;                     /* incoming message length        */
    struct senddata {
       unsigned short length;        /* length of data to send         */
       char message[120+2];          /* message buffer                 */
    } logrec;
    int rc = 0;                      /* return code                    */

       /* Create APPC RECEIVE parameter list using the external        */
       /* interrupt data area.  Issue the APPC RECEIVE.                */
    int_data->ip2.flags2 = IPWAIT;
    int_data->bf1.adr = (char *) &logrec;
    int_data->bf1.ln = sizeof(logrec);
    rc = appcrecv(int_data);
    if (rc != 2) {
       printf("Return code from receive = %d\n", rc);
       printf("IPRCODE is x'%X'\n", int_data->ip1.rcode);
       exit(40);
    }

       /* Perhaps the sender wants to sever.  If so, confirm sever.    */
    if (int_data->ip2.whatrc == IPCNFSEV) {
       int_data->sendop = IPCNFRMD;
       if ((rc = appcscfd(int_data)) != 2) {
          printf("Error %d attempting to confirm sever\n", rc);
          printf("IPRCODE is x'%X'\n", int_data->ip1.rcode);
          exit(44);
       }
       return;
    }

       /* If the return code is anything else, bad news.               */
    if (int_data->ip2.whatrc != IPSEND) {
       puts("Protocol error.  Sender not in receive state\n");
       exit(48);
    }

       /* Upon return, int_data->bf2.ln contains the number of unused  */
       /* bytes in the message buffer.  Print the message.             */
    msg_len = sizeof(logrec) - int_data->bf2.ln - 2;
    printf("SENDER says \"/%.*s\"\n", msg_len, logrec.message);
 
       /* Prompt for a reply message.  If EOF, quit.                   */
    fgets(logrec.message, sizeof(logrec.message), stdin);
    if (feof(stdin)) {
       puts("Terminating due to end-of-file.\n");
       exit(52);
    }

       /* Fill in APPC SENDDATA parameter list with buffer             */
       /* address/length send reply.                                   */
    int_data->ip2.flags2 = IPWAIT;
    int_data->sendop = IPDATA;
    int_data->bf1.adr = (char *) &logrec;
    logrec.length = strlen(logrec.message) + 1;
    int_data->bf1.ln = logrec.length;
    rc = appcsdta(int_data);
    if (rc != 2) {
       printf("Error %d on reply SENDDATA\n",
       printf("IPRCODE is x'%X'\n", int_data->ip1.rcode);
       exit(56);
    }

       /* We are still in SEND state. Ask to turn conversation around. */
    int_data->ip2.flags2 = IPWAIT;
    int_data->sendop = IPPREPRC;
    if ((rc = appcscnf(int_data)) != 2) {
       printf("Error requesting confirmation to receive. rc =%d", rc);
       printf("IPRCODE = x'%X'\n", int_data->ip1.rcode);
       exit(60);
    }
 }
 

Guidelines and Cautions

For additional information concerning APPC/VM support, see the IBM documentation.

When using SAS/C APPC/VM functions, you should be aware that if the value of the code argument in the iucvsevr function is not "ALL", "ONE" is assumed.

For more information on handling program interrupts and communications, refer to Chapter 5, "Signal-Handling Functions" in SAS/C Library Reference, Volume 1.


Copyright (c) 1998 SAS Institute Inc. Cary, NC, USA. All rights reserved.