Retrieve the subsystem your job is currently in via the undocumented QMNSBS API.
This article is the first of a series of articles to memorialize Simon Coulter, an outstanding IBM i expert who contributed so much to the prosperity of the IBM i platform. Loved by many developers from the IBM i community, he was one of the giants in the industry, on whose shoulders others could stand and reach for new heights. I deeply appreciate Gwen Hanna, Simon's partner, for providing the biography of Simon at the end of this article.
In IBM i, every single piece of work is performed in a job. All jobs except system jobs must run in a subsystem. A subsystem is a single, predefined operating environment through which the system coordinates the work flow and resource use, via either processor resources or storage resources. A job enters a subsystem when a user either performs a Submit Job (SBMJOB) command or signs on to a display station successfully. It's also possible for an active job to transfer itself to another subsystem via the Transfer Job (TFRJOB) command or the Transfer Batch Job (TFRBCHJOB) command.
As an experienced IBM i developer, have you ever wondered how to retrieve the name of the current subsystem a job is running in? A similar question was raised in a post in the midrange-l mailing list at midrange.com back in 2000: Command To Check Current Subsystem. The following is extracted from the original post:
Command To Check Current Subsystem
* Subject: Command To Check Current Subsystem
* From: Joe Giusto <jgiusto@xxxxxxxxxxxx>;
* Date: Tue, 11 Jul 2000 07:49:19 -0400
Is there a CL command that I can use to determine the current subsystem that a job is running in?
I want to use it to change subsystems via TFRJOB in the initial signon program. The problem I am having is when I do the TFRJOB my initial program starts again and I go into a loop. If I could check the current subsystem, I can skip the TFRJOB statement on the second pass.
Several people offered advice.
Problem: The Retrieve Job Attributes (RTVJOBA) command is not able to return the current subsystem name.
Solution 1: Retrieve the current subsystem name via CL commands and custom programs. Use these three steps:
- Write job status information of the current job returned by the Work Job (WRKJOB) command or Display Job (DSPJOB) command to a spooled file. For example, DSPJOB JOB(*) OUTPUT(*PRINT) OPTION(*STSA).
- Copy the contents of the resulting spooled file to a database file. For example, CRTPF QTEMP/SOME_PF RCDLEN(132) and then CPYSPLF FILE(QPDSPJOB) TOFILE(QTEMP/SOME_PF) JOB(*) SPLNBR(*LAST).
- Look for the subsystem name in the resulting database file. For example, the subsystem name is position 56 of the 14th record at V5R4. See Figure 1: Output of DSPJOB OUTPUT(*PRINT) OPTION(*STSA).
Line ....+....1....+....2....+....3....+....4....+....5....+....6.... SOME_PF 000003 Job Status Attributes 000004 Status of job . . . . . . . . . . . . . . . : ACTIVE 000005 Current user profile . . . . . . . . . . . . : LJL 000006 Job user identity . . . . . . . . . . . . . : LJL 000007 Set by . . . . . . . . . . . . . . . . . . : *DEFAULT 000008 Entered system: 000009 Date . . . . . . . . . . . . . . . . . . . : 12/04/28 000010 Time . . . . . . . . . . . . . . . . . . . : 15:21:04 000011 Started: 000012 Date . . . . . . . . . . . . . . . . . . . : 12/04/28 000013 Time . . . . . . . . . . . . . . . . . . . : 15:21:04 000014 Subsystem . . . . . . . . . . . . . . . . . : QINTER 000015 Subsystem pool ID . . . . . . . . . . . . : 2 000016 Type of job . . . . . . . . . . . . . . . . : INTER 000017 Special environment . . . . . . . . . . . . : *NONE 000018 Program return code . . . . . . . . . . . . : 0
|
Figure 1: Output of DSPJOB OUTPUT(*PRINT) OPTION(*STSA)
Solution 2: Retrieve the current subsystem name using the Retrieve Job Information (QUSRJOBI) API with formats JOBI0200 or JOBI0600.
Although the DSPJOB solution (solution 1) can achieve the goal, it requires some trivial programming. Additionally, the DSPJOB solution obviously doesn't fit for conditions where performance is critical. Using the QUSRJOBI API is much simpler; however, as an API that returns a large number of detailed job attributes, it's still too heavy. See the Efficiency Tests section below for the performance statistics of QUSRJOBI.
Finally, Simon ended this discussion thread with a perfect solution:
- An undocumented API QMNSBS returns the subsystem description (SBSD) name of the current subsystem. This API also returns the SBSD name and library of the current controlling subsystem.
- Use a CL command to wrap the invocation of the QMNSBS API so that CL programs can use the CL command to retrieve the current subsystem name conveniently.
The following is extracted from Simon's original post:
Here is a Q&D command I wrote years ago to do that. It uses an IBM program as its CPP but that program is a user domain program so you shouldn't have problems. It is easier to use than the API
suggestions. I haven't used it recently so caveat emptor.
/*‚***************************************************************€*/
/*‚ Title :- QMNSBS €*/
/*‚ CPP :- QMNSBS €*/
/*‚ Function :- To return the name of the current subsystem, the €*/
/*‚ name of the controlling subsystem and the library €*/
/*‚ the controlling subsystem description is in. €*/
/*‚ €*/
/*‚ Notes:- Command processing program QMNSBS is not a supported €*/
/*‚ user interface. It is subject to change without notic€*/
/*‚ €*/
/*‚ Author :- S.H.Coulter Modified :- €*/
/*‚ Date :- 30/03/90 Date :- €*/
/*‚ €*/
/*‚***************************************************************€*/
CMD PROMPT('Retrieve Subsystem Information')
PARM KWD(SBS) TYPE(*CHAR) LEN(10) RTNVAL(*YES) +
MIN(1) CHOICE('Character value') +
PROMPT('CL var for SBS (10)')
PARM KWD(CTLSBSD) TYPE(*CHAR) LEN(10) RTNVAL(*YES) +
MIN(1) CHOICE('Character value') +
PROMPT('CL var for CTLSBSD (10)')
PARM KWD(CTLSBSDLIB) TYPE(*CHAR) LEN(10) +
RTNVAL(*YES) MIN(1) CHOICE('Character +
value') PROMPT('CL var for CTLSBSDLIB +
(10)')
The QMNSBS API is a User Domain/System State (UDSS) program that has three parameters. The source of the CL command provided by Simon reveals the meaning of the parameters of the QMNSBS API:
- CHAR(10)SBSD name of the current subsystem
- CHAR(10)SBSD name of the current controlling subsystem
- CHAR(10)name of the library where the SBSD of the current controlling subsystem resides
Obviously, one of the advantages of the QMNSBS solution is that it is so simple that all High-Level Language (HLL) programs can utilize it with minimum coding efforts. Also, as you will see in this article, it is also the most efficient way to achieve the goal of retrieving the current subsystem.
The QMNSBS API also returns the SBSD name and library of the current controlling subsystem in its second and third parameters. The controlling subsystem is the interactive subsystem that starts automatically when the system starts, and it is the subsystem through which the system operator controls the system via the system console. Only one controlling subsystem can be active on the system at any time. The current controlling subsystem is identified in the Controlling subsystem/library (QCTLSBSD) system value. Changes to the QCTLSBSD system value take effect at the next IPL. The controlling subsystem is important for the system to enter or leave the restricted condition, within which commands such as Save System (SAVSYS) or Reclaim Storage (RCLSTG) are allowed to run. (See Place the system in restricted state for detailed explanations on the restricted condition).
Since changes to the QCTLSBSD system value take effect at the next IPL, to retrieve the exact current controlling subsystem, you should use the QMNSBS API instead of retrieving the setting of the QCTLSBSD system value.
Tests
Now let's test the undocumented QMNSBS API for correctness and efficiency via a couple of simple tests.
Correctness Tests
One of the outstanding features of IBM i's work management functionality is that a job can transfer itself to another subsystem via the Transfer Job commands (TFRJOB, TFRBCHJOB) in order to change the job's work flow or request more or less processor resources and/or storage resources. To prove the correctness of QMNSBS, I'd like to provide a test case in which work is divided into three phases. Different phases of the work are done by the same job in different subsystems. The job transfers itself from one subsystem to another via the TFRJOB command. After the job enters a new subsystem, the current subsystem name is retrieved by calling the QMNSBS API and is then reported to the QSYSOPR message queue. A data queue object called SEASONS is created for the job to receive notifications such as initiating the work flow. The work flow is shown in the table below.
Work Flow of Multiple-Phase Work |
||
Phase A in SBS SUMMER |
Phase B in SBS AUTUMN |
Phase C in SBS WINTER |
Program A |
|
|
Dequeue SEASONS |
|
|
Report current SBS |
|
|
Move to phase B |
|
|
|
Program B |
|
|
Report current SBS |
|
|
Move to phase C |
|
|
|
Program C |
|
|
Report current SBS |
|
|
Move back to phase A |
The following CL commands prepare the test subsystem descriptions and data queue SEASONS:
/* Subsystem SUMMER */ CRTSBSD SBSD(SUMMER) POOLS((1 *BASE)) CRTCLS CLS(SUMMER) RUNPTY(10) /* [1] */ CRTJOBQ JOBQ(SUMMER) ADDJOBQE SBSD(SUMMER) JOBQ(SUMMER) MAXACT(1) ADDRTGE SBSD(SUMMER) SEQNBR(9999) CMPVAL(*ANY) PGM(QSYS/QCMD) CLS(*SBSD) /* [2] */ /* Subsystem AUTUMN */ CRTSBSD SBSD(AUTUMN) POOLS((1 *BASE)) CRTCLS CLS(AUTUMN) CRTJOBQ JOBQ(AUTUMN) ADDJOBQE SBSD(AUTUMN) JOBQ(AUTUMN) ADDRTGE SBSD(AUTUMN) SEQNBR(9999) CMPVAL(*ANY) PGM(QSYS/QCMD) /* Subsystem WINTER */ CRTSBSD SBSD(WINTER) POOLS((1 *BASE)) CRTCLS CLS(WINTER) CRTJOBQ JOBQ(WINTER) ADDJOBQE SBSD(WINTER) JOBQ(WINTER) ADDRTGE SBSD(WINTER) SEQNBR(9999) CMPVAL(*ANY) PGM(QSYS/QCMD) /* Data queue SEASONS: FIFO, max message length = 10 */ CRTDTAQ DTAQ(SEASONS) MAXLEN(10) |
[1] Specify the run priority value of class object SUMMER to 10, which is higher than the default run priority value of interactive jobs.
[2] Jobs routed through this default routing entry of subsystem SUMMER will execute the CL command specified in the Request data or command (RQSDTA) parameter of a TFRJOB command via QSYS/QCMD. The job will run at the priority specified in class object SUMMER.
Here's the source of the test programs:
PGM DCL VAR(&NTFMSG) TYPE(*CHAR) LEN(10) DCL VAR(&MSG) TYPE(*CHAR) LEN(80) DCL VAR(&SBS) TYPE(*CHAR) LEN(10) DCL VAR(&RUNPTY) TYPE(*DEC) LEN(2 0) DCL VAR(&PKDATTR) TYPE(*CHAR) LEN(7) + VALUE(X'03000200000000') DCL VAR(&ZNDATTR) TYPE(*CHAR) LEN(7) + VALUE(X'02000200000000') DCL VAR(&PTY) TYPE(*CHAR) LEN(2)
LOOP: CALL PGM(QRCVDTAQ) PARM('SEASONS' '*LIBL' + X'00010F' &NTFMSG X'00001D') IF COND(&NTFMSG *EQ 'SEEYOU') THEN(GOTO + CMDLBL(SEEYOU))
CALL PGM(QMNSBS) PARM(&SBS '' '') /* Retrieve the + current subsystem */ RTVJOBA RUNPTY(&RUNPTY) CALLPRC PRC('_LBCPYNV') PARM((&PTY) (&ZNDATTR) + (&RUNPTY) (&PKDATTR)) CHGVAR VAR(&MSG) VALUE('The whole' *BCAT &SBS *BCAT + 'was hot and busy. We''ve been running + at priority' *BCAT &PTY) /* Report + returned current SBS to QSYSOPR */ SNDMSG MSG(&MSG) TOUSR(*SYSOPR)
TFRJOB JOBQ(AUTUMN) RTGDTA(HELLO_AUTUMN) + RQSDTA('CALL PGM_B') /* Move to the next + phase */
GOTO CMDLBL(LOOP) SEEYOU: ENDPGM |
The _LBCPYNV system built-in (the bound program access interface of MI instruction CPYNV) is used in this program to convert Packed(2, 0) variable &RUNPTY to Zoned(2, 0). Examples of using _LBCPYNV in other ILE HLLs, such as RPG and COBOL, are available here.
PGM DCL VAR(&MSG) TYPE(*CHAR) LEN(80) DCL VAR(&SBS) TYPE(*CHAR) LEN(10)
CALL PGM(QMNSBS) PARM(&SBS '' '') /* Retrieve the + current subsystem */ CHGVAR VAR(&MSG) VALUE(&SBS *BCAT 'is lovely here') + /* Report returned current SBS to QSYSOPR */ SNDMSG MSG(&MSG) TOUSR(*SYSOPR)
TFRJOB JOBQ(WINTER) RTGDTA(HELLO_WINTER) + RQSDTA('CALL PGM_C') /* Move to the next + phase */ SEEYOU: ENDPGM |
PGM DCL VAR(&MSG) TYPE(*CHAR) LEN(80) DCL VAR(&SBS) TYPE(*CHAR) LEN(10)
CALL PGM(QMNSBS) PARM(&SBS '' '') /* Retrieve the + current subsystem */ CHGVAR VAR(&MSG) VALUE('Teddy bear doesn''t + hibernate in' *BCAT &SBS) /* Report + returned current SBS to QSYSOPR */ SNDMSG MSG(&MSG) TOUSR(*SYSOPR)
TFRJOB JOBQ(SUMMER) RTGDTA(GOHOME) RQSDTA('CALL + PGM_A') /* Move to the next phase */ SEEYOU: ENDPGM |
First, start all three subsystems and submit a job called A_YEAR to subsystem SUMMER via job queue SUMMER:
STRSBS SUMMER STRSBS AUTUMN STRSBS WINTER SBMJOB CMD(CALL PGM(PGM_A)) JOBQ(SUMMER) JOB(A_YEAR) |
Enqueue a message to data queue SEASONS to initiate the work flow.
CALL PGM(QSNDDTAQ) PARM('SEASONS' /* Data queue name */ '*LIBL' /* Data queue library */ X'00010F' /* Packed(5,0) message length */ 'Go') /* Message text */ |
After the three-phase work has been done, messages sent to the QSYSOPR message queue would look like the following:
From . . . : LJL 12/05/04 09:23:26 The whole SUMMER was hot and busy. We've been running at priority 10 From . . . : LJL 12/05/04 09:23:26 AUTUMN is lovely here From . . . : LJL 12/05/04 09:23:26 Teddy bear doesn't hibernate in WINTER |
Job A_YEAR is moved back again to subsystem SUMMER, waiting for further notifications. The output of WRKACTJOB would look like the following:
Current Opt Subsystem/Job User Type CPU % Function Status __ AUTUMN QSYS SBS .0 DEQW __ A_YEAR LJL BCH .0 PGM-PGM_A DEQW __ SUMMER QSYS SBS .0 DEQW __ WINTER QSYS SBS .0 DEQW |
Now we've seen the QMNSBS API working as expected. You can send another message to data queue SEASONS to initiate a new test or tell the job to quit by sending a message called 'SEEYOU' to SEASONS.
Efficiency Tests
The QUSRJOBI API is able to return the current subsystem attribute with formats JOBI0200 or JOBI0600. However, it is designed to retrieve detailed job attributes from different aspects and therefore is too heavy for our goal of retrieving the current subsystem. The following table lists statistics about the QMNSBS API and the QUSRJOBI API collected at V5R4, from which you can find out the dramatic difference in complexity and performance between the two APIs.
Comparison Between QMNSBS and QUSRJOBI |
||
|
QMNSBS |
QUSRJOBI |
Program size |
32,768 |
176,128 |
Number of parameters |
3 |
5-7 |
Static storage size |
0 |
0 |
Automatic storage size |
352 |
48,928 |
Number of MI instructions |
Hex 51 (81) |
More than hex 1212 (4626) |
Number of RISC instructions |
1,960 |
116,480 |
Execution time (ms) [1] |
363,000 |
3,196,000 |
[1] Execution time in microseconds is gotten by calling QMNSBS and QUSRJOBI respectively for 100,000 times on a 520 machine. For convenience, the sources of the RPG programs to obtain the execution time of these two APIs are shown below.
YY702.RPGLE (Call QMNSBS)
d n s 10i 0
d b s z
d e s z
c eval b = %timestamp()
c for n = 1 to 100000
c call 'QMNSBS'
c parm sbs_name 10
c parm ctlsbsd 10
c parm ctlsbsdlib 10
c endfor
c eval e = %timestamp()
c eval n = %diff(e:b:*ms)
c 'ms' dsply n
c seton lr
YY701.RPGLE (Call QUSRJOBI)
d len s 10i 0 inz(192)
d jobi0200 ds 192 qualified
d 62a
d sbs_name 10a
d n s 10i 0
d b s z
d e s z
c eval b = %timestamp()
c for n = 1 to 100000
c call 'QUSRJOBI'
c parm jobi0200
c parm len
c parm 'JOBI0200' fmt 8
c parm '*' job_name 26
c parm *BLANKS int_jid 16
c endfor
c eval e = %timestamp()
c eval n = %diff(e:b:*ms)
c 'ms' dsply n
c seton lr
Biography of Simon Coulter
Simon was born in New Zealand in August 1962. After completing his primary and secondary education, he chose to go to work rather than study at university. He started working as a tire fitter, joined the sales cadet scheme, and quickly became a service manager and then store manager.
Wanting to see more of the world, Simon travelled to Australia in 1985. He had been interested in computers for some time and, while working two part-time jobs, completed a diploma of computer operations. Simon completed the course ahead of schedule and used the remaining time to complete an RPG II programming course. Graduating as Dux of the class, Simon had found his calling.
He started working as a computer operations supervisor, and after less than a year was promoted to operations manager, and less than a year after that (late 1988) was promoted to analyst/programmer. He was programming on UNIX-based computers and the IBM System/38. It was at this time that Simon's passion for IBM midrange systems began.
Hoping to further develop his skills, Simon applied to IBM Australia in August 1989 to become a software engineer. While waiting to complete the formalities of the application and find out whether or not he was successful, Simon took a short-term contract as the computer operator at a company that had just installed the (then new) IBM AS/400. He wanted to learn as much as he could about this new machine.
Simon commenced work for IBM Australia in October 1989. His first job was facilities management for the Australian Programming Centre's first AS/400. Simon went on to design and program system-level APIs and system programs, provide technical support for the development of a new AS/400 licensed program product, and provide AS/400 education for vendor personnel.
During his time with IBM and their subsidiary ISSC, Simon became proficient in PL/MI, the UIM tag language, C, and SQL, as well as CL and RPG. He was also familiar with COBOL, BASIC, Pascal, and Fortran.
Simon was also involved in programming in VisPro/REXX on OS/2, programming for the FlowMark application, and object-oriented design for the VisualBanker application in Smalltalk.
As IBM was no longer developing software for the midrange system in Australia, Simon left in March 1996 to work with a software development company on a new product for the AS/400. Unfortunately, the development team was retrenched by that company in February 1997.
From that time on, Simon worked for his own company (FlyByNight Software) as a software engineer and consultant. He developed his own AS/400 products and provided technical advice/support and analysis and programming services to a range of clients. He was also contracted by IBM to run midrange technical education training courses and spoke on technical topics at Common Conferences in Australia.
In October and November 1997, Simon participated in the "Building AS/400 Applications with Java" residency at IBM Rochester. The residency team created the Redbook on Java for the AS/400.
Simon continued to develop his skills in Java and RPG and learned about HTML, XML, TCP/IP, and a wide range of other technical subjects relevant to modern midrange computer software development
Simon believed programming to be an art as well as a science. He believed that code should be elegant, efficient, easily maintained, and well documented. He was naturally analytical, and logical, and strongly believed in writing code that could be re-used (one piece of code for one function).
Many will have seen Simon's "RTFM" responses to online forum questions. He could not tolerate lazy programmers or poor-quality code.
As a person Simon was, in many ways, quite introverted. He was very self-sufficient, loved words, had eclectic taste in books and music, and always wanted to learn new skills.
In 2007, Simon and his partner moved to a rural property. He continued to work on software development and technical courses for IBM, while taking on the challenges of rural living and managing apple and nashi orchards on the property. He learned about soil, weeds, and care of fruit trees. He joined the local Rural Fire Service and started developing practical skills that had been a background interest for many years.
Right up until he became ill suddenly in August 2010, Simon enjoyed sharing his knowledge and thoughts with the worldwide midrange community and very much enjoyed the humor, camaraderie, and friendship of that online community.
Simon passed away on October 12, 2010.
LATEST COMMENTS
MC Press Online