2

What function can I call from inside a C program, to discover what z/OS environment the program is being run in, e.g. is it z/OS UNIX (aka USS) or is it from TSO, say via JCL?

NicC
  • 293
  • 1
  • 6
Morag Hughson
  • 7,255
  • 15
  • 44
  • 2
    Use the LE callable service [CEE3INF](https://www.ibm.com/support/knowledgecenter/SSLTBW_2.4.0/com.ibm.zos.v2r4.ceea300/cee3inf.htm). – cschneid Mar 29 '20 at 13:01
  • Perfect! thank you. – Morag Hughson Mar 29 '20 at 22:48
  • @cschneid Is there something special about CEE3INF that it cannot run anywhere other than in the main function in C? It abends if I try to put the code in a function. – Morag Hughson Mar 30 '20 at 00:14
  • Nothing special I'm aware of. I don't have access to a z/OS machine so I cannot experiment right now, sorry. – cschneid Mar 30 '20 at 03:30
  • @cschneid, there is a line in Usage Notes in the doc which says "z/OS UNIX considerations — CEE3INF is allowed only in the thread' - which feels like an incomplete sentence, but I wonder if it is trying to say, "only in the main". Anyway, thanks for your pointer to this function. Even with this apparent restriction it is exactly what I needed. Such a shame you can't write your comment as an answer! – Morag Hughson Mar 30 '20 at 05:56

1 Answers1

6

There are two approaches: CEE3INF, and rummage through the z/OS data areas.

CEE3INF has the advantage of being documented and portable to any LE environment, as well as providing information about PIPI that you don't easily find in the z/OS structures.

As an alternative to CEE3INF, there's plenty of information in the system data areas if you just need to distinguish between Batch, TSO, CICS and whether or not you've been dubbed as a USS process. The alternative is easy, and it's especially helpful outside the LE environment...though it's even easy to do in C by just loading up some pointers that you can get by using the XLC DSECT to C-structure conversion utility.

A TSO address space is one where ASCBTSB is non-zero (PSAAOLD->ASCBTSB). A Batch job is one where ASCBJBNI is filled in (PSAAOLD->ASCBJBNI). A CICS address space has TCBCAUF set non-zero (PSATOLD->TCBCAUF).

In any of the above, you can also check whether your task has been dubbed as a UNIX process by checking TCB->STCB->STCBOTCB. If non-zero, you've been dubbed and can use UNIX Services. The OTCBPRLI field has process information like the PID, and THLI has thread-level information.

Note that a given task might be eligible to use USS functions, but it hasn't yet. The "querydub()" function can help you distinguish between a task that's already been dubbed, versus one that can be, but just hasn't been yet.

If you use CEE3INF, there have been some comments about it not working properly outside of the main() function, but I think the issue is a small bug in the sample IBM provides in their documentation. This sample works fine on my z/OS 2.3 and 2.4 systems:

#include <leawi.h>
#include <string.h>
#include <ceeedcct.h>

int do_call(void) 
{
 _INT4 sys_subsys,env_info,member_id,gpid;
 _FEEDBACK fc;
 CEE3INF(&sys_subsys,&env_info,&member_id,&gpid,&fc);
 if ( _FBCHECK(fc,CEE000) != 0 ) 
 {
   printf("CEE3INF failed with message number %d\n", fc.tok_msgno);
 }
 printf("System/Subsystem in hex %08x \n",sys_subsys);
 printf("Enviornment info in hex %08x \n",env_info);
 printf("Member languages in hex %08x \n",member_id);
 printf("GPID information in hex %08x \n",gpid);
 printf("\n");
}

int main(void)
{
   do_call();
}

This is the sample code from the IBM manual, except notice in the call to CEE3INF, the IBM doc has a bug ("...fc" instead of "...&fc"). There were comments about CEE3INF not working if called outside of main(), but I think the issue is simply the bug in the sample above.

To test, I compile the code above under the UNIX Services shell using this command:

xlc -o testinf testinf.c     

I then run the executable from a z/OS shell session:

> ./testinf                 
System/Subsystem in hex 02000002 
Enviornment info in hex 00540000 
Member languages in hex 10000000 
GPID information in hex 04020300 

This is a z/OS 2.3 system - I get identical results on 2.4.

UPDATE: What does "running in the z/OS UNIX Services environment" mean?

It's easy to understand batch jobs versus TSO sessions versus started tasks, but what's meant by "running in the z/OS UNIX Services environment"? In subsystems like CICS, IMS, or WebSphere "running under xxx" is easy to define because the transactions run inside a special type of service address space...but unfortunately, UNIX Services isn't like that.

Indeed, just about any task running on z/OS can make use of z/OS UNIX Services, so there really isn't a "z/OS UNIX Services environment" that you can define in a traditional way. A parallel would be VSAM...is a program that opens a VSAM file "running in VSAM?". We might care about programs running IDCAMS, programs opening VSAM files, programs using CICS/VSAM - but "running in VSAM" isn't particularly meaningful without further qualification. Plus, "running in VSAM" isn't exclusive with running as batch, STC or TSO user - it's the same with z/OS UNIX services - you can be a batch job, a started task or a TSO user, AND you can also be "running in z/OS UNIX Services" or not.

Here are three very different definitions of "running in z/OS UNIX Services":

  1. Whether the unit of work has been "dubbed" as a UNIX Services process and is therefore ready and able to request UNIX Services kernel functions.
  2. Whether the unit of work is running under a UNIX shell, such as /bin/sh.
  3. Whether an LE program is running with the POSIX(ON) runtime option.

Why would any of this matter? Well, some software - especially things like runtime library functions called by other applications - behaves differently depending on whether the caller is a UNIX process or not.

Imagine writing an "OPEN" function that gets passed a filename as an argument. If your caller is a UNIX process, you might interpret the filename as an actual filename...OPEN(XYZ) is interpreted as "check the current working directory for a file called 'XYZ'". But if the caller isn't dubbed as a UNIX process, then OPEN(XYZ) might mean to open the 'XYZ' DD statement. You can make this determination using the approach I outlined above, since it tells you that your task is in fact dubbed as a UNIX process.

Okay, but what's different between this and #2 above (running under the shell)?

Here's one example. Suppose you have a callable routine that wants to write a message to an output file. Most non-mainframe UNIX applications would simply write to STDOUT or STDERR, but this doesn't always work on z/OS because many applications are UNIX processes, but they aren't running under the shell - and without the shell, STDOUT and STDERR may not exist.

Here's the scenario...

You run a conventional program that has nothing to do with UNIX Services, but it does something to get itself dubbed as a UNIX process. Just as an example, maybe someone puts "DD PATH=/some/unix/file" in the JCL of an age-old COBOL program...miraculously, when this COBOL batch job runs, it's a UNIX process because it makes use of the UNIX Services filesystem. There are lots of things that can get your task dubbed as a UNIX process...DD PATH is one, but even calling a function that opens a TCP/IP socket or something similarly benign can do the trick. Maybe you're writing a vendor product that's just a batch assembler program, but it opens a TCP/IP socket...that's another common example of UNIX processes that run without a shell.

So why is this a problem? Well, think about what happens if that callable function decides to write it's messages to STDERR. Maybe it tests to see if it's running as a UNIX Services process, and if so it writes to STDERR, otherwise it dynamically allocates and writes to a SYSOUT file. Sounds simple, but it won't work for my example of an app having DD PATH.

Where does STDERR come from? Normally, the UNIX shell program sets it up - when you run a program under the shell, the shell typically passes your program three pre-opened file handles for STDIN, STDOUT and STDERR. Since there's no shell in my sample scenario, these file handles weren't passed to the application, so a write to STDERR is going to fail. In fact, there are many things that the shell passes to a child process besides STDIN/STDOUT/STDERR, such as environment variables, signal handling and so forth. (Certainly, the user can manually allocate STDIN/STDOUT/STDERR in his JCL...I'm not talking about that here).

If you want to have software that can handle both running under the shell and not running under the shell, you have more work to do than just seeing if your application has been dubbed as a UNIX process:

  • Check to see if you're a UNIX process...if not, you can't be running under the shell.
  • Check to see if you were launched by the shell. There are a variety of ways to do this, but generally you're checking your "parent process" or something like the environment variables you were passed. This isn't always easy to do, since there are actually many different shells on z/OS, so there's not much you can go on to spot the "legitimate" ones. One of the more bulletproof approaches is to get the login shell for the user and check for that.
  • As an alternative to checking the parent process, you can check for the resource you need directly, such as by calling ioctl() against the STDERR file handle as in my example. This, of course, can be dangerous...imagine the case where an application opens a few sockets and calls your function...what you think are really STDIN/STDOUT/STDERR could in fact be open file handles setup by your caller, and what you write could easily clobber his data.

As for my third example - LE programs running with POSIX(ON) - this is largely an issue for developers writing in high-level languages based on the LE runtime, since the behaviors of certain runtime functions are different with POSIX(ON) or POSIX(OFF).

An example is the C programmer writing a function that can be called by both POSIX(ON) and POSIX(OFF) callers. Let's say the function wants to do some background processing under a separate thread...in POSIX(ON) applications, the developer might use pthread_create(), but this won't work in POSIX(OFF). There are actually lots of things in IBM's LE runtime that behave differently depending on the POSIX setting: threads, signal handling, etc etc etc. If you hope to write "universal" code and you need these functions, you'll definitely need to query the POSIX setting at execution time and take different paths depending on how it's set.

So hopefully that sheds some light on the complexity hiding behind this question...three different definitions of "running in z/OS UNIX environment", and three different use-cases illustrating why each is important.

Valerie R
  • 1,769
  • 9
  • 29
  • If this works for @MoragHughson, I suggest you combine your two answers and (if Morag is amenable) she can accept that answer and I can delete mine. – cschneid Apr 10 '20 at 19:47
  • Thank you for following through on this! Yes, that was indeed it. I have corrected the same, and sure enough now it works just fine from inside a function. Thank you so much. I am happy accept your answer if you want to do the combination as @cschneid suggests. – Morag Hughson Apr 13 '20 at 10:51
  • Okay - combined my two answers and deleted the other one...glad to hear it's working for you. – Valerie R Apr 13 '20 at 16:16
  • Hi again! :-) How do I call CEE3INF if I'm compiling LP64? The chapter in KC says "The listed callable services are for AMODE 31 only" – Morag Hughson May 08 '20 at 00:59
  • I've been trying out the control block crawling you mentioned, and unfortunately a Batch job is not one where ASCBJBNI is filled in (PSAAOLD->ASCBJBNI). When running in USS, I also have a job name, so this does not help to detect the difference between USS, TSO and Batch. – Morag Hughson May 08 '20 at 05:19
  • @Morag: USS is independent of whether a unit of work is batch, STC or TSO...you can have batch jobs that are USS "dubbed", just as you can have a started task or TSO user that's using USS services. A USS process isn't some entirely different type of work in the system - it's just a batch/STC/TSO user that happens to be using UNIX functions. If you think of it as "a batch job using UNIX Services" or a "STC not using UNIX Services", rather than "USS is some special type of work"...this is a common misunderstanding. – Valerie R May 08 '20 at 10:33
  • @Morag: Just to ad more detail, if you submit a batch job that just runs a conventional application, it won't pass the USS test above. On the other hand, if you write yourself a short C program that calls, say getpid() or opens a TCP/IP socket or a UNIX filesystem file, then miraculously the batch job you submitted is now a UNIX process. You can read about it - it's called "dubbing", which generally means to connect a running task to the USS kernel. The usual definition of "am I USS?" means "am I dubbed and able to issue UNIX kernel calls?". – Valerie R May 08 '20 at 10:36
  • I am not looking to discover if my process is a USS dubbed process, I am looking to discover if it is running in the z/OS UNIX environment. CEE3INF could tell me that. Bit 6 of sys/subsys = "Currently executing in a z/OS® UNIX environment" – Morag Hughson May 08 '20 at 10:41
  • Any task can be dubbed as a UNIX process, so you'll have to better define what you mean by "running in the USS environment". I outlined the way to tell whether a particular task is a UNIX process. Since there's no such thing as a special "UNIX environment" on z/OS, your definition of "running in the z/OS UNIX environment" might mean "running under a UNIX shell", "logged in via SSH" or something different than "is the unit of work a UNIX process". I'm happy to explain how to do these tests too, if you can clarify what your definition of "z/OS UNIX environment" is. – Valerie R May 08 '20 at 10:53
  • I'm not sure what the difference would be between "running under a UNIX shell" and "logged in via SSH", but those things appear to both meet the criteria for Bit 6 of sys/subsys = "Currently executing in a z/OS® UNIX environment" that CEE3INF reported, and are exactly what I am after. – Morag Hughson May 08 '20 at 11:06
  • P.S. I am hoping that it is not MY definition of "running in a z/OS UNIX environment". I am copying that phrase straight out of the CEE3INF KC description. – Morag Hughson May 08 '20 at 11:07
  • Technically any application can be a USS process so a lot depends on what you're trying to detect and why. A "vanilla" COBOL program with DD PATH= in JCL is technically a UNIX process since it's accessing the UNIX filesystem...is that important to you? Consider a TSO user running a program under the ISPF ISHELL - all at once, it's a UNIX process, it's running the UNIX shell, and it's a TSO session. CEE3INF may very well just test for the LE "POSIX(ON)" runtime option, which is yet another interpretation of "running in a z/OS UNIX environment". Which to test for depends on why you need to know. – Valerie R May 08 '20 at 15:59
  • As I said, I'm not looking to discover if my process is a USS dubbed process, I'm looking for "running under a UNIX shell". My programs will be a UNIX process wherever it runs. Essentially I want to replicate the same discovery that bit 6 in CEE3INF gives me, since it is correctly able to distinguish between running in TSO, and running in Batch, and running in z/OS UNIX. Thank you for your extended answer, but you didn't really say how to detect #2 in your list? You mention env vars, but I can set env vars in Batch, so that doesn't help either? How do I get the "login shell"? – Morag Hughson May 10 '20 at 03:23
  • Testing shows CEE3INF bit 6 alone isn't always 100% accurate. I tried a simple example of a daemon process...it's started by the shell and has things like STDOUT/STDERR and environment variables, but it fails the bit 6 test you describe. Seems like CEE3INF is just getting the user's login shell program name with getpwent(), and then checking the parent process names using getppid() and w_getpsent()...if the login shell is in the parent process tree, you'll find it. Whether this is adequate depends on why you want to know, which you haven't shared. – Valerie R May 11 '20 at 16:18