zOS Callable Services for Java developer

Pipes by Bill Smith

If you are looking into this article, I guess that you are developing some Java application running on the zOS or another MainFrame operating system and you realized that: “No, there really is no Java implementation for the function you need to call. No, there isn’t even an existing C wrapper. What the hell …” and this brought you to the exploration of the Callable Services. As far as I understand, the callable services are basically HLASM macros that the developers on the platform can leverage. I learned lot about the behavior and the implementation thanks to the code from https://github.com/zowe/keyring-utilities and help of Vit Tomica from Broadcom.

The case below is built around the need to map the public part of the user’s certificate to the user id stored in the ESM such as TopSecret(Broadcom) or RACF (IBM). As it turned out there is no Java wrapper, not even C wrapper and as such I needed to dig deeper into the area of Callable Services for SAF (System Authentication Facility).

Ultimately the research will lead you just as it led me to the web page with the documentation for the specific Callable service. In my case it was R_usermap macro (https://www.ibm.com/support/knowledgecenter/SSLTBW_2.4.0/com.ibm.zos.v2r4.ichd100/mapuser.htm). This is the key documentation that is needed for the integration of the Callable service.

Menu for R_usermap assembler macro in IBM website

At this moment I had all the key information to write simple C library to gain the parameters and call the Callable service with them.

Example

This is the full example that is possible to compile on the zOS. Don’t forget that by default you need to have the text encoded in the EBCDIC encoding. As an overview the code below loads the public certificate from the cert.der file in the directory of the script and prints the SAF return code, RACF return code, RACF reason code and the RACF user id if the certificate is mapped in the RACF to specific user id.

#pragma linkage(IRRSIM00, OS)
#include <stdio.h>
#include <stdlib.h>

typedef _Packed struct _R_datalib_parm_list_64 {
double workarea[128];
int saf_rc_ALET, return_code;
int racf_rc_ALET, RACF_return_code;
int racf_rsn_ALET, RACF_reason_code;
int fc_ALET;
short function_code;
int option_word;
char RACF_userid_len;
char RACF_userid[8];
int certificate_len;
char certificate[4096];
short application_id_len;
char application_id[246];
short distinguished_name_len;
char distinguished_name[246];
short registry_name_len;
char registry_name[255];

} R_datalib_parm_list_64;

int main() {
FILE *fileptr;
char *buffer;
long filelen;

fileptr = fopen("cert.der", "rb"); // Open the file in binary mode
fseek(fileptr, 0, SEEK_END); // Jump to the end of the file
filelen = ftell(fileptr); // Get the current byte offset in the file
rewind(fileptr); // Jump back to the beginning of the file

buffer = (char *)malloc(filelen * sizeof(char)); // Enough memory for the file
fread(buffer, filelen, 1, fileptr); // Read in the entire file
fclose(fileptr); // Close the file

R_datalib_parm_list_64 example;
memset(&example, 0, sizeof(R_datalib_parm_list_64));

example.certificate_len = filelen;
memcpy(example.certificate, buffer, filelen);

example.function_code = 0x0006;
int rc;

rc = IRRSIM00(
&example.workarea, // WORKAREA
&example.saf_rc_ALET , // ALET
&example.return_code,
&example.racf_rc_ALET,
&example.RACF_return_code,
&example.racf_rsn_ALET,
&example.RACF_reason_code,
&example.fc_ALET,
&example.function_code,
&example.option_word,
&example.RACF_userid_len,
&example.certificate_len,
&example.application_id_len,
&example.distinguished_name_len,
&example.registry_name_len
);
printf("RC: %d, SAF: %d, RACF: %d. Reason: %d\n", rc, example.return_code, example.RACF_return_code, example.RACF_reason_code);
printf("Application Id: %s \n", example.application_id);
printf("RACF User id: %s\n", example.RACF_userid);
}

To compile the previous code the simplest solution is to use c89 compiler and ask the compiler to respect the c99 norm for the compilation. There will be errors for comments otherwise.

c89 -W"c,langlv(STDC99)" -o usermap usermap.c

What do you need to know to write your own code?

First you need to link to the actual callable service. The value to use is in the brackets after the name. In the example case it is IRRSIM00. The pragma linkage imports the macro so that you can later call it. You need to swap IRRSIM00 for the name of the Macro you want to use.

#pragma linkage(IRRSIM00, OS)

The second key part is the structure defined in the beginning. This structure simplifies the memory layout for the parameters to be passed to the Macro. It’s probably important to mention here that with Assembler calls all that you actually provides are a locations in memory, therefore you need to be very precise with what memory you provide. Usually when using the structures in C the code memory will be aligned to block of I think 8 bytes in size. To prevent this the _Packed keyword is used

typedef _Packed struct

It is important to start with the allocation of the structure and zeroing the whole memory allocated to the structure. This gives you the block of memory that you can reasonably work with. Part of the memory are parameters to be used by the called macro and part are parameters to be used as a place for the output from the called macro.

R_datalib_parm_list_64 example;
memset(&example, 0, sizeof(R_datalib_parm_list_64));

Now take a look at the Parameters section of the documentation. This explains order and content of the specific parameters. An example is for example the RACF_userid parameter. The documentation tells us this:

RACF_userid
The name of a 9-byte area that consists of a 1-byte length field followed by up to 8 characters. It must be specified in uppercase. If not specified, the length must equal 0.

This tells us the requirements for the memory passed in the function. The memory starts with 1 byte (in 31 bit world represented by example by char) and continues with up to 8 characters representing the actual id. This is the representation in the structure and later on how it is passed to the actual macro. As you can see only the memory of the RACF_userid_len is passed. This is because only the pointer to the first byte of the memory is expected, then the HLASM looks through the memory as explained in the documentation.

    char RACF_userid_len; 
char RACF_userid[8];
&example.RACF_userid_len

The last step is to compile and run the code

c89 -W"c,langlv(STDC99)" -o usermap usermap.c
./usermap

Why did I use the c89 compiler instead of xlc compiler? The reason is simple as stated in the documentation (Requirements section) the macro expects the C code to be compiled by 31 bits compiler (AMODE : 31). For c89 this is default while for xlc you need to set it up properly.

Conclusion

The zOS provides serious amount of functionality only in the form of Callable services, which represents the HLASM macros. It is possible to build the C programs calling them, which you can later on incorporate via JNI into the Java program. With the guide above you should be now able to leverage callable services if the need arises.

I always try to find answers to the complex questions. Now exploring the world of Mainframes in the Broadcom inc.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store