How to Check Whether a Subsystem Is Active in IBM i Using CL
If you’ve spent any time automating operations on IBM i, you’ve probably bumped into this classic requirement: “Before I run this job, I need to know whether a subsystem is active.”
It sounds simple, but depending on your tooling and IBM i version, there are several ways to approach it. Some are modern and elegant. Others are… let’s say “heritage techniques.” In this post, we’ll walk through the three most common methods so you can choose the one that fits your environment.
Option 1: The Funky Way Query SUBSYSTEM_INFO with SQL
If your system is reasonably up to date, IBM has gifted us a clean solution: the QSYS2.SUBSYSTEM_INFO view. It tells you exactly what you want to know, including whether a subsystem is ACTIVE, INACTIVE, ENDING, or RESTRICTED.
You can call it directly from CL using RUNSQL.
“But RUNSQL does not output to an outfile!” I can hear you exclaim!
Don’t worry, we will simply do a little SQL CREATE TABLE trick. Here I am using QTEMP but you can use any library you like:
PGM
DCLF FILE(QTEMP/CHKSBSTMP) ALWVARLEN(*YES)
RUNSQL SQL('CREATE TABLE QTEMP/CHKSBSTMP AS (SELECT +
STATUS FROM QSYS2/SUBSYSTEM_INFO WHERE +
SUBSYSTEM_DESCRIPTION = ''QINTER'' AND +
SUBSYSTEM_DESCRIPTION_LIBRARY = ''QSYS'') +
WITH DATA') COMMIT(*NONE) NAMING(*SYS)
RCVF RCDFMT(*FILE)
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(FAIL))
SNDPGMMSG MSG('Subsystem QINTER is Active')
RETURN
FAIL: SNDPGMMSG MSG('* Unable to retrieve subsystem status!')
ENDPGM
That’s the simple skeleton of what we want to do. Now, let write this with a little bit more sparkle:
/******************************************************************************/
/* Program: CHKSBSACT - Check if Subsystem is Active */
/* Purpose: Determines if a specified subsystem is currently active */
/* Parameters: */
/* &SBS - Subsystem name (10 chars) */
/* &LIB - Subsystem library (10 chars) */
/* &RESULT - Return value: 'YES' if active, 'NO' if inactive, 'ERR' error */
/* https://www.nicklitten.com/ibm-i-control-language-check-subsystem-status */
/******************************************************************************/
PGM PARM(&SBS &LIB &RESULT)
DCLPRCOPT LOG(*NO) DFTACTGRP(*NO) ACTGRP(*CALLER)
COPYRIGHT TEXT('CHKSBSACT Ver:001 Check if Subsystem is Active')
DCL VAR(©RIGHT) TYPE(*CHAR) LEN(256) +
VALUE('Nick Litten © 2025 | IBM i V7.5 https://www.nicklitten.com')
DCL VAR(©RIGHTP) TYPE(*PTR) STG(*DEFINED) DEFVAR(©RIGHT)
DCL VAR(&SBS) TYPE(*CHAR) LEN(10)
DCL VAR(&LIB) TYPE(*CHAR) LEN(10)
DCL VAR(&RESULT) TYPE(*CHAR) LEN(3)
DCL VAR(&MSGID) TYPE(*CHAR) LEN(7)
DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(512)
DCL VAR(&SQLSTMT) TYPE(*CHAR) LEN(512)
/* File declaration for SQL result */
DCLF FILE(CHKSBSTMP) ALWVARLEN(*YES)
/* Initialize result to error state */
CHGVAR VAR(&RESULT) VALUE('ERR')
/* Clean up any existing temp file */
DLTF FILE(QTEMP/CHKSBSTMP)
MONMSG MSGID(CPF2105 CPF2110) /* File not found errors */
/* Build dynamic SQL statement using actual parameters */
CHGVAR VAR(&SQLSTMT) VALUE('CREATE TABLE +
QTEMP.CHKSBSTMP AS (SELECT STATUS FROM +
QSYS2.SUBSYSTEM_INFO WHERE +
SUBSYSTEM_DESCRIPTION = ''' *CAT %TRIM(&SBS) +
*CAT ''' AND SUBSYSTEM_DESCRIPTION_LIBRARY = +
''' *CAT %TRIM(&LIB) *CAT ''') WITH DATA')
/* Execute SQL to retrieve subsystem status */
RUNSQL SQL(&SQLSTMT) COMMIT(*NONE) NAMING(*SQL)
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(SQL_ERROR))
/* Read the status from temp file */
RCVF RCDFMT(*FILE)
MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(NO_DATA)) /* EOF */
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(READ_ERROR))
/* Check if subsystem is active */
IF COND(%TRIM(&STATUS) *EQ 'ACTIVE') THEN(DO)
CHGVAR VAR(&RESULT) VALUE('YES')
ENDDO
ELSE CMD(DO)
CHGVAR VAR(&RESULT) VALUE('NO')
ENDDO
/* Clean up and return */
GOTO CMDLBL(CLEANUP)
/*---------------------------------------------------------------------------*/
/* Error handling routines */
/*---------------------------------------------------------------------------*/
/* No data found - subsystem doesn't exist or no match */
NO_DATA:
CHGVAR VAR(&RESULT) VALUE('NO')
SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) +
MSGDTA('Subsystem' *BCAT %TRIM(&SBS) *BCAT +
'not found in library' *BCAT %TRIM(&LIB)) +
MSGTYPE(*DIAG)
GOTO CMDLBL(CLEANUP)
/* SQL execution error */
SQL_ERROR:
RCVMSG MSGTYPE(*EXCP) MSGDTA(&MSGDTA) MSGID(&MSGID)
SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) +
MSGDTA('SQL error' *BCAT &MSGID *BCAT '-' +
*BCAT %SST(&MSGDTA 1 100)) MSGTYPE(*DIAG)
GOTO CMDLBL(CLEANUP)
/* File read error */
READ_ERROR:
RCVMSG MSGTYPE(*EXCP) MSGDTA(&MSGDTA) MSGID(&MSGID)
SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) +
MSGDTA('Read error' *BCAT &MSGID *BCAT '-' +
*BCAT %SST(&MSGDTA 1 100)) MSGTYPE(*DIAG)
GOTO CMDLBL(CLEANUP)
/* Cleanup routine */
CLEANUP:
DLTF FILE(QTEMP/CHKSBSTMP)
MONMSG MSGID(CPF2105 CPF2110) /* Ignore if not found */
RETURN
ENDPGM
The main drawback to this technique is that it needs the file QTEMP/CHKSBSTMP to exist so you can compile the code in the first place. You can do this from a command line by entering STRSQL and:
CREATE TABLE QTEMP/CHKSBSTMP AS (SELECT +
STATUS FROM QSYS2/SUBSYSTEM_INFO WHERE +
SUBSYSTEM_DESCRIPTION = ''QINTER'' AND +
SUBSYSTEM_DESCRIPTION_LIBRARY = ''QSYS'') +
WITH DATA
Of course – once you need to compile this code using offline tools like VS-Code it will always fail because the file doesn’t exist for each new compile session. Clonky right!
Pros and cons
- ✔ Simple code
- ✔ Brief calls to SQL function
- ✘ Doesn’t work on older releases of IBM-i
- ✘ Depends on file existing to compile, which is never ideal
If IBM gave us an OUTPUT(&VARIABLE) option on the RUNSQL command this technique would be the bomb. But until that time, I am stick with a good old API call from my CL program. QWDRSBSD to the rescue!
Option 2: The Classic Way Use the QWDRSBSD API
Before SQL views existed, the standard way to check subsystem status was the QWDRSBSD API. It’s still fully supported and works on every IBM i release you’re likely to encounter.
/******************************************************************************/
/* Program: CHKQINACT - Check if Subsystem is Active */
/* Purpose: Determines if a specified subsystem is currently active */
/* https://www.nicklitten.com/ibm-i-control-language-check-subsystem-status */
/******************************************************************************/
PGM
DCLPRCOPT LOG(*NO) DFTACTGRP(*NO) ACTGRP(*CALLER)
COPYRIGHT TEXT('CHKSBSACT Ver:001 Check if Subsystem is Active')
DCL VAR(©RIGHT) TYPE(*CHAR) LEN(256) +
VALUE('Nick Litten © 2025 | IBM i V7.5 https://www.nicklitten.com')
DCL VAR(©RIGHTP) TYPE(*PTR) STG(*DEFINED) DEFVAR(©RIGHT)
DCL VAR(&SBSNAME) TYPE(*CHAR) LEN(10) VALUE(QINTER)
DCL VAR(&SBSLIB) TYPE(*CHAR) LEN(10) VALUE(QSYS)
DCL VAR(&QUALNAME) TYPE(*CHAR) LEN(20)
DCL VAR(&RCVVAR) TYPE(*CHAR) LEN(512)
DCL VAR(&RCVLEN) TYPE(*INT) LEN(4) VALUE(512)
DCL VAR(&FORMAT) TYPE(*CHAR) LEN(8) VALUE('SBSI0100')
DCL VAR(&ERRCODE) TYPE(*CHAR) LEN(16) VALUE(X'00000000')
DCL VAR(&STATUS) TYPE(*CHAR) LEN(10)
CHGVAR VAR(&QUALNAME) VALUE(&SBSNAME *CAT &SBSLIB)
CALL PGM(QWDRSBSD) PARM((&RCVVAR) (&RCVLEN) +
(&FORMAT) (&QUALNAME) (&ERRCODE))
MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(FAIL))
/* Extract subsystem status (positions 29-38 in SBSI0100) */
CHGVAR VAR(&STATUS) VALUE(%SST(&RCVVAR 29 10))
SNDPGMMSG MSG('Subsystem QINTER is' *BCAT &STATUS)
RETURN
FAIL:
SNDPGMMSG MSG('* Unable to retrieve subsystem status!')
ENDPGM
Why you might choose this method
- ✔ Works everywhere
- ✔ No SQL required
- ✔ Reliable and battle tested
If you’re maintaining older systems or writing code that must run across mixed environments, this is a safe choice.
Option 3: The Quick and Dirty Way WRKSBS + MONMSG
Sometimes you just need a fast check and you don’t care about elegance. In that case, you can rely on the fact that WRKSBS fails if the subsystem isn’t active.
PGM
QSYS/WRKSBSJOB SBS(QINTER) OUTPUT(*PRINT)
MONMSG MSGID(CPF0000) EXEC(DO)
SNDPGMMSG MSG('Subsystem QINTER is NOT active')
RETURN
ENDDO
SNDPGMMSG MSG('Subsystem QINTER is active')
ENDPGM
Pros and cons
- ✔ Very simple
- ✔ Works on any release
- ✘ Not precise
- ✘ Depends on command failure, which is never ideal
Use this only when you need a quick‑and‑dirty solution.
So, Which Method Should You Use?
If your system supports it, SQL SUBSYSTEM_INFO is the clear winner. It’s clean, modern, and gives you the exact subsystem state without any fuss.
If you’re working on older systems or writing code that must run everywhere, the QWDRSBSD API is your dependable workhorse.
And if you’re in a hurry, well… the WRKSBS trick will get the job done.
Do you want to see a reusable CL command (CHKSBSACT), complete with return variables and message handling? Then jump over to my CL Programming Course lesson here
What is IBM CL?
IBM i Control Language (CL) is a powerful scripting language for the IBM AS/400, IBM iSeries and IBM i Systems. It’s got roots in the older IBM Job Control Language, and it works as a simple way to script commands, instructions and other functions into an easy-to-understand programs.
