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
<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
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
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
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
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
#include <cmsappc.h> int appcconn(const char *name, struct appc_conn_plist *conn_parmlst, void *resv);
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
.
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
.
iucvset
before calling appcconn
.
#include <cmsappc.h> #include <lcstring.h> int rc; struct appcc_conn_plist path; . . . rc = appcconn("APPCSEND",&conn, NULL);
iucvset
#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);
SAS/C Function APPC/VM Function
appcrecv RECEIVE appcscnf SENDCNF appcscfd SENDCNFD appcsdta SENDDATA appcserr SENDERR appcsreq SENDREQ
signal
or sigaction
and initialize IUCV communications with
iucvset
before invoking any of these functions.
appc
functions invoke the APPC/VM Assembler interface.
iucvset
#include <cmsappc.h> int appcsevr(const char *name, struct appc_send_plist *send_p, const char *code);
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.
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
.
iucvset
before calling appcsevr
.
appcsevr
function invokes the APPC/VM SEVER
function.
#include <cmsappc.h> short pathid; /* PATHID to be severed */ path.pathid = pathid; rc = appcsevr("SENDER",&path,"ONE");
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.
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; }
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); } }
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.