Python

Python

Python

 View Only

Walking Control Blocks with Python

By Steven Pitman posted 23 hours ago

  

On z/OS, control blocks are used to keep track of the status of the system or applications as it is running. Theres a few different types of control blocks that are found on z/OS: 

  • System related control blocks 
  • Resource related control blocks 
  • Job related control blocks 
  • Task related control blocks 

Some examples of fields contained within control blocks are storing the systems name, a job name, number of CPU’s active, or if subsystems like RACF are active. But more importantly, each control block can store a pointer to other control blocks, which store more information. In this blog we’re going to go over two examples of walking through the control blocks to find information that we’re interested in using IBM Open Enterprise SDK for Python. Since this is commonly done in other languages like REXX, an equivalent program will also be given. 

 

Both examples use the facilities within the ctypes package that permit you to peek at system memory locations through the lens of specific data types. This allows the inspection of, and ultimately the walking of control block structures. 

 

Example 1: Getting the z/OS Version 

This first example is going to walk through the system control blocks (PSA -> CVT -> ECVT) to get information about your system, specifically the version of z/OS that is currently running. You can think of the CVT control block as one that points to many other control blocks – and the one we’re interested in is ECVT, which contains two fields: ECVTPVER (z/OS version) and ECVTPREL (z/OS release).  

 

Information regarding the location and the fields provided in the control blocks for this demo can be found here: PSA, CVT and ECVT. First, the Python script to get this information: 

import ctypes 

if __name__ == '__main__': 
    PSA = 0  # PSA Is always located at memory address 0 
    CVT = ctypes.c_int.from_address(PSA + 16) # CVT Is located at PSA + 16
    ECVT = ctypes.c_int.from_address(CVT.value + 140)  # ECVT is located at CVT+140 

    # Both version and release are two bytes. They're encoded in EBCDIC 
    # however so we'll need to decode them to print them out 
    product_version = ctypes.string_at(ECVT.value + 512, 2).decode('cp1047') 
    product_release = ctypes.string_at(ECVT.value + 514, 2).decode('cp1047') 

    print('z/OS Version {} Release {}'.format(product_version, product_release))

 

With ctypes, we can specify the equivalent C type that we are interested in (integer, characters, floats, …) and where it is in memory. For example, in the above, we have the following two statements: 

  • ctypes.c_int.from_address(PSA + 16) – This gets an integer (4 bytes) located at memory address PSA + 16. In this case, this integer is a pointer to the ECVT control block 
  • ctypes.string_at(ECVT.value + 512, 2) – This gets a character string (2 bytes) located at the memory address ECVT + 512 

The one thing to note however is that because Python’s default string data type is UTF-8, and the control blocks are generally encoded as EBCDIC, we’ll need to convert them to UTF-8 after reading them. Running this program will generate different output depending on your z/OS version, but on z/OS 2.5 it will output the following: 

z/OS Version 02 Release 05 

 

For those who are more familiar with REXX, here’s a roughly equivalent REXX application: 

/* REXX */ 
PSA = 0 
CVT = C2D(STORAGE(D2X(PSA + 16), 4)) 
ECVT = C2D(STORAGE(D2X(CVT + 140), 4)) 
ECVTPVER = STORAGE(D2X(ECVT + 512), 2) 
ECVTPREL = STORAGE(D2X(ECVT + 514), 2) 

say 'z/OS Version' ECVTPVER 'Release' ECVTPREL 

 

 

Example 2: Getting the current Jobname 

This second example is going to be looking through Job related control blocks and get what the current job name that is being executed. To do this, we’ll be looking through at the control block PSA -> ASCB for the field ASCBJBNI, which is the current job name being executed. The PSA control block contains information regarding basic information about scheduling processes z/OS, and pointers to other similar control blocks like ASCB. We have the following Python script to get the current jobname: 

import ctypes

if __name__ == '__main__': 
    PSA = 0 
    PSAAOLD = ctypes.c_int.from_address(PSA + 548)
    ASCBJBNIPTR = ctypes.c_int.from_address(PSAAOLD.value + 172) 
    ASCBJBNI = ctypes.string_at(ASCBJBNIPTR.value, 8).decode('cp1047') 

    print('Jobname: {}'.format(ASCBJBNI)) 

 

PSAAOLD Points to the current control block ASCB. Since PSA always starts at memory address 0, we can skip finding PSA and look directly for PSAAOLD (ASCB), which is located at PSA + 548.  

Running this Python script will output the following for me: 

Jobname: PITMAN6 

 

However, this will be different for you based on if you’re running it directly from Unix System Services or from Batch and based on how you name your job. Like the previous example, here is a REXX script that is roughly equivalent to our Python script: 

/* REXX */ 
PSA = 0 
PSAAOLD = C2D(STORAGE(D2X(PSA + 548), 4)) 
ASCBJBNIPTR = C2D(STORAGE(D2X(PSAAOLD + 172), 4)) 
ASCBJBNI = STORAGE(D2X(ASCBJBNIPTR), 8) 

say 'Jobname:' ASCBJBNI 

 

And that’s all you need to be able to walk the control blocks from Python.  

0 comments
14 views

Permalink