In program a
EXEC CICS LINK
PROGRAM(PGMB)
COMMAREA(COMMA)
LENGTH(LENGTH OF COMMA)
RESP(CICS-RESP)
END-EXEC
In program b
EXEC CICS RETURN
END-EXEC
Does program b only return the commarea that program a passed? Or does it return the whole LINKAGE SECTION?
Program B returns neither the entire LINKAGE SECTION nor the commarea (COMMA in your example).
It returns nothing.
Why does it return nothing? Because nothing gets passed to it.
Or, rather, what gets passed to it is simply the address(es) of the parameter(s). Nothing else. That is all. Importantly, no length.
PROGA
01 some-stuff.
05 a-bit-of-stuff PIC X.
05 the-rest-of-the-stuff PIC X(99).
CALL .... USING a-bit-of-stuff
PROGB
LINKAGE SECTION.
01 stuff-that-is-somewhere-else PIC X(100).
PROCEDURE DIVISION USING stuff-that-is-somewhere-else.
a-bit-of-stuff is define as only one byte. This makes no difference. It is the definition, in the LINKAGE SECTION, of the item on the PROCEDURE DIVISION USING ... which matches, in order of reference, nothing else, to the CALL ... USING ...
PROGB will be "passed" the address of a-bit-of-stuff. If that address is then mapped to 100 bytes in the LINKAGE SECTION of the CALLed program, COBOL does not mind at all.
If we change that example CALL to use instead some-stuff, since some-stuff has the same starting address as a-bit-of-stuff, there would be absolutely no change in the generated code, and no change in the execution of the two programs.
Defining different sizes of data "between" CALLer and CALLed is usually not done, because it makes things less clear to us, humans. The compiler cares not one jot.
What you need to look at the 01s (or 77s if that silly idea takes your fancy) as is a REDEFINES. They are a REDEFINES, an implicit one, of data which is defined somewhere else. No data is defined for items in the LINKAGE SECTION (there is one exception to that on the Mainframe). The 01-levels in the LINKAGE SECTION are just redefining, or mapping, the address of the data that is passed to the program. The data does not "leave" the CALLing program, and the data is never "passed back".
Things can go wrong, of course, if you use different lengths for matching items on the USINGs. If the storage from the CALLer is "acquired" (like a GETMAIN in CICS) then attempting to referencing data outside of that storage, even one byte further on, can get you an abend due to an Addressing Exception (a S0C4, which CICS will kindly name something else for you, an AKEA).
Even without acquired storage, other fields after the one "passed" can be accidentally trashed, or the field itself may not get the expected amount of data MOVEd to it by the CALLed program, if the definition is short in the CALLed program.
There are actually two things which get "returned" from a CALLed program. They are the special-register RETURN-CODE, and the single item on the RETURNING of the PROCEDURE DIVISION (if used, likely not).
Even so, the mechanisms of how those are achieved are different from the normal misunderstanding of data "passed" between CALLing and CALLed programs.
I have not had the joy of programming CICS for sometime now, this answer is based on what knowledge I still remember.
The calling program gets at most an amount of data less than or equal to the size of the data area sent in the calling program (or as specified by the optional LENGTH parameter). Don't try to access data beyond what you have sent.
"So if program x LINKS to program y, any updates done to the COMMAREA in y will be visible in x."
Source: SOVF:How CICS Shared Memory Works.
"When a communication area is passed by way of an EXEC CICS LINK command, the invoked program is passed a pointer to the communication area itself. Any changes that are made to the contents of the data area in the invoked program are available to the invoking program, when control returns to it; to access any such changes, the program names the data area that is specified in the original COMMAREA option." Source: IBM-CICS-Ref.
So, does program b only return the commarea that program a passed?
I would answer the above as Yes.
Does it return the whole linkage section?
As for this one, it depends on the structure of the DFHCOMMAREA of the linked program. If it only contains 1 such area, then the answer is that it returns as many bytes as was sent on link command from that area (either implicitly or explicitly). Remember that this area is outside your caller. So, if the caller sends 100 bytes and the linkage section has an area of 500 bytes, you only get 100 back at the most.
If you want to allow your linked program to modify data in the commarea, there are some very serious limitations.
Exec CICS
Return
End-Exec
Will expose a changes in a commarea to the LINKing program, but only by accident and only if both tasks execute on the same CICS region. This is because the commarea is actually a pointer. On a distributed program link, the area is copied, but not copied back.
Related
In CICS on z/OS I have some questions:
What data are stored on main storage? Auxiliary storage?
Where does data in DFHCOMMAREA under linkage section exists? Is it on main storage?
If I pass DFHCOMMAREA from one program to another, will that create extra copies of the data? (pass by-value or by-reference)
There's quite some confusion here concerning the different storage types. From a COBOL perspective you won't ever be worrying about main storage or auxiliary storage. Your COBOL-data lives in an address space made up of virtual storage which in turn is backed by main or auxiliary storage as the system sees fit.
While your program will automatically allocate memory for items defined in WORKING STORAGE or LOCAL STORAGE sections, it will not do so for anything defined in the LINKAGE SECTION. For a LINKAGE SECTION item to be usable, two things are required:
Some memory must be allocated
The LINKAGE SECTION item must be associated with the address of that memory region.
These two things can happen in different ways:
For items appearing in the USING of your PROCEDURE DIVISION the memory is provided by the calling program (or some other program up the callstack) and the compiler associates the item with the respective adresses passed in the parameter list provided by the caller. In the case of DFHCOMMAREA of a top-level CICS-program the calling program that allocates the memory is CICS itself.
You can "remap" memory from your e.g. WORKING STORAGE to a LINKAGE SECTION item by using SET ADDRESS OF
With more recent compilers you can also use ALLOCATE to dynamically request memory from your program and when used with a LINKAGE SECTION item it will also automatically associate the item with the memory
As for your last question: passing parameters BY REFERENCE from one program to another will not create extra copies of that data. Passing BY VALUE or BY CONTENT will duplicate the data.
I'm working on a Wrapper/Bridge COBOL program that handles program calls and performs cross-cutting operations like logging,security-check etc.
Main motivation is checking the security access for consumer program whether it has access to call the producer program or not.
Let the bridge COBOL program be B1 and the producer program P1 and the consumer(client) C1.
When C1 wants to call P1, it have to make a call to B1. Then, B1 checks the accessibility. If C1 has access, then B1 calls P1 with C1's data.
C1 -> B1 -> P1
In here the linkage section of B1 and P1 are the same. Programs are using EXEC CICS LINK to call each other.
The COMMAREA,
COMMAREA1 (DataSet Name)
01 COMMAREA-STRUCT,
03 a-field
03 another-field
...
The client;
IDENTIFICATION DIVISION.
PROGRAM-ID. Client.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY COMMAREA1
PROCEDURE DIVISION
/* fill CommareaStruct with some values. */
....
/* call B1 Bridge */
EXEC CICS LINK PROGRAM (B1Bridge) NOHANDLE
COMMAREA (COMMAREA-STRUCT)
LENGTH (LENGTH OF COMMAREA-STRUCT)
END-EXEC
....
The Bridge,
IDENTIFICATION DIVISION.
PROGRAM-ID. B1Bridge.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
LINKAGE SECTION.
COPY COMMAREA1
PROCEDURE DIVISION
...
/* access control */
/* logging */
...
/* pass data to P1*/
EXEC CICS LINK PROGRAM (P1) NOHANDLE
COMMAREA (COMMAREA-STRUCT)
LENGTH (LENGTH OF COMMAREA-STRUCT)
END-EXEC
....
The producer ;
IDENTIFICATION DIVISION.
PROGRAM-ID. P1
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
LINKAGE SECTION.
COPY COMMAREA1
PROCEDURE DIVISION
....
*doing some business with data in COMMAREA1
...
When I try above, I got a compile-time warning for Bridge Program B1 ;
"COMMAREA-STRUCT or one of its subordinates was referenced, but COMMAREA-STRUCT was a LINKAGE SECTION item that did not have addressability. This reference will not be resolved succcessfully at execution."
What does it mean? How should I pass B1's linkage section to P1's linkage section?
When I try like this, I got EIBRESP:22 and EIBRESP2: 26 (commarea length error) at runtime.
-- Edit --
I think I should give more details;
Main motivation;
Actually there are two companies that company COM1 and COM2. COM2 was an affiliate of COM1 for several years.
COM1 and COM2 have CICS1 and CICS2 respectively. And COM2 client programs uses COM1 producer programs. COM2 clients never call the COM1 producers directly. COM2 clients put the data into COMMAREA-STRUCT and call a Generic Cobol Program (let it be GCP) remotely. COMMAREA-STRUCT has also "the producer program name" field that GCP figures out which program is wanted to be called. So, GCP exports the data from COMMAREA-STRUCT and maps to the fields of producer. GCP performs the mapping operations dynamically with addressing(not special for each producer). After the producer performs, GCP takes the result and passes back to the client via COMMAREA-STRUCT.
The system was designed like that several years ago. There are thousands of clients of COM2 and thousands of producers of COM1.
Now, COM2 wants to apart from COM1. So COM1 don't want to give full access to all COM1 resources(producers) any more. Thus, COM1 wants to put a new cics in front of the CICS1 which will be a handler CICS that runs only B1 Bridge program locally. This also about network security and company-political decisions.
To seperate the companies from each other in a little while, neither the clients nor the producers should be affected. So, problem should be solved in GCP-Bridge layer.
That's why, B1 Bridge should behave like GCP to the COM2 Clients, should check the accessibility(somehow, we applied it) and should pass all the data that coming from the clients to the GCP without any modification.
Currenty the logging operation does not have any priority. We focus on part the companies in a little while.
So I'm very appreciated for your expert comments.
*We can't use CALL because B1 will be on another CICS and can not access the LOADLIB1 of COM1 thats why B1 should call GCP remotly by EXEC CICS LINK.
*Instead of passing commarea, passing the channel sounds good to me. We will discuss on it.
*By the way, i will check fullword-halfword conflict on LENGHT OF. You are right.
*For the security check, we will discuss on "EXEC CICS QUERY SECURITY".
*As mentioned above, we can not modify copy-books. Only we can change is,
EXEC CICS LINK PROGRAM (GCP)
to
EXEC CICS LINK PROGRAM (B1)
on the clients by find&replace. Because there are thousands of clients. We don't want to change copy-book and touch them.
In lights of these details, I think the problem becomes more understandable.
In a CICS COBOL program invoked via an EXEC CICS LINK, the Linkage Section must contain an 01 level structure with the name DFHCOMMAREA. The precompiler or the COBOL compiler's CICS coprocessor will generate the appropriate USING for the Procedure Division so the program has addressability to the DFHCOMMAREA structure.
DFHCOMMAREA will contain the contents of what you are calling COMMAREA-STRUCT when you LINK to the target program.
One way to deal with the situation in which you find yourself is to modify your copybook to remove the 01 level structure name and require all clients to code their own 01 level structure name just prior to the COPY statement. In the bridge and producer programs this 01 level structure name would be DFHCOMMAREA.
Another way to deal with this situation is to eschew LINK in favor of a dynamic CALL. You would have to include DFHEIBLK as the first parameter of the CALL.
Yet another way to deal with this situation is to eschew commarea for one or more CICS containers. Instead of passing the commarea on the LINK you would pass a channel, the channel would have one or more containers hanging off of it, containing the data you wish to pass.
Something to beware of, you are using the LENGTH OF special register for your commarea length. The special register is a fullword, but the commarea length parameter is a halfword. I suspect that will cause you grief, unless IBM generates code to intercept that particular idiom and move the special register to a temporary halfword (unlikely but possible).
Update:
From your additional information it is apparent your task is to write a "drop-in replacement" for an existing program (the GCP).
A pragmatic approach might be to create a new copybook, let's call it COMAREA2, which is a copy of COMAREA1 but without the embedded 01 level structure name. Place the COPY COMAREA2 statement immediately after the DFHCOMMAREA 01 structure name in the B1 program.
This isn't ideal, as documentation somewhere must make it plain that changes to the COMAREA1 copybook must be reflected in COMAREA2. A manual process like this of course introduces the possibility of error, but it does nicely get you around having to modify any of the C1 or P1 programs.
More elegant, provided it works for you, would be to try...
COPY COMAREA1 REPLACING == 01 COMMAREA-STRUCT== BY ==*01 COMMAREA-STRUCT==.
...in your B1 program. This would remove the need for the COMAREA2 copybook proposed above. Provided this works, you would simply place the COPY statement after the DFHCOMMAREA 01 structure level name.
If a table element (table without an index) is accessed using an index of another table it can give a Table overflow error on IBM Host. But the same program does not result in a crash or a message (even with debug options) when using GnuCOBOL (formerly OpenCOBOL).
e.g.
IDENTIFICATION DIVISION.
PROGRAM-ID. TSTPROGX.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 IX PIC 9(04) COMP VALUE ZERO.
01 VARS.
05 S-PART-C.
10 S-DETAIL OCCURS 100 TIMES
INDEXED BY S-SUB.
15 S-ACTUAL PIC 9(06) VALUE ZERO.
15 S-ACTUAL-A
REDEFINES S-ACTUAL
PIC X(06).
15 S-GRADE PIC X(02) VALUE LOW-VALUE.
05 POS-USED-ARRAY PIC X(999)
VALUE SPACE.
05 FILLER REDEFINES POS-USED-ARRAY
OCCURS 999.
10 FILLER-X PIC X .
88 POSITIONS-USED-X VALUE 'T'.
PROCEDURE DIVISION.
SET S-SUB TO 1
PERFORM VARYING IX FROM 1 BY 1 UNTIL IX > 999
SET S-SUB TO IX
SET POSITIONS-USED-X(S-SUB) TO TRUE
DISPLAY IX ":" FILLER-X(S-SUB)
END-PERFORM
GOBACK.
Is there a compiler option to issue warnings to avoid this kind of usage?
This error can be avoided by using the right usage.i.e, using the variable 'I-X', instead of using the index(S-SUB) of a different table.
SET POSITIONS-USED-X(I-X) TO TRUE
In general, exchanging index of independent tables(of different size) seems to be erroneous.
Presuming that by Host you mean Mainframe, using Enterprise COBOL, the link I originally included has the answer for you.
In Enterprise COBOL, you can use an index from one table to reference data on another table, even one which does not have an index (like your example), but unless the lengths of the data subordinate to the OCCURS is the same you will not get the results you expect.
With Enterprise COBOL and compiler option SSRANGE the code in your question will fail (as you are aware). Where will it fail? The length of the OCCURS associated with the index S-SUB is eight bytes. That length is effectively "inbuilt" into the index S-SUB. Dividing 999 (length of second table) by eight yields 124 (ignore the remainder), so an S-SUB being SET to 124 will be OK, and SET to 125 will not, because 125 will start at by 1,000, and you only have 999 bytes.
In GnuCOBOL the index does not have the length inbuilt, it is a simple integer relating directly to the occurence in the table. However, having got up to 100, you start overflowing the first table (where there is no compiler/run-time check) and 125 references later you go beyond the end of the second table, and with -g in your compile options will get a crash depending on how (and how much) the storage is allocated in the C program generated by the GnuCOBOL compiler. 225 would be the first point at which a crash could occur, but it may not do so at that point, and it may, or may not, later.
Basically, there's no way you can really expect the code to work, which you know, you just want to know how to set the compiler to check for it with GnuCOBOL. Currently, you can't.
Usually there is a field defined to hold the maximum entries in a table, and another defined to say how many are being used. When using entries, you first check to see the table is not full.
You could use LENGTH OF/FUNCTION LENGTH to protect against going over the end of a table as well.
You could combine the two approaches.
You just don't have a switch for GnuCOBOL that helps you here. The -g goes down to the C compiler. It gives you something, but not everything, and just depends.
Also, it is not, to my mind, a good idea to have a data-name which you use for subscripting named IX and an index named S-SUB. That will confuse a human reader of a program.
See this answer: https://stackoverflow.com/a/36254166/1927206 for some detail about using indexes from different tables.
IBM Enterprise COBOL can check that storage referenced using an index is within the table that the referenced-element is subordinate to. This is done with compiler option SSRANGE. If data outside the table is referenced (by any manner of subscripting, or by reference-modification) a run-time diagnostic message is produced. In all but the latest Enterprise COBOL compilers, that problem will cause an "abend" (an "abnormal end"). With the latest compilers, there are some sub-options to give more control.
This is not strictly checking the "bounds" of a table. For a one-dimensional table, the table-reference checking coincides with bounds-checking. For a multi-dimensional table it does not. The second and subsequent levels of subscript can be "wrong", but unless they cause a reference outside the table, this is unproblematic.
GnuCOBOL/OpenCOBOL does not have any checking for the use of subscripts or the reference of a table by subscripting/reference-modification. If you wanted to consider adding this yourself, you would be more than welcome. Or you could post it as a feature request. Visit https://sourceforge.net/p/open-cobol/discussion/?source=navbaring, but not everything.
See this answer: https://stackoverflow.com/a/36254166/1927206 for some detail about using indexes from different tables.
IBM Enterprise COBOL can check that storage referenced using an index is within the table that the referenced-element is subordinate to. This is done with compiler option SSRANGE. If data outside the table is referenced (by any manner of subscripting, or by reference-modification) a run-time diagnostic message is produced. In all but the latest Enterprise COBOL compilers, that problem will cause an "abend" (an "abnormal end"). With the latest compilers, there are some sub-options to give more control.
This is not strictly checking the "bounds" of a table. For a one-dimensional table, the table-reference checking coincides with bounds-checking. For a multi-dimensional table it does not. The second and subsequent levels of subscript can be "wrong", but unless they cause a reference outside the table, this is unproblematic.
GnuCOBOL/OpenCOBOL does not have any checking for the use of subscripts or the reference of a table by subscripting/reference-modification. If you wanted to consider adding this yourself, you would be more than welcome. Or you could post it as a feature request. Visit https://sourceforge.net/p/open-cobol/discussion/?source=navbar
I am working on a project to convert a mainframe CICS application currently written in HLASM into COBOL. I have a number of utility programs that will continue to be used -- one of them is a "file access" utility which is accessed via CICS LINK. This utility takes the necessary input parameters (passed in the commarea) to generate a CICS file request to read a record from a specified file, and passes back the address of the storage area and length of the retrieved record in that same commarea. In assembler, it was easy to load that address into a register then associate that register with a record map (DSECT) via a USING directive.
But how is this best done in COBOL? Do I use the address passed back in the commarea and somehow associate it with the COBOL record layout so that I can reference a record field directly in the COBOL program? If so, how?
Or do I have to somehow move the data into a local working storage area? And then move it back to reflect any updates that may have been made by the COBOL program? Again, if so, how?
In the CICS COMMAREA declare a variable of type POINTER to hold the address of the record
buffer your utility program will return. For example:
01 COMM-CICS.
02 COMM-SOME-STUFF PIC whatever...
02 COMM-REC-BUFF-ADDRESS POINTER.
02 COMM-REC-BUFF-LENGTH PIC 9(9) BINARY.
02....
In your COBOL program declare the record layout in the
LINKAGE SECTION, for example:
LINKAGE SECTION.
01 LINK-REC.
02 LINK-DATA1 PIC X(10).
02 .....
This creates the layout but does not allocate any
storage to it. Upon return from your utility program use the COBOL SET ADDRESS verb to
assign the address of the record buffer to the record layout, something like:
SET ADDRESS OF LINK-REC TO COMM-REC-BUFF-ADDRESS
Now your COBOL program should be able to address any of the items in the file record by name, for example:
MOVE LINK-DATA1 TO some-other-variable
As a check on the returned data area, you can check the length of the buffer against the length of the COBOL record layout using the COBOL LENGTH OF verb, for example:
IF LENGTH OF LINK-REC NOT = COMM-REC-BUFF-LENGTH
raise an error - buffer length does not match record layout
END-IF
A warning though... This probably will not work if the LINK is to a remote machine because they will not be sharing the same address space. I would recommend that you investigate using CICS Channels and Contaners for this sort of thing.
Is a z/OS PL/I CONTROLLED variable preserved between separate invocations of a procedure? Let’s suppose that we need a counter that is internal to a subroutine and preserved across invocations. The easiest way to do it would be to use a static variable initialized to zero and incremented on each entry to the subroutine. But you can’t do that if the program has to be reentrant. So the question is whether we have access to a controlled variable that was allocated in a previous call. Would the following code work?
PROC1: PROCEDURE OPTIONS(MAIN);
...
CALL A;
...
A: PROCEDURE;
DECLARE COUNT CONTROLLED ALIGNED FIXED BIN(15);
IF (ALLOCATION(COUNT) = 0)
THEN ALLOCATE COUNT INIT(1);
ELSE COUNT = COUNT + 1;
...
END A;
END PROC1;
According to the PL/I Language Reference, after you ALLOCATE a variable, you do not need to FREE it (though that is generally good practice), and “All controlled storage is freed at the end of the program.” It doesn’t say that storage is freed at the end of the block.
The PL/I Programming Guide provides some clues in the chapter Using PLIDUMP in the Locating Controlled Variables section, but it is not definitive. It says that the key to locating a controlled variable is to find its anchor. With NORENT WRITABLE there is an anchor in static storage. With NORENT NOWRITABLE(FWS) there is an address to an anchor automatic storage. (There is an extra level of indirection.) With NORENT NOWRITABLE(PRV) there appears to be a static table with an offset into a private table for each controlled variable. In other words, depending on the processing options, maybe the variable is accessible, and maybe it isn’t. It doesn’t say anything about using the RENT option.
Any thoughts?
As per the PL/I Programming Guide compile time option "RENT", Your code is "naturally reentrant" if it does not alter any of its static variables.
The RENT option specifies that the compiler is to take code that is not naturally reentrant and make it reentrant.
Thus, you can increment STATIC variable on each entry to subroutine if the program is compiled with RENT option.
Please refer to this link => Rent Option from PL/I Programming Guide
As per "PL/I Structured Programming" by J.K. Hughes, A REENTRANT procedure may be called by other procedures asynchronously. For example task B invokes SQRT function. While this function is in process of computing square root, task A (having higher execution priority than task B) needs to gain control of the system and use SQRT function. The SQRT function is interrupted and intermediate results for task B saved; then task A uses SQRT function.
When task A completes its execution, control is returned to task B at the point where it was interrupted. Then, task B completes its use of SQRT function.