Using dynamic allocation (SVC99) from a C program.

I wanted to monitor TCPIP, and wanted to create a SYSPRINT file for each interface. I did not know the names of the interface before I started, but by using dynamic allocation, I can have “a print file” for each interface. I can programmatically create the following JCL

//COLIN DD SYSOUT=A
or
//MEMBER DD DSN=COLIN.PDS(MEMBER1),DISP=SHR

The SVC 99 routine can print error messages, or you can use the z/OS module IEFZB476 to have have the messages passed back to you program to process. Using IEFZB476 was an extra challenge.

Which function do I use?

C run time provides

  • dynalloc(), where you have a structure which you use to point to your data. For example ip.__ddname = “ABCD “;
  • svc99(), where you create an array of definitions with a numeric code, such {0x0001,..”COLIN”}

I initially use the dynalloc() function, but this does not allow you to display error messages. You get back an error code which you have to look up. Internally it creates and use an SVC 99 parameter list. z/OS has a program IEFZB476 which you can use to produce messages from the error codes, and the SVC99 parameter list, but as you do not have access to the SVC 99 parameter list, you cannot use this print message program.

The SVC99 was not that much more difficult to use, and I would recommend this to other people.

The svc99() can display error messages as WTO, or WTL, and you can pass the SVC99 parameter list to IEFZB476 to print or return the messages.

Basic dynamic allocation program.

You can create data sets, or mimic what JCL does, by associating a data set or file to a //DDNAME.

I’ll just set up output to the “printer” to illustrate the concepts. (//COLIN DD SYSOUT=A)

You pass a set of parameters to SVC 99 with the following format

  • A code to identify what this parameter is for. For example (see here for the list, and here for JCL -> value mapping)
    • 0X0001 (DALDDNAM) is to specify the DDNAME
    • 0x0018 (DALSYSOU) is for SYSOUT
  • A count of the options ( I set this to one)
  • The length of the data, for a string “COLIN” use the value 5.
  • The value of the data.
    • For SYSOUT, the data is the class, ‘A’, this has length 1
    • For DDNAME the data is “COLIN” and has a length of 5.

I set up some macros to make it easier to create these structures

#define SVC99char1(name, code,v)\     
struct{\
short key;\
short count;\
short length;\
char value[sizeof(v)]; \
} name = {code,1,1,v};
#define SVC99string(name, code,v)\
struct{\
short key;\
short count;\
short length;\
char value[sizeof(v)]; // this has a terminating null
} name = {code,1,sizeof(v)-1,v};

and used to create the structures. I then stored them in the array.

//  create them 
SVC99char1( sysoutClass, DALSYSOU,'A')
SVC99string(ddname,DALDDNAM,"COLIN")

//and use them. Create an array to contain the addresses
void *tp[2]= { /* array of text pointers */
&sysoutClass,
&ddname
};

The last entry needs the top bit set on. You can do what the documentation says

#define MASK 0x80000000 
...
void *tp[2]= { /* array of text pointers */
&sysoutClass,
&ddname};
tp[1] = (char *)((int unsigned) (tp[1]) | MASK); // set on top bit

The complication is that the documentation says

The __S99parms structure must be in 31-bit addressable storage. A call to svc99() with 64-bit addressable storage will result in -1 return code.

Which makes this harder.

I could not get this to work when running in a 64 bit program. I found it easier to create a null entry with the top bit turned on.

void * __ptr32  tp[3];   
...
int mask = 0x80000000;
memcpy(&p->tp[2],&mask,4);

You need an extension request block to get messages printed out on the job log, or returned to your program.

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

The _S99EOPTS controls what happens with the error messages:

  • X’80’ ISSUE MSG BEFORE RETURNING TO CALLER
  • X’40’ RETURN MSG TO CALLER
  • X’20’ USER STORAGE SHOULD BE BELOW 16M BOUNDARY
  • X’10’ USER SPECIFIED STORAGE KEY FOR MESSAGE BLOCKS
  • X’08’ USER SPECIFIED SUBPOOL FOR MESSAGE BLOCKS
  • X’04’ USE WTO FOR MESSAGE OUTPUT

Originally I used X’80’ + x’04’ to get the messages displayed.

I had to add X’40’ to get the messages back so I could use them as input to IEFZB476.

Define and initialise the SVC99 parmlist

#include <stdio.h>  // this contains the SVC99 parameter list
struct __S99struc parmlist;
memset(&parmlist, 0, sizeof(parmlist));
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 defined above
parmlist.__S99S99X =&rbx; // pointer to extension
int rc;
rc = svc99(&parmlist);
//if (rc != 0)
printf("S99ERROR = %hu %4.4x S99INFO = %hu %4.4x \n",
parmlist.__S99ERROR,
parmlist.__S99ERROR,
parmlist.__S99INFO,
parmlist.__S99INFO);

__S99FLAG1 is described here. You can specify things like

  • Do not use an existing allocation to satisfy this request.
  • Do use an existing allocation to satisfy this request.
  • Requests that no messages be issued for this dynamic allocation.
  • Disable symbolic substitution for the current request.

You pass a pointer to the array of the addresses of the definitions. The last address has to have the top bit to indicate the last element.

I found it easiest to get the svc99 to print the error messages than trying to find them in the documentation. When I cause an error I got out in the joblog

-STEPNAME PROCSTEP    RC   EXCP   CONN       TCB       SRB  CLOCK   
-COMPILE COMPILE 00 14633 0 .03 .00 .0
-COMPILE BIND 00 325 0 .00 .00 .0
IKJ56893I DATA SET COLIN.JCL2 NOT ALLOCATED+
IGD17101I DATA SET COLIN.JCL2 107
NOT DEFINED BECAUSE DUPLICATE NAME EXISTS IN CATALOG
RETURN CODE IS 8 REASON CODE IS 38 IGG0CLEH

If the svc 99 gave return code 0, I could use the following to write some data to the file.

FILE * hFile; 
hFile= fopen("DD:COLIN","w");
if (hFile == 0) perror("hFile is zero");
else
fprintf(hFile,"colins output\n");

Processing the error messages using IEFZB476.

You need to specify __S99EOPTS with bit 0x40 set. This tells SVC99 to return the errors in the extension.

The number of messages returned is defined in rbx.__S99ENMSG. If this value is zero, there are no messages to display.

Create the control data

  struct { 
char EMFUNCT;
#define EMWTP 0x40 //WTO
char EMIDNUM ;
char EMNMSGBK ;
char EMRSV02 ;
char * EMS99RBP ; // Address of the failing SVC 99 RB
int EMRETCOD; // The SVC 99 return code
char * EMCPPLP; // used with tso putline
char * EMBUFP ; // Address of message buffers
int EMRSV03;
char * EMWTPCDP;
} EMPARMS;
memset(&EMPARMS,0,sizeof(EMPARMS));
EMPARMS.EMFUNCT = EMWTP ; // write to programmer
EMPARMS.EMIDNUM = 50; // EMSVC99 General caller with a DYNALLOC error
EMPARMS.EMNMSGBK= rbx.__S99ENMSG;
EMPARMS.EMS99RBP= (char *) &parmlist;
//EMPARMS.EMDAPLP Not used because we are svc 99
EMPARMS.EMRETCOD= rc;

You need to fetch the program and execute it

typedef int (*funcPtr)(); // define  a pointer type to point to a fetched module
funcPtr pIZFZB;
// Fetch the module and set the address in pIZFZB
pIZFZB = (funcPtr) fetch("IEFDB476"); /* load module */
if (pIZFZB == NULL) {
PERROR("ERROR: fetch failed\n");
return;
}
int rc2;
// and invoke it
rc2 = (*pIZFZB )(&EMPARMS);

printf("RC2 from format message, %i\n",rc2);
printf("rbx __S99EERR %hi, __S99EINFO, %hi\n", rbx.__S99EERR,rbx.__S99EINFO);
printf("__S99ERCO %2.2x __S99ERCF %2.2x\n", rbx.__S99ERCO,rbx.__S99ERCF);

3 thoughts on “Using dynamic allocation (SVC99) from a C program.

  1. Since you are using a 64-bit model, make 2 changes to get the MASK working.

    1. #define MASK 0x8000000000000000
    2. tp[1] = (char *)((unsigned long) (tp[1]) | (unsigned long) MASK); // set on top bit

    The way you coded it, bit 32 was getting the X’80’ bit, not bit 0. 

    Like

    1. Hi Todd,
      Thanks for your comments. The Svc99 parameter list must be in 31 bit… the doc says
      The __S99parms structure must be in 31-bit addressable storage. A call to svc99() with 64-bit
      addressable storage will result in -1 return code.

      I used memcpy to move the mask in, and this worked

      Colin

      Like

Leave a comment