In this example, an i5/OS™ ILE program uses two different techniques to allocate and share memory storage with the i5/OS PASE procedure that it calls.
/* Name: ileMain.c * * Call an i5/OS PASE procedure from ILE * * This example uses the Qp2dlopen, Qp2dlsym, and Qp2CallPase2 ILE * functions to call an i5/OS PASE function passing in parameters * * Compile like so: * * CRTBNDC PGM(mylib/ilemain) * SRCFILE(mylib/mysrcpf) * TERASPACE(*YES *TSIFC) */ #include <stdio.h> #include <stddef.h> #include <errno.h> #include <qp2user.h> /* Use EBCDIC default job CCSID in Qp2dlopen and Qp2dlsym calls */ #define JOB_CCSID 0 /* start i5/OS PASE in this process */ void startPASE(void) { /* start64 starts the 64 bit version of i5/OS PASE */ char *start64Path="/usr/lib/start64"; char *arg_list[2]; arg_list[0] = start64Path; arg_list[1] = NULL; Qp2RunPase(start64Path, NULL, NULL, 0, 819, (char**)&arg_list, NULL); } /* open a shared library */ QP2_ptr64_t openlib(char * libname) { QP2_ptr64_t id; int * paseErrno; /* Qp2dlopen dynamically loads the specified library returning an * id value that can be used in calls to Qp2dlsym and Qp2dlcose */ id = Qp2dlopen(libname, (QP2_RTLD_NOW | QP2_RTLD_MEMBER ), JOB_CCSID); if (id == 0) { printf("Qp2dlopen failed. ILE errno=%i\n", errno); if ((paseErrno=Qp2errnop()) != NULL) printf("Qp2dlopen failed. i5/OS PASE errno=%i\n", *paseErrno); printf("Qp2dlopen failed. Qp2dlerror = %s\n", Qp2dlerror()); } return(id); } /* find an exported symbol */ void * findsym(const QP2_ptr64_t id, const char * functionname) { void * symbol; int * paseErrno; /* Qp2dlsym locates the function descriptor for the * specified function */ symbol = Qp2dlsym(id, functionname, JOB_CCSID, NULL); if (symbol == NULL) { printf("Qp2dlsym failed. ILE errno = %i\n", errno); if ((paseErrno=Qp2errnop()) != NULL) printf("Qp2dlsym failed. i5/OS PASE errno=%i\n", *paseErrno); printf("Qp2dlsym failed. Qp2dlerror = %s\n", Qp2dlerror()); } return(symbol); } /* call i5/OS PASE procedure */ int callPASE(const void * functionsymbol, const void * arglist, const QP2_arg_type_t * signature, const QP2_result_type_t result_type, void * buf, const short buflen) { int * paseErrno; int rc; /* Call Qp2CallPase2 to run the unction function */ rc = Qp2CallPase2(functionsymbol, arglist, signature, result_type, buf, buflen); if (rc != 0) { printf("Qp2CallPase failed. rc=%i, ILE errno=%i\n", rc, errno); if ((paseErrno=Qp2errnop()) != NULL) printf("Qp2CallPase failed. i5/OS PASE errno=%i\n", *paseErrno); printf("Qp2CallPase failed. Qp2dlerror=%s\n", Qp2dlerror()); } } int main(int argc, char *argv[]) { /* we will call a function in i5/OS PASE named "paseFunction" * the prototype for the function looks like this: * int paseFunction(void * input, void * output ) */ /* "signature" is the argument signature for the PASE routine "paseFunction" */ const QP2_arg_type_t signature[] = {QP2_ARG_PTR64, QP2_ARG_PTR64, QP2_ARG_END}; /* "paseFunctionArglist" are the arguments for the PASE routine "paseFunction" */ struct { QP2_ptr64_t inputPasePtr; QP2_ptr64_t outputPasePtr; } paseFunctionArglist; /* "inputString" will be one of the arguments to the PASE routine * "paseFunction" we will call * This is the string "input" in ASCII */ const char inputString[] = {0x69, 0x6e, 0x70, 0x75, 0x74, 0x00}; /* "outputILEPtr" will be a pointer to storage malloc'd from PASE heap */ char * outputILEPtr; /* "id" is the identifier for the library opened by Qp2dlopen */ QP2_ptr64_t id; /* "paseFunction_ptr" is the pointer to the routine "paseFunction" in PASE */ void * paseFunction_ptr; /* "inputAndResultBuffer" is the buffer of storage shared between ILE and PASE * by Qp2CallPase2. This buffer contains space for the PASE function result */ struct { QP2_dword_t result; char inputValue[6]; } inputAndResultBuffer; int rc; int * paseErrno; /* start i5/OS PASE in this process */ startPASE(); id = openlib("/home/joeuser/libpasefn.a(shr64.o)"); if (id !=0) { /* Locate the symbol for "paseFunction" */ paseFunction_ptr = findsym(id, "paseFunction"); if (paseFunction_ptr != NULL) { /* set input arguments for the call to paseFunction() */ /* copy the inputString into the inputAndResultBuffer */ strcpy(inputAndResultBuffer.inputValue, inputString); /* by setting inputPasePtr argument to the offset of the * inputValue by-address argument data in the * inputAndResultbuffer structure and OR'ing that with * QP2_ARG_PTR_TOSTACK QP2CallPase2 will "fixup" the * actual argument pointer passed to the PASE function * to point to the address (plus the offset) of the * copy of the inputAndResultbuffer that Qp2CallPase2 * copies to i5/OS PASE storage */ paseFunctionArglist.inputPasePtr = (QP2_ptr64_t)((offsetof(inputAndResultBuffer, inputValue)) | QP2_ARG_PTR_TOSTACK); /* allocate memory from the i5/OS PASE heap for an output * argument. Qp2malloc will also set the i5/OS PASE address * of the allocated storage in the outputPasePtr * argument */ outputILEPtr = Qp2malloc(10, &(paseFunctionArglist.outputPasePtr)); /* Call the function in i5/OS PASE */ rc = callPASE(paseFunction_ptr, &paseFunctionArglist, signature, QP2_RESULT_DWORD, &inputAndResultBuffer, sizeof(inputAndResultBuffer)); if (rc != 0) { printf("output from paseFunction = >%s<\n", (char*)outputILEPtr); printf("return code from paseFunction = %d\n", (int)inputAndResultBuffer.result); } /* rc != 0 */ } /* paseFunction_ptr != NULL */ } /* id != 0 */ /* Close the Qp2dlopen instance, and then call Qp2EndPase * to end i5/OS PASE in this job */ Qp2dlclose(id); Qp2EndPase(); return 0; } Source code for the i5/OS Procedure paseFunction that is called by the ileMain.c program: /* i5/OS PASE function to be called from ILE * * Compile with something like: * xlc -q64 -c -o paseFunction.o paseFunction.c * ld -b64 -o shr64.o -bnoentry -bexpall -bM:SRE -lc paseFunction.o * ar -X64 -r /home/joeuser/libpasefn.a shr64.o * * The ILE side of this example expects to find libpasefn.a in * /home/joeuser/libpasefn.a * * The compiler options -qalign=natural and -qldbl128 are * necessary only when interacting with i5/OS ILE programs * to force relative 16-byte alignment of type long double * (used inside type ILEpointer) */ #include <stdlib.h> #include <stdio.h> int paseFunction(void * inputPtr, void * outputPtr) { /* An output string to return from i5/OS PASE to ILE * * this is the string "output" in EBCDIC */ const char outputValue[] = {0x96, 0xa4, 0xa3, 0x97, 0xa4, 0xa3, 0x00}; printf("Entered paseFunction The input is >%s<\n", (char*)inputPtr); /* copy the output results to the outputPtr argument */ memcpy(outputPtr, outputValue, sizeof(outputValue)); return(52); /* return something more interesting than 0 */ }
Before i5/OS PASE can be used in a process, it must be started. This is done automatically by calling an i5/OS PASE application main entry point using the APIs, for example, QP2SHELL, QP2TERM, or Qp2RunPase.
However, because this example is calling an i5/OS PASE function exported from a shared library (not a main entry point), you must manually start i5/OS PASE. Two i5/OS PASE starter utilities are available for this purpose: /usr/lib/start32 (to start the 32-bit version of i5/OS PASE) and /usr/lib/start64 (to start the 64-bit version of i5/OS PASE).
These functions open the i5/OS shared library and obtain a pointer to the function you want to call using the Qp2dlopen() and Qp2dlsym(). These functions are similar to the dlopen() and dlsym() routines on many platforms.
Before calling Qp2CallPase2() through the callPASE() function, the main() routine sets up the following variables that define the interface between ILE and the i5/OS PASE function:
The first member of the structure (result in this example) will contain the return value from the i5/OS PASE function. This variable must match the result-type argument provided as the fourth argument in the call to the Qp2CallPase2 API. Anything after this first element represents storage that will be copied into the i5/OS PASE environment when the function is called.
In this example, the inputValue element of the inputAndResultBuffer structure will contain the by-address argument data that will be pointed at by the first argument for the i5/OS PASE function.
This tells the i5/OS PASE run time to modify the actual pointer value provided on the call to the i5/OS PASE function with the address where the inputAndResultBuffer.inputValue was copied into i5/OS PASE memory.
This function calls the i5/OS PASE function using the Qp2CallPase2() API and the arguments set in the main() routine.
After the call to the i5/OS PASE function, the Qp2dlclose() API is called to unload the i5/OS PASE shared library and Qp2EndPase() is called to end the start64 program called at the beginning of the example.