Calling C from Rexx – accessing variables.

Usually your program can access variables in the calling Rexx program. You can get, set or delete variables. It is sometimes more complex that the documentation implies.

If your program is called from ISPF you can also set and get ISPF variables, or use ISPF tables to pass data.

To use the Rexx variable interface the applications need access to the ENVironmentBlock (ENVB).

Getting to the ENVB

The Rexx documentation describes how this is passed in register 0, unfortunately a C program does not have access to register 0 (without writing some assembler glue code).

You can get the environment block by calling IRXINIT and passing the parameter “FINDENVB”.
I was unable to use fetch() to dynamically load IRXINIT. (It may work – I couldn’t get it to). Initially I user the binder to include the IRXINIT code, but this is not good practice as you should use the version from the system you are running on.

A better way based on code from David Crayford (thank you) is

#include <stdio.h>                     
#include <stdlib.h>
#include <string.h>
// specify how the irxinit routine is called
#pragma linkage(tsvt_rexxfunc_t,OS)

int main( int argc, char **argv ) {
struct envblock * pEnv;
int rc;
// the TSO anchor block
struct TSVT {
char dummy[140];
char * irxinit;
};

#pragma pack(1)
// this defines a function with parmlist(char *... )
// returning an int
typedef int tsvt_rexxfunc_t( char *, ... );

typedef struct tsvt TSVT;
// TSTV comes from ikjtstv - but no C header equivalent
// so fake one up.
struct tsvt {
int padding[35];
tsvt_rexxfunc_t *irxinit; // Address of IRXEXEC
};
#pragma pack(reset)

int rc2;
// now chain through the control blocks to find the rexx init
// cvtmap provided in SYS1.SIEAHDR.H(CVT)
#define CVTPTR 16L
struct cvtmap * cvt =*( (struct cvtmap ** ) CVTPTR);
// Comment: I think the *((xxx **)) is so unnatural, and always
// get it wrong.

// TSTV comes from ikjtstv - but no C header equivalent
TSVT * tsvt = cvt->cvttvt;
tsvt->irxinit("FINDENVB ",
0, //
0, // instor plist
0, // 4 user field
0, // 5 reserved
&pEnv , // 6 ->envblock
&rc2); // rexx parm 7 return code
printf(" rc2 %i\n",rc2);
}

Set a symbol

This took me a couple of hours to get right. The documentation is not clear in places.

char dummy; 
struct shvblock shv;
memset(&shv,0,sizeof(shv));
char * pSymbol = "COLINSSYMBOL";
char * pValue ="VALUECP";
shv. shvcode = 'S'; // SHVSTORE; // symbolic name set
shv. shvnama = pSymbol; // a symbolic name
shv. shvnaml = strlen(pSymbol); // Len symbolic name
shv. shvvala = pValue ;
shv. shvvall = strlen(pValue);
int rc3;
struct irxexte * pExte = (struct irxexte * ) pEnv-> envblock_irxexte;
tsvt_rexxfunc_t * fptr = (tsvt_rexxfunc_t *) pExte -> irxexcom;

rc = (fptr)("IRXEXCOM",
&dummy,
&dummy,
&shv,
&pEnv,
rc3);
rc2 = shv.shvret;
printf("post rc %i rc2 %i rc3 %i\n",rc,rc2,rc3);

Notes:

The Rexx header file provides

#define  SHVSTORE  "S"             /* Set variable from given value          */    

but you cannot use this because shv. shvcode expects a char ‘S’ , not “S”.

tsvt_rexxfunc_t is used to define the function at address fptr as a z/OS routine with parameter list in register 1, and the high end bit of the last parameter turned on.

After this executed, and the program returned, the Rexx “say COLINSSYMBOL” printed “VALUECP” so it was a success.

A slightly harder case of setting a value.

I put the above code into a subroutine so I was able to use

setSymbol('S',"COLINSSYMBOL","VALUECP");

You can use option upper case ‘S’ which takes the string you give it, and makes a Rexx variable, or you can use the lower case ‘s’ option, which says it does variables substitution.

Uppercase: (The Direct interface). No substitution or case translation takes place. Simple symbols must be valid REXX variable names (that is, in uppercase and not starting with a digit or a period), but in compound symbols any characters (including lowercase, blanks, and so on) are permitted following a valid REXX stem.

This is not entirely true.

With upper case ‘S’

With setSymbol(“MYKEY.aaa“,”VALUECP”), Rexx displayed “MYKEY.AAA” showing the variable did not exist, even though the call to defined it worked successfully.

With setSymbol(“MYKEY.AAA“,”VALUECP”), Rexx displayed “VALUECPA” showing the correct value.

If you are using ‘S’ then always specify the name in upper-case despite what the documentation says.

With lower case ‘s’

Both

setSymbol("MYKEY.aaa","VALUECPA"); 
setSymbol("mykey.bbb","VALUECPA");

worked.

But it gets more complex…

I had a small Rexx program:

/* REXX */
A = "COLINA"
address link CPLINK "A B C "
drop A
say value("MYKEY.A")
say value("MYKEY.B")
say value("MYKEY.COLINA")
say value("A")
say value("SMYKEY.A")
say value("SMYKEY.B")
say value("SMYKEY.COLINA")

If value(“MYKEY.A”) is “MYKEY.A” then there is no variable with that name.

and my program had

setSymbol('S',"MYKEY.A","BIGSA"); 
setSymbol('S',"MYKEY.B","BIGSB");
setSymbol('s',"SMYKEY.A","SMALLSA");
setSymbol('s',"SMYKEY.B","SMALLSB");

The output had

  1. say value(“MYKEY.A”) -> “BIGSA” from my program
  2. say value(“MYKEY.B”) -> “BIGSB” from my program
  3. say value(“MYKEY.COLINA”) -> “MYKEY.COLINA” not a variable
  4. say value(“SMYKEY.A”) -> “SMYKEY.A” not a variable
  5. say value(“SMYKEY.B”) -> “SMALLSB” set from my program
  6. say value(“SMYKEY.COLINA”) -> “SMALLSA” ‘.A’ was substituted with COLINA as part of the set call
  • Lines 1-3 show that there was no substitution of variables.
  • Lines 4 shows that variable SMKEY.A was not created; SMKEY.COLINA was substituted
  • Line 5 had no substitution and was like line 2
  • Line 6 this is the variable name used.

This means that if you specify a lower case ‘s’, the output may not be as you expect. I would suggest you use upper case ‘S’ unless you know what you are doing.

C includes ” is not the same as <

The one line description: I found the difference between #include <a.h> and #include “a.h”.

I had problems finding compiling some sample C code which included

#include <__iew_api.h>

I was getting

WARNING CCN3296  #include file <__iew_api.h> not found.

There were a couple of problem. The length of the name __iew_api is more than 8 characters, so will not be in a PDS or PDSE. I found it in /usr/include.

In my JCL for comping C programs I had

LSEARCH(/usr/include/)

But still it was not found. After a cup of tea and walk down to the shops I wondered if it was the include that was causing the problems.

I changed it to

#include "__iew_api.h" 

and it found it. The documentation says

The file_path can be an absolute or relative path. If the double quotation marks are used, … the preprocessor adds the directory of the including file to the list of paths to be searched for the included file.
If the double angle brackets are used, … the preprocessor does not add the directory of the including file to the list of paths to be searched for the included file.

What’s C.E.E.1 and which function is this address in?

I had an abend in a module created from C programs, and wanted to know which function had the problem.

In the old days, each function had an eye-catcher and a compile date at the top of each function, so it was easy to scroll upwards towards the start of the dump until you came across the eye-catcher.

And then we had XPLINK… I think the XP link stands for eXtra Performance. The output of the C compiler changed to make it the code more efficient, especially with Java functions. For example:

  • before XPLINK, a call to a function would save all of the registers, update the save area chain – call the module… return and restore the registers afterwards. Some functions were as simple as set this value to 0 – and the overhead of the call was many times the cost of the instructions. The overhead was reduced by only saving what needed to be saved, and passing more parameters in registers.
  • by moving constant data, such as character strings, out of the mainline instructions, meant that sometimes fewer pages were needed for instructions – and so fewer pages were needed in the hardware instruction cache, and so may be faster. Moving the function name and compile time into the “data page” and so into the data cache, was part of this.

The function name etc is available – just not in an obvious place.

What does the code look like?

Meta data is stored in Program Prolog Areas (PPAs)

In the listing, at the start of each XPLINK function is “C.E.E.1”

00000E70    00C300C5 00C500F1 00000080 000000A0a *.C.E.E.1.....

At offset 00000080 from 00000E70 is a block of storage (PPA1) which identifies this function.

0006D8  02           =AL1(2)            Version 
0006D9 CE =AL1(206) CEL signature
...
0006F0 0003 AL2(3),C'pit'
0006F8 FFFFF9C0 =F'-1600' Offset to Entry Point Marker
  • At offset 0 is 0x02… so you know you are in the right control block
  • At offset 2 is 0xCE
  • At offset 10 is the code length
  • At offset 18 is 2 bytes of length, followed by the function name, possibly with up to 3 bytes of padding to align the next field on a 4 byte work boundary.
  • (Sometimes,) after this, the offset will vary because the name length is variable, is a field like 0XFF… such as FFFFFA60. This is – 0x5a0 (-1440). The address of this section (the 0x02) minus this value gets you to the C.E.E.1. This does not always seem to work!

The start of the function “pit” is at x6d8 – 1600 = x6d8 -x640 = x98

At this address in the listing was

                                *  void pit() 
000098 00C300C5 DC =F'12779717' XPLink entrypoint marker
00009C 00C500F1 DC =F'12910833'
0000A0 00000640 DC =F'1600'
0000A4 00000080 DC =F'128'
0000A8 pit DS 0D
* Start of executable code
0000A8 9049 4780 STM r4,r9,1920(r4) .....

Note the value’s 1600 and -1600 tie up.

So not too difficult to find the name of the function.

Using a 31 bit parameter list in a 64 bit C program.

You can use svc99() to create //DDNAME entries in a job. I used it to dynamically allocate

//COLIN DD SYSOUT=H

from within my C program.

You cannot use a 31 bit C program in a 64 bit C program

If you try to fetch and use a 31 bit C program in a 64 bit program you get

EDC5256S An AMODE64 application is attempting to fetch() an AMODE31 executable. (errno2=0xC4070068)

It is hard to make this to work, because of 64 bit parameters do not work in a 31 bit program. The save area’s are complex. Overall it is easier to just call a 64 bit program from a 64 bit program and do not try to use a 31 bit. You can do it, but you need some assembler glue code.

svc99 works in both 31 and 64 bit modes because at bind time

  • the 31 bit program includes a 31 bit version of svc99
  • the 64 bit program includes a 64 bit version of svc99.

The 64 bit program has come glue code to enable it to call the 31 bit program.

Using a 64 bit program with a 31 bit parameter list

Using svc99() was pretty easy from a 31 bit C program, but the documentation says The __S99parms structure must be in 31-bit addressable storage, which is a bit of a challenge.

For the 31 bit C program, the code is like

int main () { 
SVC99char1( sysoutClass, DALSYSOU,'A')
SVC99string(ddname,DALDDNAM,"COLIN")
struct __S99struc parmlist;
struct __S99rbx rbx =
{
.__S99EID = "S99RBX",
.__S99EOPTS =0xC4, // issue message before return, and use wto
.__S99EVER =0x01}; // version
memset(&parmlist, 0, sizeof(parmlist));
void *tp[3]= { /* array of text pointers */
&sysoutClass,
&ddname,
0};
int mask = 0x80000000;
memcpy(&p->tp[2],&mask,4); // make it a null address with high bit on
parmlist.__S99RBLN = sizeof(parmlist);
parmlist.__S99VERB = 01; /* verb for dsname allocation */
parmlist.__S99FLAG1 = 0x4000; /* do not use existing allocation */
parmlist.__S99TXTPP = tp; /* pointer to pointer to text units */
parmlist.__S99S99X =&rbx; // pointer to extension
int rc;
rc = svc99(&parmlist);

Where

  • SVC99char1(..) and SVC99string(…) define the parameters for SVC 99
  • struct __S99rbx rbx defines the request block extension which indicates errors to be reported on the job log
  • void tp[3] is the array of parameter for svc 99. The value can be null. The last must have the top bit on, so mask is copied in.
  • parmlist is the svc99 parameter list which points to the other data: the definition, and the request block extension.

Getting it to work in a 64 bit C program.

You have to build a parameter list where all the parameters are in 31 bit storage.

Generate the 31 bit structure

I defined a structure to contain the 31 bit elements, then used ___malloc31 to allocate 31 bit storage for it.

struct pl31 { 
struct __S99struc parmlist;
struct __S99rbx rbx;
// 31 bit equivilants of the SVC 99 parameters
struct sysoutClass sysoutClass;
struct ddname ddname ;
void * __ptr32 tp[3]; /* array of text pointers */
} pl31;

char * __ptr32 p31 =(char * __ptr32) __malloc31(sizeof(pl31));

The __ptr32 in char * __ptr32 p31= tells C this is a pointer to 31/32 bit storage.

Set up the parameter list

struct pl31 *  p ; 
p = (struct pl31 * ) p31; // set up 31 bit pointer to data
memcpy(&p->rbx,&rbx,sizeof(rbx)); // copy from static to 31 bit area
memset(&p->parmlist, 0, sizeof(p->parmlist)); // initialise to 0
p->parmlist.__S99RBLN = sizeof(p->parmlist);
p->parmlist.__S99VERB = 01; // verb for dsname allocation
p->parmlist.__S99VERB = 07; // display
p->parmlist.__S99FLAG1 = 0x4000; // do not use existing allocation
p->parmlist.__S99TXTPP =& p->tp ; // pointer to pointer to text units
p->parmlist.__S99S99X =& p->rbx ; // pointer to extension

Create the svc99 definitions in 31 bit storage

The macros SVC99string etc generate code like

struct ddname{ 
short key;
short count;
short length;
char value[sizeof("COLIN ")]; }
ddname = {0x0001,1,sizeof("COLIN ")-1,"COLIN "};

so within the 31 bit structure I could use

struct ddname ddname;

to allocate a structure the same shape as the original definition.

I then used a macro SVC99COPY(…); which copies the data from the original, static, definition into the 31 bit structure.

#define SVC99COPY(name) memcpy(&p->name,&name,sizeof(name));

Creating and initialising the rbx

Because the 31 bit storage is dynamically allocated, you cannot use structure initialisation like:

struct pl31 { 

struct __S99struc parmlist;
struct __S99rbx rbx =
{
.__S99EID = "S99RBX",
.__S99EOPTS =0xC4, // issue message before return, and use wto
.__S99EVER =0x01 // version
};

...
} pl31;

I defined rbx in the mainline code, and copied it into the pl31 structure once it had been allocated.

Set up the pointer to the definitions

p->tp[0]  = &p-> ddname      ; 
p->tp[1] = &p-> sysoutClass ;
int mask = 0x80000000;
memcpy(&p->tp[2],&mask,4);

The array of tp[] has to be initialised at run time, rather than at compile time because the 31 bit storage is dynamically allocated.

Invoke the svc99()

rc = svc99(&p->parmlist);

So overall – not too difficult.

Making the code bimodal

If you want to make a function which can be used from 31 or 64 bit programs. You need to provide two versions of the code. One compiled as a 64 bit program, the other as a 31 bit program. You could have

  • myprog() for 31 bit programs and
  • myprog64() for 64 bit programs.

or you could specify

#ifdef __LP64
#pragma map (myprog, "myprog64")
#else
#pragma map (myprog, "myprog31")
#endif
...
myprog()

At bind time you need to include the correct code, myprog64 or myprog31.

If could just use the one name, and have two object libraries, and specify the library in JCL, for example

//BIND.OBJLIB  DD DISP=SHR,DSN=COLIN.OBJLIB31 
//BIND.SYSIN DD *
INCLUDE OBJLIB(MYPROG)
NAME...

If you have used the wrong library at bind time, you may only find out a run time.

If you use the #pragma map to force it to use object names, you will find the problem at bind time, because myprog31 or myprog64 will not be found.

p’ing and f’ing a C job or started task

I have a C program which can run as a long running batch program. I wanted to be able to stop it when I had finished using it. C runtime has the __console and __console2 which allow an operator to interact with the job, by using the operator commands stop(p) and modify(f).

Using the __console* interface I can use the operator commands

p colinjob
f colinjob,appl=’these parameters’

When the modify command is used, the string returned to the application is null terminated. I think you can enter 127 characters in the appl=’…’ parameter.

The string is converted to upper case.

__console or __console2?

__console was available first. __console2 extends the capability of __console, by having the capability to set more attributes on the message, such as where the message gets routed to.

An application can issue an operator command, and specify a Command And Response Token (CART). The target application can tag responses with the same CART value, and so the requesting application gets the responses to its original request.

Write to operator

You can use __console() __console2() just to write to the operator.

  • If the user does not have access to BPX.CONSOLE in the FACILITY class, and is not a super user, you get “BPXM023I (userid) A Message”
  • If the userid is has read access to BPX.CONSOLE in the FACILITY class or running as a super user (id(0) ), you get “A Message” without the BPXM023I

You can use __console* to write to the operator and return with no special programming.

Waiting for a stop or modify request

When using __console* to wait for a modify or stop request, the __console* request is suspended, until it receives a modify or stop request. This means that you need to set up a thread to do this work, and to notify the main program when an event occurs.

include statements

You need

 #pragma runopts(POSIX(ON)) 
/*Include standard libraries */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/__messag.h>
#define _OPEN_SYS 1
#include <pthread.h>
#define _OPEN_SYS
#include <signal.h>
// the following is used to communicate between thread and main task
struct consoleMsg{
char code;
char message[128];

};

The main program

int main( int argc, char *argv[]) 
{
...
struct consoleMsg consoleMsg;
memset(&consoleMsg,0,sizeof(consoleMsg));
consoleStart(&consoleMsg);

for...
{
...
 if (consoleMsg.code ==_CC_stop ) break;
 else
 if (consoleMsg.code == _CC_modify )
 {
  printf("modify message:%s.\n",consoleMsg.message);
  consoleMsg.code = 0;
 }
...
} // for
consoleStop(&consoleMsg);
}

consoleStart(…)

This function takes the input parameter and passes it to the thread.

pthread_t thid; 
void consoleStart( struct consoleMsg * pCons)
{
 // this creates the thread and says execute "thread" function below
if (pthread_create(&thid, NULL, thread, (void * ) pCons) != 0)
{
perror("pthread_create() error");
exit(1);
}
return;
}

consoleStop(…)

If the operator used the stop command, the thread (below) ends. If the main program wants to end it, it issues a kill. You should not issue the kill if the thread has ended.

void consoleStop( struct consoleMsg  * pCons) 
{
  // if the P command was issued, the thread ended,so we do not need to kill it
if (pCons -> status = 0) return; // it has already ended
int status;
status = pthread_kill(thid, SIGABND);
if (status != 0)
{
perror("pthread_kill() error");
}

The thread subtask

This does all of the work. It is passed the pointer which was passed in the consoleStart function. In this example, it points to a buffer for the returned data, and the reason the exit was woken up.

When the thread is started, it displays a message, giving

BPXM023I (COLIN) Use f jobname,appl=data or p Jobname

void *thread(void * pArg ) { 
   // map the passed argument to ours
    struct consoleMsg * pCM = ( struct consoleMsg * ) pArg;
char * pMessage = "Use f jobname,appl=data or p Jobname";
char reply[128]; /* it gets the data */
int concmd; // what command was issued
char consid[4] = "CONS";
unsigned int msgid = 0 ;
unsigned int routeCode[] = {0};
unsigned int descr[] = {0};
char cart[8] = "MYCART ";
struct __cons_msg2 cons;
cons.__cm2_format = __CONSOLE_FORMAT_3;
cons.__cm2_msglength = strlen(pMessage);
cons.__cm2_msg = pMessage;
cons.__cm2_routcde = routeCode;
cons.__cm2_descr = descr;
cons.__cm2_token = 0;
cons.__cm2_msgid = &msgid;
cons.__cm2_dom_token = 0;
cons.__cm2_dom_msgid = 0;
cons.__cm2_mod_cartptr = &cart[0];
cons.__cm2_mod_considptr= &consid[0];
memcpy(&cons.__cm2_msg_cart,&cart ,8);
memcpy(&cons.__cm2_msg_consid, &consid,4);
int rc;
    int loop;

for( loop = 0; loop < 1000;loop ++)
{
  // issue the message and wait
rc= __console2(&cons, &reply[0], &concmd);
if (rc != 0)
perror("__console2");
printf("__console2 gave rc %d function %d\n",rc,concmd);
pCM -> code = concmd;
if (concmd == _CC_modify )
{
printf("Modify issued %s\n",reply);
memcpy(&pCM-> message,&reply,sizeof(reply));
}
else
if (concmd == _CC_stop)
{
printf("Stop issued\n");
break;
}
}
void * ret = "thread returned\n" ;
pthread_exit(ret);
}

Converting a STCK into Unix time

A system z STCK instruction gives the number of microseconds since Jan 1st 1900. The Unix time is based on Jan 1st 1970.

I needed to convert a STCK to a Unix time.

Convert a STCK to seconds and microseconds.

Bit 51 of the STCK instructions represents 1 microsecond.

// get the STCK value 
unsigned long long stck, stck2;
__stckf(&stck); // use store clock fast

// convert STCK to microseconds
stck2 = stck >>12;
int seconds = stck2/1000000; // 1 million
int microseconds = stck2%1000000

Because the STCK will overflow on September 17, 2042, you should be using the STCKE instruction.  The format of the STCKE is a one byte epoch, the STCK value, and other data.

To get the time in seconds

unsigned long longstck4
char stcke[16];
__stcke(&stcke);
memcpy(&stck4,&stcke,8); // only get the relevant part
stck4 = stck4>>4; // shift it 4, (STCK shifts 12)
seconds= stck4/1000000;

Get the unix time

time_t t =  time(); 

This time will overflow on January 19, 2038.

You can use

#define _LARGE_TIME_API 
#include <time.h>
...
time64_t t64 ;
time64(&t64);

and t64 is a long long integer.

Converting STCK seconds to Unix time

UnixSeconds = STCKSeconds – 2208988800;

and the number of micro seconds is the same.

Format it

To format it see here.

Using RACF callable services including from a 64bit bit program

You can use RACF callable services to programatically get and set RACF information, for example to list and display digital certificates, and objects.

There is a C interface to these services. These interfaces are easy to use as long as you are careful with your data types, and get your compile JCL right. You can use 31 and 64 mode programs with these services.

JCL to compile a 64 bit program

Below is the JCL I use for compile programs which use gskit and RACF callable services.

//COLINC5    JOB 1,MSGCLASS=H,COND=(4,LE) 
//S1 JCLLIB ORDER=CBC.SCCNPRC
// SET LOADLIB=COLIN.LOAD
//*OMPILE EXEC PROC=EDCCB,
//COMPILE EXEC PROC=EDCQCB,
// LIBPRFX=CEE,
// CPARM='OPTFILE(DD:SYSOPTF),LSEARCH(/usr/include/)',
// BPARM='SIZE=(900K,124K),RENT,LIST,RMODE=ANY,AMODE=64,AC=1'
//COMPILE.SYSOPTF DD *
...
/*
//COMPILE.SYSIN DD DISP=SHR,DSN=COLIN.C.SOURCE(...)
//BIND.SYSLMOD DD DISP=SHR,DSN=COLIN.LOAD
//BIND.OBJLIB DD DISP=SHR,DSN=COLIN.OBJLIB
//BIND.GSK DD DISP=SHR,DSN=SYS1.SIEALNKE
//BIND.CSS DD DISP=SHR,DSN=SYS1.CSSLIB
//BIND.SYSIN DD *
INCLUDE GSK(GSKCMS64)
INCLUDE GSK(GSKSSL64)
INCLUDE CSS(IRRSDL64)

NAME AMSCHE64(R)

Note the 64 bit specific items

  • PROC=EDCQCB
  • RMODE=ANY,AMODE=64
  • The includes of the GSK*64 stubs
  • The include of the 64 bit RACF callable stub IRRSDL64

The 31 bit equivilants are

  • PROC=EDCCB
  • RMODE=ANY,AMODE=31
  • The includes of the GSK*31 stubs: GSKCMS31,GSKSSL
  • The include of the 31 bit RACF callable stub IRRSDL00

The source is specified via //COMPILE.SYSIN

SYSOPTF

For both 64 bit and 31 bit programs

//COMPILE.SYSOPTF DD * 
LIST,SOURCE
aggregate(offsethex) xref
SEARCH(//'COLIN.C.H',//'SYS1.SIEAHDR.H')
TEST
RENT LO
OE
INFO(PAR,USE)
NOMARGINS EXPMAC SHOWINC XREF
LANGLVL(EXTENDED) sscom dll
DEFINE(_ALL_SOURCE)
DEBUG

Skeleton of C program

#pragma linkage(IRRSFA64 ,OS) 

The pragma is needed for the bind operation. It says the module is a z/OS callable service type of module (and not a C program).

#ifdef _LP64 
#include <irrpcomy.h>
#else
#include <irrpcomx.h>
#endif

You need a different copy book for the RACF constants depending on the 31/64 bit mode.

IRRPCOMY contains definitions for 64 bit programs, IRRCOMX is for 31 bit programs.

char * workarea ; 
workarea = (char *) malloc(1024);
int ALET1= 0;
int parmAlet = 0;
int numParms =11;
short function_code = 1;
int ALET2= 0;
int ALET3= 0;
int SAF_RC = 0;
int RACF_RC = 0;
int RACF_RS = 0;

The variables have to be “int”, not “long”, as they are 4 bytes long. With 64 bit program, a long is 8 bytes long. See here for a table about the types and lengths in 31 bit and 64 bit programs. A short is 2 bytes long.

Set up the parameter list 

The macro IRRPCOM? provides header files for some definitions.

For example

char * pSTC = "AZFTOTP1"; 
char area[1000];

struct fact_getf_plist pl;
pl.fact_getf_options = 0;
pl.fact_getf_factor_length = 8;
pl.fact_getf_factor_a = pSTC;
pl.irrpcomy_dummy_34 = 0;
pl.fact_getf_af_length = sizeof(area);
pl.fact_getf_af_a = & area;

where pl is used below.

Call the function

 IRRSFA64( workarea, // WORKAREA 
&ALET1 , // ALET
&SAF_RC, // SAF RC
&ALET2, // ALET
&RACF_RC,// RACF RC
&ALET3 , // ALET
&RACF_RS,// RACF Reason
&numParms,
&parmAlet, //
&function_code,
&pl );

The irrpcomx has a structure definition for the parameter list, but I could not get it to work in these programs, as it passes the address of the data, instead of the data itself.

Accessing lots of data efficiently using C binary tree functions.

A binary tree is an efficient way of storing data in memory (in a tree!) The C run time library has a set of functions that allow you to create and manage data in a binary tree, but it is not that easy to set up.

Core concepts

Each element, or node, in the tree has two children and a pointer to the record. Each child can be null, or the address of another child. The left child contains elements which are less than the parent, the right node contains elements which are greater than the parent node. To find an element in the tree, you start at the top, or the root node, and compare the value of the nodes value, with the element you are looking for and work down the tree until you find a matching record or you hit the bottom of the tree. You provide a compare function takes two records and returns:

  • -1 if the first record is greater than the second record
  • 0 if the two records are the same item
  • 1 if the first record is less than the second record.

The comparison could be a simple string compare, or comparing data in a structure for example

typedef struct {
char pString[5]; // to the message text
int count ;
 int date;
} MyNode;

int Node_compare(const void *MyNode1, const void *MyNode2) {
  MyNode * p1, *p2;
int rc;
 // check the dates
  rc = p1-> date - p2 -> date;
  if (rc == 0 )
 // check the string
rc = strcmp(p1->pString, p2->pString);
  // return the result
  return rc;

}

You need a root for the tree

void *ROOT = NULL;

Some C I do not understand

In some of the routines you need code like

char * buffer = * (char **) in;
//char * buffer = (char *) in;

With my knowledge of C, these two statements look the same. Only the first one works.

I have code

void print_Node(const void *ptr, ...) {
...
 MyNode p = *(MyNode**) ptr;

There may be a smarter way of referencing the passed data, but the definition of “MyNode p” and using “* (Mynode **)” on the passed in pointer, works.

Referencing structures

It took me a while to work out the correct type definitions to get it to work. For example

int Node_compare(const void *, const void *);
MyNode * pMyNode;
void * p;
p = (void *) pMyNode;
MyNode * q;
/* see if it is in the tree already */
q = *(MyNode **) tsearch(p, (void **) &ROOT, Node_Compare);

The various functions expect pointer defined with type (void *).

Find an element

To look for for an element you use the tfind (tree find) function. The parameters are

  • a pointer to the record (or string) partially initialised with the values used by the compare function.
  • the root of the tree
  • the compare function, described above.

It returns the record, or a null.

To use this function you have to create a record containing the values the compare function can use it. This record can be in automatic storage, or you can use malloc() to allocate storage for it.

typedef struct {
char pString[5]; // to the message text
int count ;
 int date;
} MyNode;
MyNode tempNode;
memcpy(&tempNode.String("WXYZ"),sizeof("WXYZ");
tempNode.date = ....

You do not need to initialise tempNode.count, as this is not used in the comparison.

Adding an element

There is no tadd() function as such, there is a tsearch which provides “look for this node, and add it if it was not found“.

The parameters are

  • a pointer to the record. 
  • the root of the tree
  • the compare function, described above.

it returns the address of a record. If it is the one you passed in – then it was added. It it returns a different node – an existing record was returned.

See below for a more detailed description.

Deleting a node

You pass the standard three parameters in.

Walking the tree.

I’ve typically used a binary tree to store lots of information, such as a list of error messages and the count of occurrences, then, at the end of the job, print them all out in sequence. This is called walking the tree.

You use

twalk(root, print_function);

This calls the print_function for every element in the tree.

Planning for the tree

If you plan to walk the tree and print the elements in order, then the compare function needs to be written so the data is in the right order.

For example, I want to report the number of error messages, and the count of the messages, by day.

With my structure

typedef struct {
char pString[5]; // to the message text
int count ;
 int date;
} MyNode;

If I use

int Node_compare(const void *MyNode1, const void *MyNode2) {
  MyNode * p1, *p2;
int rc;
 // check the dates
  rc = p1-> date - p2 -> date;
  if (rc == 0 )
 // check the string
rc = strcmp(p1->pString, p2->pString);
  return rc;

}

then the data will be sorted by date, then message within date order

If I use

int Node_compare(const void *MyNode1, const void *MyNode2) {
  MyNode * p1, *p2;
int rc;
 // check the string
rc = strcmp(p1->pString, p2->pString);
  if (rc == 0 )

  // check the dates
  rc = p1-> date - p2 -> date;
  
  return rc;

}

it will report in message sequence, then by date within message.

Adding nodes to the tree.

You need to malloc storage for each node you intend to add, because the node may be stored within the tree.

MyNode  * pTode;
/* allocate our Node - we cant use automatic storage as it may */
/* be added to the tree - so must not be deleted */
pNode = (MyNode *) malloc(sizeof(MyNode));
if (pNode == 0)
{
perror("Malloc for Node failed ");
return 8;
}

/* initialise it */
strcpy(&pNode->pString[0],"ABCD") ;
pNode -> date = date();
pNode->count = 1;

void * p;
p = (void *) pNode;

MyNode * qreturned;
/* see if it is in the tree already */
qreturned = *(MyNode **) tsearch(p, (void **) &ROOT,Node_compare);

if (qreturned == p)
{
/* it didnt exist before - thus it was added */
   /* possibly do something   */
     initialise the remained of the record
}

else /* it did exist before so we need to update the count field */
{
   qreturned->count += 1; Update the values
   /* release the storage we dont need */
free(pNode);
}

All data must be self contained within the node, or to permanently allocate storage (with malloc()). If it references something in automatic storage, then when the automatic storage is reused, your pointer will point to invalid data.

Print the tree

twalk(ROOT, print_Node); // ROOT not &ROOT because of no updates, and pass the function
//
// which invokes
//
void print_Node(const void *ptr, VISIT order, int level) {
 if (order == leaf || order == postorder) {
  MyNode p = *(MyNode**) ptr;
  printf("Msg %4.4s date %d count %d\n",
        p->pString, p->date,p-> count);
  // level is how far down the tree the node is. Root = 0
  }
}

Putting it together

I wanted to collect information from RACF records and be able to refer back to the records. I treated the records as char *

The essence of the code is

Compare

#define LRECORD 294
int compare207(const void *MyNode1, const void *MyNode2) {
  int rc = memcmp( ((char * ) Mynode1) + 1
             ((char * ) Mynode2) + 1,261)
return rc;
}

Add a record

void add207(char * pRACF) 
{
void * p = malloc(LRECORD);
 ...
memcpy(p ,pRACF,294);
qreturned = *(char **) tsearch(p, (void **) &ROOT207,
compare207);
if (qreturned == p)
{
// it was added
}
else /* it did exist before so we need to update the count field */
{
//it existed - so update the fields);
  // qreturned -> ....
}

Find a record

char * find207(char * pIn) 
{
char * * pOut;
pOut =*(char **) tfind( (const void *) pIn,
(void **)&ROOT207,
compare207);
return pOut;
}

Walk the tree and print a record

void tree207print(const void *in    , VISIT order, int level) { 
char * pBuffer = * (char **) in;

 if (order == leaf || order == postorder)
{
  printf("Data %s\n",pBbuffer -> .... );
 }
}
void twalk207() 
{
twalk(ROOT207, tree207print);
}

Parsing command line values

I wanted to pass multiple parameters to a z/OS batch program and parse the data. There are several different ways of doing it – what is the best way ?

This question is complicated by

Checking options

Processing command line options can mean a two stage process. Reading the command line, and then checking to ensure a valid combination of options have been specified.

If you have an option -debug with a value in range 0 to 3. You can either check the range as the option is processed, or have a separate section of checks once all the parameters have been passed. If there is no order requirement on the parameters you need to have separate code to check the parameters. If you can require order to the parameters, you might be able to have code “if -b is specified, then check -a has already been specified

I usually prefer a separate section of code at it makes the code clearer.

Command styles

On z/OS there are two styles of commands

def xx(abc) parm1(value) xyz

or the Unix way

-def -xx abc -parm1 -1 -a –value value1 -xyz.

Where you can have

  • short options “-a” and “-1”
  • long option with two “-“, as in “–value”,
  • “option value” as is “-xx abc”
  • “option and concatenated value” as in “-xyz”; option -x, value yz

I was interested in the “Unix way”.

  • One Unix way is to have single character option names like -a -A -B -0. This is easy to program – but it means the end user needs to lookup the option name every time as the options are not usually memorable.
  • Other platforms (but not z/OS) have parsing support for long names like – -userid value.
  • You can parse a string like ro,rw,name=value, where you have keyword=value using getsubopt.
  • I wrote a simple parser, and a table driven parser for when I had many options.

Defining the parameter string toJCL.

The traditional way of defining a parameter string in batch is EXEC PGM=MYPROG,PARM=’….’ but the parameter is limited in length.

I tend to use

// SET P1=COLIN.PKIICSF.C 
// SET P2="optional"
//S1 EXEC PGM=MYPROG,PARM='parms &P1 &P2'  

You can get round the parameter length limitation using

//ISTEST   EXEC PGM=CGEN,REGION=0M,PARMDD=MYPARMS 
//MYPARMS DD * 
/ 
 -detail 0 
 -debug 0 
 -log "COLINZZZ" 
 -cert d

Where the ‘/’ on its own delimits the C run time options from my program’s options.

The values are start in column 2 of the data. If it starts in column 1, the value is concatenated to the value in the previous line.

You can use JCL and System symbols

// EXPORT SYMLIST=(*) 
// SET LOG='LOG LOG' 
//ISTEST   EXEC PGM=CGEN,REGION=0M,PARMDD=MYPARMS 
//MYPARMS DD *,SYMBOLS=EXECSYS
/ 
 -log "COLINZZZ" 
 -log "&log"
 ...

This produced -log COLINZZZ -log “LOG LOG”

Parsing the data

C main programs have two parameters, a count of the number of parameter, and an array of null terminated strings.

You can process these

int main( int argc, char *argv??(??)) 
{ 
  int iArg; 
  for (iArg = 1;iArg< argc; iArg ++   ) 
  { 
    printf(".%s.\n",argv[iArg]); 
  } 
  return 0; 
} 

Running this job

//CPARMS   EXEC  CCPROC,PROG=PARMS 
//ISTEST   EXEC PGM=PARMS,REGION=0M,PARMDD=MYPARMS 
//MYPARMS DD * 
/ 
 -debug 0 
 -log "COLIN  ZZZ" 
 -cert 
 -ae colin@gmail.com 

gave

.-debug.                   
.0.                        
.-log.                     
.COLIN  ZZZ.               
.-cert.                    
.-ae.                      
.colin@gmail.com.          

and we can see the string “COLIN ZZZ” in double quotes was passed in as a single string.

Parsing with single character options

C has a routine getopt, for processing single character options like -a… and -1… (but not -name) for example

while ((opt = getopt(argc, argv, "ab:c:")) != -1) 
   { 
       switch (opt) { 
       case 'a': 
           printf("-a received\n"); 
           break; 
       case 'b': 
           printf("-b received \n"); 
           printf("optarg %d\n",optarg); 
           if (optarg) 
             printf("-b received value %s\n",optarg); 
           else 
             printf("-b optarg is0       \n"); 
           break; 
       case 'c': 
           printf("-c received\n"); 
           printf("optarg %d\n",optarg); 
           if (optarg) 
             printf("-c received value %s\n",optarg); 
           else 
             printf("-c optarg is0       \n"); 
           break; 
       default: /* '?' */ 
           printf("Unknown n"); 
     } 
   } 

The string “ab:c:” tells the getopt function that

  • -a is expected with no option
  • -b “:” says an option is expected
  • -c “:” says an option is expected

I could only get this running in a Unix environment or in a BPXBATCH job. In batch, I did not get the values after the option.

When I used

//BPX EXEC PGM=BPXBATCH,REGION=0M,
// PARM='PGM /u/tmp/zos/parm.so -a -b 1 -cc1 '

the output included

-b received value b1
-c received value c1

This shows that “-b v1” and “-cc1” are both acceptable forms of input.

Other platforms have a getopt_long function where you can pass in long names such as –value abc.

getsubopt to parse keyword=value

You can use getsubopt to process an argument string like “ro,rw,name=colinpaice”.

If you had an argument like “ro, rw, name=colinpaice” this is three strings and you would have to use getsubopt on each string!

You have code like

int main( int argc, char *argv??(??)) 
{ 
 enum { 
       RO_OPT = 0, 
       RW_OPT, 
       NAME_OPT 
   }; 
   char *const token[] = { 
       [RO_OPT]   = "ro", 
       [RW_OPT]   = "rw", 
       [NAME_OPT] = "name", 
       NULL 
   }; 
   char *subopts; 
   char *value; 

   subopts = argv[1]; 
 while (*subopts != '\0' && !errfnd) { 
   switch (getsubopt(&subopts, token, &value)) { 
     case RO_OPT: 
       printf("RO_OPT specified \n"); 
       break; 
     case RW_OPT: 
       printf("RW_OPT specified \n"); 
       break; 
     case NAME_OPT: 
       if (value == NULL) { 
          printf("Missing value for " 
                 "suboption '%s'\n", token[NAME_OPT]); 
           continue; 
       } 
       else 
         printf("NAME_OPT value:%s\n",value);
         break; 
    default: 
         printf("Option not found %s\n",value); 
         break; 
     }  // switch 
   } // while 
 }  

Within this is code

  • enum.. this defines constants RO_OPT = 0 RW_OP = 1 etc
  • char const * token defines a mapping from keywords “ro”,”rw” etc to the constants defined above
  • getsubopt(&subopts, token, &value) processes the string, passes the mapping, and the field to receive the value

This works, but was not trivial to program

It did not support name=”colin paice” with an imbedded blank in it.

My basic command line parser(101)

I have code

for (iArg = 1;iArg< argc; iArg ++   ) 
{ 
  // -cert is a keyword with no value it is present or not
  if (strcmp(argv[iArg],"-cert") == 0) 
  { 
    function_code = GENCERT    ; 
    continue; 
  } 
  else 
  //  debug needs an option
  if (strcmp(argv[iArg],"-debug") == 0 
      && iArg +1 < argc) // we have a value 
      { 
        iArg  ++; 
        debug = atoi(argv[iArg]); 
        continue; 
      } 
  else 
  ...
  else 
    printf("Unknown parameter or problem near parameter %s\n", 
           argv[iArg]);
  }   // for outer - parameters 

This logic processes keywords with no parameters such as -cert, and keyword which have a value such as -debug.

The code if (strcmp(argv[iArg],”-debug”) == 0 && iArg +1 < argc) checks to see if the keyword has been specified, and that there is a parameter following it (that is, we have not run off the end of the parameters).

Advanced – table – ize it

For a program with a large number of parameters I used a different approach. I created a table with option name, and pointer to the fields variable.

For example

getStr lookUpStr[] = { 
    {"-debug", &debug     }, 
    {"-type",  &type       }, 
    {(char *) -1,  0} 
   }; 

You then check each parameter against the list. To add a new option – you just update the table, with the new option, and the variable.

int main( int argc, char *argv??(??)) 
{ 
   char * debug = "Not specified"; 
   char * type   = "Not specified"; 
   typedef struct getStr 
   { 
      char * name; 
      char ** value; 
   } getStr; 
   getStr lookUpStr[] = { 
       {"-debug", &debug     }, 
       {"-type",  &type       }, 
       {(char *) -1,  0} 
      }; 
  int iArg; 
  for (iArg = 1;iArg< argc; iArg ++   ) 
  { 
   int found = 0; 
   getStr * pGetStr =&lookUpStr[0];
   // iterate over the options with string values
   for (; pGetStr -> name != (char *)  -1; pGetStr ++) 
   { 
     // look for the arguement in the table
     if (strcmp(pGetStr ->name, argv[iArg]) == 0) 
     { 
       found = 1; 
       iArg ++; 
       if (iArg < argc) // if there are enough parameters
                        // so save the pointer to the data
        *( pGetStr -> value)= argv[iArg] ; 
       else 
         printf("Missing value for %s\n", argv[iArg]);       
       break;  // skip the rest of the table
     }  // if (strcmp(pGetStr ->name, argv[iArg]) == 0) 
     if (found > 0) break; 
    } // for (; pGetStr -> name != (char *)  -1; pGetStr ++) 
   
   if (found == 0) 
   // iterate over the options with int values 
   ....
  } 
  printf("Debug %s\n",debug); 
  printf("Type  %s\n",type ); 
  return 0; 
}   

This can be extended so you have

getStr lookUpStr[] = { 
    {"-debug", &debug, "char" }, 
    {"-type",  &type ,"int"       }, 
    {(char *) -1,  0, 0} 
   }; 

and have logic like

if (strcmp(pGetStr ->name, argv[iArg]) == 0) 
     { 
       found = 1; 
       iArg ++; 
       if (iArg < argc) // if there are enough parmameters
       {
       if ((strcmp(pGetStr -> type, "char") == 0 
        *( pGetStr -> value)= argv[iArg] ; 
       else 
        if ((strcmp(pGetStr -> type, "int ") == 0 )
        *( pGetStr -> value)= atoi(argv[iArg]) ;
      ...   
     }

You can go further and have a function pointer

getStr lookUpStr[] = { 
    {"-debug", &debug,myint }, 
    {"-loop", &loop  ,myint },  
    {"-type",  &type , mystring  }, 
    {"-type",  &type , myspecial  }, 
    {(char *) -1,  0, 0} 
   };f

and you have a little function for each option. The function “myspecial(argv[iarg])” looked up values {“approved”, “rejected”…} etc and returned a number representation of the data.

This takes a bit more work to set up, but over all is cleaner and clearer.

What’s the date in ‘n’ days time?

I needed to see if a certificate is due to expire within “n” days. How do I find this date? It turns out to be pretty easy using standard C functions.

                                                                          
#include <stdio.h> 
#include <time.h> 
int main( int argc, char *argv??(??)) 
{ 
....
    char expireDate[11]; 
    time_t t1, t3; 
    struct tm *t2; 
    t1 = time(NULL); 
    t2 = localtime(&t1); 
    t2 -> tm_mday += 40 ; // 40 days from now 
    t3 = mktime(t2); 
    int s; 
    s=  strftime( expireDate, 11, "%Y/%m/%d", t2  ); 
    printf("====size  %d================\n",s); 
    printf(".%10.10s\n",expireDate); 

This successfully printed out the date 40 days, from now. The only little problem I had, was with strftime. The size of the output is 10 bytes. The “11” specifies the maximum number of characters that can be copied into the array. If this was 10… the size of the data I was expecting, The output was wrong “. 2023/06/1” ; off by one character in the buffer and a leading blank.!

With the technique of changing the value within a tm structure you can get the date-time n seconds / m minutes / d days/ from now either in the future – or in the past.

Clever stuff !