OTMA C/I sample program for synchronous processing
The program below shows how to use the OTMA C/I for synchronous (one in-one out) processing.
In this sample program, the otma_send_receive API is used to send and receive IMS data.
#pragma langlvl(extended)
/*********************************************************************/
/* */
/* Callable Interface sample program using synchronous APIs */
/* */
/* Parameters: */
/* Server Name */
/* Client Name */
/* User Name */
/* Iterations */
/* Transaction */
/* User Group */
/* OTMA Data */
/* */
/* Note: The send buffer is sent as a file with a ddname of */
/* SENDBUFn in the invoking JCL. */
/* */
/* Example: //SENDBUF0 DD *,DLM=$$ */
/* SEND OTMA TO SKS1 */
/* $$ */
/* */
/* Note: COMPAR1 is the DDNAME of an input file used to compare */
/* actual output with expected output. '?' is used to delimit */
/* the compare string and '|' is used to ignore a char compare */
/* */
/* Example: //COMPAR0 DD *,DLM=$$ */
/* SEND OTMA TO SKS1? */
/* $$ */
/* */
/*********************************************************************/
/********************************************************************/
/* Entry... */
/* */
/* This test program is callable from JCL */
/* */
/* //NA1OTMA JOB CLASS=A,MSGLEVEL=(1,1),MSGCLASS=H,REGION=2M */
/* //************************************************************** */
/* //* PARM=server_member_name tpipe_name client_member_name */
/* //* iterations command groupid OTMA_Data */
/* //MINISAMP EXEC PGM=NA1OTMA, */
/* // PARM='TRAP(OFF)/IMS61CR1 IMSTESR G214992 1 /DISP groupid */
/* // OTMAData' */
/* //STEPLIB DD DISP=SHR,DSN=OTMA.TEST.LOAD */
/* //SYSUDUMP DD SYSOUT=* */
/* //STDOUT DD SYSOUT=* */
/* //STDERR DD SYSOUT=* */
/* //CEEDUMP DD SYSOUT=* */
/* //COMPAR1 DD *,DLM=$$ */
/* EXPECTED OUTPUT GOES HERE */
/* $$ */
/* //SENDBUF0 DD *,DLM=$$ */
/* SEND DATA GOES HERE */
/* $$ */
/* */
/* Note: TRAP(OFF)/ Passes LE run-time option TRAP(OFF) which turns */
/* off LE condition handling. To get a LE dump on abend set */
/* TRAP ON and provide a CEEDUMP DDNAME. */
/* */
/* Note: COMPAR1 is the DDNAME of an input file used to compare */
/* actual output with expected output. '?' is used to delimit */
/* the compare string and '|' is used to ignore a char compare*/
/* */
/********************************************************************/
/*********************************************************************/
/* An example for using the OTMA Client API in C lang. */
/* This program is broken into the following parts: */
/* Declarations for special support */
/* Process invocation parameters */
/* Setup for C signal handling */
/* Do XCF open processing and analysis */
/* Do session allocate processing */
/* Execute a command or transaction per invocation parm */
/* Do session free processing */
/* Do close */
/* End */
/*********************************************************************/
/*********************************************************************/
/* API's for non-authorized OTMA caller */
/*********************************************************************/
#include "dfsyc0.h" /* Non-authorized OTMA API's */
#include <stdlib.h> /* Standard C Header file */
#include <stddef.h> /* Standard C Header file */
#include <stdio.h> /* Standard C Header file */
/*********************************************************************/
/* Internal functions */
/*********************************************************************/
int memc(char *comp_buf, char *rec_buf1 );
/* macro to move string to blank filled left justified char field */
#define splat(t,s) \
{\
memset((char*)&(t),' ',sizeof(t));\
strncpy((char*)&(t), s ,strlen(s));}
/* standard math routines */
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
main(int argc,char *argv[])
{
/* Following fields used by all Functions */
otma_anchor_t anchor; /* Handle returned by create */
/* and used by all others. */
otma_retrsn_t retrsn; /* Return code returned by all. */
long int retsave; /* Return code save area */
/* Following fields used by several Functions */
sess_handle_t sess_handle; /* Handle returned by allocate */
/* used by send_receive and free. */
otma_grp_name_t grp_name; /* API XCF Group Member Name. */
otma_clt_name_t clt_name; /* API XCF Client Member Name. */
otma_srv_name_t srv_name; /* API XCF Server Member Name. */
/* (the IMS XCF member name). */
racf_uid_t userid; /* Our z/OS logon ID. */
racf_prf_t groupid; /* RACF Group ID */
otma_user_t otma_data; /* Otma Data */
lterm_name_t lterm; /* Lterm name */
mod_name_t modname; /* ModName */
unsigned char error_message_text[120];/* IMS error msg field */
/* A place to receive any IMS */
/* DFS error messages. */
unsigned char *error_message = (unsigned char*)&error_message_text;
/* a pointer to which is parameter */
/* on send_receive. */
char *tran; /* Transaction Name / IMS Command */
tran_name_t tran_name; /* Transaction Name / IMS Command */
#define BUFFER_LEN 4096 /* set our buffer sizes */
#define NUM_BUFFER 60
#define COM_BUFFER 80
#define GROUP_NAME "HARRY" /* Set XCF group name to join */
char compare_buf[NUM_BUFFER + 1]; /* Compare buffer */
int long buffer_length = 0;
int long rec_buffer_len = BUFFER_LEN;
char rec_buf[BUFFER_LEN];
long int rec_data_len = 0;
char send_buf[BUFFER_LEN];
char temp_buf[NUM_BUFFER];
context_t context = {0x00000000000000000000000000000000};
/* This test is not distributed sync point. */
/* Too complicated for here. */
/* Normally this is obtained from RRS */
/*********************************************************************/
/* The callable interface makes use of z/OS Event Control Blocks. */
/* Any language which call the interface must deal with this. */
/*********************************************************************/
unsigned long *(ecb_list[2]); /* z/OS pause stuff */
unsigned long **pecb_list;
ecb_t ecbOPEN = 0L; /* ecb to be posted by OTMA API */
ecb_t ecbIO = 0L; /* ecb to be posted by OTMA API */
ecb_t signal = 0L; /* ecb to be posted by C runtime */
ecb_t temp_ecb = 0L; /* used by compare and swap */
ecb_t reset_ecb = 0L; /* used by compare and swap */
/*********************************************************************/
/* Local variables */
/*********************************************************************/
int iterations;
int loop_count;
int compare_result;
long int retcode;
signed long sessions; /* number of sessions to support */
tpipe_prfx_t tpipe_prefix; /* first part of tpipe NAME */
FILE * stream;
int num; /* number of characters read from stream */
/*******************************************************************/
/* To support test functions - names of parms */
/* Print the parms out for documentation */
/*******************************************************************/
char * argdefs[8]={ "pgm name", /* 1 */
"server name", /* 2 */
"client name", /* 3 */
"userid ", /* 4 */
"iterations ", /* 5 */
"transaction", /* 6 */
"group id ", /* 7 */
"otma data ", /* 8 */
};
/*******************************************************************/
/* Declare an array of compare file ddnames to */
/* compare actual output received with expected output. */
/*******************************************************************/
char * infiledd[4]={"DD:COMPAR0", /* 1 */
"DD:COMPAR1" , /* 2 */
"DD:COMPAR2" , /* 3 */
"DD:COMPAR3" , /* 4 */
};
/*******************************************************************/
/* Declare an array of send file ddnames to */
/* send application data to OTMA. */
/*******************************************************************/
char * sndfiledd[4]= {"DD:SENDBUF0", /* 1 */
"DD:SENDBUF1" , /* 2 */
"DD:SENDBUF2" , /* 3 */
"DD:SENDBUF3" , /* 4 */
};
/* ---------------------------------------------------------------- */
/* Anounce the startup of the test program. */
/* ---------------------------------------------------------------- */
printf("Otmci01 Starting, version %s %s\n" ,__DATE__,__TIME__ );
/* ---------------------------------------------------------------- */
/* z/OS Pause Init - do this first, in case it fails bail out. */
/* This sets up a C environment for signaling from the API. */
/* ---------------------------------------------------------------- */
ecb_list[0] = (unsigned long *) &(signal); /* post by C signal */
ecb_list[1] = (unsigned long *) /* post by OTMA */
((unsigned long)&(ecbOPEN) |
(unsigned long)0x80000000);/* end of list */
pecb_list = &ecb_list[0]; /* pointer to list */
/* define callable I/F */
/*******************************************************************/
/* Begin Test Case... */
/* Anounce the startup of the test program. */
/*******************************************************************/
printf("OTMCI01 Run Date: %s Run Time: %s\n" ,__DATE__,__TIME__ );
/*******************************************************************/
/* Process parms/command line arguments. */
/*******************************************************************/
/* First, print the parameters. */
printf("Invocation parameters = \n");
for (i=1 ; i<(min(8,argc));i++)
{
printf("%d %s = ", i, argdefs[i]);
printf("%s.\n", argv[i]);
}
if (argc>1) splat( srv_name, argv[1]) /* XCF memname of IMS */
else splat( srv_name, "IMS61CR1"); /* hard coded default */
if (argc>2) splat( clt_name, argv[2]) /* Client name */
else splat( clt_name, "XCFTEST" ); /* hard coded default */
if (argc>3) splat( userid , argv[3]) /* ID to use */
else splat( userid , "XCFTEST" ); /* hard coded default */
if (argc>4) iterations = atoi(argv[4]); /* loop count */
else iterations = 1; /* hard coded default */
if (argc>5) tran = argv[5]; /* Transaction/IMS CMD*/
else tran = ""; /* hard coded default */
if (argc>6) splat( groupid, argv[6]) /* Group ID to use */
else splat( groupid, " " ); /* hard coded default */
if (argc>7) splat( otma_data, argv[7]) /* OTMA Data */
else splat( otma_data, "" ); /* hard coded default */
/* -----------------------------------------------------------*/
/* Open the file with the ddname SENDBUF0 supplied in the */
/* JCL which invoked this C driver. Then read the file into */
/* temp_buf. */
/* -----------------------------------------------------------*/
if (( stream = fopen("DD:SENDBUF0","rb")) != NULL )
{
num = fread( temp_buf, sizeof( char ), NUM_BUFFER, stream );
printf("BUFF SIZE = %d.\n", num);
if (num == NUM_BUFFER) {
printf( "Number of characters read = %i\n", num );
fclose( stream );
}
else {
if ( ferror(stream) )
printf( "Error reading DDNAME sendbuf0/n");
else if ( feof(stream)) {
printf( "EOF found\n" );
printf( "Number of characters read %d\n", num );
printf( "temp_buf = %.*s\n", num, temp_buf);
fclose( stream );
}
}
}
else
printf( "ERROR opening DDNAME sendbuf0/n" );
/* Initialize API parameters and buffers. */
splat( grp_name,GROUP_NAME ); /* XCF Group Name */
splat( tpipe_prefix,"TPAS" ); /* tpipe Prefix Name */
splat( tran_name,tran ); /* do scan here */
strncat(send_buf, temp_buf,num); /* Copy temp_buf into send_buf */
buffer_length = strlen(send_buf); /* Set send buffer length */
/*******************************************************************/
/* Example of setting up parms to Open the XCF Link */
/*******************************************************************/
retrsn.ret = -1;
retrsn.rsn[0] = -1;
retrsn.rsn[1] = -1;
retrsn.rsn[2] = -1;
retrsn.rsn[3] = -1;
sessions = 10; /* OTMA supports multiple parallel */
/* sessions (TPIPES) How many do you want?*/
/*******************************************************************/
/*BEGIN: */
/* We have a CREATE function to set up storage and */
/* an OPEN function to start the protocol. */
/* If you do not need to customize the environment you can start */
/* with the OPEN function, the CREATE will be done by OPEN. */
/*******************************************************************/
printf("-\n");
otma_create(&anchor, /* (out) ptr to addr to receive ancho*/
&retrsn, /* (out) return code */
(ecb_t *) &ecbOPEN,/* not posted by create but stored */
&grp_name, /* (in) ptr to valid groupname */
&clt_name, /* (in) Our member name */
&srv_name, /* (in) Our server name */
&sessions, /* (in) number of sessions to support*/
&tpipe_prefix /* (in) first part of tpipe name */
);
printf("OTMA_CREATE issued. ret = %d rsn = %.8x,%.8x,%.8x,%.8x\n"
" anchor is at %.8x.\n",
retrsn.ret,
retrsn.rsn[0],
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3],
anchor);
printf("-\n");
/*******************************************************************/
/* Connect to IMS */
/*******************************************************************/
otma_open(&anchor, /* out ptr to addr to receive anchor */
&retrsn, /* out return code */
(ecb_t *)&ecbOPEN, /* out posted by open if failure */
/* else posted by exit pgm */
&grp_name, /* in ptr to valid XCF groupname */
&clt_name, /* in Our member name */
&srv_name, /* in Our server name */
&sessions, /* in number of sessions to support */
&tpipe_prefix /* in first part of tpipe name */
);
printf("OTMA_OPEN issued. ret = %.8x rsn = %.8x,%.8x,%.8x,%.8x\n"
" Waiting for ecb at %.8x.=%.8x.\n",
retrsn.ret,
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3],
ecb_list[1],
*ecb_list[1]
);
printf("-\n");
/* ---------------------------------------------------------------- */
/* Here we wait for Open to signal complete */
/* ---------------------------------------------------------------- */
DFSYCWAT(ecb_list[1]); /* WAIT on ecb */
printf("OPEN_OTMA done. ret = %.8x rsn = %.8x,%.8x,%.8x,%.8x \n"
"\nEcb at %.8x.= %.8x.\n",
retrsn.ret,
retrsn.rsn[0],
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3],
ecb_list[1], *ecb_list[1]
);
printf("Local Area Anchor at %8.8X = %8.8X\n",
&anchor, anchor);
printf("-\n");
/* -----------------------------------------------------------*/
/* The post code from open indicates success or failure */
/* -----------------------------------------------------------*/
if (0!=(0x00ffffff & ecbOPEN))
{
printf("OPEN_OTMA ecb is posted failure.\n");
return(retrsn.rsn[0]);
}
/* -----------------------------------------------------------*/
/* Set userid to blanks if userid = bobdavis */
/* -----------------------------------------------------------*/
printf(" Trans = %.8s,\n ", tran_name );
printf(" Userid = %.8s,\n ", userid );
printf("Groupid = %.8s,\n ", groupid );
/**************************************************************/
/* Like CREATE the ALLOC function just creates control blocks */
/* and stores data in them. Other functions may be invented */
/* to modify these structures before the command-of-execution,*/
/* SEND_RECEIVE is issued. */
/**************************************************************/
otma_alloc(
&anchor, /* in ptr to global word */
&retrsn, /* out rc,reason(1-4) */
&sess_handle, /* out session id */
NULL, /* in default overrides */
&tran_name, /* in IMS tp name or cmd */
&userid, /* in RACFid or blanks */
&groupid /* in RACF group id or blnk*/
);
printf("OTMA_ALLOC done. ret = %.8x rsn = %.8x,%.8x,%.8x,%.8x\n",
retrsn.ret,
retrsn.rsn[0],
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3]
);
/**************************************************************/
/* Even if ALLOC fails we go on here just to prove the */
/* API will reject the call. */
/**************************************************************/
/**************************************************************/
/* This is the call that sends the data and prepares to */
/* receive the answer from IMS. */
/* */
/* This test program can iterate with multiple calls here. */
/**************************************************************/
/* ___Send message wait for reply______________________ */
for (loop_count = 0 ; loop_count<iterations ; loop_count++)
{
/* ___Change the environment to wait for ecbIO */
ecbIO = 0; /* clear ecb for reuse */
ecb_list[1] = (unsigned long *) /* posted by OTMA */
((unsigned long)&(ecbIO) |
(unsigned long)0x80000000); /* end of list */
if (loop_count != 0)
{
/* -----------------------------------------------------------*/
/* If looping more than once open the next file to send */
/* and read it into the send_buf. */
/* -----------------------------------------------------------*/
if (( stream = fopen(sndfiledd[loop_count],"rb")) != NULL )
{
num = fread( temp_buf, sizeof( char ), NUM_BUFFER, stream );
printf("BUFF SIZE = %d.\n", num);
if (num == NUM_BUFFER) {
fclose( stream );
}
else {
if ( ferror(stream) )
printf( "Error opening file
else if ( feof(stream)) {
printf( "EOF found\n" );
printf( "Number of characters read %d\n", num );
printf( "temp_buf = %.*s\n", num, temp_buf);
fclose( stream );
}
}
}
else
printf( "Error opening file %s\n", sndfiledd[loop_count]);
/* Initialize send and receiving buffers. */
memset(rec_buf ,0, sizeof(rec_buf));
memset(send_buf ,0, sizeof(send_buf));
strcat(send_buf, temp_buf );
strcat(send_buf, " " );
buffer_length = strlen(send_buf);
printf("
printf ("buffer length = %d\n", buffer_length);
} /* end if loop_count != 0 */
/* Print otma_send_receive parms and start of API */
memset(error_message_text ,0, sizeof(error_message_text));
printf("Send buf at %.8x.\n", &send_buf);
printf("Send buf = %s.\n", send_buf);
printf("Receive buf at %.8x.\n", &rec_buf);
printf("Lterm = %.8s.\n", lterm );
printf("Modname = %.8s.\n", modname );
printf("-\n");
otma_send_receivex(
&anchor, /* (in) anchor block */
&retrsn, /* (out) return status */
&ecbIO, /* (in) ecb address */
&sess_handle, /* (in) session handle */
<erm, /* (in/out) logical terminal */
&modname, /* (in/out) module name */
(unsigned char *) &send_buf, /* (in) send buffer */
&buffer_length, /* (in) size of send buffer */
0, /* (in) send_segment_list */
(unsigned char *) &rec_buf, /* (in) receive buffer */
&rec_buffer_len, /* (in) size of buffer */
&rec_data_len, /* (out) received data length */
0, /* (in/out) receive seg list */
&context, /* (in) context id */
&error_message, /* (out) ims message */
&otma_data); /* (in) Otma Data */
printf("OTMA_SEND done. ret = %.8x rsn = %.8x,%.8x,%.8x,%.8x\n",
retrsn.ret,
retrsn.rsn[0],
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3]);
/* ---------------------------------------------------------------- */
/* Here we wait for receive to signal complete */
/* An application can go do other thing while IMS is processing and */
/* while the XCF scheduled SRBs are returning data to the caller's */
/* buffers. DO NOT DEALLOCATE THE BUFERS WHILE THIS IS GOING ON! */
/* None of the output areas of the SEND_RECIEVE can be freed until */
/* the ECB is posted complete. */
/* ---------------------------------------------------------------- */
DFSYCWAT(ecb_list[1]); /* WAIT on ecb */
retsave = retrsn.ret; /* Save Receive return code */
printf("OTMA_RECEIVE done. ret = %.8x rsn = %.8x,%.8x,%.8x,%.8x\n"
"\nEcb at %.8x.= %.8x.\n",
retrsn.ret,
retrsn.rsn[0],
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3],
ecb_list[1],
*ecb_list[1]
);
if (retrsn.ret != 0)
{
/* ___Error path Free allocated session _____________________ */
printf("-error path retrsn.ret=
printf("-\n");
printf( "Error message = %s\n", error_message );
otma_free(
& anchor, /* (out) ptr to global word */
& retrsn, /* (out) rc,reason (1-4) */
& sess_handle /* (in) unique path id */
);
printf("OTMA_FREE done. ret = %.8x rsn = %.8x,%.8x,%.8x,%.8x \n",
retrsn.ret,
retrsn.rsn[0],
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3]
);
/* ___Sever IMS connection ____________________________ */
printf("-\n");
otma_close(
& anchor, /* (in,out) tr to otma anchor */
& retrsn /* (out) rc,reason (1-4) */
);
printf("OTMA_CLOSE done. ret = %.8x rsn = %.8x,%.8x,%.8x,%.8x\n",
retrsn.ret,
retrsn.rsn[0],
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3]
);
return (retsave); /* EXIT with receive API return code */
}
/* -----------------------------------------------------------*/
/* If SEND_RECEIVE worked .. */
/* -----------------------------------------------------------*/
/* -----------------------------------------------------------*/
/* Open the compare file containing the expected output */
/* of the receive buffer. Compare the expected output */
/* with the actual output and return the result. */
/* -----------------------------------------------------------*/
rec_buf[0] = ' '; /* Remove possible NL ie x'15' */
printf( "infiledd = %s\n", infiledd[loop_count] );
if (( stream = fopen(infiledd[loop_count],"rb")) != NULL )
{
num = fread( compare_buf, sizeof( char ), COM_BUFFER, stream );
if (num == COM_BUFFER) { /* fread success */
printf( "compare_buf = %s\n", compare_buf );
printf( " rec_buf = %s\n", rec_buf );
fclose( stream );
compare_result = memc( compare_buf, rec_buf );
printf( "compare_result =
if (compare_result != 0)
return(compare_result); /* Exit if NO COMPARE */
}
else { /* fread() failed */
if ( ferror(stream) ) /* possibility 1 */
printf( "Error reading file %s\n", infiledd[loop_count]);
else if ( feof(stream)) { /* possibility 2 */
printf( "EOF found\n" );
printf( "Number of characters read %d\n", num );
printf( "compare_buf = %.*s\n", num, compare_buf);
}
}
}
else
printf( "Error opening file %s\n", infiledd[loop_count]);
} /* end of loop */
/*****************************************************************/
/* Once a message is sent to IMS and the answer received it */
/* is usual to release the tpipe for use by other transactions. */
/* For conversational trans an application would keep using */
/* the handle to continue a conversational transaction with IMS. */
/* The Transaction name is specified in the ALLOC and it is */
/* intended that a FREE be done at the end of each transaction */
/* and a new ALLOC be done for the next one. This is not */
/* expensive. */
/*****************************************************************/
printf("-\n");
otma_free(
& anchor, /* (out) ptr to global word */
& retrsn, /* (out) rc,reason (1-4) */
& sess_handle /* (in) unique path id */
);
printf("OTMA_FREE done. ret = %.8x rsn = %.8x,%.8x,%.8x,%.8x \n",
retrsn.ret,
retrsn.rsn[0],
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3]
);
printf("-\n");
/* */
/* Finally, CLOSE severs the connection with IMS and frees the */
/* Storage used by the OTMA API. */
/* This will be done at job-step termination but its untidy. */
/* */
otma_close(
& anchor, /* (in,out) ptr to otma anchor */
& retrsn /* (out) rc,reason (1-4) */
);
printf("OTMA_CLOSE done. ret = %.8x rsn = %.8x,%.8x,%.8x,%.8x \n",
retrsn.ret,
retrsn.rsn[0],
retrsn.rsn[1],
retrsn.rsn[2],
retrsn.rsn[3]
);
return (compare_result); /* Retern return code */
} /* end of main */
/*===================================================================*/
/* Subroutine to compare expected results(compare_buf) */
/* with actual results(err_msg) the "|" is used to signify */
/* an ignore compare and "?" is used to mark the end of string. */
/* Note: Compare starts using an index i=1 ie. the 2nd character */
/* because the 1st character was blanked out. ( NL x'15' ) */
/*===================================================================*/
int memc(char *comp_buf, char *rec_buf1)
{
int j;
int i;
j = 0;
for (i=1;
( (j==0) && (comp_buf[i] != '?') );
i++ )
{
if( comp_buf[i] != '|' ) /* Ignore compare */
{
if( comp_buf[i] != rec_buf1[i]) /* compare ok ? */
{
j++; /* No */
printf( "MISCOMPARE !!! \n" );
printf( "comp_buf[%d] = %c\n", i, comp_buf[i] );
printf( "rec_buf1[%d] = %c\n", i, rec_buf1[i] );
}
else
;
}
else
; /* Else null */
}
return (j);
}