Writing a Rexx Function in C

By | August 1, 2013

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.

2 thoughts on “Writing a Rexx Function in C

  1. Erwin Marschalk

    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

    Reply
    1. Phil Post author

      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

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *