CICS Program Control: LINK, XCTL, RETURN — Complete Guide

Introduction: CICS Program Control and the Call Stack
In batch COBOL, programs call each other with the CALL statement and return with STOP RUN or EXIT PROGRAM. In CICS, the program control model is similar but different in critical ways: programs must use EXEC CICS commands to invoke other CICS programs, and the concept of a "task" (rather than a process) governs the lifetime of all resources.
CICS Program Control provides three core commands:
- EXEC CICS LINK — call another program, return to caller
- EXEC CICS XCTL — transfer to another program, do not return
- EXEC CICS RETURN — return to caller (or terminate the task)
Understanding when to use each command — and understanding the CICS call stack and COMMAREA passing mechanics — is essential for building modular, maintainable CICS applications.
EXEC CICS LINK: Calling a Subprogram
LINK is the CICS equivalent of a COBOL CALL statement. The calling program (the "linker") passes a COMMAREA, and the called program ("linkee") processes it and issues EXEC CICS RETURN to hand control back.
*── Caller: EMPMENU ────────────────────────────────────────────
PROCEDURE DIVISION.
MOVE '000100' TO WS-LINK-CA-KEY
INITIALIZE WS-LINK-CA-RESULT
EXEC CICS LINK PROGRAM('EMPGET')
COMMAREA(WS-LINK-COMMAREA)
LENGTH(LENGTH OF WS-LINK-COMMAREA)
RESP(WS-RESP)
RESP2(WS-RESP2)
END-EXEC
*> Control returns here after EMPGET issues EXEC CICS RETURN
EVALUATE WS-RESP
WHEN DFHRESP(NORMAL)
IF WS-LINK-CA-RESP = 'OK'
MOVE WS-LINK-CA-EMP-NAME TO SCREEN-EMP-NAME
ELSE
MOVE WS-LINK-CA-ERR-MSG TO SCREEN-MSG
END-IF
WHEN DFHRESP(PGMIDERR)
MOVE 'PROGRAM EMPGET NOT FOUND.' TO SCREEN-MSG
WHEN OTHER
PERFORM HANDLE-LINK-ERROR
END-EVALUATE.*── Called program: EMPGET ────────────────────────────────────
LINKAGE SECTION.
01 DFHCOMMAREA.
05 LK-KEY PIC X(6).
05 LK-RESP PIC X(2).
05 LK-EMP-NAME PIC X(30).
05 LK-ERR-MSG PIC X(40).
PROCEDURE DIVISION.
EXEC CICS READ FILE('EMPFILE')
RIDFLD(LK-KEY)
INTO(WS-EMP-REC)
RESP(WS-RESP)
END-EXEC
EVALUATE WS-RESP
WHEN DFHRESP(NORMAL)
MOVE 'OK' TO LK-RESP
MOVE WS-EMP-NAME TO LK-EMP-NAME
WHEN DFHRESP(NOTFND)
MOVE 'NF' TO LK-RESP
MOVE 'NOT FOUND' TO LK-ERR-MSG
WHEN OTHER
MOVE 'ER' TO LK-RESP
MOVE 'READ ERROR' TO LK-ERR-MSG
END-EVALUATE
EXEC CICS RETURN END-EXEC. *> Return to EMPMENULINK Chain Depth
CICS supports nested LINK chains (A LINKs to B, B LINKs to C, etc.) up to a depth of 255 levels. Each level in the chain is tracked by CICS. When the deepest program issues RETURN, control goes back one level. This is exactly analogous to the COBOL call stack.
Dynamic LINK: PROGRAM in a Variable
The program name in EXEC CICS LINK can be specified as a data area (variable) rather than a literal, enabling dynamic dispatch:
01 WS-PROG-NAME PIC X(8).
PROCEDURE DIVISION.
MOVE WS-CA-PROGRAM-NAME TO WS-PROG-NAME
EXEC CICS LINK PROGRAM(WS-PROG-NAME)
COMMAREA(WS-LINK-CA)
LENGTH(LENGTH OF WS-LINK-CA)
RESP(WS-RESP)
END-EXEC.This is the CICS equivalent of a dynamic CALL and is used in routing tables, menu drivers, and generic transaction controllers.
EXEC CICS XCTL: Transfer of Control (No Return)
XCTL passes control to another program without returning. The current program terminates and its GETMAIN storage is freed. Use XCTL when:
- A menu transaction navigates to a subsystem program
- A transaction chains to a completion/confirmation screen that has no need to return
- You want to "restart" a transaction by XCTLing to the same program
*── Navigate to main menu, no return expected ───────────────────
XCTL-TO-MENU.
MOVE WS-CURRENT-USER TO WS-MENU-CA-USER
MOVE 'EMP ' TO WS-MENU-CA-SUBSYS
EXEC CICS XCTL PROGRAM('MAINMENU')
COMMAREA(WS-MENU-COMMAREA)
LENGTH(LENGTH OF WS-MENU-COMMAREA)
RESP(WS-RESP)
END-EXEC
*> Code below here is never reached
MOVE 'XCTL FAILED' TO WS-ERR-MSG
PERFORM HANDLE-ERROR.Key difference from LINK: after EXEC CICS XCTL, your program is gone. You cannot write code that "expects to run after the XCTL" — it will never execute. Any code after the XCTL command is unreachable except for error handling in the case the XCTL itself fails (PGMIDERR, etc.).
XCTL vs. LINK: Which to Use
| Scenario | Use |
|---|---|
| Calling a submodule that returns a result | LINK |
| Navigating from a menu to a subsystem | XCTL |
| Chaining to next step in a workflow with no return | XCTL |
| Calling a common validation or formatting routine | LINK |
| Re-starting the same program | XCTL (to self) |
| Invoking a help program, returning to caller | LINK |
EXEC CICS RETURN: Terminating or Returning
RETURN has two distinct behaviours depending on whether TRANSID is specified:
RETURN without TRANSID — terminates the current program and returns to the caller (if called via LINK) or ends the task entirely (if this is the top-level program). This is used in subprograms and in top-level programs that are done with the transaction.
*── Subprogram: return to caller ────────────────────────────────
EXEC CICS RETURN END-EXEC.
*── Top-level: end the task and show natural next message ────────
EXEC CICS RETURN END-EXEC.RETURN with TRANSID and COMMAREA — ends the current task but schedules a new task start for the specified TRANSID when the terminal user provides input. Used exclusively for pseudo-conversational state saving.
*── Pseudo-conversational: save state and wait for next input ────
EXEC CICS RETURN
TRANSID(EIBTRNID)
COMMAREA(WS-COMMAREA)
LENGTH(LENGTH OF WS-COMMAREA)
RESP(WS-RESP)
END-EXEC.RETURN with TRANSID only (no COMMAREA) — same as above but no state is saved. The next task starts with EIBCALEN = 0. Used when the current interaction is complete but the transaction should be ready for a fresh start.
*── After a completed transaction: reset for fresh start ─────────
EXEC CICS RETURN
TRANSID('EMPI')
RESP(WS-RESP)
END-EXEC.EXEC CICS LOAD and RELEASE: Loading Data Tables
LOAD is used to load a non-executable module (a data table, parameter block, or message table) into memory and obtain its base address:
WORKING-STORAGE SECTION.
01 WS-TABLE-POINTER USAGE IS POINTER.
01 WS-TABLE-LEN PIC S9(8) COMP VALUE +0.
LINKAGE SECTION.
01 LK-MSG-TABLE.
05 LK-MSG-ENTRY OCCURS 100 TIMES.
10 LK-MSG-CODE PIC X(4).
10 LK-MSG-TEXT PIC X(60).
PROCEDURE DIVISION.
LOAD-MESSAGE-TABLE.
EXEC CICS LOAD PROGRAM('MSGTBL01')
SET(WS-TABLE-POINTER)
LENGTH(WS-TABLE-LEN)
RESP(WS-RESP)
END-EXEC
IF WS-RESP = DFHRESP(NORMAL)
SET ADDRESS OF LK-MSG-TABLE TO WS-TABLE-POINTER
PERFORM LOOKUP-MESSAGE
ELSE
MOVE 'MSG TABLE LOAD FAILED.' TO WS-ERR-MSG
END-IF.
RELEASE-TABLE.
EXEC CICS RELEASE PROGRAM('MSGTBL01')
RESP(WS-RESP)
END-EXEC.LOAD is particularly useful for large reference tables (country codes, product codes, error message texts) that should not be embedded in every program's WORKING-STORAGE.
EXEC CICS ABEND: Intentional Task Termination
While not strictly a Program Control command, EXEC CICS ABEND is used to terminate a task immediately with a specific ABEND code when an unrecoverable error occurs:
HANDLE-FATAL-ERROR.
EXEC CICS WRITEQ TS
QUEUE('ERRLOG')
FROM(WS-ERROR-DETAILS)
LENGTH(LENGTH OF WS-ERROR-DETAILS)
END-EXEC
EXEC CICS ABEND ABCODE('EMPE')
NODUMP
END-EXEC.Use NODUMP for expected, managed error conditions to avoid generating a large SVC dump. Omit NODUMP for unexpected conditions that require a full memory dump for debugging.
Common Program Control Mistakes
Mistake 1: Coding logic after EXEC CICS XCTL. After XCTL executes, your program no longer exists. Any paragraph or statement after the XCTL will never run (except in the unlikely event the XCTL itself fails). Structure your code to perform XCTL as the last action in a processing path.
Mistake 2: Using CALL instead of LINK for CICS subprograms. Subprograms that issue EXEC CICS commands must be invoked via LINK. Using CALL causes an ASRA (program interruption) because the EIB is not set up for the called program.
Mistake 3: Not checking RESP after LINK for PGMIDERR. If the linked program is not defined in CICS (not installed, misspelled name), LINK returns DFHRESP(PGMIDERR). Without RESP checking, CICS ABENDs the task with PGMI. Always check RESP after LINK.
Mistake 4: LINKing to the same program recursively. Recursive LINK chains consume one stack level per call. Recursion to a depth of 255 causes an AEIV ABEND. If you genuinely need recursive logic, implement it with EVALUATE/PERFORM loops instead.
Key Takeaways
CICS Program Control — LINK, XCTL, and RETURN — maps cleanly to the COBOL programming mental model. LINK = CALL (with return), XCTL = CALL without return, RETURN = EXIT PROGRAM or STOP RUN. The COMMAREA on LINK and XCTL is the parameter-passing mechanism. The TRANSID and COMMAREA on RETURN is the pseudo-conversational state-saving mechanism. Master these three commands and their options and you have the complete toolkit for building modular, scalable CICS application architectures.
For understanding how CICS sends and receives terminal data, continue with CICS SEND and RECEIVE. For the complete CICS course, visit the CICS Mastery Course.
