Suppose there is a C language runtime function you’d like to be able to access from Rexx. Or maybe you want to call some other system service, IGGCSI00 perhaps, and leverage tools such as sprintf() to build nice looking output. You could write an LE enabled assembler Rexx external function to call the routine or, maybe, you could write the Rexx function in C. Might as well make it reentrant, too. I’ll demonstrate this by calling CSNBRNG to generate a random number using the hardware cryptographic facility. Yes, you could do this in Rexx with something like this…
return_code = '00000000'x reason_code = '00000000'x exit_data_length = '00000000'x exit_data = '' form = 'RANDOM ' random_number = ' ' Address LINKPGM "CSNBRNG return_code reason_code exit_data_length", "exit_data form random_number" Say C2d(random_number)
But that’s not what I’m trying to demonstrate.
Headers
Unfortunately, IBM doesn’t supply C language headers for the control blocks you need to interface with the Rexx runtime environment. Luckily, however, there are assembler macros that can be used to generate the needed structures using the EDCDSECT utility. So, to build the ones we need for this demonstration, I did this:
//* // JCLLIB ORDER=(CBC.SCCNPRC) //* //DSECT EXEC PROC=EDCDSECT, // OUTFILE='XXXXXXX.SRC.H(REXX)', // DPARM='EQU(BIT,DEF),PPCOND,LOCALE(EN_US.IBM-1047),LRECL(80)' //* //ASSEMBLE.SYSIN DD * IRXARGTB REXX Argument Table (ARGTABLE) IRXCMPTB REXX Compiler Programming Table IRXDSIB REXX Data Set Information Block IRXEFPL External Functions parameter list IRXENVB REXX Environment Block IRXEVALB REXX Evaluation Block (EVALBLOCK) IRXEXECB REXX EXEC block (EXECBLK) IRXEXTE REXX Vector of External Entry Points IRXFPDIR REXX Function Package Directory IRXINSTB REXX In-Storage Block (INSTBLK) IRXMODNT REXX Module Name Table (MODNAMET) IRXPACKT REXX Function Package Table (PACKTB) IRXPARMB REXX Parameter Block (PARMBLOCK) IRXSHVB Shared REXX Variable Request Block IRXSUBCT REXX Subcommand Table (SUBCOMTB) IRXWORKB REXX Work Block Extension (WORKBLOK_EXT) END
Admittedly, I just processed all the the IRX* macros in SYS1.MACLIB even though I will never need most of these.
Linkage
Rexx doesn’t expect to be calling a Language Environment C program. So, we’ll use the system programming C facilities, freestanding reentrant application procedure to build a program without the LE Runtime. Maybe you could use the newer Metal C environment instead, but I am as yet unfamiliar. We also need to specify OS linkage for the C language entry point. I think there may be other ways to do this, but I used
#pragma environment(RXRANDOM) #pragma runopts(PLIST(OS),NOEXECOPS,NOARGPARSE,NOREDIR)
and used RXRANDOM as the entry point, instead of main():
int RXRANDOM(struct efpl efpl)
(struct efpl
is part of the header we built above).
Also, the Rexx provided services do not expect to be called with C language linkage conventions so we have to set up special OS linkage function prototypes for the ones we’re going to use. For example, to define a Rexx variable we need to call IRXEXCOM, I would do:
typedef int irxexcom_func_t (const char *, void *, void *, struct shvblock *, ...); #pragma linkage (irxexcom_func_t,os) typedef irxexcom_func_t * irxexcom_func_p; static irxexcom_func_p irxexcom;
We find the address of IRXEXCOM from the ENVBLOCK->IRXEXTE on entry from Register 0 with:
envblockp = (struct envblock *) __xregs(0); extep = (struct irxexte *) envblockp->envblock_irxexte; irxexcom = (irxexcom_func_p) extep->irxexcom;
A bit ugly, I know, but it is documented so pretty safe, I think.
Then, I would call it with something like this:
rc = (* irxexcom) ("IRXEXCOM", NULL, NULL, &shv, &envblockp);
But, anyway, I won’t need this part for RXRANDOM which only returns a result and doesn’t need any Rexx services.
Program
Here’s my little program:
??=pragma filetag ("IBM-1047") #pragma environment(RXRANDOM) #pragma runopts(PLIST(OS),NOEXECOPS,NOARGPARSE,NOREDIR) #pragma strings(readonly) #include <spc.h> #include <stdlib.h> #include <string.h> #include "rexx.h" /* built by EDCDSECT */ #include "csfbext.h" /* ** RXRANDOM: Rexx function entry point */ int RXRANDOM(struct efpl efpl) { struct envblock * envblockp; struct evalblock * evalblockp; struct irxexte * extep; struct argtable_entry * argtp; int rc, rsn; unsigned long long rnum; /* collect the parameters and rexx environment */ argtp = (struct argtable_entry *) efpl.efplarg; evalblockp = * (struct evalblock * *) efpl.efpleval; envblockp = (struct envblock *) __xregs(0); extep = (struct irxexte *) envblockp->envblock_irxexte; /* no arguments for now */ if ((int) argtp->argtable_argstring_ptr != 0xffffffff) return(21); /* invalid parameter #1 */ CSNBRNG(&rc, &rsn, NULL, NULL, "RANDOM ", (unsigned char *) &rnum); if (rc > 0) EDCXABND(rc+3000,rsn); sprintf(&evalblockp->evalblock_evdata, "%llu", rnum); evalblockp->evalblock_evlen = strlen(&evalblockp->evalblock_evdata); return(0); }
It’s not fully tested, so drop me a comment if you see something amiss.
Build
Anyway, I compiled it with:
//* // JCLLIB ORDER=(CBC.SCCNPRC,CEE.SCEEPROC) //* //CC EXEC EDCC, // INFILE='XXXXXXX.SRC.C(RXRANDOM)', // CPARM='OPTFILE(DD:CCOPTS)' //COMPILE.CCOPTS DD * LIST SOURCE NOMARGIN NOSEQUENCE OPT(3) ARCH(10) TUNE(10) LOCALE SEARCH(//'CEE.SCEEH.+',//'SYS1.SIEAHDR.H',//'XXXXXXX.SRC.H') //* //* SPC RENT modules require pre-link //* //PL EXEC EDCPL, // INFILE=&&LOADSET, // OUTFILE='XXXXXXX.SRC.LOAD(RXRANDOM),DISP=SHR' //* //LKED.SYSLIB DD DISP=SHR,DSN=CEE.SCEESPC // DD DISP=SHR,DSN=CEE.SCEELKED // DD DISP=SHR,DSN=CSF.SCSFMOD0 //* //LKED.SYSIN DD * INCLUDE SYSLIB(EDCRCINT) ENTRY RXRANDOM
Test
Here is a sample test run:
5 *-* Say Rxrandom() >>> "10280055013009162947" 10280055013009162947 6 *-* Say Rxrandom() >>> "15092822610459904595" 15092822610459904595 7 *-* Say Rxrandom() >>> "14661558202322760102"
To use the result numerically, you might need to set NUMERIC DIGITS
higher.
If you like this function, I also have an updated version that uses Metal C.
And here is a Rexx function interface to IGGCSI00.
Hi Phil,
great job, this helped me to cleanup and beautify my external function. At this time, i have issues with using IRXEXCOM. Imho. everything is ok, but rexx always reports in issue, if i use IRXEXCOM.
Erwin
Hello Erwin,
Thanks.
Here is some code I’ve used to invoke IRXEXCOM…
typedef int irxexcom_func_t
(const char *, void *, void *, struct shvblock *, …);
#pragma linkage (irxexcom_func_t,os)
typedef irxexcom_func_t * irxexcom_func_p;
static irxexcom_func_p irxexcom;
[—]
/*
** Define a Rexx variable using the IRXEXCOM service
**
** On error, abends U3002 with the return code from IRXEXCOM as the
** reason code
*/
static void variable_define(const char * v_name,
const char * v_value,
const int v_len) {
struct shvblock shv;
int rc;
memset(&shv, 0, sizeof(shv));
shv.shvcode = shvstore; /* function is store */
shv.shvnama = (void *) v_name;
shv.shvnaml = strlen(v_name);
shv.shvvala = (void *) v_value;
shv.shvvall = v_len;
if ((rc = (* irxexcom) (“IRXEXCOM”, NULL, NULL, &shv, &envblockp)) != 0)
EDCXABND(3002,rc);
}
HTH,
Phil