Getting into supervisor mode and other hard things in C, is easy.

Some functions in z/OS need a user to be in a privileged state such as key zero or supervisor state. Writing the code in assembler has been pretty easy, but writing it in a C program has been hard.

For example you could write a function in assembler, and call it. This has the cross language challenges.

I recently found an easy way – just reuse some code from Zowe. This is open source, so you need to follow the license

This program and the accompanying materials are made available under the terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html

SPDX-License-Identifier: EPL-2.0

Copyright Contributors to the Zowe Project.

The code uses __asm() Inline assembly statements (IBM extension).

The functions are

  • ddnameExists
  • wtoMessage
  • wtoPrintf3
  • atomicIncrement compare and swap
  • testauth
  • extractPSW
  • supervisorMode or back to problem state
  • setKey
  • getExternalSecurityManager
  • getCVT
  • getATCVT
  • getIEACSTBL
  • getCVTPrefix
  • getECVT
  • getTCB
  • getSTCB
  • getOTCB
  • getASCB
  • getASXB
  • getASSB
  • getJSAB
  • getCurrentACEE
  • getFirstChildTCB
  • getParentTCB
  • getNextSiblingTCB
  • resolveSymbolBySyscall Input: A symbol starting with & and not ending with .
  • resolveSymbol Input: A symbol starting with & and not ending with .
  • lots of saf functions see here
  • loadByName
  • getDSAB Data Set Association Block
  • isCallerLocked
  • isCallerCrossMemory

Some of these need to be APF protected, so although it is easy to use the above code, you may still need to get the load library APF authorised, and the code approved.


For example

Get into supervisor state

The code here.

int supervisorMode(int enable){
// Use MODESET macro for requests
int currentPSW = extractPSW();
if (enable){
if (currentPSW & PROBLEM_STATE){
__asm(ASM_PREFIX
" MODESET MODE=SUP \n"
:
:
:"r0","r1","r15");
return TRUE;
} else{
return FALSE; /* do nothing, tell caller no restore needed */
}
} else{
if (currentPSW & PROBLEM_STATE){
return TRUE; /* do nothing, tell user was in problem state */
} else{
__asm(ASM_PREFIX
" MODESET MODE=PROB \n"
:
:
:"r0","r1","r15");
return FALSE;
}
}
}

To compile it I used

// SET LOADLIB=COLIN.LOAD 
//DOCLG EXEC PROC=EDCCB,INFILE='ADCD.C.SOURCE(C)',
// CPARM='OPTF(DD:COPTS)'
//* CPARM='LIST,SSCOMM,SOURCE,LANGLVL(EXTENDED)'
//COMPILE.ASMLIB DD DISP=SHR,DSN=SYS1.MACLIB
//COMPILE.COPTS DD *
LIST,SOURCE
aggregate(offsethex) xref
SEARCH(//'ADCD.C.H',//'SYS1.SIEAHDR.H')
TEST
ASM
RENT ILP32 LO
OE
NOMARGINS EXPMAC SHOWINC XREF
LANGLVL(EXTENDED) sscom dll
DEFINE(_ALL_SOURCE)
DEBUG
/*
...

You need to specify

  • //COMPILE.ASMLIB for the the assembler macro libraries.
  • and the compiler option ASM which enables inlined assembly code inside C/C++ programs.

I was all so easy, once I had been told about it.

Using a cuckoo function

I was trying to track down a problem in Java where shared memory was not being accessed as it should. This was too early in the JVM startup for trace – so how to debug it?

One option I tried was using a cuckoo function. The Java code uses the C run time function shmget() — Get a shared memory segment. I wanted to write a stub function to display parameters, and then call the system version of shmget. To do this I had to get the Java program to use my cuckoo version of shmget.

My program

void wto( char * msg) { 
// time_t ltime;
// time(&ltime);
//printf(" the time is %s", ctime(&ltime));
struct __cons_msg cmsg;
int rc;
int cmsg_cmd = 0;
/* fill in the __cons_msg structure */
cmsg.__format.__f1.__msg = msg;
cmsg.__format.__f1.__msg_length = strlen(msg);
rc = __console(&cmsg,NULL,&cmsg_cmd);
}
// this has the same signature as the run time code
cpshmget(
key_t key, size_t size, int shmflg)

{
char buffer[200];
int i, j;
j = sprintf(buffer, ">shmget key %8.8x sz %i flg 0x%8.8x\0", key,size,shmflg);
wto(buffer);
int myresult = shmget(key,size,shmflg);
//sprintf(buffer,"<shmget1 r %i e %i e2 0x%8.8x\0",myresult, errno, __errno2());
//wto(buffer);
//myresult = shmget(key,size,shmflg);
return myresult;
}

I compiled it using a shell script (I found it easier than a make file)

name2=cp 
p1=" -DNDEBUG -O3 -qarch=10 -qlanglvl=extc99 -q64"
p2="-Wc,DLL -D_XOPEN_SOURCE_EXTENDED -D_POSIX_THREADS"
p2="-D_XOPEN_SOURCE_EXTENDED -D_POSIX_THREADS"
p3="-D_OPEN_SYS_FILE_EXT -qstrict "
p4="-Wa,asa,goff -qgonumber -qenum=int"
p5="-I."
p6=""
p7="-Wc,ASM,SHOWINC,ASMLIB(//'SYS1.MACLIB') "
p8="-Wc,LIST(c.lst),SOURCE,NOWARN64,XREF -Wa,LIST,RENT"
/bin/xlc $p1 $p2 $p3 $p4 $p5 $p6 $p7 $p8 -c $name2.c -o $name2.o

It is used as an object .o code – not as an executable module.

Bind it to the Java code

I wanted to cuckoo the module libj9prt29.so .

I saved a copy of the original module libj9prt29.so as libj9prt29.so.orig. I could then include this into the binder, and not worry about any of my old code staying around.

When I needed “a clean run”, without any of my code, I copied libj9prt29.so.orig over libj9prt29.so .

The bind job

//IBMBIND JOB 1,MSGCLASS=H 
// SET BPARM='CASE=MIXED,SIZE=(900K,124K),XREF,RMODE=ANY,CALL=YES'
// SET CPARM='MAP,DYNAM=DLL,LIST=ALL,AMODE=64,TERM=YES'
//BIND EXEC PGM=IEWL,REGION=0M,PARM='&CPARM,&BPARM'
//SYSLIB DD DSN=CEE.SCEEBND2,DISP=SHR
// DD DSN=CBC.SCCNOBJ,DISP=SHR
// DD DSN=SYS1.CSSLIB,DISP=SHR
//Z8921 DD DSN=CEE.SCEELIB(CELQS003),DISP=SHR
//SYSLMOD DD PATH='/usr/lpp/java/J17.0_64/lib/default/',
// PATHDISP=(KEEP,KEEP)
//SYSPRINT DD SYSOUT=*
//SYSLIN DD *
ORDER CELQSTRT
ENTRY CELQSTRT
INCLUDE /u/tmp/java/cp.o
change shmget(cpshmget)
INCLUDE /usr/lpp/java/J17.0_64/lib/default/libj9prt29.so.orig
include Z8921
name libj9prt29.so
//

Notes:

  • You specify SYSLMOD as a directory and specify the “name” later. This was not obvious from the documentation
  • INCLUDE /u/tmp/java/cp.o copies in my code
  • change shmget(cpshmget) says any code which is included after this, cuckoo the entry point shmget to point to cpshmget
  • INCLUDE /usr/lpp/java/J17.0_64/lib/default/libj9prt29.so.orig include the original module
  • include Z8921 = CEE.SCEELIB(CELQS003) contains statements like IMPORT CODE64,CELQV003,’shmget’,280 which defines where shmget is really defined
  • name libj9prt29.so save it as this name in //SYSLMOD

This worked well – but…

I originally had

  wto(buffer); 
int myresult = shmget(key,size,shmflg);
sprintf(buffer,"<shmget1 r %i e %i e2 0x%8.8x\0",myresult, errno, __errno2());
wto(buffer);
//myresult = shmget(key,size,shmflg);

But the wto function, which invokes __console(), sets errno and __errno2. The values passed back to Java were the values from the __console function, not the shmget function.

Issuing the myresult = shmget(key,size,shmflg); a second time set the errno and __errno2 – but if the request was to create, or delete, issuing it a second time gave a different error code. For example if “delete” was specified – the first time worked, the second time gave “not found”, and this confused the Java program calling this function.

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);
}