My high level problem is that I want to extend a very large package to be able to have my own code process some file requests, such as fopen(), fread() and fclose(). I decided to map fopen() to cpfopen() and process the request that way.
The first step was easy; I changed fopen
#define fopen(a,b) cpfopen(a,b)
FILE * cpfopen(const char *__restrict__filename, const char *__restrict__mode);
I include that code in the package routines, and recompile them all.
The problem is that I want information used in the fopen function, to be available in the fclose function. I could change these routines not to return a FILE * handle, but return a COLINFILE , but this makes it very messy.
At first glance the use of C global storage looks as if it may work. However because I can open two files, opening a second file will overwrite information from the first file, I need to be able to keep status on each file.
I found a neat, self contained solution – the use of name tokens.
You can define a name token pair to z/OS, and you can retrieve the token by giving the name.
How does it work?
Define the token
I did this in my cpfopen routine using IEANTCR.
- The “name” field is 16 bytes long. You can specify any value for the identifying label. In the example below I used the value COLIN.
- The “token” field is 16 bytes long. You can specify any value to be stored under the label. In the example below I used the value MYVALUE.
#pragma linkage(IEANTCR ,OS)
...
char name[16];
char token[16];
memset(&name,0,sizeof(name));
memcpy(&name[0],"COLIN",5);
memset(&token[0],0,sizeof(token));
memcpy(&token[0],"MYVALUE",7);
int task = 1; // for TCB level
int persist = 0; // Not relevant when TCB or address space
int rc; // return code passed back
IEANTCR(&task,&name,&token,&persist,&rc);
printf("Return code from ieantcr %i\n",rc);
Retrieve the token
I did this in my cpfread routine using IEANTRT.
#pragma linkage(IEANTRT ,OS)
...
char name[16];
char token[16];
memset(&name,0,sizeof(name));
memcpy(&name[0],"COLIN",5);// this label
int task = 1;
int persist = 0;
int rc;
IEANTRT(&task,name,token,&rc);
printf("IEANTRT %i\n",rc);
if ( rc == 0)
{
printf("read token %7.7s\n",token);
}
When this code ran it printed out MYVALUE, as expected.
You can have name token pairs on each TCB, or name token pairs available for any TCB within the address space, (and authorised users can make name token pairs available across address spaces).
Each name token pair is chained off a TCB/Address space/system control block. If you have many name token pairs, the CPU needed to locate an entry will increase as the number of entries increases.
How to bind
The SYSLIB of the bind step needs SYS1.CSSLIB to find the stub for the name token code to work.
My actual code for the fopen etc
I wanted to pass the file name used in fopen, to the fread and fclose routines. I used the FILE * handle as the identifying label.
I wanted to pass the variable length file name, and not just a constant, so I needed to make a copy of the file name, and pass the address of the copy of the name in the token.
cpfopen
#pragma linkage(IEANTCR ,OS)
...
FILE * cpfopen(const char * filename, const char *mode)
{
printf("In cpfopen %s,%s\n",filename, mode);
FILE * handle;
handle = fopen(filename,mode);
if (handle > 0)
{
printf("handle %8.8x\n",handle);
char name[16];
char token[16];
memset(&name,0,sizeof(name));
memcpy(&name[0],handle,4);
char * dupname ;
dupname = strdup(filename); // save the file name
memset(&token[0],0,sizeof(token));
memcpy(&token[0],&dupname,4); // 31 bit
int task = 1;
int persist = 0;
int rc;
IEANTCR(&task,&name,&token,&persist,&rc);
printf("Return code from ieantcr %i\n",rc);
}
return handle;
}
cpfread
#pragma linkage(IEANTRT ,OS)
...
size_t cpfread(void * buffer, size_t size, size_t count, FILE * stream)
{
char name[16];
char token[16];
memset(&name,0,sizeof(name));
memcpy(&name[0],stream,4); // use the passed stream value( handle)
char * dupname;
int task = 1;
int persist = 0;
int rc;
IEANTRT(&task,name,token,&rc);
printf("IEANTRT %i\n",rc);
if ( rc == 0) // it was found
{
// copy from the token to my variable
memcpy(&dupname,&token[0],4);
printf("read file name %s\n",dupname);
if (dupname != 0)
printf("In Read: Value from NT is %s\n",dupname);
else printf("dupname is 0\n");
}
// Do the read file read
size_t n = fread(buffer, size, count, stream);
printf("Bytes read %i\n",n);
return n;
}
cpfclose
You need to free any storage you allocated, and delete the name token pair using IEANTDL.
#pragma linkage(IEANTRT ,OS)
...
size_t cpfclose(FILE * handle)
{
char name[16];
char token[16];
memset(&name,0,sizeof(name));
memcpy(&name[0],handle,4); // use the passed stream value( handle)
char * dupname;
int task = 1;
int persist = 0;
int rc;
IEANTRT(&task,name,token,&rc);
printf("IEANTRT %i\n",rc);
if ( rc == 0) // it was found
{
// copy from the token to my variable
memcpy(&dupname,&token[0],4);
printf("close file name %s\n",dupname);
free(dupname); // free the storage we allocated
// now delete the token
IEANTDL(&task,name,&rc);
if( rc != 0)
printf("Fclose: IEANTDL return code %i\n",rc) ;
}
// now the close
rc = fclose(handle);
return rc;
}