Programming API Overview and Concepts: System I
Programming API Overview and Concepts: System I
System i
Programming
API overview and concepts
Version 6 Release 1
System i
Programming
API overview and concepts
Version 6 Release 1
Note
Before using this information and the product it supports, read the information in “Notices,” on
page 575.
This edition applies to version 6, release 1, modification 0 of IBM i5/OS (product number 5761-SS1) and to all
subsequent releases and modifications until otherwise indicated in new editions. This version does not run on all
reduced instruction set computer (RISC) models nor does it run on CISC models.
© Copyright IBM Corporation 1998, 2008.
US Government Users Restricted Rights – Use, duplication or disclosure restricted by GSA ADP Schedule Contract
with IBM Corp.
Contents
Application programming interfaces . . 1 User spaces and receiver variables . . . . . . 70
APIs overview . . . . . . . . . . . . . . 1 User spaces . . . . . . . . . . . . 70
What's new for V6R1 . . . . . . . . . . . 3 General data structure . . . . . . . . 71
PDF files for APIs . . . . . . . . . . . . 5 Common data structure formats . . . . 72
API concepts . . . . . . . . . . . . . . 7 Example: User space format . . . . . . 75
API terminology . . . . . . . . . . . . 7 List sections . . . . . . . . . . . 75
Generic library names . . . . . . . . . . 8 Receiver variables . . . . . . . . . . 76
API naming conventions . . . . . . . . . 9 Bytes available and bytes returned fields 77
Language selection considerations . . . . . . 11 Keyed interface . . . . . . . . . . 78
Types of APIs. . . . . . . . . . . . . 12 User space alternative . . . . . . . . 78
APIs for the program-based environment . . 13 Continuation handle . . . . . . . . . . 78
APIs for the service-program-based Using a continuation handle . . . . . . . 79
environment . . . . . . . . . . . . 13 List APIs overview . . . . . . . . . . . 79
APIs for the ILE Common Execution General data structure for list APIs . . . . 79
Environment . . . . . . . . . . . . 14 User spaces for list APIs . . . . . . . . 82
Differences between program-based APIs and Logic flow of processing a list of entries . . 83
service-program-based APIs . . . . . . . 15 Manipulating a user space with pointers . 84
Example in ILE C: Logging software errors Manipulating a user space without pointers 85
(program API without pointers) . . . . 16 Examples: Changing a user space . . . . 85
Example in OPM COBOL: Logging Additional information about list APIs and
software errors (program API without user spaces . . . . . . . . . . . 89
pointers) . . . . . . . . . . . . 20 Example in CL: Listing database file
Example in OPM RPG: Logging software members . . . . . . . . . . . . 89
errors (program API without pointers) . . 24 Example in OPM RPG: List APIs . . . . . 90
Example in ILE RPG: Logging software Example in ILE CL: List APIs . . . . . . 97
error (program API without pointers). . . 27 Example in ILE C: List APIs . . . . . . 100
Example in ILE C: Reporting software Example in ILE RPG: List APIs . . . . . 107
errors (bindable API with pointers) . . . 29 Example in ILE COBOL: List APIs . . . . 112
Example in ILE COBOL: Reporting Domains . . . . . . . . . . . . . . 117
software errors (bindable API with Exit programs . . . . . . . . . . . . 117
pointers) . . . . . . . . . . . . 32 User index considerations . . . . . . . . 118
Example in ILE RPG: Reporting software Performance considerations. . . . . . . . 119
errors (bindable API with pointers) . . . 36 APIs and system objects . . . . . . . . . 119
APIs for the UNIX-type environment . . . . 38 Open list information format . . . . . . . 119
Examples: UNIX-type APIs . . . . . . 39 Path name format . . . . . . . . . . . 121
API information format . . . . . . . . . 50 Using APIs . . . . . . . . . . . . . . 123
API description . . . . . . . . . . . 50 Examples: Program-based APIs . . . . . . 123
API format . . . . . . . . . . . . 53 Example in OPM RPG: Retrieving the HOLD
API field descriptions . . . . . . . . . 53 parameter (exception message) . . . . . 124
API error messages . . . . . . . . . . 54 Example in ILE COBOL: Retrieving the
Extracting a field from the format . . . . . 54 HOLD parameter (exception message) . . . 129
Processing lists that contain data structures . . 55 Example in ILE C: Retrieving the HOLD
API parameters . . . . . . . . . . . . 55 parameter (exception message) . . . . . 130
Passing parameters . . . . . . . . . . 56 Example in ILE RPG: Retrieving the HOLD
Input and output parameters . . . . . . 56 parameter (exception message) . . . . . 132
Offset values and lengths . . . . . . . . 57 Example in OPM RPG: Retrieving the HOLD
Offset versus displacement considerations for parameter (error code structure) . . . . . 136
structures . . . . . . . . . . . . . 57 Example in ILE COBOL: Retrieving the
Error code parameter . . . . . . . . . 57 HOLD parameter (error code structure) . . 142
Error code parameter format . . . . . 58 Example in ILE C: Retrieving the HOLD
Examples: Receiving error conditions . . . 60 parameter (error code structure) . . . . . 144
Using the job log to diagnose API errors. . 61 Example in ILE RPG: Retrieving the HOLD
Include files and the QSYSINC library . . . . 62 parameter (error code structure) . . . . . 146
Internal object types . . . . . . . . . . 64 Example in OPM RPG: Printing the HOLD
Data types and APIs . . . . . . . . . . 69 value . . . . . . . . . . . . . . 148
Internal identifiers . . . . . . . . . . . 70
Contents v
vi System i: Programming API overview and concepts
Application programming interfaces
i5/OS® application programming interfaces (APIs) allow your application program written in a high-level
language to use specific data or functions of the IBM® i5/OS operating system.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
APIs overview
This API information describes most of the i5/OS APIs and some APIs for related licensed programs that
run on the i5/OS operating system.
APIs are intended for experienced application programmers to develop system-level and other i5/OS
applications. The API information provides reference only. It is neither an introduction to the i5/OS
licensed program nor a guide to writing i5/OS applications.
The API information can be found either by the API name through the API finder or by category through
the API categories page.
In the API finder, you can search for APIs by category, by API name, by descriptive name, or by part of
the name. You can also search for new APIs, changed APIs, and exit programs.
The API categories are major functional categories, such as backup and recovery, objects, and work
management. Within the individual categories, the APIs are organized in alphabetical order as follows:
v By the spelled-out name for the program-based APIs, the service-program-based APIs, and the
Integrated Language Environment® (ILE) Common Execution Environment (CEE) APIs.
v By the function name for the UNIX-type APIs.
IBM intends that the APIs will continue to work as they originally worked, and any existing applications
that use the APIs will continue to work without any incompatible changes. Significant architectural
changes, however, might necessitate incompatible changes.
Additionally, some API definitions, such as the UNIX type of API definitions, are established by industry
standards organizations, where the degree of compatibility is determined by the organizations.
In future releases, IBM also intends that one of the following statements is true:
v If additional input or output parameters are provided for any of the APIs, the new parameters will be
placed after the current parameters and will be optional parameters. The existing APIs will continue to
work without any changes.
v If an additional data structure is provided, a new format (layout of that data structure) will be created.
v New information might be added to the end of an existing format.
To ensure better compatibility with future releases, retrieve and use all of the following values when you
work with user spaces that are generated by list APIs:
Before system APIs were offered on the system, you had to either code separate CL programs to perform
the needed functions using the appropriate CL commands or code a call to the Execute Command
(QCMDEXC) API in your program. Both methods made coding an application on the system more
cumbersome (less straightforward and not as fast as possible).
CL commands will always be needed; they are ideal for the interactive user and for CL applications that
are performing basic tasks. They provide a complete set of functions on the system.
APIs are not provided as a replacement for CL commands, although in many cases there might be both
an API and a CL command that perform the same function. If a CL command and an API provide the
same function, at times the API provides more flexibility and information.
Some APIs have no equivalent CL commands. These APIs have been provided in areas where customers
and business partners have indicated that they need high-level language (HLL) access.
An API can be categorized by the type of action it performs and by the system function that it relates to.
Listed here are some of the types of APIs that perform actions; several examples of these APIs are
discussed in more detail in later topics.
v List APIs, which return lists of information about something on the system.
v Retrieve APIs, which return information to the application program.
v Create, change, and delete APIs, which work with objects of a specified type on the system.
v Other APIs, which perform a variety of actions on the system.
Although many APIs are used alone, some APIs can be used together to perform tasks or functions as in
these examples:
v Defining, creating, distributing, and maintaining your own software products.
v Controlling systems and networks, which includes configuration, spooled files, network management,
and problem management.
v Handling objects, which includes creating, changing, copying, deleting, moving, and renaming objects
on the system.
You can find a list of new APIs or changed APIs for this release using the API finder.
In addition, the following categories of APIs have been enhanced this release.
v Cluster
A new subcategory named Cluster Administrative Domain has been added. The Add Monitored
Resource Entry (QfpadAddMonitoredResourceEntry), Remove Monitored Resource Entry
(QfpadRmvMonitoredResourceEntry), and Retrieve Monitored Resource Information
(QfpadRtvMonitoredResourceInfo) APIs have been moved to this subcategory from the Server Support
category.
v Problem Management
A new subcategory named QTRC Trace has been added. The Problem Determination APIs have been
moved to the Problem Management category from the UNIX-Type category.
v Security
The NetWare Authentication Entry APIs have been removed from the Security – Network Security
category.
v UNIX-Type
The Data Conversion APIs have been moved from the National Language Support category to the
UNIX-Type category. These APIs have also been renamed Character Conversion APIs.
v Miscellaneous
The Miscellaneous category no longer includes a link to the User Application APIs. A new subcategory
named Service-related APIs has been added to the Miscellaneous category.
Miscellaneous technical updates have been made to the following API documentation:
v Configuration
– Retrieve ASP Copy Information (QYASRTVINF) API
A new subcategory of APIs has been added to the Database and File–Database category. See Storage
Engine APIs.
Miscellaneous technical updates have been made to the following API documentation:
v Cluster - Cluster Control
– Change Cluster Node Entry (QcstChangeClusterNodeEntry) API
– End Cluster Node (QcstEndClusterNode) API
– Remove Cluster Node Entry (QcstRemoveClusterNodeEntry) API
v Cluster - Cluster Resource Group
– Change Cluster Resource Group (QcstChangeClusterResourceGroup) API
– Initiate Switchover (QcstInitiateSwitchOver) API
– Remove Node From Recovery Domain (QcstRemoveNodeFromRcvyDomain) API
– Start Cluster Resource Group (QcstStartClusterResourceGroup) API
v Configuration
– Open List of ASPs (QYASPOL) API
– Start Disk Management Operation (QYASSDMO) API
v Database and File
– Cancel EDRS Request (QxdaCancelEDRS) API
v UNIX-Type - Integrated File System
– stat()--Get File Information
To help you see where technical changes have been made, the information center uses:
v The image to mark where new or changed information begins.
v The image to mark where new or changed information ends.
In PDF files, you might see revision bars (|) in the left margin of new and changed information.
To find other information about what's new or changed this release, see the Memo to users.
You need Adobe Reader installed on your system to view or print these PDFs. You can download a free
copy from the Adobe Web site (www.adobe.com/products/acrobat/readstep.html) .
API concepts
An application programming interface (API) is a functional interface supplied by the operating system or a
separately orderable licensed program that allows an application program written in a high-level
language to use specific data or functions of the operating system or the licensed program.
Some APIs provide the same functions as control language (CL) commands and output file support.
Some APIs provide functions that CL commands do not. Most APIs work more quickly and use less
system overhead than the CL commands.
API terminology
Before using the i5/OS APIs, you need to understand several terms that refer to i5/OS objects.
*ALLUSR excludes System/36 libraries that have names starting with the symbol # and that do
not contain user data. The following table lists those libraries.
#CGULIB #RPGLIB
#COBLIB #SDALIB
#DFULIB #SEULIB
#DSULIB
Except for the APIs that are defined by formal standards organizations (for example, UNIX-type APIs), an
API name starts with the letter Q, followed by 2, 3, or 4 letters that comprise an internal component
identifier. The last part of the API name identifies the action or function of the API.
The following table contains all of the verbs that are either part of an API name or are implied verbs
associated with an API name.
Table 1. Verbs and abbreviations for program-based and service-program-based APIs
Verb Abbreviation
access access
Add ADD, Add
Change C, CHG, Chg, ch
Check C, CHK, CHECK
Clear CLR, Clr
Close CLO, close
Complete Cmp
Control CTL
Convert CVT, CVRT, Convert
Copy CPY, Cpy
Create CRT, Crt, create
Customize CST
Delete DLT, Dlt
Deregister DRG, Deregister
Disable D
Display DSP, Dsp
Dump DMP, Dump
duplicate dup
Edit EDT
Enable E
End END, End
Execute (run) EXC, EXEC
Filter FTR
Force FRC
Generate GEN
Related concepts
“Types of APIs” on page 12
i5/OS APIs exist in several operating environments on a system.
ILE APIs that are implemented as service programs (*SRVPGM) can be directly accessed only by ILE
languages. For non-ILE languages, the Call Service Program Procedure (QZRUCLSP) API is available to
indirectly access service-program-based APIs. In some cases, an ILE API also provides a program (*PGM)
interface so that non-ILE languages can access the function.
Some APIs also require that particular data types and particular parameter passing conventions be used.
The following table shows the languages that are available with the i5/OS operating system and the data
types that they provide.
Table 2. Language selection considerations–data types
Language Pointers Binary 2 Binary 4 Character Zoned Packed Floating Structures Single Exception
decimal decimal point array handling
1 1
BASIC X X X X X X X X
(PRPQ
5799-FPK)
6
ILE C X X X X X X X X X
VisualAge® X X X X X 7
X X X X
C++ for
i5/OS
2
CL X X X X X X X X
2
ILE CL X X X X X X X X
3
COBOL X X X X X X X X X
3
ILE X X X X X X X X X X
COBOL
MI X X X X X X X X X X
4 4
Pascal X X X X X X X X X X
(PRPQ
5799-FRJ)
PL/I X X X X X X X X X X
(PRPQ
5799-FPJ)
2 2
REXX X X X X
5
RPG X X X X X X X X
5
ILE RPG X X X X X X X X X X
The following table shows the languages that are available with the i5/OS operating system and the
parameter support that they provide. See the reference information for the specific programming
language that you plan to use.
Table 3. Language selection considerations–call conventions
1
Language Function return values Pass by reference Pass by value
BASIC X
ILE C X X X
VisualAge C++ for i5/OS X X X
CL X
2
ILE CL X X X2
3
COBOL X
ILE COBOL X X X
MI X X
Pascal X
PL/I X
REXX X
RPG X
ILE RPG X X X
Notes:
1. Return values are used by the UNIX-type APIs and the Dynamic Screen Manager (DSM) APIs.
2. This support is available only when using the Call Bound Procedure (CALLPRC) command.
3. COBOL provides a by-content phrase, but it does not have the same semantics as ILE C pass-by-value.
Related concepts
“User spaces for list APIs” on page 82
List APIs require a user space to contain returned information.
Types of APIs
i5/OS APIs exist in several operating environments on a system.
Bindable procedure APIs, exported from ILE service programs (*SRVPGM), are independent of the
high-level languages. This can be useful when mixed languages are involved.
APIs based on service programs that are defined by IBM use the following naming conventions:
v Start with the letter Q.
v Are followed by a 2-, 3-, or 4-character internal component identifier.
v Can be up to 30 characters.
v Are case sensitive.
APIs based on service programs that are defined by industry standards follow the industry naming
conventions.
Bindable APIs are contained within service programs to which the calling program binds. Some bindable
APIs provide a program interface for the original program model (OPM) languages. You can usually
distinguish between the *SRVPGM interface and the *PGM interface by the name of the API. For
A binding directory is used for APIs that are contained in service programs. A binding directory is a list of
names of modules and service programs that provides a reference by name and type. Service programs
that export bindable APIs are in the QUSAPIBD binding directory. This binding directory is implicitly
used by ILE compilers to resolve the bindable API references; therefore, it is not necessary to explicitly
name the service program or the API binding directory when you create programs that use bindable
APIs. If you provide your own APIs with the same name, make sure that you also provide your own
binding directory or service program.
Most APIs (program-based and service-program-based) have a header file supplied by the i5/OS
operating system. These header files reside in the optionally installable library QSYSINC. The header files
provide the prototypes for the API and define any structures that are used by the API. The QSYSINC
library is used by the ILE C compiler to search for header files; therefore, it is not necessary to specify a
library qualifier for any header files that reside in the QSYSINC library. When you code in ILE C,
remember to enclose the header file name in less-than (<) and greater-than (>) symbols because this
affects how the library list is processed in locating the header file.
It is typical for an API that is not retrieving information not to return any output to the caller other than
the error code parameter. If no errors occur when APIs are used, the requested function is completed
successfully.
Related concepts
“API information format” on page 50
The format of the i5/OS API information includes sections such as parameters, authorities and locks,
required parameter group, format, field descriptions, and error messages.
“Examples: Service-program-based APIs” on page 189
These program examples demonstrate the use of service-program-based APIs in several different
high-level language programs. The example APIs represent two general functions of APIs: change and
retrieve.
The CEE APIs with names that begin with CEE4 or CEES4 are specific to business computing systems.
APIs based on service programs include the UNIX-type APIs and the ILE Common Execution
Environment (CEE) APIs. You must have the ILE language compiler on your system to develop
applications that use any bindable APIs.
The following table shows the differences between program-based APIs and service-program-based APIs.
Table 4. Comparison of program-based and service-program-based APIs
Description Program APIs Bindable APIs
API name Maximum number of characters: 8. Maximum number of characters: 30.
Not case sensitive. Case sensitive.
Required parameters1 Displayed in the parameter box. Displayed in the parameter box.
Optional parameters Can include optional parameters. The No optional parameters.
optional parameters form a group.
You must either include or exclude
the entire group.
Omitted parameters No omitted parameters. Can include omitted parameters.
When these parameters are omitted,
you must pass a null pointer.
Error conditions2 The error code parameter is common The error code parameter is common
to most of the program-based APIs. It to most of the bindable APIs whose
is used to return error codes and names start with the letter Q. The ILE
exception data to the application. The CEE APIs use feedback codes and
errors that are returned for a given conditions. The UNIX-type APIs
API are in the form of an error generally use errnos to provide
message and a 7-character message error-related information.
identifier.
Pointers Can be used, but are used less Because of the greater availability of
frequently than with the bindable pointer support in ILE languages,
APIs. there is a much greater use of
pointers in the bindable APIs. The
use of pointers can provide a
performance advantage.
Notes:
1. The UNIX-type APIs include parameters in a syntax box.
2. Error conditions
v The UNIX-type APIs use errnos and return values.
v The Dynamic Screen Manager (DSM) supports returned values in addition to the error code parameter.
The errnos are provided as include files in the QSYSINC library.
In the examples that follow, a program-based API and a service-program-based API are used to perform
similar functions (log or report software errors). The bindable API uses pointers while the program API
does not. Both APIs log software errors by using first-failure data capture (FFDC).
This ILE C program calls the Log Software Error (QPDLOGER) API to perform first-failure data capture
(FFDC) without using pointers.
The ILE C program physically moves the data that is pointed to, as shown at (1), which slows down
performance.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/*********************************************************************/
/* */
/*Program Name: FFDCPGM1 */
/* */
/*Program Language: ILE C */
/* */
/*Description: This program illustrates how to use APIs to log */
/* software errors using FFDC. */
/* */
/* */
/*Header Files Included: except */
/* stdio */
/* string */
/* qmhchgem */
/* qpdloger */
/* qusec */
/* */
/*APIs Used: QPDLOGER */
/* */
/*********************************************************************/
/*********************************************************************/
/*********************************************************************/
/* System Includes */
/*********************************************************************/
#include <except.h> /* from QSYSINC/H */
#include <stdio.h> /* from QSYSINC/H */
#include <string.h> /* from QSYSINC/H */
/*********************************************************************/
/* Miscellaneous Includes */
/*********************************************************************/
#include <qmhchgem.h>
#include <qpdloger.h>
#include <qusec.h>
/*********************************************************************/
/* Structures */
/*********************************************************************/
typedef struct {
void *parm1;
void *parm2;
char *pgm_name;
int pgm_name_size;
} ffdc_info_t;
/*********************************************************************/
/*********************************************************************/
/* FUNCTION NAME: main */
/* */
/* FUNCTION: Generates exception and then passes control */
/* to exception handler. */
/* */
/* INPUT: Two character strings. */
/* */
/* OUTPUT: NONE */
/* */
/* EXCEPTIONS: CPFxxxx - All unexpected CPF exceptions */
/* MCHxxxx - All unexpected MCH exceptions */
/* */
/*********************************************************************/
void main(int argc, char *argv[])
{
/*******************************************************************/
/* NOTE: argv will contain the parameters passed in to this */
/* function. In this case, two parameters are passed */
/* in. */
/*******************************************************************/
/*******************************************************************/
/* The argv parameter contains the parameters that were passed as */
/* character arrays. argv[0] contains the program name, and the */
/* parameter(s) starts with argv[1]. */
/*******************************************************************/
/*******************************************************************/
/* Set up FFDC information for unexpected error. */
/*******************************************************************/
ffdc_info.parm1 = argv[1];
ffdc_info.parm2 = argv[2];
ffdc_info.pgm_name = pgm_name;
memcpy(pgm_name, argv[0], strlen(argv[0]));
ffdc_info.pgm_name_size = strlen(argv[0]);
/*******************************************************************/
/* Enable the exception handler, and pass ffdc_info into the */
/* exception handler via the communications area so that data */
/* can be used for FFDC. */
/*******************************************************************/
/*******************************************************************/
/* Set the pointer to null, then try to increment. This will */
/* generate an MCH3601 error that will be trapped by the */
/* unexpected handler. */
/*******************************************************************/
nulptr = NULL;
nulptr++;
#pragma disable_handler
} /* main */
/*********************************************************************/
typedef struct {
char obj_name[30];
char obj_lib[30];
char obj_type[10];
} obj_info_t;
typedef struct {
int data_offset;
int data_length;
} data_info_t;
char pgm_suspected[10],
msg_id[12],
msg_key[4],
print_job_log,
data[2*(sizeof(char *))],
*data_item,
ile_mod_name[11];
int point_of_failure,
num_items,
num_objs;
data_info_t data_info[2];
obj_info_t obj_info[1];
ffdc_info_t *ffdc_info;
Qus_EC_t ErrorCode;
ErrorCode.Bytes_Provided = 0;
/*******************************************************************/
/* Getting pointer in local storage to the Communications Area. */
/*******************************************************************/
ffdc_info = (ffdc_info_t *)(errmsg->Com_Area);
/*******************************************************************/
/* Need to notify message handler that we will handle the error. */
/* Leave the message in the job log, just mark it handled. */
/*******************************************************************/
QMHCHGEM(&(errmsg->Target), /* Invocation pointer */
0, /* Call stack counter */
(char *)&errmsg->Msg_Ref_Key,/* Message key */
"*HANDLE ", /* Modification option */
"", /* Reply text */
0, /* Reply text length */
&ErrorCode); /* Error code */
/*******************************************************************/
/* Set up the suspected program. */
/*******************************************************************/
memcpy(pgm_suspected, "*PRV ", 10);
/*******************************************************************/
/* Set up the message key. */
/*******************************************************************/
memcpy(msg_key, (char *)&errmsg->Msg_Ref_Key, 4);
/*******************************************************************/
/* Set up point of failure. Since this example program is small */
/* and we know where the error occurred, we will just put a dummy */
/* value in. However, this can be very useful information in */
/* larger programs. */
/*******************************************************************/
point_of_failure = 100;
/*******************************************************************/
/* Set up to print the job log. */
/*******************************************************************/
print_job_log = ’Y’;
/*******************************************************************/
/* Set up data items. */
/*******************************************************************/
data_item = data;
/*******************************************************************/
/* Put in first parameter. */
/*******************************************************************/
memcpy(data_item, (char *)ffdc_info->parm1, sizeof(char *)); (1)
/*******************************************************************/
/* Add in the second parameter. */
/*******************************************************************/
data_item += sizeof(char *);
memcpy(data_item, (char *)ffdc_info->parm2, sizeof(char *));
/*******************************************************************/
/* Reset the data item pointer. */
/*******************************************************************/
data_item -= sizeof(char *);
/*******************************************************************/
/* Set up data item offset/length information. */
/*******************************************************************/
data_info[0].data_offset = 0;
data_info[0].data_length = sizeof(char *);
data_info[1].data_offset = sizeof(char *);
data_info[1].data_length = sizeof(char *);
/*******************************************************************/
/* Set up the number of data items. In this case we only have one.*/
/*******************************************************************/
num_items = 2;
/*******************************************************************/
/* Set up the object name array. In this case, we have no objects */
/* to dump, but we will put dummy values in to illustrate. */
/*******************************************************************/
memcpy(obj_info[0].obj_name, "OBJUSRSPC ", 30);
memcpy(obj_info[0].obj_lib, "QTEMP ", 30);
memcpy(obj_info[0].obj_type, "*USRSPC ", 10);
/*******************************************************************/
/*******************************************************************/
/* Set up the ILE module name. */
/*******************************************************************/
memcpy(ile_mod_name, ffdc_info->pgm_name, ffdc_info->pgm_name_size);
/*******************************************************************/
/* Call QPDLOGER to perform FFDC. */
/*******************************************************************/
ErrorCode.Bytes_Provided = sizeof(ErrorCode);
QPDLOGER(pgm_suspected,
msg_id,
msg_key,
point_of_failure,
&print_job_log,
data_item,
data_info,
num_items,
obj_info,
num_objs,
&ErrorCode,
ile_mod_name);
} /* UNEXPECTED_HDLR */
Related reference
“Example in OPM COBOL: Logging software errors (program API without pointers)”
One OPM COBOL program registers an error handler. After successful completion of the registration, this
program creates a data decimal error. The error causes the error handler, the other OPM COBOL
program, to call the Log Software Error (QPDLOGER) API without using pointers.
“Example in OPM RPG: Logging software errors (program API without pointers)” on page 24
This OPM RPG program performs a divide-by-zero operation to cause an exception. The exception is
caught with RPG *PSSR support, which calls the Log Software Error (QPDLOGER) API to perform FFDC
without using pointers.
“Example in ILE RPG: Logging software error (program API without pointers)” on page 27
This ILE RPG program performs a divide-by-zero operation to cause an exception. The exception is
caught with RPG *PSSR support, which calls the Log Software Error (QPDLOGER) API to perform FFDC
without using pointers.
Example in OPM COBOL: Logging software errors (program API without pointers):
One OPM COBOL program registers an error handler. After successful completion of the registration, this
program creates a data decimal error. The error causes the error handler, the other OPM COBOL
program, to call the Log Software Error (QPDLOGER) API without using pointers.
The program CBLERR1 registers the error handler and causes the error. The program ERRHDL1 receives
control and calls the QPDLOGER API to perform FFDC.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
CBLERR1 program
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: Register an OPM COBOL Error Handler
* Cause a data decimal exception to demonstrate
ERRHDL1 program
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: Log a software error
*
* Language: COBOL
*
* Description: This program receives control for exceptions
* within a COBOL run unit. This program is used
* in conjunction with CBLERR1. ,
* Any exception causes this error handler to be
* called which then logs the software error.
*
* APIs Used: QPDLOGER - Log Software Error
*
***************************************************************
*
***************************************************************
PROGRAM-ID. ERRHDL1.
INPUT-OUTPUT SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
*
* Error Code parameter include. As this sample program
* uses COPY to include the error code structure, only the first
* 16 bytes of the error code structure are available. If the
* application program needs to access the variable length
* exception data for the error, the developer should physically
* copy the QSYSINC include and modify the copied include to
* define additional storage for the exception data.
*
COPY QUSEC OF QSYSINC-QLBLSRC.
*
* Miscellaneous elements
*
01 MISC.
05 LOG-EXCEPTION-ID PIC X(12).
05 MESSAGE-KEY PIC X(04).
05 POINT-OF-FAILURE PIC S9(09) BINARY VALUE 1.
05 PRINT-JOBLOG PIC X(01) VALUE "Y".
05 NBR-OF-ENTRIES PIC S9(09) BINARY.
05 NBR-OF-OBJECTS PIC S9(09) BINARY VALUE 1.
01 MESSAGE-INFO.
Example in OPM RPG: Logging software errors (program API without pointers):
This OPM RPG program performs a divide-by-zero operation to cause an exception. The exception is
caught with RPG *PSSR support, which calls the Log Software Error (QPDLOGER) API to perform FFDC
without using pointers.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F****************************************************************
F*
F* Program: Demonstrate use of OPM-based Log Software Error
F*
F* Language: OPM RPG
F*
F* Description: This program performs a divide-by-0 operation
F* to cause an exception. This exception is
F* caught using RPG *PSSR support,
F* and the exception is then logged as a
F* software error.
F*
F* APIs used: QPDLOGER
F*
F****************************************************************
E*
E* Arrays used to extract source line number where error happened
E*
E SRC 8 1
E TGT 8 1
I*
I* Error Code parameter include. As this sample program uses
I* /COPY to include the error code structure, only the first
I* 16 bytes of the error code structure are available. If the
I* application program needs to access the variable length
I* exception data for the error, the developer should physically
I* copy the QSYSINC include and modify the copied include to
I* define additional storage for the exception data.
I*
I/COPY QSYSINC/QRPGSRC,QUSEC
I*
I* Define Program Status Data Structure
Example in ILE RPG: Logging software error (program API without pointers):
This ILE RPG program performs a divide-by-zero operation to cause an exception. The exception is
caught with RPG *PSSR support, which calls the Log Software Error (QPDLOGER) API to perform FFDC
without using pointers.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F********************************************************************
F*
F* Program: Demonstrate use of OPM based Log Software Error
F*
F* Language: ILE RPG
F*
F* Description: This program performs a divide by 0 operation to
F* cause an exception. This exception is caught using
F* RPG’s *PSSR support, and the exception is then
F* logged as a software error.
F*
F* APIs used: QPDLOGER
F*
F********************************************************************
D*
D* Include Error Code Parameter
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC
D*
D* Misc. data elements
D*
Dfactor1 S 5B 0 INZ(10)
Dfactor2 S 5B 0 INZ(0)
Dresult S 5B 0
Dline_nbr S 9B 0
Ddata DS 4096
Ddatapt DS
D data_off 9B 0
D data_len 9B 0
Ddata# S 9B 0
Dobjl DS 2590
Dobjl# S 9B 0
D*
D* Program status data structure
D*
DPSDS SDS
D pgm_name 1 10
D status 11 15 0
D src_line 21 28
D exception 40 46
D lib_name 81 90
C*
C* Attempt to divide by 0
C*
C factor1 div factor2 result
C*
C* Should not get here due to divide by 0 exception
C*
C move ’1’ *INLR
C return
C*
This ILE C program calls the Report Software Error (QpdReportSoftwareError) API to perform FFDC
using pointers.
The ILE C program sets a pointer, as shown at (2), to point to the same location as in “Example in ILE C:
Logging software errors (program API without pointers)” on page 16 at (1).
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/*********************************************************************/
/* */
/*Program Name: FFDCPGM2 */
/* */
/*Program Language: ILE C */
/* */
/*Description: This program illustrates how to use APIs to log */
/* software errors using FFDC. */
/* */
/* */
/*Header Files Included: except */
/* stdio */
/* string */
/* qmhchgem */
/* qpdsrvpg */
/* qusec */
/* */
/*APIs Used: QpdReportSoftwareError */
/* */
/*********************************************************************/
/*********************************************************************/
/*********************************************************************/
/* System Includes */
/*********************************************************************/
#include <except.h> /* from QSYSINC/H */
#include <stdio.h> /* from QSYSINC/H */
#include <string.h> /* from QSYSINC/H */
/*********************************************************************/
/* Miscellaneous Includes */
/*********************************************************************/
#include <qmhchgem.h>
#include <qpdsrvpg.h>
#include <qusec.h>
/*********************************************************************/
/* Definitions used for developing key information for FFDC. */
/*********************************************************************/
#define CHARACTER ’C’
#define MAX_KEYS 3
#define MESSAGE "MSG"
#define MESSAGE_LEN 7
#define MSG_SYMPTOM_LEN 3
/*********************************************************************/
/* Structures */
/*********************************************************************/
typedef struct {
void *parm1;
/*********************************************************************/
/* Prototypes */
/*********************************************************************/
void UNEXPECTED_HDLR(_INTRPT_Hndlr_Parms_T *);
/*********************************************************************/
/* FUNCTION NAME: main */
/* */
/* FUNCTION: Generates exception and then passes control */
/* to exception handler. */
/* */
/* INPUT: Two character strings. */
/* */
/* OUTPUT: NONE */
/* */
/* EXCEPTIONS: CPFxxxx - All unexpected CPF exceptions */
/* MCHxxxx - All unexpected MCH exceptions */
/* */
/*********************************************************************/
void main(int argc, char *argv[])
{
/*******************************************************************/
/* NOTE: argv will contain the parameters passed in to this */
/* function. In this case, two parameters are passed */
/* in. */
/*******************************************************************/
/*******************************************************************/
/* The argv parameter contains the parameters that were passed as */
/* character arrays. argv[0] contains the program name, and the */
/* parameter(s) starts with argv[1]. */
/*******************************************************************/
/*******************************************************************/
/* Set up FFDC information for unexpected error. */
/*******************************************************************/
ffdc_info.parm1 = argv[1];
ffdc_info.parm2 = argv[2];
ffdc_info.pgm_name = pgm_name;
memcpy(pgm_name, argv[0], strlen(argv[0]));
ffdc_info.pgm_name_size = strlen(argv[0]);
/*******************************************************************/
/* Enable the exception handler, and pass ffdc_info into the */
/* exception handler via the communications area so that data */
/* can be used for FFDC. */
/*******************************************************************/
/*******************************************************************/
/* Set the pointer to null, then try to increment. This will */
/* generate an MCH3601 error that will be trapped by the */
/* unexpected handler. */
/*******************************************************************/
nulptr = NULL;
nulptr++;
#pragma disable_handler
/*********************************************************************/
/* FUNCTION NAME: UNEXPECTED_HDLR */
/* */
/* FUNCTION: Handle unexpected exception. This exception */
/* handler is used to log the software error via */
/* FFDC. */
/* */
/* INPUT: Interrupt handler information */
/* */
/* OUTPUT: NONE */
/* */
/* EXCEPTIONS: CPFxxxx - All unexpected CPF exceptions */
/* MCHxxxx - All unexpected MCH exceptions */
/* */
/*********************************************************************/
void UNEXPECTED_HDLR(_INTRPT_Hndlr_Parms_T *errmsg)
{
int i = 0,
MsgLen = 0,
number_of_keys = 0;
char pgm_name[30],
context_name[30],
lib_name[5],
symptom_msg_data[MESSAGE_LEN],
symptom_msg_keyword[MSG_SYMPTOM_LEN];
ffdc_info_t *ffdc_info;
Qpd_Data_t data_key,
data_key2;
Qpd_Key_Pointer_t ffdc_keys[MAX_KEYS];
Qpd_Suspected_Module_t module_key;
Qpd_Symptom_t symptom_msg_key;
Qus_EC_t ErrorCode;
ErrorCode.Bytes_Provided = 0;
/*******************************************************************/
/* Getting pointer in local storage to the Communications Area. */
/*******************************************************************/
ffdc_info = (ffdc_info_t *)(errmsg->Com_Area);
/*******************************************************************/
/* Need to notify message handler that we will handle the error. */
/* Leave the message in the job log, just mark it handled. */
/*******************************************************************/
QMHCHGEM(&(errmsg->Target), /* Invocation pointer */
0, /* Call stack counter */
(char *)&errmsg->Msg_Ref_Key,/* Message key */
"*HANDLE ", /* Modification option */
"", /* Reply text */
0, /* Reply text length */
&ErrorCode); /* Error code */
/*******************************************************************/
/* Initialize module suspected key for FFDC. */
/*******************************************************************/
ffdc_keys[number_of_keys++].Suspected_Module = &module_key;
module_key.Key = Qpd_Suspected_Module;
module_key.Module_Name_Length = ffdc_info->pgm_name_size;
module_key.Library_Name_Length = 7;
module_key.Module_Name = pgm_name;
memcpy(pgm_name, ffdc_info->pgm_name, ffdc_info->pgm_name_size);
module_key.Library_Name = lib_name;
memcpy(lib_name, "TESTLIB", 7);
/*******************************************************************/
/* Parameter 1 information */
/*******************************************************************/
ffdc_keys[number_of_keys++].Data = &data_key;
data_key.Key = Qpd_Data;
data_key.Data_Length = sizeof(char *);
data_key.Data_Id = 1;
data_key.Data = ffdc_info->parm1; (2)
/*******************************************************************/
/* Parameter 2 information */
/*******************************************************************/
ffdc_keys[number_of_keys++].Data = &data_key2;
data_key2.Key = Qpd_Data;
data_key2.Data_Length = sizeof(char *);
data_key2.Data_Id = 1;
data_key2.Data = ffdc_info->parm2;
/*******************************************************************/
/* Call QpdReportSoftwareError to perform FFDC. */
/*******************************************************************/
ErrorCode.Bytes_Provided = sizeof(ErrorCode);
QpdReportSoftwareError(ffdc_keys,
&number_of_keys,
&ErrorCode);
} /* UNEXPECTED_HDLR */
Example in ILE COBOL: Reporting software errors (bindable API with pointers):
One ILE COBOL program registers an error handler. After the successful completion of the registration,
this program creates a data decimal error. The error causes the error handler, the other ILE COBOL
program, to call the Report Software Error (QpdReportSoftwareError) API using pointers.
In this example, the “CBLERR2 program” registers the error handler and causes the error. The
“ERRHDL2 program” on page 34 sets a pointer, as shown at (2), to point to the same location as in
“Example in ILE C: Logging software errors (program API without pointers)” on page 16 at (1) and calls
the QpdReportSoftwareError API to perform FFDC.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
CBLERR2 program
PROCESS NOMONOPRC.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: Register an ILE COBOL Error Handler
ERRHDL2 program
PROCESS NOMONOPRC.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: Log a software error
*
* Language: ILE COBOL
*
* Description: This program receives control for exceptions
* within a COBOL run unit. This program is used
* in conjunction with CBLERR2. ,
* Any exception causes this error handler to be
* called which then logs the software error.
*
* APIs Used: QpdReportSoftwareError
*
***************************************************************
*
***************************************************************
PROGRAM-ID. ERRHDL2.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
WORKING-STORAGE SECTION.
*
* Error Code parameter include. As this sample program
* uses COPY to include the error code structure, only the first
* 16 bytes of the error code structure are available. If the
* application program needs to access the variable length
* exception data for the error, the developer should physically
* copy the QSYSINC include and modify the copied include to
* define additional storage for the exception data.
*
COPY QUSEC OF QSYSINC-QCBLLESRC.
*
* QpdReportSoftwareError include
*
COPY QPDSRVPG OF QSYSINC-QCBLLESRC.
*
Example in ILE RPG: Reporting software errors (bindable API with pointers):
This ILE RPG program performs a divide-by-zero operation to cause an exception. The exception is
caught with RPG *PSSR support, which calls the Report Software Error (QpdReportSoftwareError) API
using pointers.
The ILE RPG program sets a pointer, as shown at (2), to point to the same location as in “Example in ILE
C: Logging software errors (program API without pointers)” on page 16 at (1) and calls the
QpdReportSoftwareError API to perform FFDC.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F********************************************************************
F*
F* Program: Demonstrate use of ILE-based Report Software Error
F*
F* Language: ILE RPG
F*
F* Description: This program performs a divide-by-0 operation to
F* cause an exception. This exception is caught using
F* RPGs *PSSR support, and the exception is then logged
F* as a software error.
F*
F* APIs used: QpdReportSoftwareError
F*
F********************************************************************
D*
D* Include Error Code Parameter
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC
D*
The socket functions and the integrated file system reduce the amount of effort to move UNIX
applications to the system.
The naming conventions for the UNIX-type APIs are determined by industry standards organizations.
Related reference
UNIX-Type APIs
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
char InitialFile[BUFFER_SIZE];
char InitialDirectory[BUFFER_SIZE] = ".";
char Buffer[32];
int main ()
{
(1)
/* Get and print the real user id with the getuid() function. */
UserID = getuid();
printf("The real user id is %u. \n",UserID);
(2)
/* Get the current working directory and store it in InitialDirectory. */
if ( NULL == getcwd(InitialDirectory,BUFFER_SIZE) )
{
perror("getcwd Error");
CleanUpOnError(1);
return 0;
(3)
/* Create the file TEST_FILE for writing, if it does not exist.
Give the owner authority to read, write, and execute. */
FilDes = open(TEST_FILE, O_WRONLY | O_CREAT | O_EXCL, S_IRWXU);
if ( -1 == FilDes )
{
perror("open Error");
CleanUpOnError(2);
return 0;
}
printf("Created %s in directory %s.\n",TEST_FILE,InitialDirectory);
(4)
/* Write TEST_DATA to TEST_FILE via FilDes */
BytesWritten = write(FilDes,TEST_DATA,strlen(TEST_DATA));
if ( -1 == BytesWritten )
{
perror("write Error");
CleanUpOnError(3);
return 0;
}
printf("Wrote %s to file %s.\n",TEST_DATA,TEST_FILE);
(5)
/* Close TEST_FILE via FilDes */
if ( -1 == close(FilDes) )
{
perror("close Error");
CleanUpOnError(4);
return 0;
}
FilDes = -1;
printf("File %s closed.\n",TEST_FILE);
(6)
/* Open the TEST_FILE file for reading only. */
if ( -1 == (FilDes = open(TEST_FILE,O_RDONLY)) )
{
perror("open Error");
CleanUpOnError(5);
return 0;
}
printf("Opened %s for reading.\n",TEST_FILE);
(7)
/* Read from the TEST_FILE file, via FilDes, into Buffer. */
BytesRead = read(FilDes,Buffer,sizeof(Buffer));
if ( -1 == BytesRead )
{
perror("read Error");
CleanUpOnError(6);
return 0;
}
printf("Read %s from %s.\n",Buffer,TEST_FILE);
if ( BytesRead != BytesWritten )
{
printf("WARNING: the number of bytes read is "\
"not equal to the number of bytes written.\n");
}
(8)
/* Close the TEST_FILE file via FilDes. */
(9)
/* Unlink the file TEST_FILE */
if ( -1 == unlink(TEST_FILE) )
{
perror("unlink Error");
CleanUpOnError(8);
return 0;
}
printf("Unlinking file %s.\n",TEST_FILE);
API description
For most APIs, the description information contains similar sections.
v “Parameters”
v “Authorities and locks” on page 51
v “Required parameter group” on page 51
v “Optional parameter group” on page 53
This topic uses the Retrieve Job Description Information (QWDRJOBD) API as an example to illustrate
how to use the information in each section. In the following discussion, assume that you are interested in
accessing the value of the HOLD parameter of a job description. For example programs to which the
discussion of the QWDRJOBD API is related, see “Examples: Program-based APIs” on page 123.
Parameters
The first column in the Parameters box lists the required order of the parameters.
The third column lists whether the parameter is defined for input, output, or input and output. Input
parameters and fields are not changed by the API. They have the same values on the return from the API
call as they do before the API call. In contrast, output parameters are changed. Any information that an
API caller places in an output parameter or output field before the call can be lost on the return from the
call.
For the QWDRJOBD API, you must pass 5 parameters as shown in the following RPG CALL statement:
C CALL ’QWDRJOBD’
C PARM QWDBH Receiver Var.
C PARM RCVLEN Length QWDBH
C PARM FORMAT Format Name
C PARM LFNAM Qual. Job Desc
C PARM QUSBN Error Code
Note: There is no parameter for the HOLD information. The first parameter, receiver variable (QWDBH),
is where the information is passed back from the QWDRJOBD API. You receive a data structure
that contains the information. You need to find the specific location within the data structure for
where the HOLD information is stored.
For complete RPG example programs that use the QWDRJOBD API, see “Examples: Program-based
APIs” on page 123.
The Authorities and Locks section lists all the authorities that you need to use an API. This section also
lists the locks that the API uses.
To use an API, you must have the correct authority to the API, to all the objects that the API uses, and to
any locks that the API places on any objects.
Locks are based on the objects that the API uses. The type of locking that occurs, such as whether the
object can be used by more than one user at the same time, is based on what actions the API performs on
the object.
For the QWDRJOBD API, you must have *USE authority to both the job description object and the library
to access the object. This is the same type of authority that is required for most situations where you
want to display or retrieve information in an object. For example, it is the same authority that you would
need to use the Display Job Description (DSPJOBD) command. Because no specific information is
described for locks, you can assume that nothing unusual is required.
The Required Parameter Group section lists all the parameters that are required for an API. You must use
all of the parameters in the order that they are listed. None of the parameters can be left out.
The details about each parameter that must be used on the call to the QWDRJOBD API are described in
the Required Parameter Group section in Retrieve Job Description Information (QWDRJOBD) API.
Receiver variable
A receiver variable is the name of the variable where the retrieved information is placed, for example,
QWDBH in the example RPG program in “Parameters” on page 50.
You can see from the Dec (decimal offset) column of the JOBD0100 format table that at least 390 bytes
plus additional bytes (of unknown length) for the initial library list and the request data are returned.
“Example in OPM RPG: Accessing a field value (initial library list)” on page 156 describes how to
determine the lengths of these fields. For now, you should focus on the fixed portion (390 bytes) of the
format.
You can receive the maximum or enough bytes to contain the information in which you are interested.
Because the value of the hold on job queue field starts at decimal 76, you can specify that the receiver
variable is 100 bytes in length (or any number greater than or equal to 86 bytes). You do not need to be
precise about the length of the receiver variable. Whatever you specify is the amount of data that is
returned. You can truncate a value in the middle of a field in the format or specify more length than the
format has.
Assume that you want to receive the fixed information, a length of 390 bytes, shown at (1) in “Example
in OPM RPG: Retrieving the HOLD parameter (exception message)” on page 124. If you are going to call
the API once, no measurable performance gain occurs if you specify anything less than the maximum.
When defining the length of your receiver variable, you usually use the length of the information that
you want to receive. The length of the receiver variable parameter must be set to a value equal to or less
than the length that you defined the receiver variable parameter to be.
You normally enter the length that you have specified for the receiver variable. Remember that in this
example, you want to declare the receiver variable to be 390 bytes in length. The length of the receiver
variable parameter has a value of 390 assigned to it, shown at (2) in “Example in OPM RPG: Retrieving
the HOLD parameter (exception message)” on page 124. You could have specified a different value, but
the value must be the same or less than the size of the variable in your program. In Example: RPG call
statement parameters, RCVLEN is the length of the receiver variable parameter.
The length field, according to the required parameter group, must be described as BINARY(4). This
means that a field of 4 bytes is passed where the value is specified in binary. You need to know how
your high-level language allows you to define a 4-byte field and place a binary value in it.
Format name
A format name is a name that identifies what type of information you want returned in the receiver
variable. Because the QWDRJOBD API has a single format name, JOBD0100, you use this format name
shown at (3) in “Example in OPM RPG: Retrieving the HOLD parameter (exception message)” on page
124. The format name variable in the example program is called FORMAT. You can place the format
name in a variable or pass it as a literal.
This name must be passed as a 20-character name with the job description name in the first 10 characters
and the library qualifier beginning in the 11th character. If you want JOBD1 in LIBX, specify as follows:
1 11 20
. . .
. . .
JOBD1 LIBX
The special value *CURLIB or *LIBL can be used as the library qualifier.
Error code
The include file QUSEC contains the definition for the error code structure that is used for the error code
parameter.
You can choose to receive exceptions (escape messages) or to receive an error-code data structure that
allows you to determine whether an exception occurs. Depending on your high-level language, you
might not have a choice for which method you use. You might have to use the error-code data structure
because some languages do not provide for the monitoring of escape messages.
In “Example in OPM RPG: Retrieving the HOLD parameter (exception message)” on page 124, the RPG
program requests that exceptions be sent if any errors occur. To provide this type of exception handling, a
4-byte binary field with a value of zero must be passed, as shown at (4). This indicates to the API that
you want exception messages to be sent.
Some APIs have optional parameters. The optional parameters form a group. You must either include or
exclude the entire group. You cannot use only one of these parameters. You must include all preceding
parameters.
The API can be called either with or without the optional parameters.
The Retrieve Job Description Information (QWDRJOBD) API does not have an optional parameter group.
The List Job (QUSLJOB) API has an optional parameter group.
Related reference
List Job (QUSLJOB) API
API format
The format section in the API information shows the type of information to be returned by an API.
For example, for the Retrieve Job Description Information (QWDRJOBD) API, the format described is
JOBD0100 Format. Listed within the format are the individual fields that contain the attributes of the job
description. The offset in the Dec (decimal offset) column for the hold on job queue field (hold parameter
on the Create Job Description (CRTJOBD) or Change Job Description (CHGJOBD) command) begins at
decimal offset 76.
The fields in the format do not occur in any particular sequence. To determine what you want, you need
to scan the format.
The QWDRJOBD API has only a single format; other APIs can have multiple formats where each format
has different levels of information. With multiple formats, a format name parameter allows you to specify
which format you want to retrieve.
Related reference
Retrieve Job Description Information (QWDRJOBD) API
“General data structure for list APIs” on page 79
The data structure for the list APIs consists of several fields.
Message IDs normally exist in the QCPFMSG file. You might want to program for these messages,
regardless of the high-level language that you use. If you need more information about the messages, use
the Display Message Description (DSPMSGD) command.
Related reference
Retrieve Job Description Information (QWDRJOBD) API
Display Message Description (DSPMSGD) command
An offset is shown in both decimal and hexadecimal. Depending on the high-level language that you use,
either offset might be helpful. For CL and RPG, you normally use the decimal offset. With either offset,
you must remember whether your language works with the offset from a base of 0 or a base of 1. The
API format tables are prepared for languages that work from a base of 0, but not all languages can use
this base. CL and RPG, for example, work from a base of 1, so you need to add 1 to the decimal value of
each offset. The hold on job queue field begins at decimal offset 76, for example. To access the
information in CL or RPG, you need to address byte 77 within the receiver variable.
Using the format, you can tell that the field after the hold on job queue field, output queue name, begins
in offset 86. This means that the hold on job queue information is in the following location from a CL or
RPG perspective:
77 86
. .
. .
XXXXXXXXXX
The only possible values for the hold on job queue field are *YES and *NO. They are left-aligned in the
field and the remaining positions are blank.
Most of the formats provide additional bytes for each field to allow expansion, such as a new value for
the hold on job queue field that is more than 4 bytes.
Many of the needed structures are provided by the system include library, QSYSINC. However, any fields
of a structure that are variable in length are not defined in the QSYSINC library. These variable-length
fields must be defined by the user, as shown at (3) in “Example in OPM RPG: Accessing a field value
(initial library list)” on page 156.
A good example is the Retrieve System Status (QWCRSSTS) API. It supports multiple formats for
different types of information. The SSTS0300 format contains a list where each entry in the list has the
information about a particular storage pool. In addition to the two critical fields (the offset to where the
list begins field and the number of entries in the list field), the format also supports a field that describes
the length of each entry. In the initial library list, each entry was 11 bytes long. But in a storage pool, a
field (length of pool information entry) describes the length and should be used instead of a fixed-length
increment. This allows for growth, such as more information being available in another release for each
list entry.
If another field is added to describe additional information about a storage pool, it is probably added
after the paging option field. The length of pool information entry allows your code to be compatible
with future releases while it retains the locations (relative to the start of a list entry) of the current fields.
Related reference
Retrieve System Status (QWCRSSTS) API
API parameters
After you decide which API to use, you need to code a call to the API and pass to the API the set of
parameters that are appropriate for it.
For program-based and service-program-based APIs, the values for all parameters that identify objects on
the system must be in *NAME (basic name) format, left-aligned, uppercase, and with valid special
characters. (The *NAME format is a character string that must begin with an alphabetic character (A
through Z, $, #, or @) followed by up to 9 characters (A through Z, 0 through 9, $, #, @, ), or _). The
system uses an object name as is; it does not change or check the object name before locating the object.
This can improve the performance of the API. An incorrect name usually causes an Object not found
error.
Passing parameters
High-level languages pass parameters to an API by value, directly; by value, indirectly; or by reference.
Depending on the high-level language that you use, parameters can be passed in the following ways:
By value, directly
The value of the data object is placed directly into the parameter list.
By value, indirectly
The value of the data object is copied to a temporary location. The address of the copy (a pointer)
is placed into the parameter list. By value, indirectly is not done explicitly by the application
programmer. It is done by the high-level language at run time.
By reference
A pointer to the data object is placed into the parameter list. Changes made by the called API to
the parameter are reflected in the calling application.
When you call an API, the protocol for passing parameters is to typically pass a space pointer that points
to the information being passed. (This is also referred to as pass-by-reference.) This is the convention
used by default for the control language (CL), RPG, and COBOL compilers. Care must be used in those
languages that support pass-by-value (such as ILE C) to ensure that these conventions are followed. Refer
to the appropriate language documentation for instructions. The parameter passing convention of
pass-by-reference can be used in all programming languages. Some of the UNIX-type APIs require
pass-by-value parameter passing. VisualAge C++ for i5/OS also supports pass-by-value parameter
passing.
High-level semantics usually determine when data is passed by value and when it is passed by reference.
For example, ILE C passes and accepts parameters by value, directly, while for OPM and ILE COBOL and
OPM and ILE RPG parameters are usually passed by reference. You must ensure that the calling program
or procedure passes parameters in the manner expected by the called API. The OPM or ILE HLL
programmer's guides contain more information about passing parameters to different languages.
Input parameters and fields are not changed by the API. They have the same values on the return from
the API call as they do before the API call. In contrast, output parameters and fields are changed. Any
information that an API caller (either an application program or an interactive entry on the display)
places in an output parameter or output field before the call will be lost on the return from the call.
Because of the following considerations, you need to use the offset values and lengths returned by a list
API in the generic header of the user space, rather than specifying what the current version of the API
returns:
v The offset values to the different sections of the user space might change in future releases.
v The length of the entries in the list data section of the user space might change in future releases.
As long as your high-level language application program uses the offset values and lengths returned in
the generic header of the user space, your program will run in future releases of the i5/OS licensed
program.
Note: While your application program should use the length returned in the generic header to address
subsequent list entries, your application program should retrieve only as many bytes as it has
allocated storage for.
For example, the Retrieve Data Queue Message (QMHRDQM) API uses offset; the List Objects
(QUSLOBJ) API uses displacement.
An offset is the distance from the beginning of an object (user spaces and receiver variables) to the
beginning of a particular field. However, a displacement is the distance from the beginning of a specific
record, block, or structure to the beginning of a particular field.
The error code parameter must be initialized before the program calls the API. Depending on how the
error code structure is set, this parameter either returns information associated with an error condition or
causes errors to be returned as exception messages.
For some APIs, the error code parameter is optional. If you do not code the optional error code
parameter, the API returns diagnostic and escape messages. If you code the optional error code
parameter, the API can either signal exceptions or return the exception information in the error code
parameter.
Notes:
v The ILE CEE APIs use feedback codes and conditions.
The error code structure is provided in the QSYSINC library and is called QUSEC.
Related concepts
“Include files and the QSYSINC library” on page 62
An Include file is a text file that contains declarations that are used by a group of functions, programs, or
users. The system include (QSYSINC) library provides all source include files for APIs that are included
with the i5/OS operating system.
Related reference
Errno Values for UNIX-Type Functions
i5/OS Messages and the ILE CEE API Feedback Code
Most i5/OS APIs include an error code parameter to return error codes and exception data to the
application.
The error code parameter can be one of these variable-length structures: format ERRC0100 or format
ERRC0200.
In format ERRC0100, one field is an INPUT field; it controls whether an exception is returned to the
application or the error code structure is filled in with the exception information. When the bytes
provided field is greater than or equal to 8, the rest of the error code structure is filled in with the
OUTPUT exception information associated with the error. When the bytes provided INPUT field is zero,
all other fields are ignored and an exception is returned.
Format ERRC0200 must be used if the API caller wants convertible character (CCHAR) support. Format
ERRC0200 contains two INPUT fields. The first field, called the key field, must contain a -1 to use
CCHAR support. When the bytes provided field is greater than or equal to 12, the rest of the error code
structure is filled in with the OUTPUT exception information associated with the error. When the bytes
provided INPUT field is zero, all other fields are ignored and an exception is returned.
For some APIs, the error code parameter is optional. If you do not code the optional error code
parameter, the API returns diagnostic and escape messages. If you do code the optional error code
parameter, the API returns only escape messages or error codes; it never returns diagnostic messages.
The following tables show the structures of the error code parameter. The fields are described in detail
after the tables.
Note: The error code structures for both formats are provided in the QUSEC include file in the QSYSINC
library. Include files exist in these source physical files: QRPGSRC, QRPGLESRC, QLBLSRC,
QCBLLESRC, and H.
Format ERRC0100
Table 5. Format ERRC0100 for the error code parameter
Offset
Dec Hex Use Type Field
0 0 INPUT BINARY(4) Bytes provided
4 4 OUTPUT BINARY(4) Bytes available
8 8 OUTPUT CHAR(7) Exception ID
15 F OUTPUT CHAR(1) Reserved
Format ERRC0200
Table 6. Format ERRC0200 for the error code parameter
Offset
Dec Hex Use Type Field
0 0 INPUT BINARY(4) Key
4 4 INPUT BINARY(4) Bytes provided
8 8 OUTPUT BINARY(4) Bytes available
12 C OUTPUT CHAR(7) Exception ID
19 13 OUTPUT CHAR(1) Reserved
20 14 OUTPUT BINARY(4) CCSID of the CCHAR
data
24 18 OUTPUT BINARY(4) Offset to the
exception data
28 1C OUTPUT BINARY(4) Length of the
exception data
OUTPUT CHAR(*) Exception data
Field descriptions
Bytes available. The length of the error information available for the API to return, in bytes. If it is 0, no
error was detected and none of the fields that follow this field in the structure are changed.
Bytes provided. The number of bytes that the calling application provides for the error code. If the API
caller is using format ERRC0100, the bytes provided must be 0, 8, or more than 8. If more than 32 783
bytes (32KB for exception data plus 16 bytes for other fields) are specified, it is not an error, but only 32
767 bytes (32KB) can be returned in the exception data.
If the API caller is using format ERRC0200, the bytes provided must be 0, 12, or more than 12. If more
than 32 799 bytes (32KB for exception data plus 32 bytes for other fields) are specified, it is not an error,
but only 32 767 bytes (32KB) can be returned in the exception data.
Table 7. Possible values for bytes provided
Bytes Description
0 If an error occurs, an exception is returned to the
application to indicate that the requested function failed.
>=8 If an error occurs, the space is filled in with the
exception information. No exception is returned. This
occurs only for format ERRC0100.
>=12 If an error occurs, the space is filled in with the
exception information. No exception is returned. This
occurs for formats ERRC0100 and ERRC0200.
Exception data. A variable-length character field that contains the insert data associated with the
exception ID.
Exception ID. The identifier for the message for the error condition.
Key. The key value that enables the message handler error function if CCHAR support is used. This
value should be -1 if CCHAR support is expected.
Length of the exception data. The length, in bytes, of the exception data returned in the error code.
Offset to the exception data. The offset from the beginning of the error code structure to the exception
data in the error code structure.
Depending on how you set the error code parameter, your application can receive error conditions as
exceptions or receive the error code with or without exception data.
This example defines an error code structure that receives error conditions as exceptions using format
ERRC0100 of the error code structure. The application allocates an error code parameter that is a
minimum of 4 bytes long to hold the bytes provided field. The only field used is the bytes provided
INPUT field, which the application sets to zero to request exceptions. The error code parameter contains
the following field.
Table 9. Error code structure for receiving error conditions as exceptions
Field INPUT OUTPUT
Bytes provided 0 0
This example defines a format ERRC0100 error code structure that receives the error message or
exception ID but no exception replacement data. To do this, the application allocates an error code
parameter that is a minimum of 16 bytes long for the bytes provided, bytes available, exception ID, and
reserved fields. It sets the bytes provided field of the error code parameter to 16.
If the called API were to return the error message CPF7B03, the error code parameter would contain the
data shown in the following table. In this example, 16 bytes are provided for data, but 36 bytes are
available. Twenty more bytes of data could be returned if the bytes provided field were set to reflect a
larger error code parameter.
This example defines a format ERRC0100 error code structure that receives the error message or
exception ID and up to 100 bytes of exception replacement data that is associated with the exception. To
do this, the application allocates an error code parameter that is 116 bytes long: 16 bytes for the bytes
provided, bytes available, exception ID, and reserved fields, and 100 bytes for the exception data for the
exception. (In some cases, the exception data might be a variable-length directory or file name, so this
might not be large enough to hold all of the data; whatever fits is returned in the error code parameter.)
Finally, it sets the bytes provided field to 116.
If the called API were to return the error message CPF7B03 with replacement variable &1 set to the value
'USRMSG ' and replacement variable &2 set to the value 'QGPL ', the error code parameter
would contain the data shown in the following table.
Table 11. Error code structure for receiving the error code with the exception data
Field INPUT OUTPUT
Bytes provided 116 116
Bytes available Ignored 36
Exception ID Ignored CPF7B03
Reserved Ignored 0
Exception data Ignored USRMSG QGPL
When your program encounters an API error, use messages in the job log to determine the cause.
Sometimes an API might issue a message stating that the API failed, and the message might direct you to
see the previously listed messages in the job log. If you need to determine the cause of an error message,
use the Receive Message (RCVMSG) command or the receive message APIs to receive the messages that
explain the reason for the error.
In some cases, you can write an application program to use the diagnostic message to identify and
correct the parameter values that caused the error.
The following CL program receives error messages from the job log.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/* */
/*********************************************************************/
/* */
/* PROGRAM: CLRCVMSG */
/* */
/* LANGUAGE: CL */
/* */
GOTO CMDLBL(ALLDONE)
The QSYSINC library is optionally installed. It is fully supported, which means that you can write an
authorized program analysis report (APAR) if you find an error in an include file.
You can install the QSYSINC library by using the GO LICPGM functions of the i5/OS operating system.
Select the Install Licensed Programs option on the Work with Licensed Programs display and the i5/OS
System Openness Includes option on the Install Licensed Programs display.
The terms include file and header file are interchangeable and pertain to the contents of the QSYSINC
library. These files are intended to be compatible with future releases.
The naming conventions for the include files are the same as either the API program name or the ILE
service program name. If both exist, the include file has both names.
The following table lists the include files that are shipped with the QSYSINC library.
Besides the include files for specific APIs, the QSYSINC library also contains the following include files.
Table 13. Other include files in the QSYSINC library
File name Description
QLIEPT and QUSEPT Allows C language application programs to call
program-based APIs directly through the system entry
point table.
QUSGEN Defines the generic header for the list APIs.
QUSEC Contains the structures for the error code parameter.
Qxx Provides common structures that are used by multiple
APIs (where the xx is the component identifier, for
example, QMH, QSY, and so forth).
The include files that are included with the system define only the fixed portions of the formats. You
must define the varying-length fields. The QSYSINC include files are read-only files. If you use a
structure that contains one or more varying-length fields, you have two options for defining these
varying-length fields. You can copy the include file to your own source file and edit your copy.
Uncomment the varying-length fields in your copy of the include file, and specify the actual lengths you
want.
An exit program has an include file only when it contains a structure. The member names for exit
programs start with the letter E. Except for RPG array definitions for APIs that also start with E, any
member names in the QSYSINC library that start with the letter E are include files for exit programs. The
QSYSINC member name of these include files is provided in the parameter box for the applicable exit
programs.
All source physical files are included with read capabilities only; changes cannot be made to the
QSYSINC library. All these files are built with a CCSID of 00037. When you compile a program in a
specific CCSID, any QSYSINC include file is converted to the program CCSID.
If you are coding in ILE C, the header files in the QSYSINC library are considered system include files.
You should use the < and > symbols on the #include statement; this affects how the library list is used to
search for header files.
If you are developing applications on a release n system that will run on a release n-1 system, you might
want to copy the include files of each release to user source libraries. This minimizes the impact of
include file changes when APIs are enhanced over time with additional fields.
Related concepts
“Extracting a field from the format” on page 54
You can determine from the API format section where the field that you want to extract is located within
the receiver variable.
Related reference
“Error code parameter” on page 57
An API error code parameter is a variable-length structure that is common to most of the system APIs.
The error code parameter controls how errors are returned to the program.
“Examples: APIs and exit programs” on page 301
These examples show how to use a wide variety of APIs and exit programs.
Related reference
External object types
“Creating an MI version of the CLCRTPG program” on page 510
This topic discusses how to create an MI version of the CLCRTPG program that can be used to create MI
programs. This program is called MICRTPG.
Character data
In the API parameter tables, CHAR(*) represents the character data that has:
v A type that is not known, such as character or binary
v A length that might not be known or is based on another value (for example, a length you specify)
Binary data
In the API parameter tables, BINARY(2), BINARY(4), and BINARY(8) represent numeric data. These
parameters must be signed, 2-, 4-, or 8-byte numeric values with a precision of 15 (halfword), 31
(fullword), or 43 bits and 1 high-order bit for the sign. Numeric parameters that must be unsigned
numeric values are explicitly defined as BINARY(x) UNSIGNED.
When you develop applications that use binary values, be aware that some high-level languages allow
the definition of binary variables by using precision and not length. For example, an RPG definition of
binary length 4 specifies a precision of 4 digits, which can be stored in a 2-byte binary field. For API
BINARY fields, RPG developers should use one of the following:
v Positional notation (1 2B 0) for 2-byte binary
v Positional notation (1 4B 0) for 4-byte binary
v A length of 1 to 4 for 2-byte binary (4B 0)
v A length of 5 to 9 in order to allocate a 4-byte binary field (9B 0)
v A length of 5 for 2-byte signed integer (5i 0)
v A length of 5 for 2-byte unsigned integer (5u 0)
v A length of 10 to allocate a 4-byte signed integer field (10i 0)
v A length of 10 to allocate a 4-byte unsigned integer field (10u 0)
v A length of 20 to allocate an 8-byte signed integer field (20i 0)
v A length of 20 to allocate an 8-byte unsigned integer field (20u 0)
Internal identifiers
Several APIs either require or allow you to use an internal identifier (ID) to identify an external name.
When you use an internal ID, the processing can be faster because the system does not need to convert
the external name to the internal ID.
A variety of terminology is used to identify an internal ID. Here are some examples:
v Work management uses an internal job identifier.
v Spooling uses an internal spooled file identifier.
v Security uses the term handle to mean the user profile that is currently running the job.
v Message handling uses the term message key (also appears in CL commands) to identify a message in a
message queue.
The internal values are often accessed in one API and then used in another. For example, if you want a
list of jobs, you use the List Jobs (QUSLJOB) API, which provides the internal job ID for each job in the
list. You can use the internal job ID to access a spooled file for a job with the Retrieve Spooled File
Attributes (QUSRSPLA) API.
User spaces
List APIs return information to user spaces. A user space is an object consisting of a collection of bytes that
can be used for storing any user-defined information.
To provide a consistent design and use of the user space (*USRSPC) objects, the list APIs use a general
data structure.
List APIs use a general data structure. A data structure is an area of storage that defines the layout of the
fields within the area.
The following figure shows the general data structure for the list APIs.
All offset values are from the beginning of the user space. The offset values for the Dump Object
(DMPOBJ) and Dump System Object (DMPSYSOBJ) commands also start at the beginning of the user
space. To get the correct starting position for the Change User Space (QUSCHGUS) and Retrieve User
Space (QUSRTVUS) APIs, add one to the offset value.
Here are the data structure formats and field descriptions for the list APIs.
Format 0100 is for the list APIs that are called as programs (*PGMs).
Offset
Dec Hex Type Field
0 0 CHAR(64) User area
64 40 BINARY(4) Size of generic header
68 44 CHAR(4) Structure's release and level
72 48 CHAR(8) Format name
80 50 CHAR(10) API used
90 5A CHAR(13) Date and time created
103 67 CHAR(1) Information status
104 68 BINARY(4) Size of user space used
108 6C BINARY(4) Offset to input parameter
section
112 70 BINARY(4) Size of input parameter
section
116 74 BINARY(4) Offset to header section
120 78 BINARY(4) Size of header section
124 7C BINARY(4) Offset to list data section
128 80 BINARY(4) Size of list data section
132 84 BINARY(4) Number of list entries
136 88 BINARY(4) Size of each entry
140 8C BINARY(4) CCSID of data in the list
entries
144 90 CHAR(2) Country or region ID
146 92 CHAR(3) Language ID
149 95 CHAR(1) Subsetted list indicator
150 96 CHAR(42) Reserved
Format 0300 is for the list APIs that are called as procedures exported from ILE service programs
(*SRVPGM).
Offset
Dec Hex Type Field
0 0 Everything from the 0100
format
192 C0 CHAR(256) API entry point name
448 1C0 CHAR(128) Reserved
API entry point name. The name of the ILE bindable API entry point that generates the list.
API used. For format 0100, this is the name of the program-based API that generates the list. For format
0300, this is a reserved field. See the API entry point name field for the API used.
CCSID of the data in the list entries. The coded character set ID for data in the list entries. If the value
is 0, the data is not associated with a specific CCSID and should be treated as hexadecimal data.
Country or region ID. The country or region identifier of the data written to the user space.
Date and time created. The date and time when the list was created. The table shows the possible values.
Table 15. Possible values for date and time created
Value Description
1 Century, where 0 indicates years 19 xx and 1 indicates
years 20 xx.
2-7 The date, in YYMMDD (year, month, day) format.
8-13 The time of day, in HHMMSS (hours, minutes, seconds)
format.
Format name. The name of the format for the list data section.
Information status. Whether the information is complete and accurate. The table shows the possible
values.
Table 16. Possible values for information status
Value Description
C Complete and accurate.
I Incomplete. The information that you receive is not
accurate or complete.
P Partial but accurate. The information that you receive is
accurate, but the API has more information to return
than the user space can hold. See “List sections” on page
75 for more information about partial lists.
Language ID. The language identifier of the data written to the user space.
Number of list entries. The number of fixed-length entries in the list data section.
Offset to (all) section. The byte offset from the beginning of the user space to the start of the section.
Size of each entry. The size of each list data section entry, in bytes. All entries are the same size. For
formats that return variable length records, this is zero.
Size of generic header. The size of the generic header, in bytes. This does not include the size of the user
area. See “General data structure” on page 71 for a diagram showing the user area.
Size of input parameter section. The size of the input parameter section, in bytes.
Size of list data section. The size of the list data section, in bytes. For formats that return variable length
records, this is zero.
Size of user space used. The combined size of the user area, generic header, input parameter section,
header section, and list data section, in bytes. This determines what is changed in the user space.
Structure's release and level. The release and level of the generic header format for this list. The value of
this field is 0100 for generic header format 0100 and 0300 for generic header format 0300. List APIs put
this value into the user space.
Subsetted list indicator. A flag that indicates whether the data selected from the list API can be stored in
that format. The table shows the possible values.
Table 17. Possible values for subsetted list indicator
Value Description
0 List is not subsetted; all of the information can be stored
in the format.
1 List is subsetted. For example, integrated file system
names might be longer than the available area in the
format.
User area. An area within the user space that is provided for the caller to use to communicate system
programmer-related information between applications that use the user space.
This example illustrates the format of a user space. It does not contain all of the fields in the fixed
portion of a user space.
User Space Fixed locations
*------------------------* in the user space
| | *----------------*
| |
| |
| XXXX *----+---> Offset to data section
| |
| XXXX *----+---> Number of list entries
| |
| XXXX *----+---> Size of each entry
| |
| |
2nd entry *--|-----------------* |
| | | Variable locations in the
1st entry *--|--* | | user space
| V V | *-----------------------*
|AAAAABBBBCCCDDDEEAAAABB |*---> List of entries
|BBBCCCDDDEEAAAABBBBBCCD |
|DDDEEAAAABBBBCCCDDDEE.. |
*------------------------*
List sections:
Each list API provides an input parameter section, a header section, and a list data section.
When you retrieve list entry information from a user space, use the allocated size defined in your
application. To get the next entry, use the entry size returned in the generic header. The size of each entry
might be padded at the end. If you do not use the entry size from the generic header, the result might
not be valid.
Some APIs might be able to return more information to the application than fits in a receiver variable or
a user space. The information returned is correct, but not complete.
If the list information is not complete, the first situation and possibly the second situation occur:
v A P is returned in the information status field of the generic user space layout.
v The API supports a continuation handle.
If an indicator of a partial list is returned and the API supports a continuation handle, the application
should call the API again with the continuation handle in the list header section and specify that the list
begins with the next entry to be returned.
Note: If this is the first time that the API attempts to return information, the continuation handle must be
set to blanks. If the API does not support a continuation handle, you need to call the API again
and to use more restrictive values for the parameters.
Related reference
“General data structure” on page 71
List APIs use a general data structure. A data structure is an area of storage that defines the layout of the
fields within the area.
Receiver variables
A receiver variable is a program variable that is used as an output field to contain information that is
returned from a retrieve API.
Retrieve APIs use receiver variables rather than user spaces to place returned information. A retrieve API
requires only addressability to storage of fixed size (typically a field or structure defined in your
program). A list API, in comparison, requires a user space because the amount of information returned by
a list API might be large and not of a predictable size.
Retrieve APIs that return information to receiver variables use the storage provided for the receiver
variable parameter. The returned information is in a specific format. The format name is usually a
parameter on the call to the API, and the format indicates to the API the information that you want
returned. On the return from the call to the API, the caller parses through the receiver variable and
extracts the information that is needed. The caller knows how the information is returned by the
documented format of the information. An API might have one or many formats that give you the
flexibility to choose the information that you need.
Offsets and displacements are not the same. An offset is relative to the beginning of a receiver variable or
the beginning of a user space, whereas a displacement is relative to the current position of the pointer plus
the value within the displacement field. If a format uses a displacement, you see the word displacement in
the Field column of the API description.
Related concepts
“API information format” on page 50
The format of the i5/OS API information includes sections such as parameters, authorities and locks,
required parameter group, format, field descriptions, and error messages.
Related reference
“User spaces” on page 70
List APIs return information to user spaces. A user space is an object consisting of a collection of bytes that
can be used for storing any user-defined information.
Example: Receiver variables using ILE APIs
Example: Keyed interface using ILE APIs
“Defining byte alignment” on page 549
Correct byte alignment ensures that an API reads the data from the beginning of a record rather than at
some other point. Here are the program examples that show the incorrect and correct ways of defining
byte alignment.
“Example in OPM RPG: Using keys with the List Spooled Files (QUSLSPL) API” on page 171
This OPM RPG program processes a list of spooled file information that you have specified using keys.
Most formats used by retrieve APIs have a bytes available field and a bytes returned field.
The bytes available field contains the length in bytes of all the data that is available to be returned to the
user. The bytes returned field contains the length in bytes of all the data that is actually returned to the
user.
All available data is returned if enough space is provided in the receiver variable. If the size of the
receiver variable is at least large enough to contain all of the data, the bytes returned field equals the
bytes available field. If the receiver variable is not large enough to contain all of the data, the bytes
available field contains the number of bytes that can be returned.
Your code can check the values for both the bytes available and bytes returned fields. If the value of the
bytes available field is greater than the value of the bytes returned field, the API has more information to
return than what can fit in the receiver variable. This might occur, over time, because the APIs that you
use might be enhanced with new releases. The API might also have more information to return if the
receiver variable is being used to return a variable-length field (or array) and a very large value is
returned on this API call. If both values are the same, the API returns all the information.
Depending on the capabilities of your high-level language, some API users take advantage of the
following technique to avoid guessing the appropriate size for the receiver variable:
1. Call the API with a receiver variable length of 8 bytes (that is, just enough for the bytes available and
the bytes returned fields).
2. Dynamically allocate an amount of storage equivalent to the bytes available.
3. Set the length of the receiver variable parameter to the amount of storage allocated.
This technique provides for highly flexible use of APIs that can return variable amounts of data.
Keyed interface:
Some APIs have a keyed interface for selecting what information you want returned. A keyed interface
allows you to provide information to an API through the use of keys.
Keys are API-specific values that inform an API that a certain function should be performed. Keys are
also used to pass information to an API or to retrieve information from an API.
Through the use of keys, you can be more selective; you can choose one item or a number of items rather
than all of them. For example, using the List Job (QUSLJOB) API, you can receive selected information
about a job based on the keys that you specify. If you want job information about the output queue
priority, you only need to specify the output queue priority key.
Although there are some exceptions, the keys are typically supplied and passed to an API through a
variable-length record. A variable-length record is a collection of information that specifies the key being
used and the data that is associated with the key. If a given structure contains binary values, it must be
4-byte aligned.
Some APIs that use variable-length records in addition to the QUSLJOB API are the Change Object
Description (QLICOBJD) API and the Register Exit Point (QUSRGPT, QusRegisterExitPoint) API. You can
use the appropriate include file in member QUS in the system include (QSYSINC) library when you have
variable-length records as either input or output.
A keyed interface provides an easy-to-use means for enhancing an API without affecting the user who
chooses not to use the enhancements.
Related reference
Example: Keyed interface using ILE APIs
“Example in ILE C: Using keys with the List Spooled Files (QUSLSPL) API” on page 181
This ILE C program processes a list of spooled file information that you have specified using keys.
“Example in ILE COBOL: Using keys with the List Spooled Files (QUSLSPL) API” on page 177
This ILE COBOL program processes a list of spooled file information that you have specified using keys.
“Example in ILE RPG: Using keys with the List Spooled Files (QUSLSPL) API” on page 184
This ILE RPG program processes a list of spooled file information that you have specified using keys.
“Example in OPM RPG: Using keys with the List Spooled Files (QUSLSPL) API” on page 171
This OPM RPG program processes a list of spooled file information that you have specified using keys.
If the amount of information to be returned by a retrieve API is not known or is large, a user space is
preferred to contain the information.
The disadvantage of using a receiver variable when it is too small for the amount of data being returned
is that the API must be called again to receive the remaining data. You can create a user space so that it
can automatically extend up to 16 MB of storage to accommodate the information being retrieved.
Continuation handle
When a call to an API is made and the API has more information to return than what can fit in the
receiver variable or the user space, the API returns a continuation handle, which is used to mark the last
value put in the receiver variable or the user space.
When you use the continuation handle parameter, that is the only parameter that can change. All other
parameters must appear as they did on the call to the API that generated the continuation handle to
obtain predictable results.
Related reference
“General data structure for list APIs”
The data structure for the list APIs consists of several fields.
For a program example that uses a continuation handle, see “Example in ILE C: Retrieving exit point and
exit program information” on page 214.
The following data structure shows the common fields that the list APIs use. All list APIs have an input
parameter section, a header section, and a list data section.
Header
*------------------------------------* *-------------------------*
+00| | | |
| 64-Byte User Area | | |
| | | |
*------------------------------------* *--->| Input Parameter Section |
+40| Size of Generic Header | | | |
*------------------------------------* | . .
| | | . .
| Generic Header | | *-------------------------*
| | | | |
User area
The first field in the general data structure is called the user area. This is a 64-byte field that is not used
or changed by the system. Whatever information you place in this field remains there. For example, you
can specify the date last used or include comments about the list.
The size of the generic header does not include the size of the user area. All sections have a size, which
might differ for each API.
Some fields might be added to the generic header from release to release. Because fields might be added,
you might want to check the size of this field. If your application works across multiple releases, it is
recommended that you check the size of this field to determine which fields are applicable.
The offset to the input parameter section is an offset to the start of the input parameter section. The input
parameter section might contain a copy of the input parameters that you pass to a list API. For an
example, see Input Parameter Section in List Objects That Adopt Owner Authority (QSYLOBJP) API.
The input parameter section contains a copy of the continuation handle value that you passed as the
continuation handle parameter to the API. “Other fields of generic header” on page 81 discusses
continuation handles further.
The header section includes an offset to where the header section starts and the size of the header section.
This section is needed in the event any input parameters have a special value. The fields in the header
section tell what the special value resolved to. For example, the special value *CURRENT for the user
name parameter would resolve to the user profile name for the job that called the API.
This section is also sometimes used for API-specific control information that is not related to a particular
list entry.
For an example, see Header Section in List Objects That Adopt Owner Authority (QSYLOBJP) API.
The offset to the list data section is the offset to the start of the format. The specific format that the API
uses is determined by the name you specify for the format name parameter. The specific format that you
use determines what information is returned in the user space.
The number of list entries field tells how many entries have been returned to you.
The size of each entry field within the list data section tells how large each entry is. In the list data
section, each entry is of the same length for a given list. If the size of each entry field is 0, the entries
have different lengths and the format tells the length of each entry.
The list data sections for the QSYLOBJP API are shown in the OBJP0100 Format, OBJP0110 Format, and
the OBJP0200 Format. This API has three possible formats.
For more information about formats and how to extract a field from a format, see Format and Extracting
a field from the format.
The field called structure's release and level is part of the generic header. This field describes the layout of
the generic header. For a program-based API, this value should be 0100. For a service-program-based
API, the value should be 0300.
The information status field tells you whether the information in the user space is complete and accurate,
or partial. You need to check the value of this field before you do anything with the information in the
user space, shown at (1) in the RPG example program. Possible values for this field follow.
If the value is P, the API has more information to return than what could fit in the user space. If you
received the value P, you need to process the current information in the user space before you get the
remaining information. The API returns a continuation handle usually in the form of a parameter. You
can use this continuation handle value to have the remaining information placed in the user space. You
specify the continuation handle value that the API returned as the value of the continuation handle input
parameter on your next call to the API.
If the API does not have a continuation handle and the information status field value is P, you must
further qualify what you want in the list. In other words, you must be more specific on the parameter
values that you pass to the API. For example, the QUSLOBJ API asked to get a list of objects; however, all
of the objects on the system would not fit in the user space. To further qualify or limit the number of
objects returned, the user might specify all libraries that start with a specific letter.
Related concepts
“API format” on page 53
The format section in the API information shows the type of information to be returned by an API.
“Extracting a field from the format” on page 54
You can determine from the API format section where the field that you want to extract is located within
the receiver variable.
Related tasks
“Continuation handle” on page 78
When a call to an API is made and the API has more information to return than what can fit in the
receiver variable or the user space, the API returns a continuation handle, which is used to mark the last
value put in the receiver variable or the user space.
Related reference
“Example in OPM RPG: List APIs” on page 90
This OPM RPG program prints a report that shows all objects that adopt owner authority.
A user space is an object type that is created by the Create User Space (QUSCRTUS) API. Generally, a
user space is used when information about more than one object is being requested.
Most lists returned by APIs are made up of a series of entries where each entry is a data structure.
Special fields are placed in the user space at consistent locations that describe:
v Where the list begins.
v The number of entries. Logic flow of processing a list of entries shows the logic for processing a list of
entries.
v The length of each entry.
User spaces are used for such functions as returning either a list of members in a file or objects in a
library. When you use one of the list APIs, the parameter list requires that you name the user space that
will be used.
When you process a list of entries returned by a list API, do not statically encode the values. Use the
offset, the length of each entry, and the number of entries so that applications are compatible with future
releases.
This is the logic flow for processing a list that contains multiple entries:
*------------------------*
| |
| Initialize the next |
| entry with ’Where |
| the list begins’ |
*------------*-----------*
|
V
*------------------------*
| |
| Have all of the | YES
*------->| entries been *-------*
| | processed? | |
| *------------*-----------* *
| |NO List is
| V complete
| *------------------------*
| | |
| | Access the entry. Use |
| | the next entry value |
| | as an index. |
| *-----------*------------*
| |
| V
| *------------------------*
| | Process |
| | the |
| | entry |
| | |
| *------------*-----------*
| |
| V
| *------------------------*
| | |
| | Add the length of |
| | each entry |
| | to the next entry |
| *------------*-----------*
| |
| V
| *------------------------*
| | Add 1 to a count of |
| | how many have |
| | been processed |
Some high-level languages support pointers. A pointer is a data element or variable that holds the address
of a data object or a function. Using pointers, you can manipulate information more rapidly from the user
space.
The high-level languages that support pointers include ILE C, Visual Age for C++, ILE COBOL, ILE RPG,
ILE CL, COBOL, CL, Pascal, and PL/I.
If you are using the Change User Space (QUSCHGUS) or Retrieve User Space (QUSRTVUS) API to
manipulate user spaces, you do not need to synchronize update and retrieve operations when multiple
jobs access the user space. The APIs already do that for you. However, if you are using space pointers to
retrieve the information directly from the user space, you should synchronize your application programs
to avoid data errors. This ensures that no two users update the space at the same time, which can cause
unpredictable results.
Locks are typically used to synchronize two jobs on the system, and you can lock user spaces. To
synchronize multiple jobs, you can use one of the following:
v Compare and swap (CMPSWP MI instructions)
v Space location locks (LOCKSL and UNLOCKSL MI instructions)
v Object locks (LOCK and UNLOCK MI instructions)
v Allocate Object (ALCOBJ) and Deallocate Object (DLCOBJ) commands
The preceding list is ordered by relative performance where CMPSWP is the fastest. If you do not
synchronize two or more jobs, multiple concurrent updates to the user space or read operations might
occur while information is being updated. As a result, the data might not be accurate.
See Example: Changing a user space with an ILE RPG program for an example of this procedure.
If you are using the Change User Space (QUSCHGUS) or Retrieve User Space (QUSRTVUS) API to
manipulate user spaces, you do not need to update usage data information. If you directly retrieve data
using pointers, your application programs should update the usage data information. To do this, use the
QUSCHGUS API to update the date last changed and use the QUSRTVUS API to update the date last
retrieved. You do not need to do this for each retrieve or change operation to the user space, but you
should do this once within each application program to maintain accurate usage data information.
When programming in a high-level language that does not support pointers, you can use the Change
User Space (QUSCHGUS) and Retrieve User Space (QUSRTVUS) APIs to manipulate data. However, you
must first understand how to use positions and lengths with these APIs.
Position values
Some APIs return offset values into a user space. To use other APIs, such as the Retrieve User Space
(QUSRTVUS) API, you must use position values to locate bytes.
Position values and offset values are different ways to express the same thing. An offset value is the
relative distance of a byte from the first byte of the user space, which has an offset value of 0. A position
value is the offset value plus 1.
Lengths
List APIs return the length of the information in the different sections of the user space, as well as the
length of the list entries in the user space. You need to code your application using the lengths returned
instead of specifying the current length that is returned by the API or the size of a data structure in the
data structure files. The amount of information returned for any format might increase in future releases,
but the information will be placed at the end of the existing information. To function properly, your
application should retrieve the length of the information that is returned and add that length to a pointer
or to a starting position.
Using offset values with the change and retrieve user space APIs
When you use the Change User Space (QUSCHGUS) or Retrieve User Space (QUSRTVUS) API, your
application program should first retrieve the offset value for the information you want. You must then
add one to the offset value to get the starting position for the information.
Related reference
“Example in OPM RPG: List APIs” on page 90
This OPM RPG program prints a report that shows all objects that adopt owner authority.
These high-level language programs update the contents of a user space with and without using pointers.
Related concepts
“Manipulating a user space with pointers” on page 84
Some high-level languages support pointers. A pointer is a data element or variable that holds the address
of a data object or a function. Using pointers, you can manipulate information more rapidly from the user
space.
This example compares the user space before and after you change it using the Change User Space
(QUSCHGUS) API.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
.POINTERS-
NONE
OIR DATA-
.TEXT
000000 E4A28599 40A29781 83854086 969940C3 88819587 8540E4A2 859940E2 97818385 *user space for Change User *
000020 40C5A781 94979385 *Space Example *
.SERVICE-
000000 40404040 40404040 40404040 40404040 40404040 40404040 40404040 40404040 * 1 *
000020 40404040 40404040 404040D9 F0F3D4F0 F0F0F9F0 F0F3F2F2 F1F1F0F2 F5F64040 * VxRxMx007032210256 *
000040 40404040 40404040 40404040 40404040 40404040 40F14040 40404040 40404040 *
000060 40404040 40404040 40404040 40404040 40404040 40404040 00000000 00000000 *
000080 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 *
0000A0 00000000 00000000
END OF DUMP
* * * * * E N D O F L I S T I N G * * * * *
Here is the user space after you change it with one of the change examples. The change takes place in
SPACE-.
xxxxSS1 VxRxMx yymmdd System i5 Dump 128747/ERICJ/ERICJS1 03/22/07 11:03:26
DMPSYSOBJ PARAMETERS
TEMPSPACE CONTEXT-QGPL
OBJ-
*USRSPC
OBJTYPE-
OBJECT TYPE- SPACE *USRSPC
NAME- TEMPSPACE TYPE- 19 SUBTYPE- 34
LIBRARY- QGPL TYPE- 04 SUBTYPE- 01
CREATION- 3/22/07 11:02:56 SIZE- 0000400
OWNER- ERICJ TYPE- 08 SUBTYPE- 01
ATTRIBUTES- 0800 ADDRESS- 01841400 0000
SPACE ATTRIBUTES-
000000 00000080 00000060 1934E3C5 D4D7E2D7 C1C3C540 40404040 40404040 40404040 * -TEMPSPACE
000020 40404040 40404040 E0000000 00000000 00000200 5C800000 00000000 00000000 * \ * *
000040 00000000 00000000 00020002 6E000400 00000000 00000000 00000000 00000000 * > *
SPACE-
000000 C2898740 E2A39989 95874097 81848485 8440A689 A3884082 93819592 A2404040 *Big string padded with blanks*
000020 40404040 40404040 40404040 40404040 40404040 40404040 40404040 40404040
000040 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C *******************************
LINES 000060 TO 0001FF SAME AS ABOVE
.POINTERS-
NONE
OIR DATA-
.TEXT
000000 E4A28599 40A29781 83854086 969940C3 88819587 8540E4A2 859940E2 97818385 *user space for Change User *
000020 40C5A781 94979385 *Space Example *
.SERVICE-
000000 40404040 40404040 40404040 40404040 40404040 40404040 40404040 40404040 * 1 *
000020 40404040 40404040 404040D9 F0F3D4F0 F0F0F9F0 F0F3F2F2 F1F1F0F2 F5F64040 * VxRxMx007032210256 *
000040 40404040 40404040 40404040 40404040 40404040 40F14040 40404040 40404040 *
000060 40404040 40404040 40404040 40404040 40404040 40404040 00000000 00000000 *
000080 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 *
0000A0 00000000 00000000
END OF DUMP
* * * * * E N D O F L I S T I N G * * * * *
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
H*****************************************************************
H*
H* PROGRAM: CHANGUSPTR
H*
H* LANGUAGE: ILE RPG for i5/OS
H*
H* DESCRIPTION: CHANGE THE CONTENTS OF INFORMATION IN THE USER
H* AREA IN THE USER SPACE USING A POINTER
H*
H*****************************************************************
D*
DUSRSPCNAM S 20 INZ(’TEMPSPACE QTEMP ’)
DNEWVALUE S 64 INZ(’Big String padded with blanks’)
DUSRSPCPTR S *
DUSERAREA DS BASED(USRSPCPTR)
D CHARFIELD 1 64
D*
D* Following QUSEC structure copied from QSYSINC library
D*
DQUSEC DS
D* Qus EC
D QUSBPRV 1 4B 0
D* Bytes Provided
D QUSBAVL 5 8B 0
D* Bytes Available
D QUSEI 9 15
D* Exception Id
D QUSERVED 16 16
D* Reserved
D* End of QSYSINC copy
D*
C*
C* Initialize Error code structure to return error ids
C*
C Z-ADD 16 QUSBPRV
C*
C* Set USRSPCPTR to the address of the user space
C*
C CALL ’QUSPTRUS’
C PARM USRSPCNAM
C PARM USRSPCPTR
C PARM QUSEC
C*
C* Check for successful setting of pointer
C*
C QUSBAVL IFGT 0
C*
C* If an error, then display the error message id
C*
C DSPLY QUSEI
C ELSE
C*
C* Otherwise, update the user space via the based structure
C*
C MOVEL NEWVALUE USERAREA
C END
C*
C* And return to our caller
C*
C SETON LR
C RETURN
This OPM RPG program changes the user area of a user space without using a pointer.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
H****************************************************************
H****************************************************************
H* *
H* PROGRAM: CHANGUS *
H* *
H* LANGUAGE: RPG *
H* *
H* DESCRIPTION: THIS PROGRAM WILL CHANGE THE CONTENTS OF *
H* INFORMATION IN THE USER AREA IN THE USER SPACE*
H* (FIRST 64 BYTES). *
H* *
H* APIs USED: QUSCHGUS *
H* *
H****************************************************************
H****************************************************************
E ARY 1 1 20
E CHG 1 1 64
IUSRSPC DS
I 1 10 USNAME
I 11 20 USLIB
I DS
I B 1 40LENDTA
I B 5 80STRPOS
C* *
C****************************************************************
C****************************************************************
C* *
C* OPERABLE CODE STARTS HERE *
C* *
C****************************************************************
C****************************************************************
C* *
C* MOVE THE USER SPACE AND LIBRARY NAME FROM ARY ARRAY INTO THE *
C* USRSPC DATA STRUCTURE. ALSO, MOVE THE NEW USER DATA FROM *
C* CHG ARRAY INTO NEWVAL. *
C* *
C MOVELARY,1 USRSPC
C MOVELCHG,1 NEWVAL 64
C* *
C Z-ADD64 LENDTA LEN OF USERAREA
C Z-ADD1 STRPOS STARTING POS
C MOVE ’1’ FORCE 1 FORCE PARM
C* *
C* CALL THE QUSCHGUS API WHICH WILL CHANGE THE USER AREA IN THE *
C* USER SPACE. *
C* *
C CALL ’QUSCHGUS’
C PARM USRSPC
C PARM STRPOS
C PARM LENDTA
C PARM NEWVAL
C PARM FORCE
C* *
C* IF MORE OF THE USER SPACE NEEDS TO BE CHANGED, THIS PROGRAM *
C* COULD BE UPDATED TO LOOP UNTIL THE END OF THE ARRAY WAS *
C* REACHED. *
C* *
C SETON LR
C RETRN
A list API returns only the number of list entries that can fit inside the user space. If you have *CHANGE
authority to the user space, the list API can extend the user space when it is too small to contain the list.
Before you can use a list API to create a list, the *USRSPC object must exist.
If the user space is too small to contain the list and you have *CHANGE authority to the user space, the
list API extends the user space to the nearest page boundary. If the user space is too small and you do
not have *CHANGE authority, an authority error results. An extended user space is not truncated when
you run the API again.
When you are creating a list into a user space and the user space cannot hold all of the available
information (the list is greater than 16 MB in length), the API places as much information as possible in
the user space and sends a message (typically CPF3CAA) to the user of the API. The returned list
contains only the number of entries that can fit inside the user space (not the total number of entries
available).
This CL program generates a list of members in a database file that start with M and places the list in a
user space.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/****************************************************************/
/* */
/* PROGRAM: LSTMBR2 */
/* */
/* LANGUAGE: CL */
/* */
/* DESCRIPTION: THIS PROGRAM WILL GENERATE A LIST OF MEMBERS, */
/* THAT START WITH M, AND PLACE THE LIST INTO A */
/* USER SPACE NAMED EXAMPLE IN LIBRARY QGPL. */
/* */
/* APIs USED: QUSCRTUS, QUSLMBR */
/* */
/****************************************************************/
PGM
/****************************************************************/
/* CREATE A *USRSPC OBJECT TO PUT THE LIST INFORMATION INTO. */
/****************************************************************/
CALL QUSCRTUS +
(’EXAMPLE QGPL ’ /* USER SPACE NAME AND LIB */ +
’EXAMPLE ’ /* EXTENDED ATTRIBUTE */ +
X’0000012C’ /* SIZE OF USER SPACE */ +
’ ’ /* INITIALIZATION VALUE */ +
’*CHANGE ’ /* AUTHORITY */ +
’USER SPACE FOR QUSLMBR EXAMPLE ’)
/****************************************************************/
/* LIST THE MEMBERS BEGINNING WITH "M" OF A FILE CALLED */
/* QCLSRC FROM LIBRARY QGPL USING THE OUTPUT FORMAT MBRL0200. */
/* OVERRIDE PROCESSING SHOULD OCCUR. */
/****************************************************************/
CALL QUSLMBR +
(’EXAMPLE QGPL ’ /* USER SPACE NAME AND LIB */ +
’MBRL0200’ /* FORMAT NAME */ +
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F*
F*****************************************************************
F*****************************************************************
F*****************************************************************
F*****************************************************************
F*
F*Program Name: List objects which adopt owner authority
F*
F*Language: OPM RPG
F*
F*Description: This program prints a report showing all objects
F* that adopt owner authority. The two parameters
F* passed to the program are the profile to be
F* checked and the type of objects to be listed.
F* The parameter values are the same as those
F* accepted by the QSYLOBJP API.
F*
F*APIs Used: QSYLOBJP - List Objects that Adopt Owner Authority
F* QUSCRTUS - Create User Space
F* QUSROBJD - Retrieve Object Description /
F* QUSRTVUS - Retrieve From User Space /
F*
F*****************************************************************
F*****************************************************************
F*
FQSYSPRT O F 132 OF PRINTER
F***************************************************************
I/COPY QSYSINC/QRPGSRC,QSYLOBJP
I/COPY QSYSINC/QRPGSRC,QUSROBJD
I/COPY QSYSINC/QRPGSRC,QUSGEN
C*****************************************************************
I* Error Code Structure
I*
I* This shows how the user can define the variable length portion
I* of error code for the exception data.
I*
I*/COPY QSYSINC/QRPGSRC,QUSEC
I*** START HEADER FILE SPECIFICATIONS ****************************
I*
I*Header File Name: H/QUSEC
I*
I*Descriptive Name: Error Code Parameter.
I*
I*5763-SS1, 5722-SS1 (C) Copyright IBM Corp. 1994, 2001
I*All rights reserved.
I*US Government Users Restricted Rights -
I*Use, duplication or disclosure restricted
I*by GSA ADP Schedule Contract with IBM Corp.
I*
I*Licensed Materials-Property of IBM
I*
I*
I*Description: Include header file for the error code parameter.
I*
I*Header Files Included: None.
I*
The value in the information status field is shown at (1). The continuation handle in the header section to
return the remaining information to the user space is shown at (2). The user then passes this value back
to the API as an input parameter so that the API can locate the remaining information and place it in the
user space, as shown at (3).
This is the preferred method for processing lists. To correctly process through a list, take the following
actions:
1. Use the offset to list data section field (5).
2. Look at the number of list entries field in the list (6).
3. For processing lists with fixed-length entries, add the size of each entry field to get to the start of the
next entry (7).
4. For variable-length entries, add the length of the entry (or displacement in some cases) to the next
entry.
IBM might add fields to the bottom of formats in future releases. If this occurs and your code uses the
size of each entry for a previous release, your list will not process at the start of each entry.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/* */
/* Program: List objects which adopt owner authority */
/* */
/* Language: ILE CL */
/* */
/* Description: This program displays all objects that adopt */
/* owner authority. The two parameters passed to */
/* the program are the profile to be checked and */
/* the type of objects to be listed. The parameter */
/* values are the same as those accepted by the */
/* QSYLOBJP API */
/* */
/* APIs Used: QSYLOBJP - List Objects that Adopt Owner Authority */
/* QUSCRTUS - Create User Space */
/* QUSPTRUS - Retrieve Pointer to User Space */
/* QUSROBJD - Retrieve Object Description */
SUBR SUBR(PROCES)
/* */
/* This subroutine processes each entry returned by QSYLOBJP */
/* */
/* Do until the list is complete */
/* */
CHGVAR VAR(&LST_STATUS) VALUE(&LISTSTS)
DOUNTIL COND(&LST_STATUS *EQ ’C’)
IF COND((&LISTSTS *EQ ’C’) *OR (&LISTSTS *EQ +
’P’)) THEN(DO)
/* */
/* And list entries were found */
/* */
IF COND(&LISTENTNBR *GT 0) THEN(DO)
/* */
/* Set &LSTPTR to first byte of the User Space */
/* */
CHGVAR VAR(&LSTPTR) VALUE(&SPCPTR)
/* */
/* Increment &LSTPTR to the first list entry */
/* */
SUBR SUBR(GETLST)
/* */
/* Call QSYLOBJP to generte a list */
/* The continuation handle is primed by the caller of this */
/* subroutine */
/* */
CALL PGM(QSYLOBJP) PARM(&SPC_NAME ’OBJP0200’ +
&USR_PRF &OBJ_TYPE &CONTIN_HDL &ERRCDE)
/* */
/* Check for errors on QSYLOBJP */
/* */
IF COND(&BYTAVL *GT 0) THEN(DO)
SNDPGMMSG MSG(’Failure with QSYLOBJP’) TOPGMQ(*EXT)
RETURN
SUBR SUBR(INIT)
/* */
/* One time initialization code for this program */
/* */
/* Set Error Code structure not to use exceptions */
/* */
CHGVAR VAR(&BYTPRV) VALUE(16)
/* */
/* Check if the User Space was previously created */
/* */
CALL PGM(QUSROBJD) PARM(&RCVVAR &RCVVARSIZ +
’OBJD0100’ &SPC_NAME ’*USRSPC’ &ERRCDE)
/* */
/* Check for errors on QUSROBJD */
/* */
IF COND(&BYTAVL *GT 0) THEN(DO)
/* */
/* If CPF9801, then User Space not found */
/* */
IF COND(&MSGID *EQ ’CPF9801’) THEN(DO)
/* */
/* So create a User Space for the list generated by QSYLOBJP */
/* */
CALL PGM(QUSCRTUS) PARM(&SPC_NAME ’QSYLOBJP’ +
&SPC_SIZE &SPC_INIT ’*ALL’ &BLANKS ’*YES’ +
&ERRCDE ’*USER’)
/* */
/* Check for errors on QUSCRTUS */
/* */
IF COND(&BYTAVL *GT 0) THEN(DO)
SNDPGMMSG MSG(’Failure with QUSCRTUS’) TOPGMQ(*EXT)
RETURN
ENDDO
/* */
/* Else an error accessing the User Space */
/* */
ELSE CMD(DO)
SNDPGMMSG MSG(’Failure with QUSROBJD’) TOPGMQ(*EXT)
RETURN
ENDDO
ENDDO
ENDDO
/* */
/* Set QSYLOBJP (via GETLST) to start a new list */
/* */
CHGVAR VAR(&CONTIN_HDL) VALUE(&BLANKS)
CALLSUBR SUBR(GETLST)
/* */
/* Get a resolved pointer to the User Space */
/* */
CALL PGM(QUSPTRUS) PARM(&SPC_NAME &SPCPTR &ERRCDE)
/* */
/* Check for errors on QUSPTRUS */
/* */
IF COND(&BYTAVL *GT 0) THEN(DO)
SNDPGMMSG MSG(’Failure with QUSPTRUS’) TOPGMQ(*EXT)
RETURN
ENDDO
ENDSUBR
ENDPGM
#include <stdio.h>
#include <string.h>
#include <qsylobjp.h> /* QSYLOBJP API Header */
#include <quscrtus.h> /* QUSCRTUS API Header */
#include <qusptrus.h> /* QUSPTRUS API Header */
#include <qusrobjd.h> /* QUSROBJD API Header */
#include <qusgen.h> /* Format Structures for User Space */
#include <qusec.h> /* Error Code Parameter Include for the APIs */
#include <qliept.h> /* Entry Point Table Include */
/**********************************************************************/
/* Error Code Structure */
/* */
/* This shows how the user can define the variable length portion of */
/* error code for the exception data. */
/* */
/**********************************************************************/
typedef struct {
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_t;
/**********************************************************************/
/* Global Variables */
/**********************************************************************/
char api_name[10];
char cont_hdl[20];
char ext_attr[10];
char list_status;
char mbr_list[8];
char obj_type[10];
char rcvvar[8];
char rjobd_fmt[8];
char space_auth[10];
char space_dmn[10];
char space_init;
char space_name[20];
char space_rep[10];
char space_text[50];
char space_type[10];
char usr_prf[10];
char *usrspc_ptr, *usrspc_base;
/**********************************************************************/
/* Function: done */
/* */
/* Description: This function prints the end of listing print line */
/* and returns to the caller. */
/**********************************************************************/
void done()
{
char command_string[32];
} /* done */
/**********************************************************************/
/* Function: apierr */
/* */
/* Description: This function prints the API name, and exception */
/* identifier of an error that occurred. */
/**********************************************************************/
void apierr()
{
printf("API: %.10s\n", api_name);
printf("Failed with exception: %.7s\n",
error_code.ec_fields.Exception_Id);
done();
} /* apierr */
/**********************************************************************/
/* Function: getlst */
/* */
/* Description: This function calls QSYLOBJP to build a list. */
/* */
/**********************************************************************/
void getlst()
{
memcpy(mbr_list, "OBJP0200", 8);
/********************************************************************/
/* Call QSYLOBJP API to generate a list. The continuation handle */
/* is set by the caller of this function. */
/********************************************************************/
QSYLOBJP(space_name, /* User space and library */
mbr_list, /* Member list */
usr_prf, /* User profile */
obj_type, /* Object type */
cont_hdl, /* Continuation handle (3) */
&error_code); /* Error code */
/********************************************************************/
/* Check for errors on QSYLOBJP. */
/********************************************************************/
if(error_code.ec_fields.Bytes_Available > 0)
{
memcpy(api_name, "QSYLOBJP ", 10);
apierr();
}
/**********************************************************************/
/* Function: init */
/* */
/* Description: This function does all the necessary initialization */
/* for this program. */
/**********************************************************************/
void init()
{
memcpy(space_name, "ADOPTS QTEMP ", 20);
space_init = 0x00;
memcpy(mbr_list, "OBJP0200", 8);
memcpy(rjobd_fmt, "OBJD0100", 8);
memcpy(space_type, "*USRSPC ", 10);
memcpy(ext_attr, "QSYLOBJP ", 10);
memcpy(space_auth, "*ALL ", 10);
memcpy(space_rep, "*YES ", 10);
memcpy(space_dmn, "*USER ", 10);
/********************************************************************/
/* Open QPRINT file so that data can be written to it. If the file */
/* cannot be opened, print a message and exit. */
/********************************************************************/
if((record = fopen("QPRINT", "wb, lrecl=132, type=record")) == NULL)
{
printf("File could not be opened\n");
exit(1);
}
error_code.ec_fields.Bytes_Provided = sizeof(error_code_t);
/********************************************************************/
/* Call QUSROBJD to see if the user space was previously created in */
/* QTEMP. If it was, simply reuse it. */
/********************************************************************/
QUSROBJD(rcvvar, /* Receiver variable */
rcvlen, /* Receiver variable length */
rjobd_fmt, /* Format */
space_name, /* User space name and library */
space_type, /* User object type */
&error_code); /* Error code */
if(error_code.ec_fields.Bytes_Available > 0)
{
/******************************************************************/
/* If a CPF9801 error was received, then the user space was not */
/* found. */
/******************************************************************/
if(memcmp(error_code.ec_fields.Exception_Id, "CPF9801", 7) == 0)
{
/****************************************************************/
/* Create a user space for the list generated by QSYLOBJP. */
/****************************************************************/
QUSCRTUS(space_name, /* User space name and library */
ext_attr, /* Extended attribute */
space_size, /* Size of the user space */
&space_init, /* Space initialization */
space_auth, /* Public authority to user space */
space_text, /* User space text */
space_rep, /* Replace existing user space? */
&error_code, /* Error Code */
space_dmn); /* Domain of created user space */
/****************************************************************/
/* Check for errors on QUSCRTUS. */
/******************************************************************/
/* Set QSYLOBJP (via GETLST) to start a new list. */
/******************************************************************/
memset(cont_hdl, ’ ’, 20);
getlst();
/******************************************************************/
/* Get a resolved pointer to the user space for performance. */
/******************************************************************/
QUSPTRUS(space_name, /* User space name and library */
&usrspc_ptr, /* User space pointer */
&error_code); /* Error Code */
/******************************************************************/
/* Check for errors on QUSPTRUS. */
/******************************************************************/
if(error_code.ec_fields.Bytes_Available > 0)
{
memcpy(api_name, "QUSPTRUS ", 10);
apierr();
}
usrspc_base = usrspc_ptr;
} /* init */
/**********************************************************************/
/* Function: proces2 */
/* */
/* Description: This function processes each entry returned by */
/* QSYLOBJP. */
/* */
/**********************************************************************/
void proces2()
{
char obj_type[112];
/********************************************************************/
/* After each entry, increment to the next entry. */
/********************************************************************/
usrspc_ptr += size_entry; (7)
} /* proces2 */
/********************************************************************/
/* If valid information was returned. (1) */
/********************************************************************/
if((((Qus_Generic_Header_0100_t *)usrspc_ptr)->Information_Status == ’C’) ||
(((Qus_Generic_Header_0100_t *)usrspc_ptr)->Information_Status == ’P’))
{
if(num_entries > 0)
{
/****************************************************************/
/* Get the size of each entry to use later. (4) */
/****************************************************************/
size_entry = ((Qus_Generic_Header_0100_t *)usrspc_ptr)->Size_Each_Entry;
/****************************************************************/
/* Increment to the first list entry. */
/****************************************************************/
offset = ((Qus_Generic_Header_0100_t *)usrspc_ptr)->Offset_List_Data; (5)
usrspc_ptr += offset;
/****************************************************************/
/* Process all of the entries. */
/****************************************************************/
for(i=0; i<num_entries; i++) (6)
proces2();
/****************************************************************/
/* Reset the user space pointer to the beginning. */
/****************************************************************/
usrspc_ptr = usrspc_base;
/****************************************************************/
/* If all entries in this user space have been processed, check */
/* if more entries exist than can fit in one user space. */
/****************************************************************/
if(((Qus_Generic_Header_0100_t *)usrspc_ptr)->Information_Status == ’P’)
{
/**************************************************************/
/* Address the input parameter header. */
/**************************************************************/
offset = ((Qus_Generic_Header_0100_t *)\
usrspc_ptr)->Offset_Input_Parameter;
usrspc_ptr += offset;
/**************************************************************/
/* If the continuation handle in the input parameter header */
/* is blank, then set the list status to complete.
/**************************************************************/
if(memcmp(((Qsy_OBJP_Input_T *)usrspc_ptr)->Continuation_Handle,
" ", 20) == 0)
{
/**********************************************************************/
/* Function: proces */
/* */
/* Description: Processes entries until they are complete. */
/* */
/**********************************************************************/
void proces()
{
list_status = ((Qus_Generic_Header_0100_t *)usrspc_ptr)->Information_Status;
do
{
proces1();
} while (list_status != ’C’);
} /* proces */
/**********************************************************************/
/* main */
/**********************************************************************/
init();
proces();
done();
} /* main */
Related reference
“Example in OPM RPG: List APIs” on page 90
This OPM RPG program prints a report that shows all objects that adopt owner authority.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program: List objects that adopt owner authority
F*
F* Language: ILE RPG
F*
F* Description: This program prints a report showing all objects
F* that adopt owner authority. The two parameters
F* passed to the program are the profile to be
F* checked and the type of objects to be listed.
F* The parameter values are the same as those
F* accepted by the QSYLOBJP API.
F*
F* APIs Used: QSYLOBJP - List Objects that Adopt Owner Authority
F* QUSCRTUS - Create User Space
F* QUSPTRUS - Retrieve Pointer to User Space
F* QUSROBJD - Retrieve Object Description
F*
F***************************************************************
F***************************************************************
F*
FQPRINT O F 132 PRINTER OFLIND(*INOF)
D*
D* Error Code parameter include
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC
D*
DSPC_NAME S 20 INZ(’ADOPTS QTEMP ’)
DSPC_SIZE S 9B 0 INZ(1)
DSPC_INIT S 1 INZ(X’00’)
DLSTPTR S *
DSPCPTR S *
DARR S 1 BASED(LSTPTR) DIM(32767)
DRCVVAR S 8
DRCVVARSIZ S 9B 0 INZ(8)
D*****************************************************************
D*
D* The following QUSGEN include from QSYSINC is copied into
D* this program so that it can be declared as BASED on SPCPTR
D*
D*****************************************************************
DQUSH0100 DS BASED(SPCPTR)
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: List objects that adopt owner authority
*
* Language: COBOL
*
* Description: This program prints a report showing all objects
* that adopt owner authority. The two parameters
* passed to the program are the profile to be
* checked and the type of objects to be listed.
* The parameter values are the same as those
* accepted by the QSYLOBJP API.
*
* APIs Used: QSYLOBJP - List Objects that Adopt Owner Authority
* QUSCRTUS - Create User Space
* QUSPTRUS - Retrieve Pointer to User Space
* QUSROBJD - Retrieve Object Description
*
***************************************************************
***************************************************************
*
PROGRAM-ID. LISTADOPT.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LISTING ASSIGN TO PRINTER-QPRINT
ORGANIZATION IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD LISTING RECORD CONTAINS 132 CHARACTERS
Domains
A domain is a characteristic of an object that controls how programs can access the object. All objects are
assigned a domain attribute when they are created.
After a domain is set, it remains in effect for the life of the object. The possible attributes are system and
user.
Most object types on the system are created in system domain. When you run your system at security
level 40 or 50, you can access system domain objects only by using the commands and callable APIs
provided.
The following object types can be either system or user domain. The list includes the symbolic object
type.
v User space (*USRSPC)
v User index (*USRIDX)
v User queue (*USRQ)
Objects of the type *USRSPC, *USRIDX, and *USRQ in the user domain can be manipulated directly by
MI instructions without using the system-provided APIs and commands.
Note: Objects of the type *PGM, *SRVPGM, and *SQLPKG can also be in the user domain. Their contents
cannot be manipulated directly by MI instructions.
User objects can exist in either the user domain or the system domain. The QALWUSRDMN system value
determines which libraries can contain user-domain user objects. The default QALWUSRDMN system
value is set to *ALL, but it can be changed by system administrators on individual systems to be one
library or a list of libraries. If your application requires direct pointer access to user-domain user objects
in a library that is not specified in the QALWUSRDMN system value, your system administrator can add
the library to the system value.
The ability to create user domain objects on a system with a security level 40 or 50 is controlled by the
QALWUSRDMN system value. For more information, see the User queue domain table in Create User
Queue (QUSCRTUQ) API.
Note: On a system configured for C2 system security, QALWUSRDMN is set to QTEMP (only the
QTEMP library can contain user-domain user objects).
Related reference
Security reference
“Examples: Using data queues or user queues” on page 286
Both data queues and user queues provide a means for one or more processes to communicate
asynchronously. Both queues can be processed by first-in first-out (FIFO), last-in first-out (LIFO), or by
key.
Exit programs
An exit program is a program to which control is passed from a calling application program or system
program. Exit programs can be used to customize particular functions to your needs.
Exit programs are usually user-written programs; however, a few are system-supplied (such as a few of
the Operational Assistant exit programs).
There are no general requirements for using exit programs. For any specific requirements, see the
documentation for the specific exit program.
Exit points
An exit point signifies the point in a system function or program where control is turned over to one or
more exit programs to perform a function.
The registration facility provides a central point to store and retrieve information about i5/OS and
non-i5/OS exit points and their associated exit programs. This information is stored in the registration
facility repository and can be retrieved to determine which exit points and exit programs already exist.
You can use the registration facility APIs to register and deregister exit points, to add and remove exit
programs, and to retrieve information about exit points and exit programs. You can also perform some of
these functions by using the Work with Registration Information (WRKREGINF) command.
The exit point provider is responsible for defining the exit point information, defining the format in which
the exit program receives data, and calling the exit program.
Related reference
Work with Registration Information (WRKREGINF) command
The contents of a database file are not affected by an abnormal system end. On the other hand, the
contents of a user index might become totally unusable if the system ends abnormally. Therefore, you
should not use a user index if the information that you want to store needs to remain without errors after
an abnormal system end.
If your system abnormally ends when you are removing or inserting a user index entry, unpredictable
results might occur. If you are inserting or removing a user index entry, you need to force the index entry
to the disk unit using one of the following:
v A user index created with the immediate update parameter set to 1 (affects performance)
v A Modify Independent Index (MODIDX) MI instruction with the immediate update bit set to 1
v The Set Access State (SETACST) MI instruction
If you do not force the index entry and the system abnormally ends, your index is probably damaged.
To determine if your last system power-down was normal or abnormal, you can check the system value
QABNORMSW.
If your index is damaged, you do not get an error message. The definition of your index is usable; it is
probably the data in your index that is bad.
You can log changes to a database file in a journal, and you can use the journal to apply or remove those
changes later. You can also use the journal to audit who is using the database file. However, the system
does not support the journaling of indexes. As a result, user applications should log entries in a journal
to keep track of changes to the index, but you cannot update the index using apply and remove journal
entry functions.
Performance considerations
The format specified for an API influences the performance cost of the API. In general, when more
information is returned, the performance is slower.
Some list APIs, such as List Job (QUSLJOB), List Spooled Files (QUSLSPL), and List Objects (QUSLOBJ),
generate the list with minimal cost. This is why the formats specified for these APIs do not retrieve very
much information. Some of the APIs, such as List Record Formats (QUSLRCD) and List Fields
(QUSLFLD), have only one format, because there is no additional performance cost to supply the
complete information.
The retrieve APIs allow you to control the performance cost for information that you retrieve. The
retrieve APIs, such as Retrieve Member Description (QUSRMBRD) and Retrieve Spooled File Attributes
(QUSRSPLA), have formats that are generally ordered from the fastest performance to the slowest
performance. That is, the lower-numbered formats run faster but retrieve less information, and the
higher-numbered formats run slower but retrieve more information. One exception is the Retrieve Job
Information (QUSRJOBI) API, where the order of the formats does not have anything to do with
performance characteristics.
Related reference
Retrieve Job Information (QUSRJOBI) API
Some of the returned information contains special values. For example, the List Objects (QUSLOBJ) API
returns the object type as a special value (such as *PGM and *LIB). However, special values might be
added in future releases. Even numeric values might have new special values. When you code to APIs,
assume that the format of the information returned does not change from release to release, but the
content of the information might change.
This common open list structure provides information necessary for the API caller to properly process the
list. If the API error code parameter indicates that no error occurred, the information complete indicator
should be checked for a value of either C (complete and accurate) or P (partial and accurate). If one of
these values is found, the API caller should process the number of entries indicated by the records
returned field.
When these records have been processed, the API caller should determine whether all records that can be
returned in the list have been returned. The API caller can determine this by comparing the total records
value with the sum of the first record in receiver variable value and the records returned value less 1.
When the total records value is greater than or equal to the first record in receiver variable value plus the
records returned value minus 1, additional calls to the Get List Entries (QGYGTLE) API can continue to
receive new records if the list status indicator is not 3 or 5. When total records have been processed and
the list status indicator is 2 or 5, or if the caller no longer needs to process the list, a call to QGYCLST
should be done.
The following table shows the format of the list information parameter in the open list APIs. For a
detailed description of each field, see “Field descriptions.”
Offset
Dec Hex Type Field
0 0 BINARY(4) Total records
4 4 BINARY(4) Records returned
8 8 CHAR(4) Request handle
12 C BINARY(4) Record length
16 10 CHAR(1) Information complete
indicator
17 11 CHAR(13) Date and time created
30 1E CHAR(1) List status indicator
31 1F CHAR(1) Reserved
32 20 BINARY(4) Length of information
returned
36 24 BINARY(4) First record in receiver
variable
40 28 CHAR(40) Reserved
Field descriptions
Date and time created. The date and time when the list was created. The 13 characters follow.
Character Description
1 Century, where 0 indicates years 19 xx and 1 indicates
years 20 xx.
2-7 The date, in YYMMDD (year, month, day) format.
8-13 The time of day, in HHMMSS (hours, minutes, seconds)
format.
First record in receiver variable. The number of the first record returned in the receiver variable.
Information complete indicator. Whether all requested information has been supplied. Possible values
follow.
Value Description
C Complete and accurate information. All of the requested
records have been returned in the receiver variable.
I Incomplete information. An interruption causes the
receiver variable to contain incomplete information.
P Partial and accurate information. Partial information is
returned when the receiver variable is full and not all of
the records requested are returned.
List status indicator. The status of building the list. Possible values follow.
Value Description
0 The building of the list is pending.
1 The list is in the process of being built.
2 The list has been completely built.
3 An error occurred when building the list. The next call to
the Get List Entries (QGYGTLE) API will cause the error
to be signaled to the caller of the QGYGTLE API.
4 The list is primed and ready to be built. The list will be
built asynchronously by a server job, but the server job
has not necessarily started building the list yet.
5 Given the current selection criteria and information
requested, there is too much data to be returned. The list
is incomplete, but data collected to this point is available.
Record length. The length of each record of information returned. For variable length records, this value
is set to zero. For variable length records, you can access the next record in the list by using Offset to the
next entry, Displacement to the next entry, or Length of this entry, which is provided with each list entry
returned.
Records returned. The number of records that are returned in the receiver variable. This number is the
smallest of the following values:
v The number of records that will fit into the receiver variable.
v The number of records in the list.
v The number of records that are requested.
Request handle. The handle of the request that can be used for subsequent requests of information from
the list. The handle is valid until the Close List (QGYCLST) API is called to close the list, or until the job
ends.
Note: This field should be treated as a hexadecimal field. It should not be converted from one CCSID to
another, for example, EBCDIC to ASCII, because doing so could result in an unusable value.
The format of the path name is as follows. For a detailed description of each field, see “Field
descriptions” on page 122.
Field descriptions
This section describes the path name format fields in further detail. Field descriptions are in alphabetical
order.
CCSID. The CCSID (coded character set ID) the path name is in. The possible values follow.
Value Description
0 Use the current job default CCSID.
1-65533 A valid CCSID in this range.
Country or region ID. The country or region ID for the path name. The possible values follow.
Value Description
X'0000' Use the current job country or region ID.
Country or region ID A valid country or region ID.
Language ID. The language ID for the path name. The possible values follow.
Value Description
X'000000' Use the current job language ID.
Language ID A valid language ID.
Path name. Depending on the path type indicator field, this field contains either a pointer to a character
string that contains the path name, or a character string that contains the path name.
The path name must be an absolute path name or a relative path name. An absolute path name is a path
name that starts with the path name delimiter, usually the slash (/) character. A relative path name is a
path name that does not start with the path name delimiter. When a relative name is specified, the API
assumes that this path name starts at the current directory of the process that the API is running in.
The dot and dot dot (. ..) directories are valid in the path name. The home directory, generally
represented by using the tilde character in the first character position of the path name, is not supported.
122 System i: Programming API overview and concepts
A null character value is not allowed as one of the characters in the path name unless a null character is
specified as a path name delimiter.
To avoid confusion with i5/OS special values, path names cannot start with a single asterisk (*) character.
Path name delimiter character. The delimiter character used between the element names in the path
name. This is in the same CCSID as the path name. The most common delimiter is the slash (/) character.
If the delimiter is 1 character, the first character of the 2-character field is used.
Path type indicator. Whether the path name contains a pointer or is a character string and whether the
path name delimiter character is 1 or 2 characters long. The possible values follow.
Value Description
0 The path name is a character string, and the path name
delimiter character is 1 character long.
1 The path name is a pointer, and the path name delimiter
character is 1 character long.
2 The path name is a character string, and the path name
delimiter character is 2 characters long.
3 The path name is a pointer, and the path name delimiter
character is 2 characters long.
Using APIs
These examples show how to use program-based APIs and service-program-based APIs.
The examples focus on descriptions, formats, variable-length fields as output, and optional parameters.
They access information from a job description to demonstrate how to code APIs. While this might not be
what your application requires, you can use the same approach to access information when you use most
of the APIs.
Assume that you are interested in accessing the value of the HOLD parameter on the Create Job
Description (CRTJOBD) or Change Job Description (CHGJOBD) command. The HOLD parameter
determines whether the job is held on the job queue. The following values are supported:
The first step is to find the correct API to use. To do this, you must identify the part of the i5/OS licensed
program that is most closely related to the function in which you are interested. If you want to access
information from a job description, as in these examples, you need to know that a job description object
is considered part of the work management function. API names contain verbs that are similar to the
i5/OS licensed program: change, create, remove, and retrieve. These examples use the Retrieve Job
Description Information (QWDRJOBD) API.
To make the RPG program JOBDAPI (this program name is also used in other examples in this topic
collection) more general purpose, two parameters for the job description (JOBD) name and library
(JOBDL) name are passed to it, as shown at (5). A message is sent for the value found. The program does
not handle errors. Any errors are returned as exception messages.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
I*****************************************************************
I*****************************************************************
I*
I*Program Name: JOBDAPI
I*
I*Language: OPM RPG
I*
I*Descriptive Name: Job Description
I*
I*Description: This example expects errors to be sent as escape
I* messages.
I*
I*Header Files Included: QUSEC - Error Code Parameter
I* QWDRJOBD - Retrieve Job Description API
I*
I*****************************************************************
I*****************************************************************
I*
I* Error Code Parameter Include for the APIs
I*
I/COPY QSYSINC/QRPGSRC,QUSEC
I*
I* Retrieve Job Description API Include
I*
I/COPY QSYSINC/QRPGSRC,QWDRJOBD (1)
I*
I* Command String Data Structure
I*
ICMDSTR DS
I I ’SNDMSG MSG(’’HOLD - 1 26 CMD1
I ’value is ’
I 27 36 HOLD
I I ’’’) TOUSR(QPGMR)’ 37 51 CMD2
I*
I* Miscellaneous Data Structure
I*
I DS
I* (2)
The program declares the variables to be used. The QWDBH variable is length 390 as shown at (2).
In the example, the program places a value of JOBD0100 in the format variable. A literal could have been
used instead for those languages that support a literal on a call, as shown at (5). The program generates
the qualified name of the job description (JOBD) by concatenating the simple name and the library
qualifier, as shown at (6). A 20-character variable must be used, and the simple name must begin in byte
1 with the library qualifier in byte 11. Because CAT is used, a simple concatenation of two 10-byte
variables occurs so that the names are in the correct place for the LFNAM parameter.
The QWDRJOBD API is called with the correct parameter list. The API uses the parameter list and
accesses the job description specified. The API extracts the values from the internal object form and
places them in a data structure that matches the JOBD0100 format. The API then returns with the data
structure placed in variable QWDBH, which is located in member QWDRJOBD in the QSYSINC library.
System: GENSYS90
Queue . . . . . : QPGMR Program . . . . : *DSPMSG
Library . . . : QUSRSYS Library . . . :
Severity . . . : 00 Delivery . . . : *HOLD
Type reply (if required), press Enter.
From . . . : SMITH 07/23/94 10:25:14
HOLD value is *NO
The API does not need to be called each time that you want a separate field because all fields are
returned that would fit within the size indicated by the length of receiver variable (RCVLEN) parameter.
You can run the program against the QBATCH job description in library QGPL by using the following
call statement:
CALL JOBDAPI PARM(QBATCH QGPL)
If QGPL is on the library list, you can run the program against the QBATCH job description by using the
following call statement:
CALL JOBDAPI PARM(QBATCH *LIBL)
You can run the program on one of your own job descriptions or on a test job description where you
have specified HOLD(*YES).
For this example, assume that the XYZ job description does not exist:
CALL JOBDAPI PARM(XYZ *LIBL)
You probably will receive the inquiry message CPA0701 that states an unmonitored exception (CPF9801)
has occurred and offers several possible replies. At this point, you would enter C for Cancel and press
the Enter key.
If you displayed the low-level messages, you would see the following: CPF9801 (Object not found),
followed by the inquiry message (CPA0701), followed by your reply.
When you specify the error code parameter as zero, you are specifying that exceptions be sent as escape
messages. You can code the RPG program so that any errors on the call set the indicator 01 to on, as
shown at (10). This causes a different path to be taken in the code.
For RPG, the CALL operation specifies the error indicator. Based on whether the error indicator is on or
off, a set of instructions can be processed. The API must receive an error code parameter that consists of a
binary 4 field with a value of binary zeros, as shown at (11)). The message ID can be accessed from the
program-status data structure. You would define this as follows:
I* Program status DS ((12))
IPGMSTS SDS
I 40 46 MSGIDD
If you are going to do something about an error condition, you must test for an error condition in RPG:
v If you use the error-code data structure, test the bytes available field.
v If you let exceptions occur, test the error indicator on the CALL operation ((10)).
Because you must test for some condition (one of the error messages in Error Messages), no great
difference exists in how you handle error conditions in RPG. The error-code data structure is a little more
straightforward (the program-status data structure is not used). The only disadvantage of the error-code
data structure is that the escape message that occurred was removed from the job log.
If the CPF9801 exception occurs, your program sends a message to the QPGMR message queue as shown
in the following display:
Display Messages
System: GENSYS90
Queue . . . . . : QPGMR Program . . . . : *DSPMSG
Library . . . : QUSRSYS Library . . . :
Severity . . . : 00 Delivery . . . : *HOLD
Library . . . :
Type reply (if required), press Enter.
From . . . : SMITH 07/25/94 11:10:12
No such *JOBD exists
If another exception occurs (for example, a library name that is not valid), you do not receive an
indication that an error occurred because of the way the error subroutine is currently coded.
In addition, you can use the Message Handling APIs to receive the messages sent to your program
message queue.
The call to the API fails if you specify a valid job description but use a library qualifier such as *ALLUSR.
The value *ALLUSR is not supported by the description of the required parameter group.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
*****************************************************************
*****************************************************************
*
*Program Name: JOBDAPI
*
*Programming Language: COBOL
*
*Description: This example expects errors sent as
* escape messages.
*
*Header Files Included: QUSEC - Error Code Parameter
* QWDRJOBD - Retrieve Job Description API
*
*****************************************************************
*****************************************************************
*
PROGRAM-ID. JOBDAPI.
*
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY QUSEC OF QSYSINC-QLBLSRC.
*
* Retrieve Job Description API Include
*
COPY QWDRJOBD OF QSYSINC-QLBLSRC. (2)
*
* Command String Data Structure
*
01 COMMAND-STRING.
05 TEXT1 PIC X(26) VALUE ’SNDMSG MSG(’’HOLD value is’.
05 HOLD PIC X(10).
05 TEXT2 PIC X(15) VALUE ’’’) TOUSR(QPGMR)’.
*
01 COMMAND-LENGTH PIC S9(10)V99999 COMP-3.
01 RECEIVER-LENGTH PIC S9(9) COMP-4. (4)
01 FORMAT-NAME PIC X(8) VALUE ’JOBD0100’. (5)
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/***********************************************************************/
/***********************************************************************/
/* */
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <qusec.h> /* Error Code Parameter Include for the APIs */
#include <qwdrjobd.h> (2)
/* Retrieve Job Description API Include */
#include <qliept.h>
char received[8];
/* Used to receive error msgs signaled */
/* from QWDRJOBD API. */
/***********************************************************************/
/* Function: error_handler */
/* Description: This function handles exceptions signalled from the */
/* QWDRJOBD API. The message identifier received is */
/* assigned to the variable ’received’. */
/***********************************************************************/
_GetExcData(&ExcDta);
memcpy(received,ExcDta.Msg_Id,7);
signal(SIGALL,error_handler);
}
/***********************************************************************/
/* Error Code Structure */
/* */
/* This shows how the user can define the variable length portion of */
/* error code for the exception data. */
/* */
/***********************************************************************/
typedef struct {
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_t;
/*********************************************************************/
/*********************************************************************/
/* Make sure we received the correct number of parameters. The argc */
/* parameter will contain the number of parameters that was passed */
/* to this program. This number also includes the program itself, */
/* so we need to evaluate argc-1. */
/*********************************************************************/
/*********************************************************************/
/* Move the two parameters passed into qual_job_desc. (9) */
/*********************************************************************/
memcpy(qual_job_ptr, argv[1], 10);
qual_job_ptr += 10;
memcpy(qual_job_ptr, argv[2], 10); (6)
/*********************************************************************/
/* Set the error code parameter to 0. */
/*********************************************************************/
error_code.ec_fields.Bytes_Provided = 0;
/*********************************************************************/
/* Call the QWDRJOBD API. */
/*********************************************************************/
QWDRJOBD(rec_var, /* Receiver Variable */
390, (3) /* Receiver Length */
"JOBD0100", (5) /* Format Name */
qual_job_desc, /* Qualified Job Description */
&error_code); /* Error Code */
/*********************************************************************/
/* Let’s tell everyone what the hold value was for this job. */
/*********************************************************************/
sprintf(command_string,
"SNDMSG MSG(’HOLD value is %.7s’) TOUSR(QPGMR)",
hold_value);
system(command_string);
} /* main */
Related reference
“Example in OPM RPG: Retrieving the HOLD parameter (exception message)” on page 124
This OPM RPG program retrieves the value of the HOLD parameter of a job description (specified on the
Create Job Description (CRTJOBD) or Change Job Description (CHGJOBD) command). Any errors are
returned as exception messages.
This program can be written only in OPM RPG and ILE RPG.
D***************************************************************
D***************************************************************
D*
D* Program Name: JOBDAPI
D*
D* Programming Language: ILE RPG
D*
D* Description: This program retrieves the HOLD value from
D* a job description. It expects errors to be
D* sent as escape messages.
D*
D* Header Files Included: QUSEC - Error Code Parameter
D* QWDRJOBD - Retrieve Job Description API
D*
D***************************************************************
D***************************************************************
D*
D* Error Code parameter include
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC
D*
D* Retrieve Job Description API Include
D*
D/COPY QSYSINC/QRPGLESRC,QWDRJOBD
D*
D* Program status DS
D*
DPGMSTS SDS (12)
D MSG_ID 40 46
D*
D* Command string data structure
D*
DCMD_STRING DS
D 26 INZ(’SNDMSG MSG(’’HOLD value is ’)
D HOLD 10
D 15 INZ(’’’) TOUSR(QPGMR)’)
D*
D* Miscellaneous data structure
D*
DRCVLEN S 9B 0 INZ(%SIZE(QWDD0100))
DFORMAT S 8 INZ(’JOBD0100’)
DLENSTR S 15 5 INZ(%SIZE(CMD_STRING))
DNO_JOBD S 47 INZ(’SNDMSG MSG(’’No such *JOBD -
D exists’’) TOUSR(QPGMR)’)
DNO_JOBD_SZ S 15 5 INZ(%SIZE(NO_JOBD))
C*
C* Beginning of mainline
C*
C* Two parameters are being passed into this program
C*
Example in OPM RPG: Retrieving the HOLD parameter (error code structure)
This OPM RPG program retrieves the value of the HOLD parameter of a job description (specified on the
Create Job Description (CRTJOBD) or Change Job Description (CHGJOBD) command). Any errors are
returned in an error code structure.
In “Example in OPM RPG: Retrieving the HOLD parameter (exception message)” on page 124, QUSBNB
was set to a value of binary zero to tell the API to send exceptions (escape messages) for any error
conditions. The example in this topic uses an error-code data structure as an alternative to receiving
exceptions.
Some languages do not support the use of exceptions, so you might prefer to code for errors using error
code structures.
In your programs, you can use error code structures in the following ways:
v Define an 8-byte error code structure that provides feedback on whether an error occurred. If an error
does occur, you are not able to determine the specifics of the problem.
v Define a 16-byte error code structure that allows you to determine if an error exists and to access the
exception message ID. The exception message IDs are the same as shown in Error messages.
v Define a larger than 16-byte error code structure that provides the same information as described in the
previous two error code structures as well as some or all of the exception data. The exception data is
the message data that is sent with the exception message. Because the vast majority of exception
messages do not have more than 512 bytes of message data, a 600-byte error code structure would be
adequate for almost all cases.
Note: Lengths of 1 through 7 bytes are not valid for the error code structure.
Offset
Dec Hex Use Type Field
0 0 INPUT BINARY(4) Bytes provided
4 4 OUTPUT BINARY(4) Bytes available
8 8 OUTPUT CHAR(7) Exception ID
15 F OUTPUT CHAR(1) Reserved
16 10 OUTPUT CHAR(*) Exception data
The error code structure can be found in the QUSEC member in the QSYSINC library, as shown at (1).
Which of the files you use depends on the language.
The bytes provided field describes the size of the error code structure that you declared in your program
and how you want errors returned. (This was set to 0 as shown by (6) in Example in OPM RPG:
Retrieving the HOLD parameter (exception message).)
The exception ID is the normal 7-character message ID, such as CPF9801, that occurs for an
object-not-found condition. Do not test this field to determine if an error exists. The field is properly set
by the system only if the number of bytes available is greater than 0. Similarly, the exception data
(message data) information is not set properly unless an error exists; for example, any information left
from a prior call is not changed.
The following program is the same as the previous program except that a 16-byte error code structure is
used:
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
I*****************************************************************
I*****************************************************************
I*
I*Program Name: JOBDAPI
I*
I*Language: OPM RPG
I*
I*Descriptive Name: Get Job Description
I*
I*Description: This sample program shows exceptions being
I* returned in the error code parameter.
I*
I*Header Files Included: QUSEC - Error Code Parameter
I* QWDRJOBD - Retrieve Job Description API
I*
I*****************************************************************
I*****************************************************************
I*
I* Error Code Parameter Include for the APIs
I*
I/COPY QSYSINC/QRPGSRC,QUSEC (1)
I*
I* Retrieve Job Description API Include
I*
I/COPY QSYSINC/QRPGSRC,QWDRJOBD
I*
I* Command String Data Structure
I*
ICMDSTR DS
I I ’SNDMSG MSG(’’HOLD - 1 26 CMD1
I ’value is ’
I 27 36 HOLD
I I ’’’) TOUSR(QPGMR)’ 37 51 CMD2
I*
IMSG2 DS
I I ’SNDMSG MSG(’’Progr- 1 43 MSG2A
I ’am failed with mes-
I ’sage ID ’
I 44 50 MSGIDD
I I ’’’) TOUSR(QPGMR)’ 51 65 MSG2B
I*
I* Miscellaneous Data Structure
I*
I DS
I I 390 B 1 40RCVLEN
I I ’JOBD0100’ 5 12 FORMAT
C*
C* Beginning of Mainline
C*
The QUSBN error-code data structure is defined in the QUSEC include file, as shown at (1). The program
initializes the bytes provided field (QUSBNB) with a value of 16, as shown at (3). This sets the first field
of the error code structure to tell the API not to send an exception but to use the first 16 bytes of the
QUSBN parameter to return the error information. After the call to the API, the program accesses the
bytes available (QUSBNC), as shown at (2). This contains the number of bytes of information about the
If an error occurred, you might want to handle the error in many different methods. The program shown
extracts the specific error message ID that occurred and sends the 7-character value as a message. The
QUSBN parameter is used for both input and output (see Format of an error code structure). The first 4
bytes are input to the API to tell it how to handle exceptions. The remaining bytes are output from the
API about any exception conditions.
To see the value of the HOLD attribute, use the following call statement to run the program against the
QBATCH job description in library QGPL:
CALL JOBDAPI (QBATCH QGPL)
You should see that the value of the HOLD attribute is *NO:
+--------------------------------------------------------------------------------+
| |
| |
| Display Messages |
| |
| |
| System: GENSYS90 |
| Queue . . . . . : QPGMR Program . . . . : *DSPMSG |
| Library . . . : QUSRSYS Library . . . : |
| Severity . . . : 00 Delivery . . . : *HOLD |
| Type reply (if required), press Enter. |
| From . . . : SMITH 07/23/94 10:25:14 |
| HOLD value is *NO |
| |
+--------------------------------------------------------------------------------+
For this error condition, you should assume that the XYZ job description does not exist. Use the
following call statement to run the error condition:
CALL JOBDAPI (XYZ *LIBL)
You should see that the CPF9801 message (Object not found) was issued:
+--------------------------------------------------------------------------------+
| |
| |
| Display Messages |
| |
| System: GENSYS90 |
| Queue . . . . . : QPGMR Program . . . . : *DSPMSG |
| Library . . . : QUSRSYS Library . . . : |
| Severity . . . : 00 Delivery . . . : *HOLD |
| Type reply (if required), press Enter. |
| From . . . : SMITH 07/23/94 10:56:13 |
| Program failed with message ID CPF9801 |
| |
| |
+--------------------------------------------------------------------------------+
Then run another error condition. For this error condition, you should assume that the XYZ library does
not exist. Use the following call statement:
CALL JOBDAPI (QPGMR XYZ)
You should see that the CPF9810 message (Library not found) was issued. An advantage of the error
return variable is that it can contain other information such as message data. The following are the
changes needed to return a 200-byte error code structure:
I*****************************************************************
I*****************************************************************
I*
I*Program Name: JOBDAPI
I*
I*Language: OPM RPG
I*
I*Descriptive Name: Get Job Description
I*
I*Description: This sample program shows the incorrect
I* way of using the offset in a user space in RPG.
I*
I*Header Files Included: QUSEC - Error Code Parameter
I* (Copied into Program)
I* QWDRJOBD - Retrieve Job Description API
I*
I*****************************************************************
I* Error Code Parameter Include for the APIs
I*
I* The following QUSEC include is copied into this program
I* so that the variable-length field can be defined as
I* fixed length.
I*
I*** START HEADER FILE SPECIFICATIONS ****************************
I*
I*Header File Name: H/QUSEC
I*
I*Descriptive Name: Error Code Parameter.
I*
I*5763-SS1, 5722-SS1 (C) Copyright IBM Corp. 1994, 2001
I*All rights reserved.
I*US Government Users Restricted Rights -
I*Use, duplication or disclosure restricted
I*by GSA ADP Schedule Contract with IBM Corp.
I*
I*Licensed Materials-Property of IBM
I*
I*
I*Description: Include header file for the error code parameter.
I*
I*Header Files Included: None.
I*
I*Macros List: None.
I*
I*Structure List: Qus_EC_t
I*
I*Function Prototype List: None.
I*
.
.
.
C Z-ADD200 QUSBNB
C*
C CALL ’QWDRJOBD’
C PARM QWDBH Receiver Var.
C PARM RCVLEN Length RCVVAR
C PARM FORMAT Format Name
C PARM LFNAM Qual. Job Desc
C PARM QUSBN Error Code
The value placed in the QUSBNG variable (4) is the message data associated with the message ID that is
identified as the exception. The message data follows the same format as if you had entered a Receive
Message (RCVMSG) command and requested the message data (MSGDTA) parameter. You can use the
Display Message Description (DSPMSGD) command to determine the layout of the message data for a
particular message ID. When you handle exceptions, the only information provided is the exception ID
and the message data associated with the exception. You cannot receive a diagnostic message (if one were
sent in addition to the escape message) in the error-code data structure. You can use the message
handling APIs to receive messages from your program message queue and to access the other messages
that might be issued from the API.
When you instruct the API to return all errors in the error-code data structure, the escape message does
not appear in the job log. The escape message not appearing in the job log is one of the major differences
between letting the API return errors in an error-code data structure and letting the API send escape
messages. For the error-code data structure, the escape messages have been removed from the job log by
the API. If a diagnostic message is sent first, the diagnostic message exists in the job log and can be
received.
Example in ILE COBOL: Retrieving the HOLD parameter (error code structure)
This ILE COBOL program retrieves the value of the HOLD parameter of a job description (specified on
the Create Job Description (CRTJOBD) or Change Job Description (CHGJOBD) command). Any errors are
returned in an error code structure.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
*****************************************************************
*****************************************************************
*
*Program Name: JOBDAPI
*
*Programming Language: COBOL
*
*Description: This example shows how to make use of an
* error returned in the error code
* structure.
*
*Header Files Included: QUSEC - Error Code Parameter
* QWDRJOBD - Retrieve Job Description API
*
*****************************************************************
*****************************************************************
*
PROGRAM-ID. JOBDAPI.
*
DATA DIVISION.
WORKING-STORAGE SECTION.
*
* Error Code parameter include. As this sample program
* uses COPY to include the error code structure, only the first
* 16 bytes of the error code structure are available. If the
* application program needs to access the variable length
* exception data for the error, the developer should physically
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/***********************************************************************/
/***********************************************************************/
/* */
/*Program Name: JOBDAPI */
/* */
/*Programming Language: ILE C */
/* */
/*Description: This example shows how to make use of an */
/* error returned in the error code structure. */
/* */
/*Header Files Included: STDIO - Standard Input/Output */
/* STRING - String Functions */
/* QUSEC - Error Code Parameter */
/* QWDRJOBD - Retrieve Job Description API */
/* QLIEPT - Entry Point Table */
/* */
/***********************************************************************/
/***********************************************************************/
#include <stdio.h>
#include <string.h>
/***********************************************************************/
/* Error Code Structure */
/* */
/* This shows how the user can define the variable length portion of */
/* error code for the exception data. */
/* */
/***********************************************************************/
typedef struct {
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_t;
memset(hold_value, ’ ’, 10);
/*********************************************************************/
/* Make sure we received the correct number of parameters. The argc */
/* parameter will contain the number of parameters that was passed */
/* to this program. This number also includes the program itself, */
/* so we need to evaluate argc-1. */
/*********************************************************************/
/*********************************************************************/
/* Move the two parameter passed in into qual_job_desc. */
/*********************************************************************/
memcpy(qual_job_ptr, argv[1], 10);
qual_job_ptr += 10;
memcpy(qual_job_ptr, argv[2], 10);
/*********************************************************************/
/* Set the error code parameter to 16. */
/*********************************************************************/
error_code.ec_fields.Bytes_Provided = 16; (3)
/*********************************************************************/
/* Call the QWDRJOBD API. */
/*********************************************************************/
QWDRJOBD(rec_var, /* Receiver Variable */
390, /* Receiver Length */
"JOBD0100", /* Format Name */
qual_job_desc, /* Qualified Job Description */
&error_code); /* Error Code */
/*********************************************************************/
} /* main */
Related reference
“Example in OPM RPG: Retrieving the HOLD parameter (error code structure)” on page 136
This OPM RPG program retrieves the value of the HOLD parameter of a job description (specified on the
Create Job Description (CRTJOBD) or Change Job Description (CHGJOBD) command). Any errors are
returned in an error code structure.
Example in ILE RPG: Retrieving the HOLD parameter (error code structure)
This ILE RPG program retrieves the value of the HOLD parameter of a job description (specified on the
Create Job Description (CRTJOBD) or Change Job Description (CHGJOBD) command). Any errors are
returned in an error code structure.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
D***************************************************************
D***************************************************************
D*
D* Program Name: JOBDAPI
D*
D* Programming Language: ILE RPG
D*
D* Description: This program retrieves the HOLD value from
D* a job description. It expects errors to be
D* returned via the error code parameter.
D*
D* Header Files Included: QUSEC - Error Code Parameter
D* QWDRJOBD - Retrieve Job Description API
D*
D***************************************************************
D***************************************************************
D*
D* Error Code parameter include
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC (1)
D*
D* Retrieve Job Description API Include
D*
D/COPY QSYSINC/QRPGLESRC,QWDRJOBD
D*
D* Command string data structure
D*
DCMD_STRING DS
This is the same type of program as the programs in “Example in OPM RPG: Retrieving the HOLD
parameter (exception message)” on page 124 and “Example in OPM RPG: Retrieving the HOLD
parameter (error code structure)” on page 136. The program, named JOBDAPI, prints the HOLD value if
it is found, as shown at 1. If an error occurs, the program prints a line that contains the error message ID
to a spooled file called QPRINT, as shown at 2.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F*****************************************************************
F*****************************************************************
F*
F*Program Name: JOBDAPI
F*
F*Language: OPM RPG
F*
F*Descriptive Name: Get Job Description
F*
F*Description: The following program prints out the name of
F* the job description or prints an error if the
F* API could not find the job description name
F* specified.
F*
F*
F*Header Files Included: QUSEC - Error Code Parameter
F* QWDRJOBD - Retrieve Job Description API
F*
F*****************************************************************
F*****************************************************************
F* JOBDAPIR - Print value of HOLD parameter using API
F* Uses error-code data structure
F*
FQPRINT O F 132 OF PRINTER
I*
I* Error Code Parameter Include for the APIs
I*
I/COPY QSYSINC/QRPGSRC,QUSEC
I*
I* Retrieve Job Description API Include
I*
I/COPY QSYSINC/QRPGSRC,QWDRJOBD
I*
I*
I* Dummy data structure used to declare binary field (3)
The program retrieves the parameter list that is passed and initializes the fields to be passed to the API.
The API is called and places information into the receiver-variable data structure if information is found.
The API places the information in the error-code data structure if an error occurred and if enough space
was provided to receive the information.
The program prints one of two different lines depending on whether any errors were found:
HOLD value - *NO (1)
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
*****************************************************************
*****************************************************************
*
*Program Name: JOBDAPI
*
*Programming Language: ILE COBOL
*
*Description: This example shows how to print messages
* to spool files.
*
*Header Files Included: QUSEC - Error Code Parameter
* QWDRJOBD - Retrieve Job Description API
*
*****************************************************************
*****************************************************************
*
PROGRAM-ID. JOBDAPI.
*
INPUT-OUTPUT SECTION.
FILE-CONTROL.
*
SELECT LISTING ASSIGN TO PRINTER-QPRINT
ORGANIZATION IS SEQUENTIAL.
*
DATA DIVISION.
FILE SECTION.
*
FD LISTING RECORD CONTAINS 132 CHARACTERS
*
STOP RUN.
*
* End of Mainline
*
*
* Subroutine to perform if no errors were encountered.
*
GOOD.
*
* Subroutine to perform if an error was returned in error code.
*
BAD.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/***********************************************************************/
/***********************************************************************/
/* */
/*Program Name: JOBDAPI */
/* */
/*Programming Language: ILE C */
/* */
/*Description: This example shows how to print messages */
/* to spool files. */
/* */
/*Header Files Included: STDIO - Standard Input/Output */
/* STRING - String Functions */
/* QUSEC - Error Code Parameter */
/* QWDRJOBD - Retrieve Job Description API */
/* QLIEPT - Entry Point Table */
/* */
/***********************************************************************/
#include <stdio.h>
#include <string.h>
#include <qusec.h> /* Error Code Parameter Include for the APIs */
#include <qwdrjobd.h> /* Retrieve Job Description API Include */
#include <qliept.h> /* Entry Point Table Include */
/***********************************************************************/
/* Error Code Structure */
/* */
/* This shows how the user can define the variable length portion of */
/* error code for the exception data. */
/* */
/***********************************************************************/
typedef struct {
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_t;
memset(hold_value, ’ ’, 10);
/*********************************************************************/
/* Make sure we received the correct number of parameters. The argc */
/* parameter will contain the number of parameters that was passed */
/* to this program. This number also includes the program itself, */
/* so we need to evaluate argc-1. */
/*********************************************************************/
/*********************************************************************/
/* Move the two parameter passed into qual_job_desc. */
/*********************************************************************/
memcpy(qual_job_ptr, argv[1], 10);
qual_job_ptr += 10;
memcpy(qual_job_ptr, argv[2], 10);
/*********************************************************************/
/* Set the error code parameter to 16. */
/*********************************************************************/
error_code.ec_fields.Bytes_Provided = 16;
/*********************************************************************/
/* Open QPRINT file so that data can be written to it. If the file */
/* cannot be opened, print a message and exit. */
/*********************************************************************/
if((stream = fopen("QPRINT", "wb")) == NULL)
/*********************************************************************/
/* Call the QWDRJOBD API. */
/*********************************************************************/
QWDRJOBD(rec_var, /* Receiver Variable */
390, /* Receiver Length */
"JOBD0100", /* Format Name */
qual_job_desc, /* Qualified Job Description */
&error_code); /* Error Code */
/*********************************************************************/
/* If an error was returned, print the error message to the QPRINT */
/* spool file. */
/*********************************************************************/
if(error_code.ec_fields.Bytes_Available > 0)
{
memcpy(message_id, error_code.ec_fields.Exception_Id, 7);
sprintf(message_string,
"Failed. Error ID - %.7s",
message_id);
fprintf(stream, message_string);
}
/*********************************************************************/
/* Let’s tell everyone what the hold value was for this job. */
/* The result will be printed in the QPRINT spool file. */
/*********************************************************************/
else
{
memcpy(hold_value, ((Qwd_JOBD0100_t *)rec_var)->Hold_Job_Queue, 10);
sprintf(command_string,
"HOLD value - %.10s",
hold_value);
fprintf(stream, command_string);
}
fclose(stream);
} /* main */
Related reference
“Example in OPM RPG: Printing the HOLD value” on page 148
This OPM RPG program prints the HOLD value of a job description (specified on the Create Job
Description (CRTJOBD) or Change Job Description (CHGJOBD) command).
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program Name: JOBDAPI
F*
F* Programming Language: ILE RPG
F*
F* Description: This program retrieves the HOLD value from
F* a job description and then prints the value.
F* It expects errors to be returned via the
F* error code parameter.
The discussion of the initial library list field in the job description format, JOBD0100 Format, indicates
that the initial library list field is 11 bytes per entry, where each entry is a library name followed by a
blank. Depending on how many libraries are named for the initial library list, the actual amount of space
used varies (by multiples of 11).
The format does not have an entry in the Offset columns for initial library list. It might begin in offset
390, but do not rely on this offset value. For example, if a new field is added to the job description
format, it will probably be placed at offset 390, and the initial library list information will be shifted.
To access the initial library list field, use the following fields in the format:
v Offset to the initial library list field, as shown at 1 in the program.
v Number of libraries in the initial library list field, as shown at 2.
If you use these field values in the format instead of statically encoding an offset and a number of
libraries, your program can work on any future release of a business computing system, even if more job
description attributes are defined in the format. This is an important approach to ensuring compatibility
with future releases. Use this approach whenever you code for a list of entries.
The following RPG code sends a message for each library found in the initial library list field. Exceptions
are handled by the RPG program. Although a library name cannot exceed 10 bytes, each entry is 11 bytes
long.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
I*****************************************************************
I*****************************************************************
I*
I*Program Name: JOBDAPI
I*
I*Language: OPM RPG
I*
I*Descriptive Name: Get Job Description
I*
I*Description: This sample program shows the correct
I* way of using the offset in a user space in RPG.
I*
I*Header Files Included: QUSEC - Error Code Parameter
I* (Copied into Program)
I* QWDRJOBD - Retrieve Job Description API
I* (Copied into Program)
I*
I*****************************************************************
I*****************************************************************
Note: It is important to access the count and to compare for the exact number of libraries to be
processed. If you do not check for the exact number of libraries, you may begin to access
information in the format for the next set of information (in this example, it may be the request
data value).
The handling of the initial library list field is typical of what you will find in many APIs.
Related reference
Retrieve Job Description Information (QWDRJOBD) API
“Example in ILE COBOL: Accessing a field value (initial library list)”
This ILE COBOL program accesses a variable-length array. The variable-length array is the initial library
list for a job description.
“Example in ILE C: Accessing a field value (initial library list)” on page 165
This ILE C program accesses a variable-length array. The variable-length array is the initial library list for
a job description.
“Example in ILE RPG: Accessing a field value (initial library list)” on page 168
This ILE RPG program accesses a variable-length array. The variable-length array is the initial library list
for a job description.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
*****************************************************************
*****************************************************************
*
*Program Name: JOBDAPI
*
*Programming Language: COBOL
*
*Description: This example shows how to access a
* field value returned from a retrieve
* API.
*
*Header Files Included: QUSEC - Error Code Parameter
* QWDRJOBD - Retrieve Job Description API
*
*****************************************************************
*****************************************************************
*
PROGRAM-ID. JOBDAPI.
*
DATA DIVISION.
WORKING-STORAGE SECTION.
*
* Error Code parameter include. As this sample program
* uses COPY to include the error code structure, only the first
* 16 bytes of the error code structure are available. If the
* application program needs to access the variable length
* exception data for the error, the developer should physically
* copy the QSYSINC include and modify the copied include to
* define additional storage for the exception data.
*
COPY QUSEC OF QSYSINC-QLBLSRC.
*
* Retrieve Job Description API Include
*
* The header file for the QWDRJOBD API was included in this
* program so that the varying length portion of the structure
* can be defined as a fixed portion.
*
*** START HEADER FILE SPECIFICATIONS ****************************
*
*Header File Name: H/QWDRJOBD
*
*Descriptive Name: Retrieve Job Description Information API
*
*5763-SS1, 5722-SS1 (C) Copyright IBM Corp. 1994, 2001
*All rights reserved.
*US Government Users Restricted Rights -
*Use, duplication or disclosure restricted
*by GSA ADP Schedule Contract with IBM Corp.
*
*Licensed Materials-Property of IBM
*
*
*Description: The Retrieve Job Description Information API
* retrieves information from a job description
* object and places it into a single variable in the
* calling program.
*
*Header Files Included: None.
*
*Macros List: None.
*
STOP RUN.
*
* End of Mainline
*
*
* Subroutine to handle errors returned in the error code
* parameter.
*
ERRCOD.
*
IF BYTES-AVAILABLE OF QUS-EC > 0
*
* Process errors returned from the API.
*
STOP RUN.
*
* Subroutine to check to see if there is enough room in the
* receiver variable for the next library in the list.
*
RECLEN.
*
IF (X + 10) >= RECEIVER-LENGTH
STOP RUN.
Related reference
“Example in OPM RPG: Accessing a field value (initial library list)” on page 156
This OPM RPG program accesses a variable-length array. The variable-length array is the initial library
list for a job description.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/***********************************************************************/
/***********************************************************************/
/* */
/*Program Name: JOBDAPI */
/* */
/*Programming Language: ILE C */
/* */
/*Description: This example shows how to access a field */
/* value returned from a retrieve API. */
/* */
/*Header Files Included: STDIO - Standard Input/Output */
/* STRING - String Functions */
/* QUSEC - Error Code Parameter */
/* QWDRJOBD - Retrieve Job Description API */
/* QLIEPT - Entry Point Table */
#include <stdio.h>
#include <string.h>
#include <qusec.h> /* Error Code Parameter Include for the APIs */
#include <qwdrjobd.h> /* Retrieve Job Description API Include */
#include <qliept.h> /* Entry Point Table Include */
/***********************************************************************/
/* Error Code Structure */
/* */
/* This shows how the user can define the variable-length portion of */
/* error code for the exception data. */
/* */
/***********************************************************************/
typedef struct {
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_t;
/***********************************************************************/
/* JOBD0100 Structure */
/* */
/* This shows how the user can define the variable-length portion of */
/* the JOBD0100 format. */
/* */
/***********************************************************************/
typedef struct {
Qwd_JOBD0100_t data;
char Lib_Data[5000]; (1) (2)
} JOBD0100;
memset(hold_value, ’ ’, 10);
/*********************************************************************/
/* Make sure we received the correct number of parameters. The argc */
/* parameter will contain the number of parameters that was passed */
/* to this program. This number also includes the program itself, */
/* so we need to evaluate argc-1. */
/*********************************************************************/
/*********************************************************************/
/* Set the error code parameter to 16. */
/*********************************************************************/
error_code.ec_fields.Bytes_Provided = 16;
/*********************************************************************/
/* Call the QWDRJOBD API. */
/*********************************************************************/
QWDRJOBD(rec_var, /* Receiver Variable */
rec_len, /* Receiver Length */
"JOBD0100", /* Format Name */
qual_job_desc, /* Qualified Job Description */
&error_code); /* Error Code */
/*********************************************************************/
/* If an error was returned, send an error message. */
/*********************************************************************/
if(error_code.ec_fields.Bytes_Available > 0)
{
/* In this example, nothing was done for the error condition. */
}
/*********************************************************************/
/* Let’s tell everyone what the library value was for this job. */
/*********************************************************************/
else
{
num_libs = ((JOBD0100 *)rec_var)->data.Number_Libs_In_Lib_list;
offset = ((JOBD0100 *)rec_var)->data.Offset_Initial_Lib_List;
/*******************************************************************/
/* Advance receiver variable pointer to the location where the */
/* library list begins. */
/*******************************************************************/
rec_ptr += offset;
rec_ptr += 11;
if((offset + 10) >= rec_len)
break;
offset += 11;
}
} /* main */
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
D***************************************************************
D***************************************************************
D*
D* Program Name: JOBDAPI
D*
D* Programming Language: ILE RPG
D*
D* Description: This program retrieves the library list from
D* a job description. It expects errors to be
D* returned via the error code parameter.
D*
D* Header Files Included: QUSEC - Error Code Parameter
D*
D* Header Files Modified: QWDRJOBD - Retrieve Job Description API
D*
D***************************************************************
D***************************************************************
D*
D* Error Code parameter include
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC
D*
D* The following QWDRJOBD include from QSYSINC is copied into
D* this program so that it can be declared as 1000 bytes in
D* size. This size should accommodate the variable length Library
D* List array.
D*
D*** START HEADER FILE SPECIFICATIONS ****************************
D*
D*Header File Name: H/QWDRJOBD
D*
D*Descriptive Name: Retrieve Job Description Information API
D*
D*5763-SS1, 5722-SS1 (C) Copyright IBM Corp. 1994, 2001
D*All rights reserved.
D*US Government Users Restricted Rights -
D*Use, duplication or disclosure restricted
D*by GSA ADP Schedule Contract with IBM Corp.
D*
D*Licensed Materials-Property of IBM
D*
D*
D*Description: The Retrieve Job Description Information API
D* retrieves information from a job description
D* object and places it into a single variable in the
D* calling program.
D*
D*Header Files Included: None.
D*
D*Macros List: None.
D*
D*Structure List: Qwd_JOBD0100_t
D*
Example in OPM RPG: Using keys with the List Spooled Files (QUSLSPL) API
This OPM RPG program processes a list of spooled file information that you have specified using keys.
Unlike the earlier JOBDAPI program examples, where format JOBD0100 of the Retrieve Job Description
(QWDRJOBD) API returned dozens of fields while we were only interested in the HOLD field, the
QUSLSPL API provides a keyed interface that allows LSTSPL to request that only the relevant fields
(spooled file name, date created, and number of pages) be returned.
In addition to providing a keyed interface, QUSLSPL also differs from QWDRJOBD in that the QUSLSPL
API retrieves a list of all spooled files into a User Space (*USRSPC) while QWDRJOBD retrieves
information about one specific job description into a program variable.
In the following program example, all the pieces have been put together with an OPM RPG program that
accesses specific information related to spooled files. A report listing this information is created. The
program example does not handle API-related errors. Any errors that are received are returned as
exception messages, as shown at 1.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F*
F* Program Name: LSTSPL
F*
F* Program Language: OPM RPG
F*
F* Descriptive Name: List Spooled Files for Current User
F*
F* Description: This example shows the steps necessary
F* to process keyed output from an API.
F*
F* Header Files Included: QUSEC - Error Code Parameter
F* QUSGEN - User Space Generic Header
F* QUSLSPL - List Spooled Files
F*
F* APIs Used: QUSLSPL - List Spooled Files
F* QUSCRTUS - Create User Space
F* QUSRTVUS - Retrieve User Space
F*
F***************************************************************
FQSYSPRT O F 132 OF PRINTER
I*
I* Copy User Space Generic Header
I*
I/COPY QSYSINC/QRPGSRC,QUSGEN (11)
I*
I* Copy API Error Code parameter
I*
I/COPY QSYSINC/QRPGSRC,QUSEC
I*
I* Copy List Spooled Files API include
I*
I/COPY QSYSINC/QRPGSRC,QUSLSPL
I*
I* Data structure to hold space name
I*
ISPCNAM DS
I I ’SPCNAME ’ 1 10 SPC
I I ’QTEMP ’ 11 20 LIB
I*
I* Data structure to hold requested key values
I*
IKEYARA DS (7)
List APIs do not automatically create the user space (*USRSPC) to receive the list. You must first create
one using the Create User Space (QUSCRTUS) API (2). Similar to CL create commands, the QUSCRTUS
API has several parameters that identify the name of the object, the public authority, the object
description text, and so forth.
After creating the user space, you can call the QUSLSPL API to return spooled file information into the
user space (3). The QUSLSPL API supports two formats: SPLF0100, which returns a fixed set of
information about each selected spooled file, and SPLF0200, which returns only user-selected fields.
LSTSPL uses SPLF0200 (4) and passes to the QUSLSPL API a list of keys to identify the selected fields (5)
and the number of keys (6). Because OPM RPG does not support an array (list) of binary values, LSTSPL
defines the key array (KEYARA) as a data structure comprised of contiguous binary(4) fields (7). The
fields are initialized to 201, 216, and 211, which correspond to the keys named spooled file name, date file
Having generated the list, you can now process the user space data.
List APIs (like QUSLSPL) generally provide a generic list header at the beginning of the user space,
which provides information such as the API that created the list, the number of entries (spooled files for
this example) in the list, the size of each entry, and so on. To access the generic list header, use the
Retrieve User Space (QUSRTVUS) API (9). Program LSTSPL retrieves the generic list header into the data
structure QUSBP (10), which is defined in the QUSGEN QSYSINC /COPY (include) file (11). Note that
languages, such as ILE RPG, COBOL, and C, which support pointers, can avoid this call to QUSRTVUS
(and the resulting movement of data) by using the Retrieve Pointer to User Space (QUSPTRUS) API.
Program LSTSPL now checks that the format of the generic list header is the one expected (12), and if
not, prints an error line (13). Having verified the header format, LSTSPL now checks the information
status of the list (14) (and if it is not accurate, prints an error line (15)) and that at least one list entry is
available (16).
Having determined that accurate list entries are available, program LSTSPL performs the following
operations:
v Initialize the COUNT variable to keep track of how many entries have been processed (17).
v Add one to the base 0 offset (to the first entry in the list) as the QUSRTVUS API assumes base 1
positional values (18).
v Determine how much data is associated with each entry (19) (which is the lesser of either the amount
of storage you allocated to receive a list entry or the size of a list entry).
v Fall into a DO loop to process all of the available list entries (20).
Within this loop, LSTSPL retrieves each list entry (21), extracts the number of fields returned (22), and
enters an inner DO loop to process all of the available list entry fields (23).
Within this inner loop, the program extracts the field information (24) and processes the field data based
on the key field (25).
When all fields for a given list entry have been processed, LSTSPL generates a print line (26) and
proceeds to the next list entry (27).
When all the list entries have been processed, LSTSPL ends (28).
Example in ILE COBOL: Using keys with the List Spooled Files (QUSLSPL) API
This ILE COBOL program processes a list of spooled file information that you have specified using keys.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: List Spooled Files for Current User
*
* Language: ILE COBOL
*
* Description: This example shows the steps necessary to
* process keyed output from an API.
*
* APIs Used: QUSLSPL - List Spooled Files
* QUSCRTUS - Create User Space
* QUSPTRUS - Retrieve Pointer to User Space
*
***************************************************************
***************************************************************
*
PROGRAM-ID. LSTSPL.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LISTING ASSIGN TO PRINTER-QPRINT
ORGANIZATION IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD LISTING RECORD CONTAINS 132 CHARACTERS
LABEL RECORDS ARE STANDARD
DATA RECORD IS LIST-LINE.
01 LIST-LINE PIC X(132).
*
WORKING-STORAGE SECTION.
*
* Error Code parameter include. As this sample program
* uses COPY to include the error code structure, only the first
* 16 bytes of the error code structure are available. If the
ELSE
WRITE LIST-LINE FROM LSTERR. (15)
STOP RUN. (28)
*****************************************************************
PROCES.
*
* address the first variable length record for this entry
*
SET ADDRESS OF QUS-LSPL-KEY-INFO TO ADDRESS OF
QUS-SPLF0200(5:).
*
* process all variable length records associated with this entry
*
PERFORM PROCES2 NUM-FIELDS-RETD TIMES. (22) (23)
Example in ILE C: Using keys with the List Spooled Files (QUSLSPL) API
This ILE C program processes a list of spooled file information that you have specified using keys.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/******************************************************************/
/* */
/* Program: List Spooled Files for Current User */
/* */
/* Language: ILE C */
/* */
/* Description: This example shows the steps necessary to */
/* process keyed output from an API */
/* */
/* APIs Used: QUSLSPL - List Spooled Files */
/* QUSCRTUS - Create User Space */
/* QUSPTRUS - Retrieve Pointer to User Space */
/* */
/******************************************************************/
#include <stdio.h>
#include <string.h>
#include <quslspl.h> /* QUSLSPL API header */
#include <quscrtus.h> /* QUSCRTUS API header */
#include <qusptrus.h> /* QUSPTRUS API header */
#include <qusgen.h> /* Format Structures for User Space (11) */
#include <qusec.h> /* Error Code parameter include for APIs */
#include <qliept.h> /* Entry Point Table include for APIs */
/******************************************************************/
/* Global variables */
/******************************************************************/
char spc_name[20] = "SPCNAME QTEMP ";
int spc_size = 2000;
char spc_init = 0x00;
char *spcptr, *lstptr, *lstptr2;
int pages;
struct keys { int key1; (7)
int key2;
int key3;} keys = {201, 211, 216}; (8)
int number_of_keys = 3;
char ext_attr[10] = "QUSLSPL ";
char spc_aut[10] = "*ALL ";
char spc_text[50] = " ";
char spc_replac[10] = "*YES ";
char spc_domain[10] = "*USER ";
char format[8] = "SPLF0200"; (4)
char usr_prf[10] = "*CURRENT ";
char outq[20] = "*ALL ";
char formtyp[10] = "*ALL ";
char usrdta[10] = "*ALL ";
char jobnam[26] = " ";
char prtfil[10];
char opndat[7];
typedef struct {
Qus_LSPL_Key_Info_t Key_Info;
char Data_Field[100];
main()
{
/***************************************************************/
/* Open print file for report */
/***************************************************************/
error_code.Bytes_Provided = 0; (1)
/***************************************************************/
/* Create a User Space for the List generated by QUSLSPL */
/***************************************************************/
/***************************************************************/
/* Call QUSLSPL to get all spooled files for *CURRENT user */
/***************************************************************/
/***************************************************************/
/* Get a resolved pointer to the User Space */
/***************************************************************/
/***************************************************************/
/* If valid information returned */
/***************************************************************/
if(memcmp\
(((Qus_Generic_Header_0100_t *)spcptr)->Structure_Release_Level, (12)
"0100", 4) != 0) { printf("Unknown Generic Header"); (13)
exit();
/***************************************************************/
/* address current list entry */
/***************************************************************/
{
lstptr = spcptr + (((Qus_Generic_Header_0100_t *)spcptr)\
->Offset_List_Data);
/***************************************************************/
/* process all the entries */
/***************************************************************/
/***************************************************************/
/* set lstptr2 to first variable length record for this entry */
/***************************************************************/
lstptr2 = lstptr + 4;
/***************************************************************/
/* process all the variable length records for this entry */
/***************************************************************/
/***************************************************************/
/* extract spooled file name for report */
/***************************************************************/
/***************************************************************/
/* extract number of pages for report */
/***************************************************************/
/***************************************************************/
/* extract age of spooled file for report */
/***************************************************************/
/***************************************************************/
/* bump lstptr2 to next variable length record */
/***************************************************************/
lstptr2 = lstptr2 +
(((Qus_LSPL_Key_Info_t *)lstptr2)\
->Len_Field_Info_Retd);
}
/***************************************************************/
/* print collected information */
/***************************************************************/
/***************************************************************/
/* bump lstptr to next list entry */
/***************************************************************/
/***************************************************************/
/* exit at end of list */
/***************************************************************/
fclose(record);
exit();
}
}
else
{ printf("List data not valid"); (15)
exit();
}
} (28)
Related reference
“Example in OPM RPG: Using keys with the List Spooled Files (QUSLSPL) API” on page 171
This OPM RPG program processes a list of spooled file information that you have specified using keys.
Example in ILE RPG: Using keys with the List Spooled Files (QUSLSPL) API
This ILE RPG program processes a list of spooled file information that you have specified using keys.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program: List Spooled Files for Current User
F*
F* Language: ILE RPG
F*
The examples use the registration facility APIs. The registration facility APIs provide a means for storing
and retrieving information about exit points and exit programs. An exit point is a specific point in a
system function or program where control is passed to one or more exit programs. An exit program is a
program to which control is passed from an exit point. The examples show how to manipulate exit points
and exit programs, how to retrieve information about exit points and exit programs that are stored with
the registration facility, and how to call an exit program.
Several of the registration facility APIs manipulate the information that the registration facility repository
contains. One API is provided for retrieving information from the repository.
For a detailed description of how to use the API, see “API information format” on page 50. These
descriptions and the programs that support them are in RPG. You can, however, view the same programs
in different languages.
Related concepts
“APIs for the service-program-based environment” on page 13
APIs based on service programs are called as procedures exported from ILE service programs
(*SRVPGM).
Related reference
Generic header files using ILE APIs
Example: Keyed interface using ILE APIs
Error handling using ILE APIs
Examples: Receiver variables using ILE APIs
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/* PROGRAM: Register an Exit Point */
/* Add an Exit Program */
/* */
/* LANGUAGE: ILE C */
/* */
/* DESCRIPTION: This program registers an exit point with the */
/* registration facility. After the successful */
/* completion of the registration of the exit point, */
/* an exit program is added to the exit point. */
/* */
/* APIs USED: QusRegisterExitPoint - Register Exit Point */
/* QusAddExitProgram - Add Exit Program */
/* */
/********************************************************************/
/********************************************************************/
/* Includes */
/********************************************************************/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <qusrgfa1.h>
#include <qusec.h>
#include <qliept.h>
/********************************************************************/
/* Structures */
/********************************************************************/
/********************************************************************/
/* */
/* main */
/* */
/********************************************************************/
int main()
{
int ccsid,
pgm_num,
num_of_attrs,
epgm_num,
len_epgm_data,
add_epgm_num,
*ccsid_ptr,
*pgm_num_ptr;
error_code_struct error_code;
rgpt_controls control_keys;
addep_attributes attrib_keys;
/******************************************************************/
/* Register the exit point with the registration facility. If the */
/* registration of the exit point is successful, add an exit */
/* program to the exit point. */
/******************************************************************/
/******************************************************************/
/* Set the exit point controls. Each control field is passed to */
/* the API using a variable length record. Each record must */
/* start on a 4-byte boundary. */
/******************************************************************/
/******************************************************************/
/* Set the total number of controls that are being specified on */
/* the call. This program lets the API take the default for the */
/* controls that are not specified. */
/******************************************************************/
control_keys.num_rec=2;
/******************************************************************/
/* Set the values for the two controls that are specified: */
/* Maximum number of exit programs = 10 */
/* Exit point text description = "EXIT POINT EXAMPLE" */
/******************************************************************/
control_keys.max_pgms_rec.Length_Vlen_Record=16;
control_keys.max_pgms_rec.Control_Key=3;
control_keys.max_pgms_rec.Length_Data=4;
control_keys.max_pgms=10;
control_keys.descrip_rec.Length_Vlen_Record=62;
control_keys.descrip_rec.Control_Key=8;
control_keys.descrip_rec.Length_Data=50;
memcpy(control_keys.text_desc,
"EXIT POINT EXAMPLE ",50);
/******************************************************************/
/* Call the API to register the exit point. */
/******************************************************************/
QusRegisterExitPoint("EXAMPLE_EXIT_POINT ",
"EXMP0100",
&control_keys,
&error_code);
/******************************************************************/
/* If an exception occurs, the API returns the exception in the */
/* error code parameter. The bytes available field is set to */
/* zero if no exception occurs and nonzero if an exception does */
/* occur. */
/******************************************************************/
if (error_code.ec_fields.Bytes_Available != 0)
{
printf("ATTEMPT TO REGISTER EXIT POINT FAILED WITH EXCEPTION: %.7s",
error_code.ec_fields.Exception_Id);
exit(1);
}
/******************************************************************/
/* If the call to register an exit point is successful, add */
/* an exit program to the exit point. */
/******************************************************************/
/******************************************************************/
/******************************************************************/
/* Set the values for the two attributes that are being */
/* specified: */
/* Replace exit program = 1 */
/* Exit program data CCSID = 37 */
/******************************************************************/
attrib_keys.replace_rec.Length_Vlen_Record=16;
attrib_keys.replace_rec.Control_Key=4;
attrib_keys.replace_rec.Length_Data=1;
attrib_keys.replace=’1’;
attrib_keys.CCSID_rec.Length_Vlen_Record=16;
attrib_keys.CCSID_rec.Control_Key=3;
attrib_keys.CCSID_rec.Length_Data=4;
attrib_keys.CCSID=37;
/******************************************************************/
/* Call the API to add the exit program. */
/******************************************************************/
QusAddExitProgram("EXAMPLE_EXIT_POINT ",
"EXMP0100",
1,
"EXAMPLEPGMEXAMPLELIB",
"EXAMPLE EXIT PROGRAM DATA",
25,
&attrib_keys,
&error_code);
/******************************************************************/
/* If an exception occurs, the API returns the exception in the */
/* error code parameter. The bytes available field is set to */
/* zero if no exception occurs and nonzero if an exception does */
/* occur. */
/******************************************************************/
if (error_code.ec_fields.Bytes_Available != 0)
{
printf("ATTEMPT TO ADD AN EXIT PROGRAM FAILED WITH EXCEPTION: %.7s",
error_code.ec_fields.Exception_Id);
exit(1);
}
} /* End program */
Example in OPM COBOL: Registering exit points and adding exit programs
This OPM COBOL program registers an exit point with the registration facility. After the successful
completion of the registration, the program adds an exit program to the exit point.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: Register an Exit Point
* Add an Exit Program
*
* Language: OPM COBOL
Example in ILE COBOL: Registering exit points and adding exit programs
This ILE COBOL program registers an exit point with the registration facility. After the successful
completion of the registration, the program adds an exit program to the exit point.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
Example in OPM RPG: Registering exit points and adding exit programs
This OPM RPG program registers an exit point with the registration facility. After the successful
completion of the registration, the program adds an exit program to the exit point.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program: Register an Exit Point
F* Add an Exit Program
F*
F* Language: OPM RPG
F*
F* Description: This program registers an exit point with the
F* registration facility. After the successful
F* completion of the registration of the exit point,
F* an exit program is added to the exit point.
F*
F* APIs Used: QUSRGPT - Register Exit Point
F* QUSADDEP - Add Exit Program
F*
F***************************************************************
F***************************************************************
F*
FQPRINT O F 132 PRINTER UC
E* COMPILE TIME ARRAY
E REC 1000 1
I*
I* Keyed Variable Length Record includes
I*
I/COPY QSYSINC/QRPGSRC,QUS
I*
I* Error Code parameter include. As this sample program
I* uses /COPY to include the error code structure, only the first
I* 16 bytes of the error code structure are available. If the
I* application program needs to access the variable length
I* exception data for the error, the developer should physically
I* copy the QSYSINC include and modify the copied include to
I* define additional storage for the exception data.
I*
I/COPY QSYSINC/QRPGSRC,QUSEC
I*
I* Miscellaneous data
I*
IVARREC DS 1008
I B 1 40NBRREC
I 51004 REC
I I 1 B100510080VO
I*
IOVRLAY DS
I B 1 40BINARY
I 1 4 BINC
I*
I DS
I I ’EXAMPLE_EXIT_POINT ’ 1 20 EPNTNM
I I ’EXAMPLEPGMEXAMPLELIB’ 21 40 EPGM
I I ’EXAMPLE EXIT PROGRAM- 41 65 EPGMDT
I ’ DATA’
I I ’EXAMPLE POINT EXAMPL- 66 115 EPTXT
Example in ILE RPG: Registering exit points and adding exit programs
This ILE RPG program registers an exit point with the registration facility. After the successful completion
of the registration, the program adds an exit program to the exit point.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program: Register an Exit Point
F* Add an Exit Program
F*
F* Language: ILE RPG
F*
F* Description: This program registers an exit point with the
F* registration facility. After the successful
F* completion of the registration of the exit point,
F* an exit program is added to the exit point.
F*
F* APIs Used: QusRegisterExitPoint - Register Exit Point
F* QusAddExitProgram - Add Exit Program
F*
F***************************************************************
F***************************************************************
F*
FQPRINT O F 132 PRINTER OFLIND(*INOF) USROPN
D*
D* Keyed Variable Length Record includes
D*
D/COPY QSYSINC/QRPGLESRC,QUS
D*
D* Error Code parameter include. As this sample program
D* uses /COPY to include the error code structure, only the first
D* 16 bytes of the error code structure are available. If the
D* application program needs to access the variable length
D* exception data for the error, the developer should physically
D* copy the QSYSINC include and modify the copied include to
D* define additional storage for the exception data.
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC
D*
D*****************************************************************
D*Prototype for calling Register Exit Point API.
D*****************************************************************
D QUSREP05 C ’QusRegisterExitPoint’
D*****************************************************************
D*Prototype for calling Add Exit Program API.
D*****************************************************************
D QUSAEPGM C ’QusAddExitProgram’
D*
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/* PROGRAM: Remove an Exit Program */
/* Deregister an Exit Point */
/* */
/* LANGUAGE: ILE C */
/* */
/* DESCRIPTION: This program removes an exit program and */
/* deregisters an exit point from the registration */
/* facility. */
/* */
/* APIs USED: QusRemoveExitProgram - Remove Exit Program */
/* QusDeregisterExitPoint - Deregister Exit Point */
/* */
/********************************************************************/
/* NOTE: This example uses APIs that are shipped with *EXCLUDE */
/* authority. The user needs *USE authority to the service */
/* program QUSRGFA1 to use these APIs. */
/********************************************************************/
/********************************************************************/
/* Includes */
/********************************************************************/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <qusrgfa1.h>
#include <qusec.h>
#include <qliept.h>
/********************************************************************/
/* Structures */
/********************************************************************/
/********************************************************************/
/* */
/* main */
/* */
/********************************************************************/
int main()
{
int pgm_num=1;
error_code_struct error_code;
/******************************************************************/
/* Remove an exit program from the exit point and then deregister */
/* the exit point. It is not necessary to remove exit programs */
/* from an exit point before deregistering the exit point. It is */
/* done here only for illustration purposes. */
/******************************************************************/
/******************************************************************/
/* Initialize the error code parameter. To have exceptions */
/* signaled to this program by the API, set the bytes provided */
/* field of the code to zero. This program has exceptions sent */
/* through the error code parameter; therefore, the bytes */
/* provided field is set to the number of bytes that this program */
/* gives the API for the parameter. */
/******************************************************************/
error_code.ec_fields.Bytes_Provided=sizeof(error_code_struct);
/******************************************************************/
/* Call the API to remove the exit program. */
/******************************************************************/
QusRemoveExitProgram("EXAMPLE_EXIT_POINT ",
"EXMP0100",
pgm_num,
&error_code);
/******************************************************************/
/* If an exception occurs, the API returns the exception in the */
/* error code parameter. The bytes available field is set to */
/* zero if no exception occurs and nonzero if an exception does */
/* occur. */
/******************************************************************/
if (error_code.ec_fields.Bytes_Available != 0)
{
printf("ATTEMPT TO REMOVE EXIT PROGRAM FAILED WITH EXCEPTION: %.7s",
error_code.ec_fields.Exception_Id);
exit(1);
}
/******************************************************************/
/* If the call to remove the exit program is successful, */
/* deregister the exit point. */
/******************************************************************/
/******************************************************************/
/* Call the API to add the exit program. */
/******************************************************************/
QusDeregisterExitPoint("EXAMPLE_EXIT_POINT ",
"EXMP0100",
&error_code);
/******************************************************************/
/* If an exception occurs, the API returns the exception in the */
/* error code parameter. The bytes available field is set to */
/* zero if no exception occurs and nonzero if an exception does */
} /* End program */
Example in OPM COBOL: Removing exit programs and deregistering exit points
This OPM COBOL program removes an exit program from an exit point. After the successful completion
of the removal, the program deregisters the exit point from the registration facility.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: Remove an Exit Program
* Deregister an Exit Point
*
* Language: OPM COBOL
*
* Description: This program removes an exit program and
* deregisters an exit point from the registration
* facility.
*
* APIs Used: QUSRMVEP - Remove Exit Program
* QUSDRGPT - Deregister Exit Point
*
***************************************************************
*
***************************************************************
PROGRAM-ID. REGFAC1.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LISTING ASSIGN TO PRINTER-QPRINT
ORGANIZATION IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD LISTING RECORD CONTAINS 132 CHARACTERS
LABEL RECORDS ARE STANDARD
DATA RECORD IS LIST-LINE.
01 LIST-LINE PIC X(132).
WORKING-STORAGE SECTION.
*
* Error Code parameter include. As this sample program
* uses COPY to include the error code structure, only the first
* 16 bytes of the error code structure are available. If the
* application program needs to access the variable length
* exception data for the error, the developer should physically
* copy the QSYSINC include and modify the copied include to
* define additional storage for the exception data.
*
COPY QUSEC OF QSYSINC-QLBLSRC.
*
* Error message text
*
01 BAD-EXIT-POINT.
05 TEXT1 PIC X(41)
VALUE "Attempt to deregister exit point failed: ".
Example in ILE COBOL: Removing exit programs and deregistering exit points
This ILE COBOL program removes an exit program from an exit point. After the successful completion of
the removal, the program deregisters the exit point from the registration facility.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: Remove an Exit Program
* Deregister an Exit Point
*
* Language: ILE COBOL
*
* Description: This program removes an exit program and
* deregisters an exit point from the registration
* facility.
*
* APIs Used: QusRemoveExitProgram - Remove Exit Program
* QusDeregisterExitPoint - Deregister Exit Point
*
***************************************************************
*
***************************************************************
PROGRAM-ID. REGFAC3.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LISTING ASSIGN TO PRINTER-QPRINT
ORGANIZATION IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD LISTING RECORD CONTAINS 132 CHARACTERS
LABEL RECORDS ARE STANDARD
DATA RECORD IS LIST-LINE.
01 LIST-LINE PIC X(132).
WORKING-STORAGE SECTION.
*
* Error Code parameter include. As this sample program
* uses COPY to include the error code structure, only the first
* 16 bytes of the error code structure are available. If the
* application program needs to access the variable length
* exception data for the error, the developer should physically
* copy the QSYSINC include and modify the copied include to
* define additional storage for the exception data.
*
COPY QUSEC OF QSYSINC-QLBLSRC.
*
* Error message text
*
01 BAD-EXIT-POINT.
05 TEXT1 PIC X(41)
VALUE "Attempt to deregister exit point failed: ".
05 EXCEPTION-ID PIC X(07).
01 BAD-EXIT-PGM.
05 TEXT1 PIC X(39)
VALUE "Attempt to remove exit program failed: ".
05 EXCEPTION-ID PIC X(07).
*
Example in OPM RPG: Removing exit programs and deregistering exit points
This OPM RPG program removes an exit program from an exit point. After the successful completion of
the removal, the program deregisters the exit point from the registration facility.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program: Remove an Exit Program
F* Deregister an Exit Point
F*
F* Language: OPM RPG
F*
F* Description: This program removes an exit program and
F* deregisters an exit point from the registration
F* facility.
F*
F* APIs Used: QUSRMVEP - Remove Exit Program
F* QUSDRGPT - Deregister Exit Point
F*
F***************************************************************
F***************************************************************
F*
FQPRINT O F 132 PRINTER UC
I*
I* Error Code parameter include. As this sample program
I* uses /COPY to include the error code structure, only the first
I* 16 bytes of the error code structure are available. If the
I* application program needs to access the variable length
I* exception data for the error, the developer should physically
I* copy the QSYSINC include and modify the copied include to
I* define additional storage for the exception data.
I*
I/COPY QSYSINC/QRPGSRC,QUSEC
I*
I*
I* Miscellaneous data
I*
I DS
I B 1 40PGMNBR
I I ’EXAMPLE_EXIT_POINT ’ 5 24 EPNTNM
C*
C* Beginning of mainline
C*
C* Remove an exit program from the exit point and then deregister
C* the exit point. It is not necessary to remove exit programs
C* from an exit point before deregistering the exit point. It is
C* done here only for illustrative purposes.
C*
C* Initialize the error code parameter. To signal exceptions to
C* this program by the API, you need to set the bytes provided
C* field of the error code to zero. Because this program has
C* exceptions sent back through the error code parameter, it sets
C* the bytes provided field to the number of bytes it gives the
C* API for the parameter.
C*
C Z-ADD16 QUSBNB
C*
C* Call the API to remove the exit program.
Example in ILE RPG: Removing exit programs and deregistering exit points
This ILE RPG program removes an exit program from an exit point. After the successful completion of
the removal, the program deregisters the exit point from the registration facility.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
The Retrieve Exit Information (QusRetrieveExitInformation) API returns a continuation handle when it
has more information to return than what can fit in the receiver variable.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/* PROGRAM: Retrieve Exit Point and Exit Program Information */
/* */
/* LANGUAGE: ILE C */
/********************************************************************/
/* Includes */
/********************************************************************/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <except.h>
#include <qusrgfa2.h>
#include <qusec.h>
#include <qmhchgem.h>
#include <miptrnam.h>
#include <qliept.h>
/********************************************************************/
/* Prototypes */
/********************************************************************/
typedef void Pgm_OS(void *arg,...);
#pragma linkage(Pgm_OS,OS)
/********************************************************************/
/* Structures */
/********************************************************************/
/********************************************************************/
/* FUNCTION NAME: RSLVSP_PGM_HDLR */
/* */
/* FUNCTION : This function handles all exceptions that */
/* may occur while resolving to the exit */
/* program. */
/* */
/* INPUT: Interrupt handler information */
/* */
/* OUTPUT: NONE */
/* */
/********************************************************************/
void RSLVSP_PGM_HDLR(_INTRPT_Hndlr_Parms_T *errmsg)
{
error_code_struct Error_Code;
/******************************************************************/
/* Set the rsl_ok indicator to not valid. */
/******************************************************************/
int *rsl_ok = (int *)(errmsg>Com_Area);
*rsl_ok = 0;
/******************************************************************/
/* Let message handler know that the program handled the message */
/* and to remove it from the job log. */
/******************************************************************/
/********************************************************************/
/* FUNCTION NAME: Call_Exit_Program */
/* */
/* FUNCTION : This function calls the exit programs that */
/* were retrieved from the registration facility */
/* repository. */
/* */
/* INPUT: Information retrieved */
/* */
/* OUTPUT: NONE */
/* */
/********************************************************************/
void Call_Exit_Program(char *rcv_var)
{
int num_exit_pgms,
i;
char exit_pgm_name[10],
exit_pgm_lib[10],
info_for_exit_pgm[10],
*rcv_ptr;
volatile int rsl_ok;
Pgm_OS *exit_pgm_ptr;
/******************************************************************/
/* Save the number of exit programs returned and set the pointer */
/* to point to the first exit program entry. */
/******************************************************************/
rcv_ptr=rcv_var;
num_exit_pgms=((Qus_EXTI0200_t *)rcv_ptr)>Number_Programs_Returned;
rcv_ptr += ((Qus_EXTI0200_t *)rcv_ptr)>Offset_Program_Entry;
rsl_ok=1;
/****************************************************************/
/* Resolve to the exit program. If an error occurs on the */
/* resolve operation to the library, the rsl_ok indicator is */
/* set to failed in the RSL_PGM_HDLR exception handler. */
/* The rslvsp MI instruction signals all errors to this */
/* program; therefore, enable the exception handler to capture */
/* any errors that may occur. */
/****************************************************************/
#pragma exception_handler (RSLVSP_PGM_HDLR,rsl_ok,0,_C2_MH_ESCAPE)
exit_pgm_ptr=((Pgm_OS *)rslvsp(_Program,
exit_pgm_name,
exit_pgm_lib,
_AUTH_POINTER));
#pragma disable_handler
/****************************************************************/
/****************************************************************/
/* Set the receiver variable to point to the next exit program */
/* that is returned. */
/****************************************************************/
rsl_ok=1;
rcv_ptr=rcv_var +
((Qus_EXTI0200_Entry_t *)rcv_ptr)>Offset_Next_Entry;
}
}
/********************************************************************/
/* */
/* main */
/* */
/********************************************************************/
void main()
{
int sel_criteria=0,
len_rcv_variable=3500,
exit_pgm_num=-1;
char continuation_hdl[16],
rcv_variable[3500],
*rcv_ptr;
error_code_struct error_code;
/******************************************************************/
/* Retrieve the exit point information first. If the current */
/* number of exit programs is not zero, retrieve the exit */
/* programs. It is not necessary to call for the exit point */
/* information to determine if the exit point has any exit */
/* programs. It is done here for illustration purposes only. */
/* You can make one call to the API for the exit program */
/* information and check the number of exit program entries */
/* returned field to see if there are any exit programs to call. */
/******************************************************************/
/******************************************************************/
/* Initialize the error code to inform the API that all */
/* exceptions should be returned through the error code parameter.*/
/******************************************************************/
error_code.ec_fields.Bytes_Provided=sizeof(error_code_struct);
/******************************************************************/
/* Blank out the continuation handle to let the API know that this*/
/* is a first attempt at the retrieve operation. */
/******************************************************************/
memset(continuation_hdl,’ ’,16);
/******************************************************************/
/* Call the API to retrieve the exit point information. */
/******************************************************************/
QusRetrieveExitInformation(continuation_hdl,
&rcv_variable,
len_rcv_variable,
"EXTI0100",
"EXAMPLE_EXIT_POINT ",
"EXMP0100",
exit_pgm_num,
&sel_criteria,
/******************************************************************/
/* If an exception occurs, the API returns the exception in the */
/* error code parameter. The bytes available field is set to */
/* zero if no exception occurs and nonzero if an exception does */
/* occur. */
/******************************************************************/
if (error_code.ec_fields.Bytes_Available != 0)
{
printf("ATTEMPT TO RETRIEVE INFORMATION FAILED WITH EXCEPTION: %.7s",
error_code.ec_fields.Exception_Id);
exit(1);
}
/******************************************************************/
/* If the call to retrieve exit point information is successful, */
/* check to see if there are any exit programs to call. */
/******************************************************************/
rcv_ptr=rcv_variable;
rcv_ptr += ((Qus_EXTI0100_t *)rcv_ptr)->Offset_Exit_Point_Entry;
if (((Qus_EXTI0100_Entry_t *)rcv_ptr)->Number_Exit_Programs != 0)
{
/*****************************************************************/
/* Blank out the continuation handle to let the API know that */
/* this is a first attempt at the retrieve operation. */
/*****************************************************************/
memset(continuation_hdl,’ ’,16);
/*****************************************************************/
/* Call the API to retrieve the exit program information. */
/*****************************************************************/
QusRetrieveExitInformation(continuation_hdl,
&rcv_variable,
len_rcv_variable,
"EXTI0200",
"EXAMPLE_EXIT_POINT ",
"EXMP0100",
exit_pgm_num,
&sel_criteria,
&error_code);
/*****************************************************************/
/* Verify that the call to the API is successful. */
/*****************************************************************/
if (error_code.ec_fields.Bytes_Available != 0)
{
printf("ATTEMPT TO RETRIEVE EXIT PROGRAMS FAILED WITH EXCEPTION:\
%.7s", error_code.ec_fields.Exception_Id);
exit(1);
}
/*****************************************************************/
/* If the call is successful, call the exit programs. */
/*****************************************************************/
Call_Exit_Program(rcv_variable);
/*****************************************************************/
/* If the continuation handle field in the receiver variable is */
/* not set to blanks, the API has more information to return */
/* than what could fit in the receiver variable. */
/*****************************************************************/
rcv_ptr=rcv_variable;
/***************************************************************/
/* Call the API to retrieve the exit program information. */
/***************************************************************/
QusRetrieveExitInformation(continuation_hdl,
&rcv_variable,
len_rcv_variable,
"EXTI0200",
"EXAMPLE_EXIT_POINT ",
"EXMP0100",
exit_pgm_num,
&sel_criteria,
&error_code);
/***************************************************************/
/* Verify that the call to the API is successful. */
/***************************************************************/
if (error_code.ec_fields.Bytes_Available != 0)
{
printf("RETRIEVE EXIT PROGRAMS FAILED WITH EXCEPTION: %.7s",
error_code.ec_fields.Exception_Id);
exit(1);
}
/***************************************************************/
/* If the call is successful, call the exit programs. */
/* The receiver variable offers enough room for a minimum of */
/* one exit program entry because the receiver variable was */
/* declared as 3500 bytes. Therefore, this example only */
/* checks the number of exit programs returned field. If the */
/* receiver variable were not large enough to hold at least */
/* one entry, the bytes available field would need to be */
/* checked as well as the number of exit programs returned */
/* field. If the number of exit programs returned field is */
/* set to zero and the bytes available field is greater than */
/* the bytes returned field, the API had at least one exit */
/* program entry to return but was unable to because the */
/* receiver variable was too small. */
/***************************************************************/
Call_Exit_Program(rcv_variable);
} /* While continuation handle not set to blanks */
} /* Number of exit programs not equal to zero */
} /* End program */
Example in OPM COBOL: Retrieving exit point and exit program information
This OPM COBOL program retrieves exit point and exit program information. It then resolves to each
exit program and calls the exit program.
The Retrieve Exit Information (QUSRTVEI) API returns a continuation handle when it has more
information to return than what can fit in the receiver variable.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: Retrieve Exit Point and Exit Program Information
*
* Language: OPM COBOL
*
* Description: This program retrieves exit point and exit
* program information. After retrieving the
* exit point information, the program calls each
* exit program.
*
* APIs Used: QUSCRTUS - Create User Space
* QUSPTRUS - Retrieve Pointer to User Space
* QUSRTVEI - Retrieve Exit Information
*
***************************************************************
***************************************************************
*
PROGRAM-ID. REGFAC2.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LISTING ASSIGN TO PRINTER-QPRINT
ORGANIZATION IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD LISTING RECORD CONTAINS 132 CHARACTERS
LABEL RECORDS ARE STANDARD
DATA RECORD IS LIST-LINE.
01 LIST-LINE PIC X(132).
WORKING-STORAGE SECTION.
Example in ILE COBOL: Retrieving exit point and exit program information
This ILE COBOL program retrieves exit point and exit program information. It then resolves to each exit
program and calls the exit program.
The Retrieve Exit Information (QusRetrieveExitInformation) API returns a continuation handle when it
has more information to return than what can fit in the receiver variable.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
Example in OPM RPG: Retrieving exit point and exit program information
This OPM RPG program retrieves exit point and exit program information. It then resolves to each exit
program and calls the exit program.
The Retrieve Exit Information (QUSRTVEI) API returns a continuation handle when it has more
information to return than what can fit in the receiver variable.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program: Retrieve Exit Point and Exit Program Information
F*
F* Language: OPM RPG
F*
F* Description: This program retrieves exit point and exit
F* program information. After retrieving the
F* exit point information, the program calls each
F* exit program.
F*
F* APIs Used: QUSRTVEI - Retrieve Exit Information
F*
F***************************************************************
F***************************************************************
F*
FQPRINT O F 132 PRINTER UC
I*
I* Error Code parameter include. As this sample program
I* uses /COPY to include the error code structure, only the first
I* 16 bytes of the error code structure are available. If the
I* application program needs to access the variable length
I* exception data for the error, the developer should physically
I* copy the QSYSINC include and modify the copied include to
I* define additional storage for the exception data.
I*
I/COPY QSYSINC/QRPGSRC,QUSEC
I*
I* Formats for the Retrieve Exit Information API.
I*
Example in ILE RPG: Retrieving exit point and exit program information
This ILE RPG program retrieves exit point and exit program information. It then resolves to each exit
program and calls the exit program.
The Retrieve Exit Information (QusRetrieveExitInformation) API returns a continuation handle when it
has more information to return than what can fit in the receiver variable.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program: Retrieve Exit Point and Exit Program Information
To package your product, you first create all the objects that comprise your product.
The first example product being packaged is called ABC Product. The product is made up of one library,
ABC, with no options off of this product. ABC Product consists of the following objects.
Table 19. ABC software packaging
Number Object name Object type Text description
1 ABCPGMMRM1 *PGM MRM 1 preprocessing
program
2 ABCPGMMRM2 *PGM MRM postprocessing
program
3 ABCPGMMRI1 *PGM MRI 2 preprocessing
program
4 ABCPGMMRI2 *PGM MRI postprocessing
program
5 ABCPGM *PGM CPP 3 for ABC command
6 QCLSRC *FILE(SRCPF) Source physical file
7 ABCDSPF *FILE(DSPF) Display file
8 ABCPF *FILE(PF) Physical file
9 ABCMSG *MSGF Message file
10 ABC *CMD Command for ABC Product
11 ABCPNLGRP *PNLGRP Panels for ABC
12 ABC0050 *PRDDFN Product definition
13 ABC0029 *PRDLOD Product load for MRI
14 ABC0050 *PRDLOD Product load for MRM
15 ABC *LIB ABC Product
Notes:
1. Machine readable material
2. Machine readable information
3. Command processing program
You create all the objects (numbers 1 through 11 and number 15 in the table) that comprise your product.
“Example in CL: Creating objects for packaging a product” on page 243 shows the code that creates the
objects. After you create the objects, you follow the steps listed in “Example in ILE COBOL: Packaging a
product” on page 257.
The following figure is an overview of the steps required to create a product. An explanation is given in
the figure below of the numbers. The same numbers also appear in the code.
(1) Create a product definition with information about the licensed program, such as ID, version, and
release.
(2) Create a product load, which further defines each option of a licensed program, such as the
libraries, folders, and exit programs that comprise the product.
(3) Identify all objects associated with the product by changing the product ID, release level, product
option, and load ID in the object description by using the Change Object Description API.
(4) Package the product. Verify and store a list of all objects marked for this product in the product
load object.
(5) Use the Save Licensed Program (SAVLICPGM) command to save the product to tape.
This CL program creates objects 1 through 11 and 15 that comprise your product.
/* MRM Objects */
CRTLIB ABC
CRTCLPGM ABC/ABCPGMMRM1 ABCDEV/QCLSRC +
TEXT(’MRM Preprocessing Program’)
CRTCLPGM ABC/ABCPGMMRM2 ABCDEV/QCLSRC +
TEXT(’MRM Postprocessing Program’)
CRTCLPGM ABC/ABCPGM ABCDEV/QCLSRC +
TEXT(’CPP for ABC command’)
/* MRI Objects */
CRTCLPGM ABC/ABCPGMMRI1 ABCDEV/QCLSRC +
TEXT(’MRI Preprocessing Program’)
CRTCLPGM ABC/ABCPGMMRI2 ABCDEV/QCLSRC +
TEXT(’MRI Postprocessing Program’)
CRTSRCPF ABC/QCLSRC TEXT(’Source Physical File for ABC Product’)
CRTDSPF ABC/ABCDSPF ABCDEV/QDDSSRC +
TEXT(’Display File for ABC Product’)
CRTPF ABC/ABCPF ABCDEV/QDDSSRC +
This OPM RPG program creates objects 12 through 14 and packages your product.
Before you can build PTFs for the product, you need to save the product and install the product by using
the Save Licensed Program (SAVLICPGM) and Restore Licensed Program (RSTLICPGM) commands.
After the product is built, you can perform the following tasks:
v Build PTFs for the product by using the following APIs:
– Create Program Temporary Fix (QPZCRTFX)
– Retrieve Program Temporary Fix Information (QPZRTVFX)
– Program Temporary Fix Exit Program
v Use save, restore, or delete license program (SAVLICPGM, RSTLICPGM, DLTLICPGM) commands on
it.
v Retrieve information about the product by using the Retrieve Product Information (QSZRTVPR) API.
v Check the product to verify the existence of libraries, folders, and objects that are part of the specified
product (Check Product Option (CHKPRDOPT) command).
Related reference
“Example in ILE C: Packaging a product”
This ILE C program creates objects 12 through 14 and packages your product.
“Example in ILE COBOL: Packaging a product” on page 257
This ILE COBOL program creates objects 12 through 14 and packages your product.
“Example in ILE RPG: Packaging a product” on page 264
This ILE RPG program creates objects 12 through 14 and packages your product.
This ILE C program creates objects 12 through 14 and packages your product.
/********************************************************************/
/* Function: Create_Prod_Def_Obj */
/* Description: Create the product definition ABC0050 for product */
/* ABC. */
/********************************************************************/
void Create_Prod_Def_Obj()
{
Qsz_Prd_Inf_t prod_info; /* Product information */
Qsz_Prd_Opt_t prod_opt_list; /* Product option list */
Qsz_Lng_Lod_t prod_lang_load; /* Product language load list */
Qus_EC_t error_code; /* Error code parameter */
char text_desc[50]; /* Text description */
/******************************************************************/
/* Fill in the product information. */
/******************************************************************/
memset(&prod_info,’ ’,sizeof(prod_info));
memcpy(prod_info.PID,"0ABCABC",7);
memcpy(prod_info.Rls_Lvl,"V3R1M0",6);
memcpy(prod_info.Msg_File,"ABCMSG ",10);
memcpy(prod_info.Fst_Cpyrt,"*CURRENT ",10);
memcpy(prod_info.Cur_Cpyrt,"*CURRENT ",10);
memcpy(prod_info.Rls_Date,"941201",6);
memcpy(prod_info.Alw_Mult_Rls,"*NO ",4);
memcpy(prod_info.Reg_ID_Type,"*PHONE ",10);
memcpy(prod_info.Reg_ID_Val,"5072530927 ",14);
/******************************************************************/
/* Fill in the product option list. */
/******************************************************************/
memset(&prod_opt_list,’ ’,sizeof(prod_opt_list));
memcpy(prod_opt_list.Opt,"0000",4);
memcpy(prod_opt_list.Msg_ID,"ABC0001",7);
memcpy(prod_opt_list.Alw_Dyn_Nam,"*NODYNNAM ",10);
memcpy(prod_opt_list.Cod_Lod,"5001",4);
/******************************************************************/
/* Fill in the product language load list. */
/******************************************************************/
memset(&prod_lang_load,’ ’,sizeof(prod_lang_load));
memcpy(prod_lang_load.Lng_Lod,"2924 ",8);
memcpy(prod_lang_load.Opt,"0000",4);
memset(text_desc,’ ’,50);
memcpy(text_desc,"Product ABC",11);
/******************************************************************/
/* Initialize the error code to have the API send errors through */
/* the error code parameter. */
/******************************************************************/
error_code.Bytes_Provided=sizeof(error_code);
QSZCRTPD("ABC0050 ABC ", /* Product definition name */
&prod_info, /* Product definition info */
&prod_opt_list, /* Product option list */
1, /* Number of options */
&prod_lang_load, /* Language load list */
1, /* Number languages */
text_desc, /* Text description */
"*USE ", /* Public authority */
&error_code); /* Error code */
/********************************************************************/
/* Function: Create_Prod_Load_Obj */
/* Description: Create the product loads ABC0050 (MRM object) and */
/* ABC0029 (MRI object) for product ABC. */
/********************************************************************/
void Create_Prod_Load_Obj()
{
Qsz_Lod_Inf_t prod_load_info; /* Product load information */
Qsz_Lib_Inf_t prin_lib_info; /* Principal library info */
Qsz_Add_Lib_t add_libs; /* Additional library list */
Qsz_Pre_Ext_t preop_expgm; /* Preoperational exit program */
Qsz_Flr_Lst_t folder_list; /* Folder list */
Qus_EC_t error_code; /* Error code parameter */
char text_desc[50]; /* Text description */
/******************************************************************/
/* Fill in the product load information. */
/******************************************************************/
memset(&prod_load_info,’ ’,sizeof(prod_load_info));
memcpy(prod_load_info.PID,"0ABCABC",7);
memcpy(prod_load_info.Rls_Lvl,"V3R1M0",6);
memcpy(prod_load_info.Opt,"0000",4);
memcpy(prod_load_info.Lod_Type,"*CODE ",10);
memcpy(prod_load_info.Lod_ID,"*CODEDFT",8);
memcpy(prod_load_info.Reg_ID_Type,"*PRDDFN ",10);
memcpy(prod_load_info.Min_Tgt_Rls,"*CURRENT ",10);
/******************************************************************/
/* Fill in the principal library information. There are no */
/* additional libraries. */
/******************************************************************/
memcpy(prin_lib_info.Dev_Lib,"ABC ",10);
memcpy(prin_lib_info.Prim_Lib,"ABC ",10);
memcpy(prin_lib_info.Post_Exit_Pgm,"ABCPGMMRM2",10);
memset(&add_libs,’ ’,sizeof(add_libs));
/******************************************************************/
/* Fill in the preoperational exit program. */
/******************************************************************/
memcpy(preop_expgm.Pre_Ext_Pgm,"ABCPGMMRM1",10);
memcpy(preop_expgm.Dev_Lib,"ABC ",10);
/******************************************************************/
/* There are no folders. */
/******************************************************************/
memset(&folder_list,’ ’,sizeof(folder_list));
memset(text_desc,’ ’,50);
memcpy(text_desc,"Product ABC",11);
/******************************************************************/
/* Initialize the error code to have the API send errors through */
/* the error code parameter. */
/******************************************************************/
error_code.Bytes_Provided=sizeof(error_code);
QSZCRTPL("ABC0050 ", /* Product load name */
&prod_load_info, /* Product load information */
if (error_code.Bytes_Available > 0)
{
printf("Failed in QSZCRTPL API with error: %.7s",
error_code.Exception_Id);
exit(1);
}
/******************************************************************/
/* Fill in the product load information. */
/******************************************************************/
memcpy(prod_load_info.Lod_Type,"*LNG ",10);
memcpy(prod_load_info.Lod_ID,"2924 ",8);
/******************************************************************/
/* Fill in the principal library information. There are no */
/* additional libraries. */
/******************************************************************/
memcpy(prin_lib_info.Post_Exit_Pgm,"ABCPGMMRI2",10);
/******************************************************************/
/* Fill in the preoperational exit program. */
/******************************************************************/
memcpy(preop_expgm.Pre_Ext_Pgm,"ABCPGMMRI1",10);
if (error_code.Bytes_Available > 0)
{
printf("Failed in QSZCRTPL API with error: %.7s",
error_code.Exception_Id);
exit(1);
}
/********************************************************************/
/* Function: Change_Obj_Descr */
/* Description: Change object descriptions for all objects */
/* that make up Product ABC. Currently there are 15 */
/* objects. */
/********************************************************************/
void Change_Obj_Descr()
{
typedef struct {
int numkey;
Qus_Vlen_Rec_3_t PID_rec;
char PID[4];
Qus_Vlen_Rec_3_t LID_rec;
char LID[4];
Qus_Vlen_Rec_3_t LP_rec;
char LP[13];
} change_obj_info_t;
int i;
obj_info_t obj_info[15] = {"ABCPGMMRM1ABC ","*PGM ",
"0000","5001","0ABCABCV3R1M0",
"ABCPGMMRM2ABC ","*PGM ",
"0000","5001","0ABCABCV3R1M0",
"ABCPGMMRI1ABC ","*PGM ",
"0000","2924","0ABCABCV3R1M0",
"ABCPGMMRI2ABC ","*PGM ",
"0000","2924","0ABCABCV3R1M0",
"ABCPGM ABC ","*PGM ",
"0000","5001","0ABCABCV3R1M0",
"QCLSRC ABC ","*FILE ",
"0000","2924","0ABCABCV3R1M0",
"ABCDSPF ABC ","*FILE ",
"0000","2924","0ABCABCV3R1M0",
"ABCPF ABC ","*FILE ",
"0000","2924","0ABCABCV3R1M0",
"ABCMSG ABC ","*MSGF ",
"0000","2924","0ABCABCV3R1M0",
"ABC ABC ","*CMD ",
"0000","2924","0ABCABCV3R1M0",
"ABCPNLGRP ABC ","*PNLGRP ",
"0000","2924","0ABCABCV3R1M0",
"ABC0050 ABC ","*PRDDFN ",
"0000","5001","0ABCABCV3R1M0",
"ABC0050 ABC ","*PRDLOD ",
"0000","5001","0ABCABCV3R1M0",
"ABC0029 ABC ","*PRDLOD ",
"0000","2924","0ABCABCV3R1M0",
"ABC ABC ","*LIB ",
"0000","5001","0ABCABCV3R1M0"};
change_obj_info_t cobji; /* Change object information */
Qus_EC_t error_code; /* Error code parameter */
char rtn_lib[10]; /* Return library */
/******************************************************************/
/* Fill in the changed object information. */
/******************************************************************/
cobji.numkey=3;
cobji.PID_rec.Key=13;
cobji.PID_rec.Length_Vlen_Record=4;
cobji.LID_rec.Key=12;
cobji.LID_rec.Length_Vlen_Record=4;
cobji.LP_rec.Key=5;
cobji.LP_rec.Length_Vlen_Record=13;
/******************************************************************/
/* Initialize the error code to have the API send errors through */
/* the error code parameter. */
if (error_code.Bytes_Available > 0)
{
printf("Failed in QLICOBJD API with error: %.7s",
error_code.Exception_Id);
exit(1);
}
}
}
/********************************************************************/
/* Function: Package_Prod */
/* Description: Package Product ABC so that all the SAVLICPGM, */
/* RSTLICPGM and DLTLICPGM commands work with the */
/* product. */
/********************************************************************/
void Package_Prod()
{
Qsz_Prd_Opt_Inf_t prod_opt_info; /* Product option information */
Qus_EC_t error_code; /* Error code parameter */
/******************************************************************/
/* Fill in the product option information. */
/******************************************************************/
memset(&prod_opt_info,’ ’,sizeof(prod_opt_info));
memcpy(prod_opt_info.Opt,"0000",4);
memcpy(prod_opt_info.PID,"0ABCABC",7);
memcpy(prod_opt_info.Rls_Lvl,"V3R1M0",6);
memcpy(prod_opt_info.Lod_ID,"*ALL ",8);
/******************************************************************/
/* Initialize the error code to have the API send errors through */
/* the error code parameter. */
/******************************************************************/
error_code.Bytes_Provided=sizeof(error_code);
QSZPKGPO(&prod_opt_info, /* Product option information */
"*YES", /* Repackage */
"*NO ", /* Allow object change */
&error_code); /* Error code */
if (error_code.Bytes_Available > 0)
{
printf("Failed in QSZPKGPO API with error: %.7s",
error_code.Exception_Id);
exit(1);
}
}
/********************************************************************/
/* Start of main procedure */
/********************************************************************/
/******************************************************************/
/* Create Product Definition Object */
/******************************************************************/
Create_Prod_Def_Obj();
/******************************************************************/
/* Create Product Load Objects */
/******************************************************************/
Create_Prod_Load_Obj();
/******************************************************************/
/* Change Object Description */
/******************************************************************/
Change_Obj_Descr();
/******************************************************************/
/* Package Product ABC */
/******************************************************************/
Package_Prod();
}
Related reference
“Example in OPM RPG: Packaging a product” on page 244
This OPM RPG program creates objects 12 through 14 and packages your product.
This ILE COBOL program creates objects 12 through 14 and packages your product.
This ILE RPG program creates objects 12 through 14 and packages your product.
These examples use an automatically extended user space as the receiver variable on a retrieve API. A
user space can return a varying amount of information depending on the file description being retrieved.
The user space is automatically extended up to 16MB to accommodate the information being retrieved.
Related reference
Retrieve Database File Description (QDBRTVFD) API
This ILE C program creates a user space, changes the user space to be automatically extendable, and
retrieves the file description to the user space using pointers.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/* Program Name: RTVFD */
/* */
/* Program Language: ILE C */
/* */
_GetExcData(&ExcDta);
error_flag = 1;
signal(SIGALL,error_handler);
}
/********************************************************************/
/* Start of main procedure */
/********************************************************************/
/******************************************************************/
/* Start of executable code. */
/******************************************************************/
if (argc != 4) {
printf("This program requires 3 parameters:\n");
printf(" 1) User space name and library\n");
printf(" 2) File name and library\n");
printf(" 3) Record format name\n");
printf("Please retry with those parameters.\n");
exit(1);
}
/******************************************************************/
/* Create the user space. */
/******************************************************************/
QUSCRTUS(user_space, /* User space */
" ", /* Extended attribute */
1024, /* Initial size */
&initial_value, /* Initial value */
"*CHANGE ", /* Public authority */
descr, /* Text description */
"*YES ", /* Replace if it exists */
&error_code, /* Error code */
"*USER "); /* Domain = USER */
if (error_flag) {
exit(1);
}
/******************************************************************/
/* Initialize the attributes to change structure. */
/******************************************************************/
attrib_info.attrib_count = 1; /* Number of attributes */
attrib_info.keyinfo.Key = 3; /* Key of attribute to change */
attrib_info.keyinfo.Length_Vlen_Record = 1;
/* Length of data */
attrib_info.key_value=’1’; /* Autoextend space */
/******************************************************************/
/* Change the user space to be automatically extendable. */
/******************************************************************/
QUSCUSAT(return_lib, /* Return library */
user_space, /* User space name and library */
&attrib_info, /* Attributes to change */
&error_code); /* Error code */
if (error_flag) {
exit(1);
}
/******************************************************************/
/* Retrieve a pointer to the user space object. */
/******************************************************************/
QUSPTRUS(user_space,&space_ptr);
/******************************************************************/
/* Retrieve the file description information to the user space. */
/******************************************************************/
QDBRTVFD(space_ptr, /* Receiver variable */
16776704, /* Return up to 16MB minus 512 */
/* bytes of data */
ret_file_lib, /* Returned file and library */
"FILD0100", /* File definition template */
file_and_lib, /* File and library name */
record_fmt, /* Record format name */
"0", /* No override processing */
"*LCL ", /* Local system */
"*INT ", /* Internal formats (1) */
&error_code); /* Error code */
if (error_flag) {
exit(1);
}
The program uses the value *INT ((1)). A description and examples of the internal (*INT) and external
(*EXT) formats are provided in the Retrieve Database File Description (QDBRTVFD) API.
Related reference
“Example in ILE COBOL: Retrieving a file description to a user space”
This ILE COBOL program creates a user space, changes the user space to be automatically extendable,
and retrieves the file description to the user space using pointers.
“Example in ILE RPG: Retrieving a file description to a user space” on page 277
This ILE RPG program creates a user space, changes the user space to be automatically extendable, and
retrieves the file description to the user space using pointers.
This ILE COBOL program creates a user space, changes the user space to be automatically extendable,
and retrieves the file description to the user space using pointers.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program: RTVFD
*
* Language: COBOL
*
* Description: This program retrieves a file definition
* template to a user space.
*
* APIs Used: QDBRTVFD - Retrieve File Description
* QUSCRTUS - Create User Space
* QUSCUSAT - Change User Space Attributes
* QUSPTRUS - Retrieve a pointer to a User Space
*
***************************************************************
This ILE RPG program creates a user space, changes the user space to be automatically extendable, and
retrieves the file description to the user space using pointers.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
D***************************************************************
D***************************************************************
D*
D* Program: RTVFD
D*
D* Language: ILE RPG
D*
D* Description: This program retrieves a file definition
D* template to a user space.
D*
D* APIs Used: QDBRTVFD - Retrieve File Description
D* QUSCRTUS - Create User Space
D* QUSCUSAT - Change User Space Attributes
D* QUSPTRUS - Retrieve a pointer to a User Space
D*
D***************************************************************
D***************************************************************
D*
D* Error Code parameter include
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC
D*
D* Not shown due to its size, this program also includes QDBRTVFD
D* and defines all of the data structures in QDBRTVFD as being
D* BASED(SPCPTR). For illustrative purposes, this sample shows
D* only the first significant data structure.
D*
D****************************************************************
D*
D*File Definition Template (FDT) Header
D*
D****************************************************************
D*This section is always located at the beginning of the
D*returned data.
D****************************************************************
DQDBQ25 DS BASED(SPCPTR)
To decide whether to use data queues or user queues, you need to consider your programming
experience, the performance of each queue type, and the operations on queue entries.
First, your programming experience is an important consideration in selecting a queue type. If you are
familiar with C or MI programming, you might want to select the user queue. User queues can be
accessed only through MI, and MI can be used only by ILE RPG, ILE COBOL, C, and MI programs.
Next, performance plays an important part in determining what type of queue to use. As stated in
System APIs or CL commands--when to use each, APIs generally give better performance than CL
Last, you need to consider how the queue entries are manipulated. For example, you need a way to
perform enqueue and dequeue operations on entries from a queue. As stated earlier, user queues use MI
instructions to manipulate entries. Specifically, you use the ENQ MI instruction to enqueue a message,
and the DEQ MI instruction to dequeue a message. If you are running at security level 40 or greater, you
must ensure that the user queue is created in the user domain in order to directly manipulate a user
queue using MI instructions. Because data queue entries are manipulated by APIs, the security level of
the machine does not limit the use of the API.
You cannot create a user queue object in a library that does not permit user-domain objects, which is
determined by the QALWUSRDMN system value. Data queues are always created in the system domain,
so there is no problem with the data queue being created into a specific library.
The following summary can help you select the type of queue that is right for your program:
v Use user queues when:
– You have a programming background in MI.
– You need the additional performance of an API for creating and deleting and MI instructions for
manipulating entries.
– You do not need to create a user-domain queue into a library where the QALWUSRDMN system
value does not permit user-domain user objects when at security level 40 or 50.
v Use data queues when:
– You have a programming background in or prefer to program in a high-level language such as
COBOL, C, or RPG.
– You do not need the additional performance of MI instructions for directly manipulating entries.
– You need to create queues into a library that is not listed in the QALWUSRDMN system value.
Related concepts
“Domains” on page 117
A domain is a characteristic of an object that controls how programs can access the object. All objects are
assigned a domain attribute when they are created.
“APIs overview” on page 1
This API information describes most of the i5/OS APIs and some APIs for related licensed programs that
run on the i5/OS operating system.
This ILE C program shows how to use APIs to create and manipulate a data queue.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/*********************************************************************/
/* */
/*Program Name: DQUEUEX */
/* */
/*Program Language: ILE C */
/* */
/*Description: This program illustrates how to use APIs to create */
/* and manipulate a data queue. */
/* */
/* */
/*Header Files Included: <stdio.h> */
/* <string.h> */
/* <stdlib.h> */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <decimal.h>
#include <qsnddtaq.h> /* from QSYSINC/h */
#include <qrcvdtaq.h> /* from QSYSINC/h */
/*********************************************************************/
/* */
/* Main */
/* */
/*********************************************************************/
void main()
{
decimal(5,0) DataLength = 10.0d,
WaitTime = 0.0d;
char QueueData[10];
/*******************************************************************/
/* Create library QUEUELIB. */
/*******************************************************************/
system("CRTLIB LIB(QUEUELIB)");
/*******************************************************************/
/* Create a data queue called EXAMPLEQ in library QUEUELIB. The */
/* queue will have a maximum entry length set at 10, and will be */
/* FIFO (first-in first-out). */
/*******************************************************************/
/*******************************************************************/
/* Send information to the data queue. */
/*******************************************************************/
/*******************************************************************/
/* Receive information from the data queue. */
/*******************************************************************/
system("DLTDTAQ DTAQ(QUEUELIB/EXAMPLEQ)");
/*******************************************************************/
/* Delete the library. */
/*******************************************************************/
system("DLTLIB LIB(QUEUELIB)");
}
Related reference
“Example in ILE COBOL: Using data queues”
This ILE COBOL program shows how to use APIs to create and manipulate a data queue.
“Example in OPM RPG: Using data queues” on page 292
This OPM RPG program shows how to use APIs to create and manipulate a data queue.
“Example in ILE RPG: Using data queues” on page 295
This ILE RPG program shows how to use APIs to create and manipulate a data queue.
This ILE COBOL program shows how to use APIs to create and manipulate a data queue.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
***************************************************************
***************************************************************
*
* Program Name: DQUEUEX
*
* Programming Language: COBOL
*
* Description: This program illustrates how to use APIs to
* create and manipulate a *DTAQ.
*
* Header Files Included: QUSEC - Error Code Parameter
* QCAPCMD - Process Command API
*
***************************************************************
*
***************************************************************
PROGRAM-ID. DQUEUEX.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LISTING ASSIGN TO PRINTER-QPRINT
ORGANIZATION IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD LISTING RECORD CONTAINS 132 CHARACTERS
LABEL RECORDS ARE STANDARD
DATA RECORD IS LIST-LINE.
01 LIST-LINE PIC X(132).
WORKING-STORAGE SECTION.
*
* Error Code parameter include
*
COPY QUSEC OF QSYSINC-QLBLSRC.
This OPM RPG program shows how to use APIs to create and manipulate a data queue.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program Name: DQUEUEX
F*
F* Programming Language: OPM RPG
F*
F* Description: This program illustrates how to use APIs to
F* create and manipulate a *DTAQ.
F*
F* Header Files Included: QUSEC - Error Code Parameter
F* QCAPCMD - Process Command API
F*
F***************************************************************
F*
FQPRINT O F 132 PRINTER UC
F***************************************************************
I*
I* Error Code parameter include
I*
I/COPY QSYSINC/QRPGSRC,QUSEC
I*
I* Process Command API Include
I*
I/COPY QSYSINC/QRPGSRC,QCAPCMD
I*
I* Command strings
I*
I DS
I I ’CRTLIB LIB(QUEUELIB)’ 1 20 CRTLIB
I I ’DLTLIB LIB(QUEUELIB)’ 21 40 DLTLIB
I I ’CRTDTAQ DTAQ(QUEUELI- 41 82 CRTDQ
I ’B/EXAMPLEQ) MAXLEN(1-
I ’0)’
I I ’DLTDTAQ DTAQ(QUEUELI- 83 113 DLTDQ
I ’B/EXAMPLEQ)’
I*
I* Miscellaneous data structure
This ILE RPG program shows how to use APIs to create and manipulate a data queue.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
F***************************************************************
F***************************************************************
F*
F* Program Name: DQUEUEX
F*
F* Programming Language: ILE RPG
F*
F* Description: This program illustrates how to use APIs to
F* create and manipulate a *DTAQ.
F*
F* Header Files Included: QUSEC - Error Code Parameter
F* QCAPCMD - Process Command API
F*
F***************************************************************
F*
FQPRINT O F 132 PRINTER OFLIND(*INOF) USROPN
F***************************************************************
D*
D* Error Code parameter include
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC
D*
D* Process Command API Include
D*
D/COPY QSYSINC/QRPGLESRC,QCAPCMD
D*
D* Command strings
D*
D
DCRTLIB C ’CRTLIB LIB(QUEUELIB)’
DDLTLIB C ’DLTLIB LIB(QUEUELIB)’
DCRTDQ C ’CRTDTAQ DTAQ(QUEUELIB/+
D EXAMPLEQ) MAXLEN(10)’
DDLTDQ C ’DLTDTAQ DTAQ(QUEUELIB/EXAMPLEQ)’
D*
D* Miscellaneous data structure
D*
DCMD_STR S 100
DLEN_STR S 9B 0
DCAP0100_SZ S 9B 0 INZ(%SIZE(QCAP0100))
DRCVVAR_SZ S 9B 0 INZ(0)
DAPI_NAME S 10
DFIRST_ERR S 1 INZ(’0’)
C*
C* Beginning of mainline
C*
C* Initialize the error code parameter. To signal exceptions to
C* this program by the API, you need to set the bytes provided
C* field of the error code to zero. Because this program has
C* exceptions sent back through the error code parameter, it sets
This ILE C program shows how to use APIs to create and manipulate a user queue.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <milib.h> /* from QCLE/h */
#include <miptrnam.h> /* from QCLE/h */
#include <miqueue.h> /* from QCLE/h */
#include <pointer.h>
#include <quscrtuq.h> /* from QSYSINC/h */
#include <qusdltuq.h> /* from QSYSINC/h */
#include <qusec.h> /* from QSYSINC/h */
/*********************************************************************/
/* Structures */
/*********************************************************************/
typedef struct {
Qus_EC_t ec_fields;
char exception_data[100];
} error_code_struct;
/*********************************************************************/
/* */
/* Main */
/* */
/*********************************************************************/
void main()
{
char text_desc[50];
error_code_struct error_code;
_SYSPTR queuelib_sysptr,
user_queue_obj_sysptr;
_RSLV_Template_T rslvsp_template;
_ENQ_Msg_Prefix_T enq_msg_prefix;
_DEQ_Msg_Prefix_T deq_msg_prefix;
char enq_msg[50],
/*******************************************************************/
/* Create a library to create the user queue into. */
/*******************************************************************/
system("CRTLIB LIB(QUEUELIB)");
/*******************************************************************/
/* Initialize the error code parameter. */
/*******************************************************************/
error_code.ec_fields.Bytes_Provided=sizeof(error_code_struct);
/*******************************************************************/
/* Call the QUSCRTUQ API to create a user queue. */
/* */
/* This will create a user queue called EXAMPLEQ in library */
/* QUEUELIB, with the following attributes: */
/* */
/* 1. Extended attribute of "VALID ", which could have */
/* been any valid *NAME. */
/* 2. A queue type of "F", or First-in, first-out. */
/* 3. A key length of 0. If the queue is not keyed, this */
/* value must be 0. */
/* 4. A maximum message size of 10 bytes. This number can */
/* be as large as 64K bytes. */
/* 5. The initial number of messages set to 10. */
/* 6. Additional number of messages set to 10. */
/* 7. Public authority of *USE. */
/* 8. A valid text description. */
/* 9. Replace option of *YES. This means that if a user queue */
/* already exists by the name specified, in the library */
/* specified, that it will be replaced by this */
/* request. */
/* 10. Domain value of *USER. */
/* 11. Pointer value of *NO. Messages in the queue cannot */
/* contain pointer data. */
/*******************************************************************/
/*******************************************************************/
/* If an exception occurred, the API would have returned the */
/* exception in the error code parameter. The bytes available */
/* field will be set to zero if no exception occurred and greater */
/* than zero if an exception did occur. */
/*******************************************************************/
if (error_code.ec_fields.Bytes_Available > 0)
{
printf("ATTEMPT TO CREATE A USER QUEUE FAILED WITH EXCEPTION:%.7s",
/*******************************************************************/
/* Send information to the queue. */
/* */
/* We will need to use MI instructions to accomplish this. */
/* There are three steps that must be done: */
/* */
/* 1. Resolve a system pointer to the library containing the user */
/* queue object. */
/* 2. Using the system pointer to the library, resolve a system */
/* pointer to user queue object in the library. */
/* 3. Enqueue the entry using the system pointer for the user */
/* queue. */
/* */
/*******************************************************************/
/*******************************************************************/
/* First we must resolve to library QUEUELIB. */
/*******************************************************************/
memset(rslvsp_template.Obj.Name,’ ’,30);
memcpy(rslvsp_template.Obj.Name,"QUEUELIB",8);
rslvsp_template.Obj.Type_Subtype = _Library; /* found in milib.h */
rslvsp_template.Auth = _AUTH_NONE; /* found in milib.h */
/*******************************************************************/
/* We can now resolve to the user queue object. We will pass the */
/* system pointer to library QUEUELIB to RSLVSP so the resolve */
/* will only search library QUEUELIB for the user queue object. */
/* This is necessary so that we ensure that we are using the */
/* correct object. */
/*******************************************************************/
memset(rslvsp_template.Obj.Name,’ ’,30);
memcpy(rslvsp_template.Obj.Name, "EXAMPLEQ", 8);
rslvsp_template.Obj.Type_Subtype = _Usrq; /* found in milib.h */
rslvsp_template.Auth = _AUTH_ALL; /* found in milib.h */
/*******************************************************************/
/* Enqueue the entry. */
/*******************************************************************/
enq_msg_prefix.Msg_Len = 10;
enq_msg_prefix.Msg[0] = ’\0’; /* Only used for keyed queues*/
memcpy(enq_msg, "EXAMPLE ", 10);
/*******************************************************************/
/* Dequeue the entry. */
/*******************************************************************/
success = _DEQI(&deq_msg_prefix, /* message prefix */
(_SPCPTR)deq_msg, /* message text */
&user_queue_obj_sysptr); /* sys ptr to user queue */
if(success)
/*******************************************************************/
/* Delete the user queue. */
/*******************************************************************/
/*******************************************************************/
/* If an exception occurred, the API would have returned the */
/* exception in the error code parameter. The bytes available */
/* field will be set to zero if no exception occurred and greater */
/* than zero if an exception did occur. */
/*******************************************************************/
if (error_code.ec_fields.Bytes_Available > 0)
{
printf("ATTEMPT TO DELETE A USER QUEUE FAILED WITH EXCEPTION:%.7s",
error_code.ec_fields.Exception_Id);
exit(1);
}
/*******************************************************************/
/* Delete the library created for this example. */
/*******************************************************************/
system("DLTLIB LIB(QUEUELIB)");
}
Note: To use these examples, you need the header files in the system include (QSYSINC) library.
Related concepts
“Include files and the QSYSINC library” on page 62
An Include file is a text file that contains declarations that are used by a group of functions, programs, or
users. The system include (QSYSINC) library provides all source include files for APIs that are included
with the i5/OS operating system.
“Performing tasks using APIs” on page 241
You can use APIs to perform different types of tasks.
You can also reduce the run priority of jobs using a specified user name. You have the following options:
v Specify a job name or the *ALL value.
v Specify the user name as the *ALL value.
v Use the default run priority of 99.
The following is the message description needed for the Change Active Jobs (CHGACTJOB) command:
ADDMSGD MSGID(USR3C01) MSGF(QCPFMSG) +
MSG(’JOB(*ALL) is not valid with USER(*ALL)’) SEV(30)
The following is the command-processing program that is written in CL to list the active jobs and reduce
the run priority if necessary:
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/* ***************************************************************** */
/* PROGRAM: CHGACTJOB */
/* */
/* LANGUAGE: CL */
/* */
/* DESCRIPTION: THIS PROGRAM WILL REDUCE THE RUN PRIORITY OF ACTIVE */
/* JOBS WITH THE SAME NAME. */
/* */
/* APIs USED: QUSCRTUS, QUSLJOB, QUSRTVUS, QUSRJOBI */
/* */
/* ***************************************************************** */
PGM PARM(&JOB &USER &RUNPTY)
/* */
/* Input parameters */
/* */
/* */
/* Start of executable code */
/* */
/* */
/* Retrieve job number to use for local user space name */
/* */
RTVJOBA NBR(&NUMBER)
CHGVAR VAR(%SST(&USRSPC 5 6)) VALUE(&NUMBER)
CHGVAR VAR(&EUSRSPC) VALUE(%SST(&USRSPC 1 10))
DLTUSRSPC USRSPC(QTEMP/&EUSRSPC)
MONMSG CPF0000
/* */
/* Create user space */
/* */
/* */
/* Set up job name for list jobs */
/* */
/* */
/* List active jobs with job name specified */
/* */
/* */
/* Retrieve number of entries returned. Convert to decimal and */
/* if zero go to NOJOBS label to send out ’No jobs’ message. */
/* */
/* */
/* Retrieve list entry length, convert to decimal. */
/* Retrieve list entry offset, convert to decimal, and add one */
/* to set the position. */
/* */
/* */
/* Loop for the number of jobs until no more jobs then go to */
/* ALLDONE label */
/* */
/* */
/* Convert decimal position to binary 4 and retrieve list job entry */
/* */
/* */
/* Copy internal job identifier and retrieve job information for */
/* basic performance information. */
/* */
/* */
/* Copy job type and if subsystem monitor, spool reader, system job, */
/* spool writer, or SCPF system job then loop to next job */
/* */
/* */
/* Copy run priority, convert to decimal, convert to decimal 5,0, */
/* and if request run priority is less than or equal to the current */
/* run priority then loop to next job. */
/* */
/* */
/* Retrieve job name, convert to run priority to character, change */
/* the job run priority and seen message stating the run priority */
/* was changed. */
/* */
/* */
/* At end of loop set new decimal position to next entry and */
/* decrement loop counter by one. */
/* */
/* */
/* Send message that no jobs were found. */
/* */
The program can be changed to change the run priority by removing the IF statement to compare the
current and requested run priority.
The following is the command-processing program that is written in CL to list the job schedule entries
and change the user if necessary:
/* */
/* Input parameters are as follows: */
/* */
/* */
/* Local variables are as follows: */
/* */
/* */
/* Start of code */
/* */
/* */
/* You may want to monitor for additional messages here. */
/* */
/* */
/* This creates the user space. The user space will be 256 bytes */
/* and will be initialized to blanks. */
/* */
/* */
/* This lists job schedule entries of the name specified. */
/* */
/* */
/* Retrieve the generic header from the user space. */
/* */
/* */
/* Get the number of entries returned. Convert to decimal and */
/* if zero go to NOENTS label to send out ’No entries’ message. */
/* */
/* */
/* Get the list entry length and the list entry offset. */
/* These values are used to set up the starting position. */
/* */
/* */
/* This loops for the number of entries until no more entries are */
/* */
/* This retrieves the list entry. */
/* */
CALL PGM(QUSRTVUS) PARM(&USRSPC &STRPOSB &ELENB +
&LENTRY)
MONMSG MSGID(CPF3C00) EXEC(GOTO CMDLBL(ERROR))
/* */
/* This copies the information status, job name, entry number, and */
/* user name. */
/* */
/* */
/* This checks to make sure the list entry contains the user name. */
/* If it does, the user name is compared to the old user name */
/* passed in. If either of these checks fails, this entry will */
/* be skipped. */
/* */
/* */
/* This code will issue the CHGJOBSCDE command for the entry. */
/* */
/* */
/* At end of loop, set new decimal position to the next entry and */
/* decrement the loop counter by one. */
/* */
/* */
/* This sends a message that no entries were found. */
/* */
/* */
/* This sends a message that the list was incomplete. */
/* */
/* */
/* This sends a message that an unexpected error occurred. */
/* */
/* */
/* This will check for a partial list in the user space and */
/* finish processing the rest of the list. */
/* */
/* */
/* All done. Now the temporary user space is deleted. */
/* */
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
main()
{
_ENQ_Msg_Prefix_T e_msg_prefix;
_SYSPTR queue;
char INMsg[100];
/********************************************************************/
/* Resolve to the queue created by $USQEXSRV. */
/********************************************************************/
queue = rslvsp(_Usrq,"TESTQ","QGPL",_AUTH_ALL);
e_msg_prefix.Msg_Len = 100;
/********************************************************************/
/* Loop until the user enters ’quit’ as the command. */
/********************************************************************/
while (1) {
printf("\nEnter command to put on queue, or ’quit’ \n ");
scanf("%100s", INMsg);
gets(INMsg);
printf("\nCommand entered was ==> %.100s\n",INMsg);
/********************************************************************/
/* Check to see if the user entered ’quit’ as the command. */
/* If true then break out of the ’while’ loop. */
/********************************************************************/
/********************************************************************/
/* Add the user-entered command to the queue. */
/********************************************************************/
enq(queue,&e_msg_prefix,INMsg);
strcpy(INMsg," ");
} /*while*/
/********************************************************************/
/* Add the command end to the queue which causes the */
/* server program ($USQEXSRV) to end */
/********************************************************************/
main()
{
_DEQ_Msg_Prefix_T d_msg_prefix;
_SYSPTR queue;
char OUTMsg[100];
int cmd_name_lngth;
decimal(15,5) pack_name_lngth;
char igc_param[] = "IGC";
/********************************************************************/
/* Set up the parameters to be used in the call to ’QUSCRTUQ’ */
/********************************************************************/
QUSCRTUQ(q_name,ext_atr,q_type,key_lngth,max_msg_s,int_msgs,
add_msgs,auth,desc);
/********************************************************************/
/* Resolve to the queue created above. */
/********************************************************************/
queue = rslvsp(_Usrq,"TESTQ","QGPL",_AUTH_ALL);
/********************************************************************/
/* Set the deq operation to wait for command indefinitely. */
/********************************************************************/
d_msg_prefix.Wait_Forever = 1;
/********************************************************************/
/* Loop until the command ’END’ is extracted from the queue */
/********************************************************************/
while (1) {
deq(&d_msg_prefix,OUTMsg,queue);
/********************************************************************/
/* Check to see if the command extracted is ’END’ */
/* If true then break out of the ’while’ loop. */
/********************************************************************/
if (strncmp(OUTMsg,"END",3) == 0)
{ break; }
cmd_name_lngth = strlen(OUTMsg);
/********************************************************************/
/* Convert the integer in cmd_name_lngth to a packed decimal */
/********************************************************************/
cpynv(NUM_DESCR(_T_PACKED,15,5), &pack_name_lngth,
NUM_DESCR(_T_SIGNED,4,0), &cmd_name_lngth);
/********************************************************************/
/* Execute the command extracted from the queue */
/********************************************************************/
QCMDEXC(OUTMsg,pack_name_lngth,igc_param);
} /* while */
} /* $USQEXSRV */
For another example using the Create User Index (QUSCRTUI) API, see “Example: Creating your own
telephone directory” on page 317.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/* */
/* PROGRAM: GLOBALV */
/********************************************************************/
/* PARAMETER VALUE POINTERS FOR GLOBALV. */
/********************************************************************/
/********************************************************************/
/* PARAMETER VALUES FOR GLOBALV. */
/********************************************************************/
/********************************************************************/
/* PARAMETER LIST FOR GLOBALV. */
/********************************************************************/
/********************************************************************/
/* ARGUMENT VALUES FOR CREATE USER INDEX (QUSCRTUI) API. */
/********************************************************************/
/********************************************************************/
/* POINTERS TO ARGUMENT VALUES FOR QUSCRTUI API. */
/********************************************************************/
/********************************************************************/
/* ARGUMENT LIST FOR QUSCRTUI API. */
/********************************************************************/
/********************************************************************/
/* SYTSEM POINTER TO QUSCRTUI API *PGM OBJECT. */
/********************************************************************/
/********************************************************************/
/* SYSTEM POINTER TO GLOBALV *USRIDX OBJECT. */
/********************************************************************/
/********************************************************************/
/* EXCEPTION MONITOR TO DETECT 2201X EXCEPTIONS (OBJECT NOT FOUND) */
/********************************************************************/
/********************************************************************/
/* PASA INVOCATION ENTRY FOR RETURN FROM EXCEPTION. */
/********************************************************************/
/********************************************************************/
/* RECEIVER VARIABLE FOR INDEPENDENT INDEX OPERATIONS. */
/********************************************************************/
/********************************************************************/
/* OPTION TEMPLATE FOR INDEPENDENT INDEX OPERATIONS. */
/********************************************************************/
/********************************************************************/
/* ARGUMENT VARIABLE FOR INDEPENDENT INDEX OPERATIONS. */
/********************************************************************/
/********************************************************************/
/* START OF CODE */
/********************************************************************/
NOT_UPDATE:
FOUND_ENTRY:
NOT_RETRIEVE:
/********************************************************************/
/* "OBJECT NOT FOUND" EXCEPTION HANDLER. */
/********************************************************************/
To insert entries into the user index, use the following program $USIDXEX:
/********************************************************************/
/* PROGRAM: $USIDXEX */
/* */
/* LANGUAGE: ILE C */
/* */
/* DESCRIPTION: THIS PROGRAM USES A USER INDEX TO KEEP TRACK OF */
/* NAMES AND PHONE NUMBERS. THERE ARE TWO OPERATIONS THAT ARE */
/* DEMONSTRATED IN THIS EXAMPLE. THE FIRST IS THE INSERTION OF */
/* AN ENTRY INTO THE INDEX, AND SECONDLY THE FINDING OF A GIVEN */
/* INDEX ENTRY. */
/* THE INDEX IS KEYED ON THE LAST NAME, THEREFORE ENTER AS MUCH */
/* OF THE NAME AS YOU KNOW AND THE PROGRAM WILL LIST ALL ENTRIES */
/* MATCHING YOUR STRING (IN ALPHABETICAL ORDER). */
/* */
/* APIs USED: NONE */
/* */
/********************************************************************/
#include <stdio.h>
#include <string.h>
#include <miindex.h>
_SYSPTR index;
_IIX_Opt_List_T ins_option_list;
_IIX_Opt_List_T *fnd_option_list;
char Name_And_Num[50];
char In_Name[50];
char Out_Num[5000];
char response[1];
char name[35];
char number[15];
int Ent_Found,count,start,length_of_entry;
/********************************************************************/
/* Procedure to copy ’cpylngth’ elements of ’string2’ into the */
/* new string, ’string1’; starting at position ’strpos’. */
/********************************************************************/
void strncpyn(string1,string2,strpos,cpylngth)
char string1[],string2[];
int strpos,cpylngth;
{
int x = 0;
while (x < cpylngth)
string1[x++]=string2[strpos++];
} /*strncpyn*/
/********************************************************************/
/* Procedure to convert any string into uppercase, where applicable */
/********************************************************************/
void convert_case(string1)
char string1[];
{
int x = 0;
while (x < (strlen(string1))) {
string1[x] = toupper(string1[x]);
x++;
} /*while*/
} /*convert_case*/
main()
{
fnd_option_list = malloc(sizeof(_IIX_Opt_List_T)
+99*sizeof(_IIX_Entry_T));
/********************************************************************/
/* Resolve to the index created in $USIDXCRT. */
/********************************************************************/
index = rslvsp(_Usridx,"TESTIDX","QGPL",_AUTH_ALL);
/********************************************************************/
/* Set up the insert option list */
/********************************************************************/
ins_option_list.Rule = _INSERT_REPLACE;
ins_option_list.Arg_Length = 50;
ins_option_list.Occ_Count = 1;
ins_option_list.Entry[0].Entry_Length = 50;
ins_option_list.Entry[0].Entry_Offset = 0;
fnd_option_list->Rule = _FIND_EQUALS;
fnd_option_list->Occ_Count = 100;
/********************************************************************/
/* Loop until the choice ’Q’ is entered at the menu */
/********************************************************************/
while (1==1) {
printf("\n\n***********************\n");
printf("* TELEPHONE INDEX *\n");
printf("***********************\n");
printf("* ’A’ Add name & num *\n");
printf("* ’L’ List a number *\n");
printf("* ’Q’ Quit index *\n");
printf("***********************\n");
gets(response);
if ((strncmp(response,"A",1)==0)||(strncmp(response,"a",1)==0))
{ printf("\nEnter name to add. ex(Last, First)\n");
gets(name);
convert_case(name);
printf("\nEnter number to add. ex(999-9999)\n");
gets(number);
strcpy(name,strcat(name," "));
strcpy(Name_And_Num,strcat(name,number));
printf("\nName and number to add is => %s\n",Name_And_Num);
insinxen(index,Name_And_Num,Integrated Netfinity Server_option_list);
} /* if ’a’*/
if ((strncmp(response,"L",1)==0)||(strncmp(response,"l",1)==0))
{
printf("\nEnter name to find. ex(Last, First)\n");
gets(In_Name);
convert_case(In_Name);
fnd_option_list->Arg_Length = strlen(In_Name);
fndinxen(Out_Num,index,fnd_option_list,In_Name);
length_of_entry = fnd_option_list->Entry[0].Entry_Length;
Ent_Found = fnd_option_list->Ret_Count;
if (Ent_Found == 0)
printf("\nName not found in index => %s\n",In_Name);
else {
if (Ent_Found > 1) {
printf("\n%d occurences found,\n",Ent_Found);
count = 0;
start = 0;
while (count++ < Ent_Found) {
printf("Name and number is => %s\n",Out_Num);
start = start + length_of_entry;
strncpyn(Out_Num,Out_Num,start,length_of_entry);
} /* while */
}else
printf("\nName and number is => %s\n",Out_Num);
} /*else*/
} /*if ’l’*/
if ((strncmp(response,"Q",1)==0)||(strncmp(response,"q",1)==0))
{ break; }
} /*while*/
} /*$USIDXEX*/
To create the ILE C program to insert entries into the user index, specify
CRTBNDC PGM(QGPL/$USIDXEX) SRCFILE(QGPL/QCSRC)
These programs use the QQAPI header (or include) file and the QQFUNCS query code.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
QQAPI header
/********************************************************************/
#ifndef _QQAPIH
#define _QQAPIH
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: Defines constants and structures for use */
/* with the QQQQRY API examples. */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: None */
/* */
/*******************************************************************/
/*******************************************************************/
/* The following define will enable some debug procedures and code */
/* #define QQDEBUG */
/* simple defines */
#define ON 1
#define OFF 0
/* Function prototypes: */
void dumpPtr(char *, char *, int );
char *strcnv400(char *, int );
int strcpy400(char *, char *, int );
void initUFCB(QDBUFCB_T *, int , Qdb_Qddfmt_t *);
void initQDT(QDBQH_T *, char , int , int ,
char , int );
void initFile(QDBQFHDR_T *, char , char );
void initFormat(Qdb_Qddfmt_t *, char *);
void initSelection(QDBQS_T *);
void initOrderBy(QDBQKH_T *);
void initGroupBy(QDBQGH_T *);
void initJoin(QDBQJHDR_T *);
int addFile(QDBQFHDR_T *, QDBQN_T *,
char *, char *, char *, char *);
int getRecordFmt(Qdb_Qddfmt_t *, long,
char *, char *, char *);
long copyField(Qdb_Qddfmt_t *, char *, int ,
Qdb_Qddfmt_t *);
void setFieldUsage(Qdb_Qddfmt_t *, char *, char );
int addSelectField(QDBQS_T *, char *, int );
int addSelectLiteral(QDBQS_T *, void *, int );
int addSelectOperator(QDBQS_T *, char *);
int addOrderBy(QDBQKH_T *, QDBQKF_T *,
char *, int );
int addGroupBy(QDBQGH_T *, QDBQGF_T *,
char *, int );
int addJoinTest(QDBQJHDR_T *, QDBQJFLD_T *, char *,
int , char *, int , char *);
void addQDTsection(QDBQH_T *, char *, int , int *);
long createAccessPlanSpace(ACCPLN_T *, char *, long );
#endif
/********************************************************************/
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: This module contains all of the functions */
/* used by the examples to build the API information. */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: QDBRTVFD, QUSCRTUS, QUSCUSAT, QUSPTRUS, QUSRUSAT */
/* */
/*******************************************************************/
/*******************************************************************/
#ifdef QQDEBUG
/* dumpPtr(comment string, pointer, length)
- prints a comment then dumps data in hexadecimal starting at the
given pointer location for the specified length */
void dumpPtr(char *text, char *ptr, int len)
{
int i;
printf("%s\n", text);
for (i=0; i < len; i++, ptr++)
{
printf("%02X ", (int) *ptr);
if ((i+1) % 16 == 0)
printf("\n");
}
printf("\n");
}
#endif
/* verify parameters */
if (ufcbPtr == NULL || openFlags == 0)
{
printf("Invalid UFCB settings\n");
return;
}
/* Clear the entire UFCB */
memset((void *) ufcbPtr, (char) 0, sizeof(QDBUFCB_T));
/* Now start initializing values */
ufcb = &ufcbPtr->qufcb;
strcpy400((char *) ufcb->relver.release, REQ_REL,
sizeof(ufcb->relver.release));
strcpy400((char *) ufcb->relver.version, REQ_VER,
sizeof(ufcb->relver.version));
/* Blocked Records (BLKRCD) should be on if CPYFRMQRYF is used */
ufcb->markcnt.flg2brcd = ON;
ufcb->parameter.maximum = MAXFORMATS;
/* Set the open option */
if (openFlags&QO_INPUT)
ufcb->open.flagui = ON;
if (openFlags&QO_OUTPUT)
ufcb->open.flaguo = ON;
if (openFlags&QO_UPDATE)
ufcb->open.flaguu = ON;
if (openFlags&QO_DELETE)
ufcb->open.flagud = ON;
/* set up options to match _Ropen options */
ufcb->parameter.keyfdbk = KEYFDBK;
ufcb->parameter.keyonoff = ON; /* Key feedback ON */
ufcb->parameter.filedep = FILEDEP;
ufcb->parameter.fldonoff = ON; /* File dependent I/O ON */
/* turn the rest of the parameters off */
ufcb->parameter.seqonly = NOTSEQUPROC;
ufcb->parameter.primrln1 = NOTRECORDLTH;
ufcb->parameter.commitc = NOTCOMITCTL;
/* if the format is supplied,
define it in the UFCB and do level checking */
if (formatPtr != NULL)
{
ufcb->parameter.lvlchk = LEVELCK;
ufcb->parameter.lvlonoff = ON; /* Level check ON */
ufcb->parameter.curnum = 1; /* only one format */
/* set the format name and format level identifier */
ufcb->parameter.recfmts = FORMATSEQ;
/* initQDT(qdt, options...)
- initialize the QDT header */
void initQDT(QDBQH_T *qdtHdr, char alwCpyDta,
int optAllAp, int statusMsgs,
char optimize, int forRows)
{
if (qdtHdr == NULL)
{
printf("Invalid QDT settings\n");
return; /* invalid pointer */
}
/* Clear the entire QDT */
memset((void *) qdtHdr, (char) 0, sizeof(QDBQH_T));
/* set the initial QDT space used size */
qdtHdr->qdbspcsize = sizeof(QDBQH_T);
/* QDT options... */
/* ordering not specified */
qdtHdr->qdbqkeyo = -1;
/* set optimize parameter (ALLIO, FIRSTIO, MINWAIT) */
if (optimize == QDBQFINA || optimize == QDBQFINF ||
optimize == QDBQFINM || optimize == QDBQFINC)
qdtHdr->qdbqfin = optimize; /* OPTIMIZE() parameter */
else
qdtHdr->qdbqfin = QDBQFINA; /* default to OPTIMIZE(*ALLIO) */
/* set allow copy data parameter (YES, NO, OPTIMIZE) */
if (alwCpyDta == QDBQTEMN || alwCpyDta == QDBQTEMO ||
alwCpyDta == QDBQTEMA)
qdtHdr->qdbqtem = alwCpyDta; /* ALWCPYDTA() parameter */
else
qdtHdr->qdbqtem = QDBQTEMA; /* default to ALWCPYDTA(*YES) */
/* status messages (YES, NO) */
qdtHdr->qdbqattr.qdbqnst = statusMsgs ? ON : OFF;
/* optimize all access path parameter (YES, NO) */
qdtHdr->qdbqdt_7.qdbqopta = optAllAp ? ON : OFF;
/* optimizer for n rows parameter */
qdtHdr->qdbq_optmrows = forRows > 0 ? forRows : 0;
}
/* initSelection(selection section)
- initialize the selection header section */
void initSelection(QDBQS_T *selectHdr)
{
if (selectHdr == NULL)
{
printf("Invalid selection settings\n");
return; /* invalid pointer */
}
/* Clear the header */
memset((void *) selectHdr, (char) 0, sizeof(QDBQS_T));
/* set initial selection spec size (minus dummy selection spec) */
selectHdr->qdbqsl = sizeof(QDBQS_T) - sizeof(selectHdr->qdbqspec);
}
/* initOrderBy(orderby section)
- initialize order by header section */
void initOrderBy(QDBQKH_T *orderByHdr)
{
if (orderByHdr == NULL)
{
printf("Invalid Order By settings\n");
return; /* invalid pointer */
}
/* Clear the header */
memset((void *) orderByHdr, (char) 0, sizeof(QDBQKH_T));
}
/* initGroupBy(groupby section)
- initialize group by header section */
void initGroupBy(QDBQGH_T *groupByHdr)
{
/* initJoin(join section)
- initialize join header section */
void initJoin(QDBQJHDR_T *joinHdr)
{
if (joinHdr == NULL)
{
printf("Invalid Join settings\n");
return; /* invalid pointer */
}
/* Clear the header */
memset((void *) joinHdr, (char) 0, sizeof(QDBQKH_T));
/* set initial join spec size */
joinHdr->qdbqjln = sizeof(QDBQJHDR_T);
}
/* addFile (file section, file spec section, file name, file library,
file member, file format)
- add file information to the file section */
int addFile(QDBQFHDR_T *fileHdr, QDBQN_T *fileSpec,
char *filename, char *library, char *member, char *format)
{
int i;
QDBQFLMF_T *fileSpecPtr;
if (formatPtr == NULL)
return; /* missing data */
if (fieldName != NULL)
strcpy400(padField, fieldName, 30);
/* set up field pointers */
fieldPtr = (Qdb_Qddffld_t *) (formatPtr + 1);
/* loop through all the fields, looking for a match */
for (i=0; i < formatPtr->Qddffldnum; i++,
fieldPtr = (Qdb_Qddffld_t *) ((char *) fieldPtr +
fieldPtr->Qddfdefl))
/* if all fields to be set or a match was found... */
if (fieldName == NULL ||
memcmp(fieldPtr->Qddfflde, padField, 30) == 0)
fieldPtr->Qddffiob = usage;
}
errcode.bytes_provided = 512;
return(0);
}
/* saveAccessPlan(access plan)
errcode.bytes_provided = 512;
errcode.msgid[0] = (char) 0;
return(accessPlan->size);
}
/********************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <recio.h>
#include <qdbrtvfd.h>
#include <qqqqry.h>
#include "qqapi.h"
/* Query variables */
QDBUFCB_T ufcbBuf;
char qdtBuf[QDT_SIZE];
char formatBuf[FORMAT_SIZE];
QDBQH_T *qdtPtr;
Qdb_Qddfmt_t *formatPtr;
QDBQFHDR_T fileHdr;
QDBQN_T fileSpec[MAX_FILES];
QDBQKH_T orderByHdr;
QDBQKF_T orderByFld[MAX_ORDERBY];
int formatSize;
int fileSpecSize;
int orderBySize;
error_code errcod;
errcod.bytes_provided = 512;
/* initialize the pointers */
qdtPtr = (QDBQH_T *) qdtBuf;
formatPtr = (Qdb_Qddfmt_t *) formatBuf;
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <recio.h>
#include <qdbrtvfd.h>
#include <qqqqry.h>
#include "qqapi.h"
/* Query variables */
QDBUFCB_T ufcbBuf;
char qdtBuf[QDT_SIZE];
char formatBuf[FORMAT_SIZE];
char selectBuf[SELECT_SIZE];
QDBQH_T *qdtPtr;
Qdb_Qddfmt_t *formatPtr;
QDBQS_T *selectPtr;
QDBQFHDR_T fileHdr;
QDBQN_T fileSpec[MAX_FILES];
QDBQJHDR_T joinHdr;
QDBQJFLD_T joinSpec[MAX_JOINTESTS];
int formatSize;
errcod.bytes_provided = 512;
/* initialize the pointers */
qdtPtr = (QDBQH_T *) qdtBuf;
formatPtr = (Qdb_Qddfmt_t *) formatBuf;
selectPtr = (QDBQS_T *) selectBuf;
This join query with selection, grouping, and ordering is equivalent to an SQL query:
SELECT LNAME, FNAME, ITEMCODE, ITEMNAME, STATUS
FROM OPENFILE1, OPENFILE2
WHERE STATE = ’AK’ AND CUSTNUM = ACCTNUM
GROUP BY LNAME, FNAME, ITEMCODE, ITEMNAME, STATUS
ORDER BY ITEMNAME
/********************************************************************/
/* PROGRAM: QQAPI11 */
/* */
/* LANGUAGE: ILE C */
/* */
/* DESCRIPTION: THIS PROGRAM DEFINES A JOIN QUERY WITH SELECTION */
/* GROUPING AND ORDERING. */
/* */
/* APIs USED: QQQQRY */
/* */
/********************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <qdbrtvfd.h>
#include <qqqqry.h>
#include "qqapi.h">
/* Query variables */
QDBUFCB_T ufcbBuf;
char qdtBuf[QDT_SIZE];
char formatBuf[FORMAT_SIZE];
char tempFormatBuf[FORMAT_SIZE];
char selectBuf[SELECT_SIZE];
QDBQH_T *qdtPtr;
Qdb_Qddfmt_t *formatPtr;
Qdb_Qddfmt_t *tempFormatPtr;
QDBQS_T *selectPtr;
QDBQFHDR_T fileHdr;
QDBQN_T fileSpec[MAX_FILES];
QDBQJHDR_T joinHdr;
QDBQJFLD_T joinSpec[MAX_JOINTESTS];
QDBQKH_T orderByHdr;
QDBQGH_T groupByHdr;
QDBQKF_T orderByFld[MAX_ORDERBY];
QDBQGF_T groupByFld[MAX_GROUPBY];
int formatSize;
int fileSpecSize;
int orderBySize;
int groupBySize;
int selectSize;
int joinSize;
error_code errcod;
Notes:
v The programs and source code used as examples in the spooled file portion of this topic exist
only in printed form. They are not stored electronically on the system.
v By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
To delete old spooled files, you can use one of the application programs provided in the following
languages:
v RPG
v COBOL
v ILE C
C* *
C* GO BACK AND PROCESS THE REST OF THE ENTRIES IN THE USER *
C* SPACE. *
C QUSBPT ADD STRPOS STRPOS
C 1 ADD COUNT COUNT
C END
C* ************************************************************* *
C* ************************************************************* *
To delete spooled files, you can use this COBOL DLTOLDSPLF program:
***************************************************************
* *
* PROGRAM: DLTOLDSPLF *
* *
* LANGUAGE: COBOL *
* *
* DESCRIPTION: DELETE OLD SPOOLED FILES *
* *
* APIs USED: QUSCRTUS, QUSLSPL, QUSRTVUS, QUSRSPLA, QUSDLTUS,*
* AND QMHSNDM. *
***************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. DLTOLDSPLF.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
COPY QUSGEN OF QSYSINC-QLBLSRC.
COPY QUSLSPL OF QSYSINC-QLBLSRC.
COPY QUSRSPLA OF QSYSINC-QLBLSRC.
*****************************************************************
* VALUES USED FOR ERROR CODE *
*****************************************************************
* The following is copied from QSYSINC/QLBLSRC member QUSEC
* so that the variable length field EXCEPTION-DATA can be defined
* as 100 bytes for exception data.
*****************************************************************
01 QUS-EC.
05 BYTES-PROVIDED PIC S9(00009) BINARY.
05 BYTES-AVAILABLE PIC S9(00009) BINARY.
05 EXCEPTION-ID PIC X(00007).
05 RESERVED PIC X(00001).
* 05 EXCEPTION-DATA PIC X(00001).
*
* Varying length
05 EXCEPTION-DATA PIC X(100).
***************************************************************
* VALUES USED FOR THE QUSCRTUS PROGRAM *
***************************************************************
01 CRTUS-INFO.
05 CRT-SPCNAME PIC X(20)
VALUE "DLTOLDSPLFQTEMP ".
05 CRT-EXTATTR PIC X(10) VALUE SPACE.
05 CRT-SPCSIZE PIC S9(9) BINARY VALUE 1024.
05 CRT-INITSPACE PIC X VALUE " ".
05 CRT-AUTHORITY PIC X(10) VALUE "*CHANGE ".
05 CRT-DESCRIPTION PIC X(50) VALUE SPACE.
05 CRT-USRRPL PIC X(10) VALUE "*YES ".
***************************************************************
* VALUES USED FOR THE QUSRTVUS PROGRAM *
***************************************************************
01 RTV-START-POS PIC S9(9) BINARY VALUE 1.
01 RTV-LENGTH PIC S9(9) BINARY VALUE 140.
***************************************************************
* VALUES USED FOR THE QUSLSPL AND QUSRSPLA PROGRAM *
***************************************************************
01 RSPLA-DATE.
***************************************************************
* VALUES USED FOR THE QMHSNDM PROGRAM *
***************************************************************
01 MSG-ID PIC X(7) VALUE SPACE.
01 MSG-FL-NAME PIC X(20) VALUE SPACE.
01 MSG-DATA.
05 DATA-MD PIC X(34)
VALUE "NUMBER OF SPOOLED FILES DELETED : ".
05 DLT-NUM-MD PIC X(20) VALUE SPACE.
01 MSG-DATA-LEN PIC S9(9) BINARY VALUE 54.
01 MSG-TYPE PIC X(10) VALUE "*INFO ".
01 MSG-QUEUE PIC X(20)
VALUE "*REQUESTER ".
01 MSG-Q-NUM PIC S9(9) BINARY VALUE 1.
01 RPY-MSG PIC X(10) VALUE SPACE.
01 MSG-KEY PIC X(4) VALUE SPACE.
***************************************************************
* PARAMETERS THAT ARE PASSED TO THIS PROGRAM FROM THE COMMAND *
***************************************************************
LINKAGE SECTION.
01 PARM-USERNAME PIC X(10).
01 PARM-OUTQ PIC X(20).
01 PARM-DATE.
05 P-CENTURY PIC X.
05 P-YEAR PIC X(2).
05 P-MONTH PIC X(2).
05 P-DAY PIC X(2).
***************************************************************
* BEGINNING OF EXECUTABLE CODE. *
***************************************************************
PROCEDURE DIVISION USING PARM-USERNAME,
PARM-OUTQ,
PARM-DATE.
MAIN-PROGRAM.
* **********************************************************
* * INITIALIZE ERROR CODE STRUCTURE. *
* **********************************************************
* **********************************************************
* * CREATE THE USER SPACE USING INPUT PARMS FOR THE CALL *
* **********************************************************
* **********************************************************
* * LIST THE SPOOLED FILES TO THE USER SPACE OBJECT. *
* **********************************************************
* **********************************************************
* * RETRIEVE ENTRY INFORMATION FROM THE USER SPACE. *
* **********************************************************
* **********************************************************
* * IF ANY SPOOLED FILES WERE FOUND MATCHING THE SEARCH *
* * CRITERIA, RETRIEVE DETAILED INFORMATION AND DECIDE *
* * WHETHER TO DELETE THE FILE OR NOT. *
* **********************************************************
IF NUMBER-LIST-ENTRIES OF QUS-GENERIC-HEADER-0100
GREATER THAN ZERO THEN
ADD 1 TO OFFSET-LIST-DATA OF QUS-GENERIC-HEADER-0100
GIVING RTV-START-POS.
PERFORM CHECK-AND-DELETE THROUGH
CHECK-AND-DELETE-END NUMBER-LIST-ENTRIES
OF QUS-GENERIC-HEADER-0100 TIMES.
* **********************************************************
* * CALL THE QUSDLTUS API TO DELETE THE USER SPACE *
* * WE CREATED, AND TO SEND A MESSAGE TELLING HOW MANY *
* * SPOOLED FILES WERE DELETED. *
* **********************************************************
STOP RUN.
CHECK-AND-DELETE.
CALL "QUSRTVUS" USING CRT-SPCNAME,
RTV-START-POS,
SIZE-EACH-ENTRY OF
QUS-GENERIC-HEADER-0100,
QUS-SPLF0100,
QUS-EC.
* **********************************************************
* * ADVANCE TO NEXT SPOOLED FILE FOR PROCESSING THE CHECK *
* * AND DELETE. *
* **********************************************************
* **********************************************************
* * RETRIEVE THE ATTRIBUTES FOR THE SPOOLED FILE TO GET *
* * THE CREATE DATE FOR THE SPOOLED FILE. *
* **********************************************************
* **********************************************************
* * COMPARE THE CREATE DATE WITH THE DATE THAT WAS PASSED *
* * IN AS PARAMETER. *
* **********************************************************
CHECK-AND-DELETE-END.
* **********************************************************
* * THIS IS THE PROCEDURE TO DELETE THE SPOOLED FILE. *
* * ALL OF THE SPOOLED FILES WITH CREATE DATE OLDER OR *
* * EQUAL TO THE DATE PASSED IN AS PARAMETER WILL BE *
DLT-SPLF.
ADD 1 TO DLT-COUNT.
MOVE SPLF-NUMBER OF QUS-SPLA0100 TO DLT-SPL-NUMBER.
DLT-SPLF-END.
To delete spooled files, you can use this ILE C DLTOLDSPLF program:
/*******************************************************************/
/* PROGRAM: DLTOLDSPLF */
/* */
/* LANGUAGE: ILE C */
/* */
/* DESCRIPTION: THIS IS AN EXAMPLE PROGRAM FOR THE USE OF */
/* USER SPACES WRITTEN IN ILE C. */
/* THE FLOW OF THIS PROGRAM IS AS FOLLOWS: */
/* (1) CREATE A USER SPACE USING QUSCRTUS */
/* (2) GET LIST OF SPOOLED FILES IN THE USER SPACE */
/* USING QUSLSPL */
/* (3) KEEP POINTER TO ENTRY LIST IN THE USER SPACE */
/* (4) ENTER LOOP */
/* RETRIEVE LIST ENTRY */
/* RETRIEVE MORE INFORMATION USING QUSRSPLA */
/* IF SPOOLED FILE IS TOO OLD */
/* DELETE SPOOLED FILE */
/* INCREMENT DELETE COUNTER */
/* END LOOP */
/* (5) DELETE USER SPACE */
/* */
/* APIs USED: QUSCRTUS, QUSLSPL, QUSRSPLA, QUSPTRUS, QUSDLTUS, */
/* QMHSNDPM, AND QMHSNDM. */
/* */
/*******************************************************************/
#include <string.h> /*strcpy, strncpy, strcmp */
#include <stdio.h>
#include <qusec.h> /*Error code structures */
#include <qusgen.h> /*General user space structures */
#include <quscrtus.h> /*Linkage info, structures for QUSCRTUS */
#include <quslspl.h> /*Linkage info, structures for QUSLSPL */
#include <qusptrus.h> /*Linkage info, structures for QUSPTRUS */
#include <qusrspla.h> /*Linkage info, structures for QUSRSPLA */
#include <qusdltus.h> /*Linkage info, structures for QUSDLTUS */
#include <qmhsndm.h> /*Linkage info, structures for QMHSNDM */
#include <qmhsndpm.h> /*Linkage info, structures for QMHSNDPM */
#pragma linkage(CLDLT,OS)
void CLDLT (char file_name[10],
char job_number[6],
char usr_name[10],
Qus_Generic_Header_0100_t *space;
char *list_section;
Qus_SPLF0100_t *entry_list;
Qus_SPLA0100_t *Rcv_Spl_Var;
/*****************************************************************/
/* PARMS FOR CLDLT */
/*****************************************************************/
char job_nmbr[6];
char usr_nm[10];
char job_nm[10];
char sp_job_name[10];
char sp_spl_number[6];
char File_Number[] = "*LAST ";
/*****************************************************************/
/* PARMS FOR QUSLSPL */
/*****************************************************************/
char frmt[8];
char usr[10];
char OutQ_Nm[20];
char ls_frm_typ[10];
char Usr_dat[10];
/*****************************************************************/
/* PARMS FOR QUSRSPLA */
/*****************************************************************/
char Rcv_Var[724];
int Rcv_lgth = 724;
char Rtv_Fmt[8];
char Qal_Jb_Nam[] = "*INT ";
char Splf_Name[] = "*INT ";
int Splf_Number = -1;
/*****************************************************************/
/* PARMS FOR QUSCRTUS */
/*****************************************************************/
char spc_name[20];
char ext_atr[10];
int initial_size;
char initial_value[1];
char auth[10];
char desc[50];
char replace[10];
/*****************************************************************/
/* PARMS FOR QMHSNDPM AND QMHSNDM */
/*****************************************************************/
char msg_id[7];
char msg_fl_name[20];
char msg_data[50];
int msg_data_len;
char msg_type[10];
char pgm_queue[10];
int pgm_stk_cnt;
char msg_key[4];
/*****************************************************************/
/* PARMS FOR QMHSNDM */
/*****************************************************************/
int msg_q_num;
char msg_queue[20];
char rpy_mq[10];
/*****************************************************************/
/* MISCELLANEOUS VARIABLES */
/*****************************************************************/
/*****************************************************************/
/* PROCEDURE TO CHECK THE ERRCODE RETURNED FROM CALLS TO APIs */
/*****************************************************************/
void error_check(void)
{
if (err_code.Bytes_Available != 0){
strncpy(msg_id,"CPF9898",7);
strncpy(msg_fl_name,"QCPFMSG *LIBL ",20);
strncpy(msg_data,"An error has occurred calling ",29);
switch (api_code){
case 1 : strncat(msg_data,"QUSCRTUS.",9);
case 2 : strncat(msg_data,"QUSLSPL. ",9);
case 3 : strncat(msg_data,"QUSPTRUS.",9);
case 4 : strncat(msg_data,"QUSRSPLA.",9);
case 5 : strncat(msg_data,"QUSDLTUS.",9);
case 6 : strncat(msg_data,"QMHSNDM. ",9);
default : strncat(msg_data,"UNKNOWN. ",9);
}
msg_data_len = 38;
strncpy(msg_type,"*ESCAPE ",10);
strncpy(pgm_queue,"* ",10);
pgm_stk_cnt = 1;
QMHSNDPM(msg_id,msg_fl_name,msg_data,msg_data_len,msg_type,
pgm_queue,pgm_stk_cnt,msg_key,&err_code);
}
}
/********************************************************************/
/* START OF MAINLINE */
/********************************************************************/
main(argc,argv)
int argc;
char *argv[];
{
/********************************************************************/
/* Read in and assign the command-line arguments to respective */
/* variables */
/********************************************************************/
strncpy(usr,argv[1],10);
strncpy(OutQ_Nm,argv[2],20);
strncpy(dlt_date,argv[3],7);
/********************************************************************/
/* Assign value to specific variables in the program */
/********************************************************************/
strcpy(spc_name,"DLTOLDSPLFQTEMP ");
memset(ext_atr,’ ’,10);
initial_size = 1024;
strcpy(initial_value," ");
strcpy(auth,"*CHANGE ");
memset(desc,’ ’,50);
strcpy(frmt,"SPLF0100");
strcpy(replace,"*YES ");
strcpy(ls_frm_typ,"*ALL ");
strcpy(Usr_dat,"*ALL ");
/********************************************************************/
/* Call external program to create a user space */
/********************************************************************/
err_code.Bytes_Provided = 0;
api_code = 1;
QUSCRTUS(spc_name,ext_atr,initial_size,initial_value,auth,desc,replace,
&err_code);
/********************************************************************/
/* Call external program to list spooled files into user space */
/********************************************************************/
api_code = 2;
QUSLSPL(spc_name,frmt,usr,OutQ_Nm,ls_frm_typ,Usr_dat,&err_code);
/********************************************************************/
/* Call external program to get a pointer to the user space */
/* and get addressability to the list data section. */
/********************************************************************/
api_code = 3;
QUSPTRUS(spc_name,&space,&err_code);
list_section = (char *)space;
list_section = list_section + space->Offset_List_Data;
entry_list = (Qus_SPLF0100_t *) list_section;
dlt_cnt = 0;
count = 1;
/********************************************************************/
/* Loop through the entry list and delete old spooled files */
/********************************************************************/
while (count <= space->Number_List_Entries) {
/********************************************************************/
/* Call external program to retrieve more spool information */
/********************************************************************/
api_code = 4;
QUSRSPLA(Rcv_Var,Rcv_lgth,Rtv_Fmt,Qal_Jb_Nam,
entry_list->Int_Job_ID,entry_list->Int_Splf_ID,
Splf_Name,Splf_Number,&err_code);
Rcv_Spl_Var = (Qus_SPLA0100_t *)Rcv_Var;
strncpy(spc_date,Rcv_Spl_Var->Date_File_Open,7);
/********************************************************************/
/* If spooled file is too old delete it */
/********************************************************************/
if (strncmp(spc_date,dlt_date,7) <= 0 ) {
strncpy(job_nm,Rcv_Spl_Var->Job_Name,10);
strncpy(job_nmbr,Rcv_Spl_Var->Job_Number,6);
strncpy(usr_nm,Rcv_Spl_Var->Usr_Name,10);
strncpy(sp_job_name,Rcv_Spl_Var->Splf_Name,10);
/********************************************************************/
/* Convert the spooled file number to character. */
/********************************************************************/
memcpy (sp_spl_number," ",6);
sprintf(tmp_spl_number,"%d",Rcv_Spl_Var->Splf_Number);
memcpy(sp_spl_number,tmp_spl_number,strlen(tmp_spl_number));
/********************************************************************/
/* Delete the spooled file. */
/********************************************************************/
CLDLT(sp_job_name,job_nmbr,usr_nm,
job_nm,sp_spl_number,ls_frm_typ,Usr_dat);
dlt_cnt++;
} /*IF*/
strcpy(spc_date," ");
count++;
entry_list++;
} /*WHILE*/
/********************************************************************/
/* Remove the user space */
/********************************************************************/
/********************************************************************/
/* Send final message to user indicating number of spooled files */
/* deleted. */
/********************************************************************/
api_code = 6;
strncpy(msg_id," ",7);
strncpy(msg_fl_name," ",20);
sprintf(msg_data,"Number of spooled files deleted: %d", dlt_cnt);
msg_data_len = strlen(msg_data);
strncpy(msg_type,"*INFO ",10);
strncpy(msg_queue,"*REQUESTER ",20);
msg_q_num = 1;
strncpy(rpy_mq," ",10);
QMHSNDM(msg_id,msg_fl_name,msg_data,msg_data_len,msg_type,
msg_queue,msg_q_num,rpy_mq,msg_key, &err_code);
The DLTOLDSPLF program, written in OPM RPG, OPM COBOL, or ILE C, calls a CL program named
CLDLT. The CLDLT program deletes the spooled files and the user space. The following is the CL source
for the CLDLT program.
/*********************************************************************/
/* */
/* PROGRAM: CLDLT */
/* */
/* LANGUAGE: CL */
/* */
/* DESCRIPTION: THIS PROGRAM WILL DELETE A SPECIFIC SPOOLED FILE */
/* USING THE DLTSPLF COMMAND AND SEND A MESSAGE WHEN */
/* THE FILE IS DELETED. */
/* */
/* */
/*********************************************************************/
/* */
PGM (&FILNAM &JOBNUM &USRNAM &JOBNAM &FILNUM &FRMTYP &USRDTA)
/* */
/* ***************************************************************** */
/* */
/* DECLARE SECTION */
/* */
/*********************************************************************/
/* */
DCL &FILNAM *CHAR 10
DCL &JOBNUM *CHAR 6
DCL &USRNAM *CHAR 10
DCL &JOBNAM *CHAR 10
DCL &FILNUM *CHAR 6
DCL &FRMTYP *CHAR 10
DCL &USRDTA *CHAR 10
MONMSG CPF0000
/* */
/*********************************************************************/
/* */
/* EXECUTABLE CODE */
/* */
/*********************************************************************/
/* */
The program calls the QMHSNDM API to send a message to message queues that do not exist. The
QMHSNDM API returns a generic exception message, CPF2469. This message indicates that the API also
returned one or more diagnostic messages describing the errors. After the program receives the exception
message and verifies that it is message CPF2469, it uses the QMHCHGEM API to handle the exception
message. The QMHRCVPM API is used to receive the diagnostic messages. The program prints the
exception message, the diagnostic messages, and the message help.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/*********************************************************************/
/* Type definition for error code structure */
/*********************************************************************/
typedef struct error_code_struct
{
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_struct;
/*********************************************************************/
/* Type definition for qualified name structure */
/*********************************************************************/
typedef struct qual_name_struct
{
char name[10];
char libr[10];
} qual_name_struct;
/*********************************************************************/
/* Type definition for message information structure used on the */
/* receive. F is the fixed portion of the record and V is the */
/* variable length portion of the record. */
/*********************************************************************/
typedef struct msg_info_struct
{
Qmh_Rcvpm_RCVM0200_t F;
char V[1200];
} msg_info_struct;
FILE *prtf;
char buf[80];
char received[7];
int exception_count;
/*********************************************************************/
/* Function to handle errors received on the API calls. */
/*********************************************************************/
static void excp_handler(_INTRPT_Hndlr_Parms_T *excp_info)
{
error_code_struct Error_Code;
if (strncmp(excp_info->Msg_Id,"CPF2469",7) == 0) {
memcpy(received,(excp_info->Msg_Id),7);
exception_count++;
QMHCHGEM(&(excp_info->Target), 0,
(char *)(&(excp_info->Msg_Ref_Key)),
"*HANDLE ", "", 0, &Error_Code);
}
}
/********************************************************************/
/* BuildQList: Routine to build the message queue list. */
/********************************************************************/
strncpy(QueueList[0].name,"QPGMR ",10);
strncpy(QueueList[1].name,"SNOOPY ",10);
strncpy(QueueList[2].name,"QSECOFR ",10);
strncpy(QueueList[3].name,"PEANUTS ",10);
strncpy(QueueList[4].name,"QUSER ",10);
/********************************************************************/
/* PrintError: Routine to print error information and exit. */
/********************************************************************/
void PrintError(char *errstring, char exception[7])
{
memset(buf,’ ’,BUF_SIZE);
buf[0] = ’0’;
strncpy(buf+1,errstring,strlen(errstring));
fwrite(buf,1,BUF_SIZE,prtf);
memset(buf,’ ’,BUF_SIZE);
buf[0] = ’0’;
strncpy(buf+1,"Exception received->",20);
strncpy(buf+21,exception,strlen(exception));
fwrite(buf,1,BUF_SIZE,prtf);
fclose(prtf);
exit(1);
}
/********************************************************************/
/* PrintData: Routine to print varying length character string data.*/
/********************************************************************/
void PrintData(char *strname, void *strptr, int strlgth)
{
char *strdata = strptr;
int i,lgth,remain;
/* Write the description and the data that will fit on one line */
memset(buf,’ ’,BUF_SIZE);
buf[0] = ’0’;
lgth = strlen(strname);
strncpy(buf+1,strname,lgth);
lgth++;
memset(buf,’ ’,BUF_SIZE);
strncpy(buf,"0 ",10);
memcpy(buf+10,strdata,lgth);
fwrite(buf,1,BUF_SIZE,prtf);
}
}
}
/********************************************************************/
/* PrintMessage: Routine to print the message data and text. */
/********************************************************************/
void PrintMessage(msg_info_struct *Msg)
{
char *DataPtr; /* Pointer to the varying length character data*/
int DataLen; /* Length of the varying length character data */
char CharType[10]; /* Message type as a string */
PrintData("Message ID->",Msg->F.Message_Id,7);
/* Convert Message Type to a character string to be printed out */
if (memcmp(Msg->F.Message_Type,"02",2)==0)
strncpy(CharType,"DIAGNOSTIC", 10);
else if (memcmp(Msg->F.Message_Type,"15",2)==0)
strncpy(CharType,"ESCAPE ",10);
PrintData("Message Type->",CharType,10);
/* Point to the beginning of the message text field and get the */
/* length of message text returned. */
DataPtr += DataLen;
DataLen = Msg->F.Length_Message_Returned;
/* If there is non-blank text, print it out */
if ((DataLen > 0) && (strspn( DataPtr," ") < DataLen))
PrintData("Message text received->",DataPtr,DataLen);
/*********************************************************************/
/* */
/* Start of main program. */
/* */
/*********************************************************************/
main()
{
qual_name_struct MsgQList[5];
qual_name_struct MsgFile;
qual_name_struct RpyMsgQ;
msg_info_struct MsgInfo;
char MsgData[128];
char MsgText[512];
char MsgHelp[512];
char PgmMsgQ[10];
char MsgType[10];
char MsgAction[10];
char Format[8];
char MsgId[7];
char MsgKey[4];
int MsgTextLen;
int MsgInfoLen;
int NumMsgQ;
int PgmCount;
int WaitTime;
int morediag;
/* Initialize variables */
exception_count = 0;
memcpy(ErrorCode.ec_fields.Exception_Id," ",7);
ErrorCode.ec_fields.Bytes_Provided = 0;
memcpy(MsgId," ",7);
memcpy(MsgFile.name," ",10);
memcpy(MsgFile.libr," ",10);
strcpy(MsgText,"This is an immediate, informational message");
MsgTextLen = strlen(MsgText);
memcpy(MsgType,"*INFO ",10);
memcpy(RpyMsgQ.name," ",10);
memcpy(RpyMsgQ.libr," ",10);
while(morediag)
{
/* Receive the previous diagnostic */
QMHRCVPM(&MsgInfo,
MsgInfoLen,
Format,
PgmMsgQ,
PgmCount,
MsgType,
MsgKey,
WaitTime,
MsgAction,
&ErrorCode);
/* Write trailer */
memset(buf,’ ’,BUF_SIZE);
strncpy(buf,"- END OF DIAGNOSTIC REPORT",48);
fwrite(buf,1,BUF_SIZE,prtf);
} /* End mainline */
Message ID->CPF2403
Message Type->DIAGNOSTIC
Message data received->PEANUTS *LIBL
Message text received->Message queue PEANUTS in *LIBL not found.
Message help text received->Cause . . . . . : The message queue you
specified was not found in the library you specified. One
of the following occurred: -- The queue name was not
entered correctly. -- The queue does not exist in the
specified library. -- You specified the wrong library name.
Recovery . . . : Do one of the following and try the
request again: -- Correct or change the message queue
name or library name used in the message queue (MSGQ)
parameter or the to-message queue (TOMSGQ) parameter.
-- Create the message queue using the Create Message
Queue (CRTMSGQ) command.
Message ID->CPF2403
Message Type->DIAGNOSTIC
Message data received->SNOOPY *LIBL
Message text received->Message queue SNOOPY in *LIBL not found.
Message help text received->Cause . . . . . : The message queue
you specified was not found in the library you specified.
One of the following occurred: -- The queue name was not
entered correctly. -- The queue does not exist in the
specified library. -- You specified the wrong library
name. Recovery . . . : Do one of the following and
try the request again: -- Correct or change the message
queue name or library name used in the message queue
(MSGQ) parameter or the to-message queue (TOMSGQ)
parameter. -- Create the message queue using the Create
Message Queue (CRTMSGQ) command.
End of Diagnostic Report
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
H
D***************************************************************
D***************************************************************
D*
D* Program Name: ALERTS
D*
D* Programming Language: ILE RPG
D*
D* Description: This program uses alert APIs. First, it
D* calls the Generate Alert (QALGENA) API to
D* generate an alert without sending a message
D* to QSYSOPR or QHST message queue. Then it
D* uses the Send Alert (QALSNDA) API to send
D* the alert to the alert manager.
D*
D* Header Files Included: QUSEC - Error Code Parameter
You should call this program with only one parameter, the parameter that represents the directory you
want to list.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/******************************************************************/
/******************************************************************/
/* FUNCTION: This program lists a directory to a spooled file. */
/* */
/* LANGUAGE: ILE C */
/* */
/* */
/* APIs USED: QHFOPNDR, QHFRDDR, QHFCLODR, QHFLSTFS, QUSCRTUS, */
/* QUSRTVUS */
/* */
/******************************************************************/
/******************************************************************/
/******************************************************************/
/* INCLUDE FILES */
/******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <qhfopndr.h>
#include <qhfrddr.h>
#include <qhfclodr.h>
#include <qhflstfs.h>
#include <quscrtus.h>
#include <qusrtvus.h>
#include <qusec.h>
/******************************************************************/
/* STRUCTURE AND VARIABLE DECLARATIONS */
/******************************************************************/
/******************************************************************/
/* Parameters for QHFOPNDR */
/******************************************************************/
char dir_handle[16]; /* Directory handle */
int namelen; /* Length of path name */
char openinfo[6]; /* Open information */
typedef struct {
Qhf_Attr_Selec_Tbl_t fixed;
int offset2;
int offset3;
int att_len1;
char att_name1[8];
int att_len2;
char att_name2[8];
int att_len3;
char att_name3[8];
} selection_struct;
selection_struct select;
int selectionlen;
/******************************************************************/
/* Error Code Structure */
/* */
/* This shows how the user can define the variable length portion */
error_code_t error_code;
/******************************************************************/
/* Parameters for QHFRDDR */
/******************************************************************/
/* The directory handle is the same as for QHFOPNDR */
typedef struct {
Qhf_Data_Buffer_t fixed;
int num_att;
int offsets[4];
char attinfo[276];
} read_buffer;
read_buffer buffer;
int result_count;
int bytes_returned;
/******************************************************************/
/* Parameters for QHFCLODR */
/******************************************************************/
/* No additional ones need to be declared */
/******************************************************************/
/* Parameters for QUSCRTUS */
/******************************************************************/
int size;
char text[50];
/******************************************************************/
/* Parameters for QHFLSTFS */
/******************************************************************/
/* No additional ones need to be declared */
/******************************************************************/
/* Parameters for QUSRTVUS */
/******************************************************************/
int startpos;
int len;
char charbin4[4];
char FSname[10];
/******************************************************************/
/* Other declarations */
/******************************************************************/
int entrypos;
int numentries;
int entrylen;
char *att;
char name[100];
char attname[30];
char attval[30];
int attnamelen;
int attvallen;
char newname[30];
int filesize;
char fileatt[10];
typedef struct {
charval chartime;
int bytes_used;
int i;
error_code.ec_fields.Bytes_Provided = 0;
/****************************************************************/
/* Make sure we received the correct number of parameters. The */
/* argc parameter will contain the number of parameters that */
/* was passed to this program. This number also includes the */
/* program itself, so we need to evaluate argc-1. */
/****************************************************************/
/****************************************************************/
/* Open QPRINT file so that data can be written to it. If the */
/* file cannot be opened, print a message and exit. */
/****************************************************************/
if((stream = fopen("QPRINT", "wb")) == NULL)
{
printf("File could not be opened\n");
exit(1);
}
memset(name, ’ ’, 100);
memcpy(name, argv[1], 100);
if(!memcmp(name, " ", 1))
{
memcpy(name,"ROOT",4);
fprintf(stream,"Directory listing for path %.100s\n", name);
size = 1;
memcpy(text, "temporary user space used by program DIR ",
50);
/****************************************************************/
/* Create the user space for QHFLSTFS to use. */
/****************************************************************/
QUSCRTUS("FSLST QTEMP ", "TEMPSPACE ", size, " ",
"*USE ", text, "*YES ", &error_code);
/****************************************************************/
/* List the file systems into that space. */
/****************************************************************/
QHFLSTFS("FSLST QTEMP ", "HFSL0100", &error_code);
/****************************************************************/
/* Get the number of entries in the user space. */
/****************************************************************/
startpos = 133;
len = 4;
/****************************************************************/
/* Find the length of the entries. */
/****************************************************************/
startpos = 137;
len = 4;
/****************************************************************/
/* Loop through the entries and get the names of the file */
/* systems. */
/****************************************************************/
for(i=0;i<numentries;++i)
{
startpos = entrypos + 1;
len = 10;
QUSRTVUS("FSLST QTEMP ", startpos, len, FSname,
&error_code);
/*************************************************************/
/* List the names into the spooled file. */
/*************************************************************/
sprintf(write_string," %.10s <DIR>", FSname);
fprintf(stream, write_string);
entrypos = entrypos + entrylen;
}
}
else
{
fprintf(stream,"Directory listing for path %.100s\n", name);
/****************************************************************/
/* Build the attribute selection table for QHFOPNDR. */
/****************************************************************/
select.fixed.Number_Attributes = 3;
select.fixed.Offset_First_Attr = 16;
select.offset2 = 28;
select.offset3 = 40;
select.att_len1 = 8;
memcpy(select.att_name1, "QFILSIZE", 8);
select.att_len2 = 8;
memcpy(select.att_name2, "QCRTDTTM", 8);
select.att_len3 = 8;
memcpy(select.att_name3, "QFILATTR", 8);
/****************************************************************/
/* Find the length of the directory name. */
/****************************************************************/
for(i=0;i<100;i++)
{
if((name[i] == ’ ’) || (name[i] == ’\x00’))
break;
}
namelen = i;
/****************************************************************/
/* Open the directory. */
/****************************************************************/
QHFOPNDR(dir_handle, name, namelen, openinfo, &select, selectionlen,
&error_code);
/****************************************************************/
/* Read one entry from the directory. */
/****************************************************************/
QHFRDDR(dir_handle, &buffer, 300, 1, &result_count, &bytes_returned,
&error_code);
while(result_count > 0)
{
memcpy(attname," ",30);
memcpy(attval," ",30);
att = buffer.attinfo;
bytes_used = 20;
/**************************************************************/
/* Loop for the number of attributes in the entry. */
/**************************************************************/
for(i=0;i<buffer.num_att;i++)
{
memcpy(charbin4, att, 4);
attnamelen = *(int *)charbin4;
att += 4;
bytes_used += 4;
memcpy(charbin4, att, 4);
attvallen = *(int *)charbin4;
att += 8;
bytes_used += 8;
memcpy(attname, att, attnamelen);
att += attnamelen;
bytes_used += attnamelen;
memcpy(attval, att, attvallen);
att += attvallen;
bytes_used += attvallen;
/************************************************************/
/* Update att so that its first character is the first */
/* character of the next attribute entry. */
/************************************************************/
if ((bytes_used == buffer.offsets[i+1]) &&
((i+1) == buffer.num_att))
att += (buffer.offsets[i] - bytes_used);
/************************************************************/
/* If the attribute is QNAME, then set newname. */
/************************************************************/
if(!memcmp(attname, "QNAME", 5))
{
memset(newname, ’ ’, 12);
memcpy(newname, attval, attvallen);
/************************************************************/
/* If the attribute is QFILSIZE, then set filesize. */
/************************************************************/
else if(!memcmp(attname, "QFILSIZE", 8))
{
memcpy(charbin4, attval, 4);
filesize = *(int *)charbin4;
}
/************************************************************/
/* If it was QCRTDTTM, then set the time. */
/************************************************************/
else if(!memcmp(attname, "QCRTDTTM", 8))
memcpy(&chartime, attval, 13);
/************************************************************/
/* Else the attribute was QFILATTR, so set fileatt. */
/************************************************************/
else
memcpy(fileatt, attval, 10);
}
/**************************************************************/
/* If the entry was a directory, list its name and <DIR>. */
/**************************************************************/
if(fileatt[3] == ’1’)
{
sprintf(write_string," %s <DIR>", newname);
fprintf(stream, write_string);
}
/**************************************************************/
/* If the entry is not a hidden file, list its name and size. */
/**************************************************************/
else if(fileatt[1] == ’0’)
{
sprintf(write_string," %s %d", newname, filesize);
fprintf(stream, write_string);
}
/**************************************************************/
/* If the entry is not a hidden file or directory, list its */
/* date of creation. */
/**************************************************************/
if(fileatt[1] == ’0’)
{
sprintf(write_string," %.2s-%.2s-%.2s", chartime.month,
chartime.day, chartime.year);
fprintf(stream, write_string);
sprintf(write_string," %.2s:%.2s:%.2s\n", chartime.hour,
chartime.minute, chartime.second);
fprintf(stream, write_string);
}
} /* while */
} /* else */
/****************************************************************/
/* Close the directory. */
/****************************************************************/
QHFCLODR(dir_handle, &error_code);
fclose(stream);
} /* main */
You should call this program with only one parameter, the parameter that represents the directory you
want to list.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/******************************************************************/
/******************************************************************/
/* FUNCTION: */
/* */
/* LANGUAGE: ILE C */
/* */
/* */
/* APIs USED: QHFOPNDR, QHFRDDR, QHFCLODR */
/* */
/******************************************************************/
/******************************************************************/
/******************************************************************/
/* INCLUDE FILES */
/******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <qhfopndr.h>
#include <qhfrddr.h>
#include <qhfclodr.h>
#include <qusec.h>
char write_string[100];
FILE *stream;
/******************************************************************/
/* Parameters for QHFOPNDR */
/******************************************************************/
char dir_handle[16]; /* Directory handle */
int namelen; /* Length of path name */
char openinfo[6]; /* Open information */
typedef struct {
Qhf_Attr_Selec_Tbl_t fixed;
int att_len;
char att_name[8];
} selection_struct;
selection_struct select;
int selectionlen;
/******************************************************************/
/* Error Code Structure */
/* */
/* This shows how the user can define the variable length */
/* portion of error code for the exception data. */
/* */
/******************************************************************/
typedef struct {
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_t;
/******************************************************************/
/* Parameters for QHFRDDR */
/******************************************************************/
/* The directory handle is the same as for QHFOPNDR */
typedef struct {
Qhf_Data_Buffer_t fixed;
int num_att;
int offsets[2];
char attinfo[180];
} read_buffer;
read_buffer buffer;
int result_count;
int bytes_returned;
/******************************************************************/
/* Parameters for QHFCLODR */
/******************************************************************/
/* No additional ones need to be declared */
/******************************************************************/
/* Other declarations */
/******************************************************************/
char *att;
char attname[30];
char attval[30];
int attnamelen;
int attvallen;
char newname[30];
int newnamelen;
int filesize;
char fileatt[10];
char tab[5];
int bytes_used;
int i, j;
char charbin4[4];
char tempname[100];
error_code.ec_fields.Bytes_Provided = 0;
/****************************************************************/
/* Build the attribute selection table for QHFOPNDR. */
/****************************************************************/
select.fixed.Number_Attributes = 1;
select.fixed.Offset_First_Attr = 8;
select.att_len = 8;
memcpy(select.att_name, "QFILATTR", 8);
selectionlen = 20;
memcpy(openinfo, "10 ", 6);
memcpy(tab," ", 5);
/****************************************************************/
/* Find the length of the directory name. */
/****************************************************************/
for(i=0;i<100;i++)
{
if((name[i] == ’ ’) || (name[i] == ’\x00’))
break;
}
namelen = i;
/****************************************************************/
/* Open the directory. */
/****************************************************************/
/****************************************************************/
/* Read one entry from the directory. */
/****************************************************************/
QHFRDDR(dir_handle, &buffer, 200, 1, &result_count, &bytes_returned,
&error_code);
fprintf(stream,"\n");
for(i=0;i<numtabs;i++)
fprintf(stream, tab);
fprintf(stream, name);
while(result_count > 0)
{
memcpy(attname," ",30);
memcpy(attval," ",30);
att = buffer.attinfo;
bytes_used = 12;
/**************************************************************/
/* Loop for the number of attributes in the entry. */
/**************************************************************/
for(i=0;i<buffer.num_att;i++)
{
memcpy(charbin4, att, 4);
attnamelen = *(int *)charbin4;
att += 4;
bytes_used += 4;
memcpy(charbin4, att, 4);
attvallen = *(int *)charbin4;
att += 8;
bytes_used += 8;
memcpy(attname, att, attnamelen);
att += attnamelen;
bytes_used += attnamelen;
memcpy(attval, att, attvallen);
att += attvallen;
bytes_used += attvallen;
/************************************************************/
/* Update att so that its first character is the first */
/* character of the next attribute entry. */
/************************************************************/
if ((bytes_used == buffer.offsets[i+1]) &&
((i+1) == buffer.num_att))
att += (buffer.offsets[i] - bytes_used);
/************************************************************/
/* If the attribute is QNAME, then set newname and */
/* newnamelen just in case the entry is a directory. */
/************************************************************/
if(!memcmp(attname, "QNAME", 5))
{
memcpy(newname, attval, attvallen);
newnamelen = attvallen;
}
/************************************************************/
/* Else the attribute was QFILATTR, so set fileatt. */
/************************************************************/
else
memcpy(fileatt, attval, 10);
}
/**************************************************************/
} /* while */
/****************************************************************/
/* Close the directory. */
/****************************************************************/
QHFCLODR(dir_handle, &error_code);
}/* print_subdir */
/****************************************************************/
/* Make sure we received the correct number of parameters. The */
/* argc parameter will contain the number of parameters that */
/* was passed to this program. This number also includes the */
/* program itself, so we need to evaluate argc-1. */
/****************************************************************/
/****************************************************************/
/* Open QPRINT file so that data can be written to it. If the */
/* file cannot be opened, print a message and exit. */
/****************************************************************/
if((stream = fopen("QPRINT", "wb")) == NULL)
{
printf("File could not be opened\n");
exit(1);
}
memset(dir_name, ’ ’, 100);
memcpy(dir_name, argv[1], 100);
if(!memcmp(dir_name, " ", 1))
{
fprintf(stream,"No directory specified");
}
else
{
fprintf(stream,"Directory substructure starting at %.100s", dir_name);
print_subdir(dir_name, 0);
}
} /* main */
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/*******************************************************************/
/* PROGRAM: SaveBigLib */
/* */
/* LANGUAGE: ILE C */
/* */
/* DESCRIPTION: This is an example program for the use of */
/* a media definition in a save operation. */
/* It saves library BIGLIB in parallel format to */
/* two media files, using tape media library */
/* TAPMLB01. */
/* */
/* The flow of this program is as follows: */
/* (1) Build media definition input. */
/* (2) Create a media definition using */
/* QsrCreateMediaDefinition. */
/* (3) Save library BIGLIB using the media */
/* definition. */
/* */
/* APIs USED: QsrCreateMediaDefinition, QCMDEXC */
/* */
/*******************************************************************/
#include <qcmdexc.h>
#include <qsrlib01.h>
#include <qusec.h>
#include <string.h>
/*******************************************************************/
/* Variables for QsrCreateMediaDefinition */
/*******************************************************************/
char Data_Buffer[1000];
Qsr_TAPE0100_t *Input_Data;
Qsr_TAPE0100_Device_t *Device;
Qsr_TAPE0100_File_t *Media_File;
char *Next_Free;
char *Volid;
Qus_EC_t Err_Code;
int Data_Length;
char Text[50];
/*******************************************************************/
/* Variables for QCMDEXC */
/*******************************************************************/
char Cmd_String[100];
decimal(15,5) Cmd_Length;
/*******************************************************************/
/* Start of main() */
/*******************************************************************/
/*******************************************************************/
/* Specify input data for QsrCreateMediaDefinition. */
/*******************************************************************/
/*-----------------------------------------------------------------*/
/* Build input data for the first device. */
/* Use device TAPMLB01 with two media files. */
/*-----------------------------------------------------------------*/
Device = (Qsr_TAPE0100_Device_t*)Next_Free;
Next_Free = (char*)(Device + 1);
memcpy(Device->Device_Name,"TAPMLB01 ",10);
Device->Offset_First_File = Next_Free - Data_Buffer;
Device->File_Count = 2;
/*-----------------------------------------------------------------*/
/* Build input data for the first media file for device TAPMLB01. */
/* Use the default sequence number, and volumes VOL11 and VOL12. */
/*-----------------------------------------------------------------*/
Media_File = (Qsr_TAPE0100_File_t*)Next_Free;
Next_Free = (char*)(Media_File + 1);
Media_File->Sequence_Number = 0;
Media_File->Offset_First_Volume_Id = Next_Free - Data_Buffer;
Media_File->Volume_Id_Count = 2;
Media_File->Volume_Id_Length = 6;
Media_File->Starting_Volume = 1;
Data_Length = Media_File->Volume_Id_Count
* Media_File->Volume_Id_Length;
Volid = Next_Free;
memcpy(Volid,"VOL11 VOL12 ",Data_Length);
if (Data_Length % 4) /* Ensure that Next_Free */
Data_Length += (4 - (Data_Length % 4)); /* is incremented by a */
Next_Free += Data_Length; /* multiple of 4. */
Media_File->Offset_Next_File = Next_Free - Data_Buffer;
/*------------------------------------------------------------------*/
/* Build input data for the second media file for device TAPMLB01. */
/* Use the default sequence number, and volumes VOL21 and VOL22. */
/*------------------------------------------------------------------*/
Media_File = (Qsr_TAPE0100_File_t*)Next_Free;
Next_Free = (char*)(Media_File + 1);
Media_File->Sequence_Number = 0;
Media_File->Offset_First_Volume_Id = Next_Free - Data_Buffer;
Media_File->Volume_Id_Count = 2;
Media_File->Volume_Id_Length = 6;
Media_File->Starting_Volume = 1;
Data_Length = Media_File->Volume_Id_Count
* Media_File->Volume_Id_Length;
Volid = Next_Free;
memcpy(Volid,"VOL21 VOL22 ",Data_Length);
if (Data_Length % 4) /* Ensure that Next_Free */
Data_Length += (4 - (Data_Length % 4)); /* is incremented by a */
Next_Free += Data_Length; /* multiple of 4. */
/********************************************************************/
/* Create the media definition. */
/********************************************************************/
Data_Length = Next_Free - Data_Buffer;
memset(Text,’ ’,sizeof(Text));
memcpy(Text,"Save BIGLIB",11);
return 0;
}
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
This program saves the system-level environment variables and the associated CCSIDs in a file for
restoring later.
Use the Create C Module (CRTCMOD) and the Create Program (CRTPGM) commands to create this
program.
Call this program with one parameter (the file to store the variable list and the CCSIDs).
/******************************************************************/
/******************************************************************/
/* */
/* FUNCTION: Save the system-level environment variable list */
/* and the CCSIDs in a file */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: Qp0zGetAllSysEnv() */
/* */
/******************************************************************/
/******************************************************************/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <qp0z1170.h>
if(argc != 2)
{
printf("Usage: call %s <filename>\n",argv[0]);
printf("Example: call %s ’/tmp/sev’\n",argv[0]);
return -1;
}
sl = listBufSize = 1000;
sc = ccsidBufSize = 1000;
listBuf = (char *)malloc(listBufSize);
ccsidBuf = (int *)malloc(ccsidBufSize);
if(rc == ENOENT)
{
numvar = 0;
bw = write(fd, &numvar, sizeof(int));
close(fd);
printf("No system-level environment variables to save");
return 0;
}
if(rc != ENOSPC)
{
printf("Error using Qp0zGetAllSysEnv(), errno = %d\n", rc);
return -1;
}
rc = Qp0zGetAllSysEnv(listBuf, &listBufSize,
numvar = ccsidBufSize/sizeof(int);
This program reads the system-level environment variable list from a file and then sets the system-level
environment variables.
Call this program with one parameter (the name of the file in which the system-level environment
variables were stored).
/******************************************************************/
/******************************************************************/
/* */
/* FUNCTION: Restore the system-level environment variable list */
/* and the associated CCSIDs stored in a file */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: Qp0zPutSysEnv() */
/* */
/******************************************************************/
/******************************************************************/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <qp0z1170.h>
if (argc != 2)
{
printf("Usage: call %s <filename>\n",argv[0]);
printf("Example: call %s ’/tmp/sev’\n",argv[0]);
return -1;
}
fd = open(argv[1], O_RDONLY);
if(fd == -1)
{
printf("open() failed. errno = %d\n", errno);
return -1;
}
if(numvar > 0)
{
listBuf += strlen(listBuf) + 1;
}
close(fd);
printf("System-level environment variables restored\n");
return 0;
Example 1
Assume that a 20-character database field contains only uppercase characters and the pattern 'ABC' is
scanned for. The user program calls the QCLSCAN API for each database record that is read. The
parameters follow.
Note: In scan 4, the string has two places where the pattern can be found. Because the STRPOS value is
1, the first value (position 004) is found. If the STRPOS value is 4, the result is still 004. If the
STRPOS value is in a range of 5 through 12, the result is 012.
Example 2
Assume that a 25-character database field contains only uppercase characters. The user program prompts
for the pattern that does not exceed 10 characters to be scanned for. The workstation user can enter 1
through 10 characters. The system trims trailing blanks from the pattern. The program calls the
QCLSCAN API for each database record that is read. The parameters follow.
Note: In scan 7, the string has two places where the pattern can be found. Because the STRPOS value is
1, only the first value (position 004) is found. If the STRPOS value is 4, the result is still 004. If the
STRPOS value is in a range of 5 through 12, the result is 012.
Example 3
Assume that a 25-character database field contains either uppercase or lowercase characters. The user
program prompts for the pattern that does not exceed 5 characters to be scanned for. The workstation
user can enter 1 through 5 characters. The system trims trailing blanks from the pattern. If the user enters
an asterisk (*) in the pattern, the asterisk is handled as a wild character. The program calls the QCLSCAN
API for each database record that is read. The parameters follow.
Notes:
1. When field translation is specified (the TRANSLATE parameter is specified as '1'), the string is
translated to uppercase characters before scanning occurs; the data in the string is not changed.
2. In scan 6, the string has two places where the pattern can be found. Because the STRPOS value is 1,
the first value (position 004) is found.
3. In scan 7, the wild character (*) is the first character in the trimmed pattern. Wild characters cannot be
the first character in a pattern.
4. In scan 8, the trimmed pattern is blank.
Notes:
v The error-handling program, ACERRF24 (shown in “Error handler for the example COBOL/400
program” on page 387), must exist in the UTCBL library.
v By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
IDENTIFICATION DIVISION.
PROGRAM-ID. ACF24.
**************************************************************
**************************************************************
*
* FUNCTION: SHOWS HOW TO CALL THE VARIOUS APIs, WHILE
* TESTING THAT THEY WORK PROPERLY.
*
* LANGUAGE: COBOL
*
* APIs USED: QLRRTVCE, QLRCHGCM, QLRSETCE
*
**************************************************************
**************************************************************
DATA DIVISION.
WORKING-STORAGE SECTION.
01 old.
05 oldname PIC X(10).
05 oldlibr PIC X(10).
77 scope PIC X VALUE "P".
01 errparm.
05 input-l PIC S9(6) BINARY VALUE ZERO.
05 output-l PIC S9(6) BINARY VALUE ZERO.
05 exception-id PIC X(7).
05 reserved PIC X(1).
05 exception-data PIC X(50).
01 new.
05 newname PIC X(10) VALUE "ACERRF24".
05 newlibr PIC X(10) VALUE "UTCBL".
77 newlib PIC X(10).
PROCEDURE DIVISION.
main-proc.
DISPLAY "in ACF24".
PERFORM variation-01 THRU end-variation.
STOP RUN.
variation-01.
**************************************************************
* *
* This variation addresses the situation where there is no *
* pending COBOL main, so no pending error handler can exist. *
* *
**************************************************************
DISPLAY "no pending so expect nothing but error LBE7052".
MOVE SPACES TO old exception-id.
**************************************************************
* By setting error parm > 8, expect escape message *
* LBE7052 to be returned in error parameter. *
**************************************************************
MOVE LENGTH OF errparm TO input-l.
CALL "QLRRTVCE" USING old scope errparm.
IF exception-id IS NOT = "LBE7052" THEN
DISPLAY "** error - expected LBE7052"
ELSE
DISPLAY "LBE7052 was found"
END-IF.
**************************************************************
* Reset input-l to ZERO, thus any further errors will cause *
This example error handler works with “Example: Using a COBOL/400 program to call APIs” on page
385.
IDENTIFICATION DIVISION.
PROGRAM-ID. ACERRF24.
**************************************************************
**************************************************************
*
* FUNCTION: Error handler for preceding example COBOL program
*
* LANGUAGE: COBOL
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
#include <string.h>
#include <stdio.h>
#include <qtactldv.h>
#include <qusec.h>
/********************************************************************/
/* Typedef structure for QTACTLDV */
/********************************************************************/
typedef struct { /* QTACTLDV command data */
Qta_CTLD0100_t data; /* command data */
char cmd_str[6]; /* command string */
} cmd_struct;
/********************************************************************/
/* Typedef structure for Error code */
/********************************************************************/
typedef struct { /* Error code structure */
Qus_EC_t Edata; /* Error code data */
/* CPF67C8 excp data */
char dev_nam[10]; /* Device name */
char reason_cd[3]; /* Reason code */
char resv1[3]; /* Reserved */
} EC_struct;
/********************************************************************/
/* Constants */
/********************************************************************/
/********************************************************************/
/* Variables for QTACTLDV */
/********************************************************************/
char device[10]; /* device name */
char send_buff[256]; /* send buffer */
int send_buff_len; /* length of send buffer */
char recv_buff[256]; /* receive buffer */
int recv_buff_len; /* length of recv buffer */
int cmd_data_len; /* length of command data */
int i; /* counter variable */
/********************************************************************/
/* OPEN connection */
/********************************************************************/
send_buff_len = 0; /* no send buffer */
/********************************************************************/
/* Send Diagnostic command */
/********************************************************************/
send_buff_len = 0; /* no send buffer */
recv_buff_len = 0; /* no recv buffer */
cmd_data_len = sizeof(Cmd); /* size of command struct */
EC.Edata.Bytes_Provided = 32; /* No exceptions */
Cmd.data.Data_transfer_direction = XFRNONE; /* No data transfer */
Cmd.data.Requested_transfer_length = 0; /* 0 transfer length */
Cmd.data.Ignore_length_errors = RPTLERR; /* report length errs */
Cmd.data.Command_timeout = 600; /* 10 minute timeout */
Cmd.data.Type_of_command = CMDSCSI; /* SCSI command */
Cmd.data.Offset_to_command_string = 32; /* offset 32 */
Cmd.data.Length_of_command_string = 6; /* 6 byte command */
Cmd.data.Reserved1=0; /* reserved */
memcpy(&Cmd.cmd_str, SNDDIAG, 6); /* command string */
else if (strncmp(EC.reason_cd,"\x02\xC1\x00", 3) == 0)
/* Selection timeout */
{
/* Send message that device might be powered off */
}
else
{
/* Send error message for unexpected reason code */
}
}
else
{
/* Handle other messages */
}
}
else
/********************************************************************/
/* CLOSE connection */
/********************************************************************/
send_buff_len = 0; /* no send buffer */
recv_buff_len = 0; /* no receive buffer */
cmd_data_len = 0; /* no command data */
EC.Edata.Bytes_Provided = 32; /* No exceptions */
/******************************************************************************/
/* Type definitions */
/******************************************************************************/
// Define the command structure for sending commands using the QTACTLDV API.
typedef struct { // Command Structure
Qta_CTLD0100_t hdr; // Header
char cmd_str[6]; // Command String
} ctldv_cmd_t;
/******************************************************************************/
/* Entry point to program. */
/******************************************************************************/
int main (int argc, char *argv[])
{
char device[10]; // Device name to get FM level
int deviceLen; // Length of device name to cop
char send_buff[1]; // Send buffer
int send_buff_len; // Length of send buffer
char recv_buff[50]; // Receive buffer
int recv_buff_len; // Length of receive buffer
ctldv_cmd_t ctldv_cmd; // Command variable
int ctldv_cmd_len; // Length of command string
char tempChar; // Used to conver ASCII to EBCD
Qus_EC_t EC; // Error code for qtactldv
/***************************************************************************/
/* Open the pipe. */
/***************************************************************************/
send_buff_len = 0; // No send buffer
recv_buff_len = 0; // No receive buffer
ctldv_cmd_len = 0; // No command
/***************************************************************************/
/* Get the drive VPD */
/***************************************************************************/
recv_buff_len = 16; // Receive buffer for VPD
send_buff_len = 0; // No send buffer
ctldv_cmd_len = 32 + 6; // Reserve command size
ctldv_cmd.hdr.Command_timeout = 600; // 10 minute timeout
ctldv_cmd.hdr.Type_of_command = 0; // SCSI Command
ctldv_cmd.hdr.Offset_to_command_string = sizeof(ctldv_cmd.hdr); // Offset 32
ctldv_cmd.hdr.Length_of_command_string = 6; // 6 byte command
ctldv_cmd.hdr.Reserved1 = 0; // Reserved
ctldv_cmd.hdr.Data_transfer_direction=XFRRECV; // Receiving inquiry data
ctldv_cmd.hdr.Requested_transfer_length=16; // Number of bytes to transfer
ctldv_cmd.hdr.Ignore_length_errors = RPTLERR; // Report length errors
memset(ctldv_cmd.cmd_str, 0x00, 6); // clear command
ctldv_cmd.cmd_str[0] = 0x12; // set to Inquiry command
ctldv_cmd.cmd_str[1] = 0x01; // set EVPD mode
ctldv_cmd.cmd_str[2] = 0x03; // Set code page - VPD
ctldv_cmd.cmd_str[4] = 0x10; // Allocation length
/**************************************************************************/
/* Convert the level to EBCDIC */
/**************************************************************************/
/* Close the pipe. */
/**************************************************************************/
PipeClose:
send_buff_len = 0; // No send buffer
recv_buff_len = 0; // No receive buffer
ctldv_cmd_len = 0; // No command
PipeFailed:
return 0; // return to user
}
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
In this example, Program B specifies to wait up to 2 hours (7200 seconds) to receive an entry from the
data queue. Program A sends an entry to data queue DTAQ1 in library QGPL. If program A sends an
entry within 2 hours, program B receives the entries from this data queue. Processing begins immediately.
If 2 hours elapse without program A sending an entry, program B processes the time-out condition
because the field length returned is 0. Program B continues receiving entries until this time-out condition
occurs. The programs are written in CL; however, either program could be written in any high-level
language.
Example 2: Processing data queue entries from a display file and an ICF file
The following example is different from the usual use of data queues because there is only one job. The
data queue serves as a communications object within the job rather than between two jobs.
The display file or ICF file entry that is put on the data queue is 80 characters in length and contains the
field attributes described in the following table. Therefore, the data queue that is specified using the
CRTDSPF, CHGDSPF, OVRDSPF, CRTICFF, CHGICFF, and OVRICFF commands must have a length of at
least 80 characters.
If the job receiving the data from the data queue has
only one display file or one ICF file open, then this is the
only field needed to determine what type of entry has
been received from the data queue.
11 through 12 (binary) The unique identifier for the file. The value of the
identifier is the same as the value in the open feedback
area for the file. This field should be used by the
program receiving the entry from the data queue only if
there is more than one file with the same name placing
entries on the data queue.
13 through 22 (character) The name of the display file or ICF file. This is the name
of the file actually opened, after all overrides have been
processed, and is the same as the file name found in the
open feedback area for the file. This field should be used
by the program receiving the entry from the data queue
only if there is more than one display file or ICF file that
is placing entries on the data queue.
23 through 32 (character) The library where the file is located. This is the name of
the library, after all overrides have been processed, and is
the same as the library name found in the open feedback
area for the file. This field should be used by the
program receiving the entry from the data queue only if
there is more than one display file or ICF file that is
placing entries on the data queue.
33 through 42 (character) The program device name, after all overrides have been
processed. This name is the same as that found in the
program device definition list of the open feedback area.
For file type *DSPF, this is the name of the display
device where the command or Enter key was pressed.
For file type *ICFF, this is the name of the program
device where data is available. This field should be used
by the program receiving the entry from the data queue
only if the file that placed the entry on the data queue
has more than one device or session invited prior to
receiving the data queue entry.
43 through 80 (character) Reserved.
.
.
DO
WRITE DSPFILE
/* Write with Invite for the Display file */
WRITE ICFFILE
/* Write with Invite for the ICF file */
CALL QRCVDTAQ
/* Receive an entry from the data queue specified */
/* on the DTAQ parameters for the files. Entries */
/* are placed on the data queue when the data is */
/* available from any invited device or session */
/* on either file. */
/* After the entry is received, determine which file */
/* has data available, read the data, process it, */
/* invite the file again and return to process the */
/* next entry on the data queue. */
IF ’ENTRY TYPE’ FIELD = ’*DSPF ’ THEN
/* Entry is from display */
DO /* file. Since this entry*/
/* does not contain the */
/* data received, the data*/
/* must be read from the */
/* file before it can be */
READ DATA FROM DISPLAY FILE /* processed. */
PROCESS INPUT DATA FROM DISPLAY FILE
WRITE TO DISPLAY FILE /* Write with Invite */
END
ELSE /* Entry is from ICF */
/* file. Since this entry*/
/* does not contain the */
/* data received, the data*/
/* must be read from the */
/* file before it can be */
/* processed. */
READ DATA FROM ICF FILE
PROCESS INPUT DATA FROM ICF FILE
WRITE TO ICF FILE /* Write with Invite */
LOOP BACK TO RECEIVE ENTRY FROM DATA QUEUE
.
.
.
END
In the following example, the program in Job B is waiting for input from a display file that it is using
and for input to arrive on the data queue from Job A. Instead of alternately waiting for the display file
and then the data queue, the program waits for one object, the data queue.
The structure of the display file entry is described in the previous example.
The structure of the entry placed on the queue by Job A is defined by the application programmer.
The following example shows coding logic that the application program in Job B might use:
.
.
.
.
OPEN DSPFILE ...
/* Open the Display file. DTAQ parameter specified on*/
/* CRTDSPF, CHGDSPF, or OVRDSPF for the file. */
.
.
DO
WRITE DSPFILE /* Write with Invite for the Display file */
CALL QRCVDTAQ
/* Receive an entry from the data queue specified */
/* on the DTAQ parameter for the file. Entries */
/* are placed on the data queue either by Job A or */
/* by display data management when data is */
/* available from any invited device on the display */
/* file. */
/* After the entry is received, determine what type */
/* of entry it is, process it, and return to receive */
/* the next entry on the data queue. */
IF ’ENTRY TYPE’ FIELD = ’*DSPF ’ THEN
/* Entry is from display */
DO /* file. Since this entry*/
Use the Create C Module (CRTCMOD) and the Create Program (CRTPGM) commands to create this
program.
Call this program with one parameter to display the environment variable specified by that parameter.
Call this program with two parameters to set the environment variable specified by the first parameter to
the value specified by the second parameter.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/******************************************************************/
/******************************************************************/
/* */
/* FUNCTION: Display the value of an environment variable and */
/* then set the environment variable to a new value. */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: getenv(), putenv() */
/* */
/******************************************************************/
/******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
if (argc == 2) {
/* Called just to display the environment variables. */
envvar = getenv(argv[1]);
if (envvar == NULL) {
printf("No environment variable %s set\n",
argv[1]);
}
else {
printf("Environment variable: %s\n", envvar);
}
return 0;
}
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
Terminal program
Use the Create Bound C Program (CRTBNDC) command to create this program (see “Creating the
terminal and interpreter programs” on page 405).
Call this program with no parameters (see “Calling the terminal program” on page 406).
/* Includes */
#include <qp0ztrml.h>
#include <qp0z1170.h>
#include <stdlib.h>
#include <stdio.h>
/* Constants */
#define NUM_PREDEFINED_ENVS 2
/* Global Variables */
extern char **environ;
/********************************************************************/
/* Build the argument array. */
/********************************************************************/
args[0] = "/QSYS.LIB/QGPL.LIB/ECHOINT.PGM";
args[1] = NULL;
/********************************************************************/
/* Build the environment variable array. */
/********************************************************************/
/********************************************************************/
/* Set the terminal attributes. */
/********************************************************************/
/********************************************************************/
/* Start and run the terminal. */
/********************************************************************/
default:
perror("Qp0zRunTerminal() failed");
exit(1);
break;
}
return 0;
}
Interpreter program
This example program is a simple echo interpreter that is used by the terminal program (see “Terminal
program” on page 402).
Use the Create Bound C Program (CRTBNDC) command to create this program (see “Creating the
terminal and interpreter programs” on page 405).
/* Echo interpreter */
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
/* Do forever. */
while (1) {
/* Read a line from stdin. */
gets(buffer);
return 0;
}
void
SignalHandler(int signo)
{
printf("Ending for signal %d\n", signo);
exit(1);
}
The following examples show how to create the example programs (“Terminal program” on page 402 and
“Interpreter program” on page 404). These examples assume that the source for the terminal program is
member TERMINAL in the file QGPL/QCSRC and that the source for the interpreter program is member
INTERPRET in the file QGPL/QCSRC.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: Illustrates how to generate, change, and release */
/* profile handles in a CL program. */
/* */
/* LANGUAGE: CL */
/* */
/* APIs USED: QSYGETPH - Get Profile Handle */
/* QWTSETP - Set Profile */
/* QSYRLSPH - Release Profile Handle */
/* */
/*******************************************************************/
/*******************************************************************/
/*----------------------------------------------------------*/
/* Parameters: */
/*----------------------------------------------------------*/
/* 10 Character user ID */
DCL VAR(&USERID) TYPE(*CHAR) LEN(10)
/*----------------------------------------------------------*/
/* Variables needed by this program: */
/*----------------------------------------------------------*/
/*----------------------------------------------------------*/
/* Generate profile handles for the user ID this program */
/* is currently running under and for the user ID passed */
/* to this program: */
/*----------------------------------------------------------*/
CALL PGM(QSYGETPH) PARM(’*CURRENT ’ +
&CURPWD /* Password ignored +
when *CURRENT is +
specified */+
&PRFHNDL1)
/*----------------------------------------------------------*/
/* Change the user for this job to the user ID passed to */
/* this program: */
/*----------------------------------------------------------*/
CALL PGM(QWTSETP) PARM(&PRFHNDL2)
/*----------------------------------------------------------*/
/* This program is now running under the user ID passed to */
/* this program. */
/*----------------------------------------------------------*/
/*----------------------------------------------------------*/
/* Now change the user ID for this job back to the user ID */
/* it was originally running under */
/*----------------------------------------------------------*/
CALL PGM(QWTSETP) PARM(&PRFHNDL1)
/*----------------------------------------------------------*/
/* The profile handles generated in this program can now */
/* be released: */
/*----------------------------------------------------------*/
CALL PGM(QSYRLSPH) PARM(&PRFHNDL1)
CALL PGM(QSYRLSPH) PARM(&PRFHNDL2)
ENDPGM
This example does not include any of the programs that are being called, nor does it show anything but
an excerpt of the calling program.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
The first thing to do, after deciding in what program object the exit point is to be placed, is to register
that exit point. It is also important to remember that the exit point format defines what the exit program
data looks like. This ILE C program registers an exit point named QIBM_QXIC_TSTXPOINTA.
/********************************************************************/
/* PROGRAM: RegisterPoint */
/* */
/* LANGUAGE: ILE C */
/* */
/* DESCRIPTION: This program registers an exit point in an */
/* application. */
/* */
/* APIs USED: QusRegisterExitPoint */
/* */
/********************************************************************/
#include <string.h>
#include <qusec.h>
#include <qusrgfa1.h>
/********************************************************************/
/* Structure for the control block */
/********************************************************************/
typedef _Packed struct Control_x{
int Num_Vlen_Recs;
Qus_Vlen_Rec_4_t Vlen_Rec_1;
char Description[50];
} Control_Rec;
int main () {
/*****************************************
*** INITIALIZING ALL STRUCTURES:: ***
*****************************************/
Error_Code.Bytes_Provided = sizeof(Error_Code);
EPnt_Controls.Num_Vlen_Recs = 1;
EPnt_Controls.Vlen_Rec_1.Length_Vlen_Record = 62;
EPnt_Controls.Vlen_Rec_1.Control_Key = 8;
EPnt_Controls.Vlen_Rec_1.Length_Data = 50;
memcpy( EPnt_Controls.Description , "Example Exit Point" , 17 );
QusRegisterExitPoint (EPnt_Name,
EPnt_F_Name,
&EPnt_Controls,
&Error_Code);
After an exit point has been registered, exit programs must be added to that point, indicating the possible
calls based on runtime conditions. The following ILE C program adds an exit program named
TSTXITPROGQGPL to the QIBM_QXIC_TSTXPOINTA exit point that is registered in the previous
example.
/********************************************************************/
/* PROGRAM: AddProgram */
/* */
/* LANGUAGE: ILE C */
/* */
/* DESCRIPTION: This program adds an exit program to a registered */
/* exit point. */
/* */
/* APIs USED: QusAddExitProgram */
/* */
/********************************************************************/
#include <qusec.h>
#include <qusrgfa1.h>
/********************************************************************/
/* Structure for the Exit Program Attributes */
/********************************************************************/
int main () {
Error_Code.Bytes_Provided=sizeof(Error_Code);
EProg_Attributes.Num_Vlen_Recs=1;
EProg_Attributes.ADPG_Vlen.Length_Vlen_Record=16;
EProg_Attributes.ADPG_Vlen.Control_Key=3;
EProg_Attributes.ADPG_Vlen.Length_Data=4;
EProg_Attributes.CCSID = 37;
QusAddExitProgram (EPnt_Name,
EPnt_F_Name,
EProg_Number,
Q_EProg_Name,
EProg_Data,
Len_EProg_Data,
&EProg_Attributes,
When you have registered an exit point and have added the exit programs to that exit point, you can do
exit program calls from within your application. The information needed to do the calls is obtained from
the Retrieve Exit Information API. In the following sample a conditional call is made based on the exit
point information.
/********************************************************************/
/* PROGRAM: RetrieveAndProcess */
/* */
/* LANGUAGE: ILE C */
/* */
/* DESCRIPTION: This is an excerpt of a program that retrieves */
/* information on an exit point, and does processing */
/* based on that information. */
/* */
/* APIs USED: QusRetrieveExitInformation */
/* */
/********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <qusec.h>
#include <qusrgfa2.h>
/********************************************************************/
/* Structure for Selection Criteria on the Retrieve */
/********************************************************************/
typedef _Packed struct RTVEI_Select_C_x {
Qus_Selcrtr_t Crit;
Qus_Select_Entry_t Select_Entry;
char RTV_String[10];
} RTVEI_Select_C;
/********************************************************************/
/* Conv_Lib converts the library name to a null terminated string */
/********************************************************************/
int main() {
Qus_EXTI0200_t *EXTI0200;
Qus_EXTI0200_Entry_t *EXTI0200_Entry;
char *Pgm_Data;
int Counter;
/*****************************************
* Initializing Structures *
*****************************************/
Error_Code.Bytes_Provided = sizeof(Error_Code);
tmp_str=(char *)malloc(sizeof(char));
lib=(char *)malloc(sizeof(char));
EXTI0200=(Qus_EXTI0200_t *) malloc ( ( sizeof( Qus_EXTI0200_t ) +
sizeof( Qus_EXTI0200_Entry_t ) + MAX_PGM_DATA_SIZE ) * 2 );
EProg_Select_C.Crit.Number_Sel_Criteria = 1;
EProg_Select_C.Select_Entry.Size_Entry = 26;
EProg_Select_C.Select_Entry.Comp_Operator = 1;
EProg_Select_C.Select_Entry.Start_Pgm_Data = 0;
EProg_Select_C.Select_Entry.Length_Comp_Data = 10;
memcpy( EProg_Select_C.RTV_String , "EXAMPLE " , 10 );
QusRetrieveExitInformation (Handle,
EXTI0200,
Length_Of_R_Var,
RTVEI_Format_Name,
EPnt_Name,
EPnt_F_Name,
EProg_Number,
&EProg_Select_C,
&Error_Code);
if ( Error_Code.Bytes_Available ) {
printf("\nEXCEPTION : %s",Error_Code.Exception_Id);
exit (1);
}
/********************************************************
* Call all of the preprocessing exit programs returned *
********************************************************/
Counter=EXTI0200->Number_Programs_Returned;
while ( Counter-- ) {
Conv_Lib(EXTI0200_Entry->Program_Library,lib);
return(0);
}
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
Server program
This program acts as a server to the client program (see “Client program” on page 415). The buffer is a
shared memory segment. The process synchronization is done using semaphores.
Use the Create C Module (CRTCMOD) and the Create Program (CRTPGM) commands to create this
program.
Call this program with no parameters before calling the client program.
/*********************************************************************/
/*********************************************************************/
/* */
/* FUNCTION: This program acts as a server to the client program. */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: semctl(), semget(), semop(), */
/* shmat(), shmctl(), shmdt(), shmget() */
/* ftok() */
/* */
/*********************************************************************/
/*********************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
/* Generate an IPC key for the semaphore set and the shared */
/* memory segment. Typically, an application specific path and */
/* id would be used to generate the IPC key. */
semkey = ftok(SEMKEYPATH,SEMKEYID);
if ( semkey == (key_t)-1 )
{
printf("main: ftok() for sem failed\n");
return -1;
}
shmkey = ftok(SHMKEYPATH,SHMKEYID);
if ( shmkey == (key_t)-1 )
{
printf("main: ftok() for shm failed\n");
return -1;
}
sarray[0] = 0;
sarray[1] = 0;
operations[1].sem_num = 0;
/* Operate on the first sem */
operations[1].sem_op = 1;
/* Increment the semval by 1 */
operations[1].sem_flg = IPC_NOWAIT;
/* Do not allow to wait */
Client program
This program acts as a client to the server program (see “Server program” on page 412). The program is
run after the message Ready for client jobs appears from the server program.
Call this program with no parameters after calling the server program.
/*********************************************************************/
/*********************************************************************/
/* */
/* FUNCTION: This program acts as a client to the server program. */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: semget(), semop(), */
/* shmget(), shmat(), shmdt() */
/* ftok() */
/* */
/*********************************************************************/
/*********************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#define NUMSEMS 2
#define SIZEOFSHMSEG 50
/* Generate an IPC key for the semaphore set and the shared */
/* memory segment. Typically, an application specific path and */
/* id would be used to generate the IPC key. */
semkey = ftok(SEMKEYPATH,SEMKEYID);
if ( semkey == (key_t)-1 )
{
printf("main: ftok() for sem failed\n");
return -1;
}
shmkey = ftok(SHMKEYPATH,SHMKEYID);
if ( shmkey == (key_t)-1 )
{
printf("main: ftok() for shm failed\n");
return -1;
}
operations[1].sem_num = 0;
/* Operate on the first sem */
operations[1].sem_op = 1;
/* Increment the semval by one */
operations[1].sem_flg = 0;
operations[1].sem_num = 1;
/* Operate on the second sem */
operations[1].sem_op = 1;
/* Increment the semval by one */
operations[1].sem_flg = 0;
/* Allow a wait to occur */
return 0;
}
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
This source application program sends management services transport requests to a target application
program.
/*********************************************************************/
/*********************************************************************/
/* */
/* FUNCTION: */
/* This is a source application that uses the management services */
/* transport APIs. It does the following: */
/* 1. Prompts for the network ID and CP name of the remote system */
/*-------------------------------------------------------------------*/
/* Type definitions */
/*-------------------------------------------------------------------*/
typedef int HANDLE; /* typedef for handle */
typedef char APPLNAME[8]; /* typedef for application name */
typedef char NETID[8]; /* typedef for network ID */
typedef char CPNAME[8]; /* typedef for control point name*/
typedef char MODENAME[8]; /* typedef for mode name */
typedef char SENSECODE[8]; /* typedef for SNA sense code (in
character format) */
typedef char LIBNAME[10]; /* typedef for library name */
typedef char QNAME[10]; /* typedef for data queue name */
typedef char MSGID[7]; /* typedef for message ID */
typedef char EXCPDATA[48]; /* typedef for exception data */
typedef char CATEGORY[8]; /* typedef for category */
typedef char APPLTYPE[10]; /* typedef for application type */
typedef char REPLREG[10]; /* typedef for replace
registration */
typedef char DATARCVD[10]; /* typedef for data received */
typedef char REQTYPE[10]; /* typedef for request type */
typedef char POSTRPL[10]; /* typedef for post reply */
typedef char REQUESTID[53]; /* typedef for request ID */
typedef char SRBUFFER[500]; /* typedef for send/receive
buffer. This program limits
the amount of data to be sent
or received to 500 bytes. The
maximum size of a management
services transport buffer is
31739. */
/*-------------------------------------------------------------------*/
/* External program declarations */
/*-------------------------------------------------------------------*/
#pragma linkage(QNMSTRAP, OS) /* Start application API */
extern void QNMSTRAP (HANDLE *handle, /* pointer to handle */
APPLNAME *applname, /* pointer to appl name */
QUALQNAME *qualqname, /* pointer to data queue
name */
ERRORCODE *errorcode); /* pointer to error code
parameter */
/*-------------------------------------------------------------------*/
/* Global declarations */
/*-------------------------------------------------------------------*/
HANDLE appl_handle; /* Handle of application */
ERRORCODE error_code_struc = /* Error code parameter */
{sizeof(error_code_struc), /* Initialize bytes provided */
0, /* initialize bytes available */
NOERROR}; /* initialize error code */
char input_line[80]; /* Input data */
QUALAPPL qual_appl = /* Qualified application name */
{" "," "," "};
REQUESTID req_id; /* Returned request ID */
int wait_time = -1; /* Wait time = wait forever */
/*-------------------------------------------------------------------*/
/* Start of main. */
/*-------------------------------------------------------------------*/
int main ()
{
APPLNAME appl_name = "MSTSOURC"; /* Application name to be used */
QUALQNAME data_queue_parm = /* Data queue name to be used */
{"*NONE ", " "}; /* Initialize structure */
NOTIFRCD notif_record; /* Area to contain notification
record */
CATEGORY category = "*NONE "; /* SNA/Management Services function
set group */
APPLTYPE appl_type = "*FPAPP "; /* Application type */
REPLREG replace_reg = "*YES "; /* Replace registration = *YES */
int sys_result; /* Result of system function */
char end_msg[] = "ENDRMTAPPL"; /* If this data is received then
the application will end */
char incoming_data[] = "01"; /* Incoming data constant */
SRBUFFER send_buffer; /* Send buffer */
int data_length; /* Length of send data */
char input_char; /* Input character */
REQTYPE req_type; /* Request type */
POSTRPL post_reply = "*NO "; /* Don’t post any received replies
*/
MODENAME mode_name = "#INTER "; /* Mode name = #INTER */
/*-------------------------------------------------------------------*/
/* Start of code */
/*-------------------------------------------------------------------*/
QNMSTRAP (&appl_handle,
&appl_name,
&data_queue_parm,
&error_code_struc); /* Start application */
check_error_code("QNMSTRAP"); /* Check error code */
QNMCHGMN (&appl_handle,
&mode_name,
&error_code_struc); /* Change mode name */
check_error_code("QNMCHGMN"); /* Check error code */
get_network_id(); /* Get network ID */
/*-------------------------------------------------------------------*/
/* process_replies function */
/*-------------------------------------------------------------------*/
void process_replies ()
{
RECEIVERVAR receiver_var = /* Receiver variable */
{sizeof(receiver_var)}; /* Initialize bytes provided */
int rcv_var_len = sizeof(receiver_var); /* Length of receiver
variable */
DATARCVD data_rcvd = "*NODATA "; /* Type of data received */
QUALAPPL qual_appl; /* Sender of reply */
/*-------------------------------------------------------------------*/
/* get_network_id function. */
/*-------------------------------------------------------------------*/
void get_network_id ()
{
int count;
printf("Enter network ID of remote system where MSTTARG "
"application has been started\n"); /* Prompt for network
ID */
gets(input_line); /* Get network ID */
while (strlen(input_line) <= 0 ||
strlen(input_line) > 8) /* While network ID is not valid */
{
printf("Network ID is too long or too short - try again\n");
gets(input_line); /* Get network ID */
}
memcpy(qual_appl.network_id,
input_line,
strlen(input_line)); /* Copy network ID */
for (count=0; count < strlen(input_line); count++)
qual_appl.network_id[count] =
toupper(qual_appl.network_id[count]); /* Convert
input to uppercase */
}
/*-------------------------------------------------------------------*/
/* get_cp_name function. */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* check_error_code - */
/*-------------------------------------------------------------------*/
void check_error_code (char func_name[8])
{
char *sense_ptr = error_code_struc.exception_data + 36; /*
Pointer to sense code in
exception data */
SENSECODE sense_code; /* SNA sense code */
if (error_code_struc.bytes_available != 0) /* Error occurred? */
{
printf("\n\nError occurred calling %1.8s.\n",func_name);
memcpy(sense_code,
sense_ptr,
sizeof(sense_code)); /* Copy sense code from exception
data */
printf("Error code is %1.7s, SNA sense code is %1.8s.\n",
error_code_struc.exception_ID,
sense_code);
if (memcmp(func_name,
"QNMSTRAP",
sizeof(func_name)) != 0) /* Error did not occur on
start application? */
{
QNMENDAP (&appl_handle,
&error_code_struc); /* End the application */
}
exit(EXIT_FAILURE); /* Exit this program */
}
}
This target application program receives management services transport requests from the source
application program. The target application program returns replies if requests specify that replies are
needed.
/*********************************************************************/
/*********************************************************************/
/* */
/* FUNCTION: */
/* This is a target application that uses the management services */
/* transport APIs. It receives management services transport */
/* requests from source application MSTSOURC and displays the data */
/* contained in the request. If the request specifies that a */
/*-------------------------------------------------------------------*/
/* Type definitions */
/*-------------------------------------------------------------------*/
typedef int HANDLE; /* typedef for handle */
typedef char APPLNAME[8]; /* typedef for application name */
typedef char NETID[8]; /* typedef for network ID */
typedef char CPNAME[8]; /* typedef for control point name*/
typedef char SENSECODE[8]; /* typedef for SNA sense code
(in character format) */
typedef char LIBNAME[10]; /* typedef for library name */
typedef char QNAME[10]; /* typedef for data queue name */
typedef char MSGID[7]; /* typedef for message ID */
typedef char EXCPDATA[48]; /* typedef for exception data */
typedef char CATEGORY[8]; /* typedef for category */
typedef char APPLTYPE[10]; /* typedef for application type */
typedef char REPLREG[10]; /* typedef for replace
registration */
typedef char DATARCVD[10]; /* typedef for data received */
typedef char REPLYTYPE[10]; /* typedef for reply type */
typedef char REQUESTID[53]; /* typedef for request ID */
typedef char PACKED5[3]; /* typedef for PACKED(5,0) field */
typedef char SRBUFFER[500]; /* typedef for send/receive
buffer. This program limits
the amount of data to be sent
or received to 500 bytes. The
maximum size of a management
services transport buffer is
31739. */
/*-------------------------------------------------------------------*/
/* External program declarations */
/*-------------------------------------------------------------------*/
#pragma linkage(QNMSTRAP, OS) /* Start application API */
extern void QNMSTRAP (HANDLE *handle, /* pointer to handle */
APPLNAME *applname, /* pointer to application
name */
QUALQNAME *qualqname,/* pointer to data queue
name */
ERRORCODE *errorcode); /* pointer to error code
parameter */
/*-------------------------------------------------------------------*/
/* Global declarations */
/*-------------------------------------------------------------------*/
HANDLE appl_handle; /* Handle of application */
ERRORCODE error_code_struc = /* Error code parameter */
{sizeof(error_code_struc), /* Initialize bytes provided */
0, /* initialize bytes available */
NOERROR}; /* initialize error code */
/*-------------------------------------------------------------------*/
/* Start of main function */
/*-------------------------------------------------------------------*/
int main ()
{
/*-------------------------------------------------------------------*/
/* Local declarations */
/*-------------------------------------------------------------------*/
APPLNAME appl_name = "MSTTARG "; /* Application name to be used */
QUALQNAME data_queue_parm = /* Data queue name to be used */
{"MSTDTAQ ", "QTEMP "}; /* Initialize structure */
NOTIFRCD notif_record; /* Area to contain notification
record */
RECEIVERVAR receiver_var = /* Receiver variable */
{sizeof(receiver_var)}; /* Initialize bytes provided */
QUALAPPL qual_appl; /* Qualified application name */
DATARCVD data_rcvd; /* Type of data received */
CATEGORY category = "*NONE "; /* SNA/Management Services function
set group */
APPLTYPE appl_type = "*FPAPP "; /* Application type */
REPLREG replace_reg = "*YES "; /* Replace registration = *NO */
REPLYTYPE reply_cmp = REPLYCMP; /* Complete reply */
REPLYTYPE reply_inc = REPLYINC; /* Incomplete reply */
int sys_result; /* Result of system function */
int rcv_var_len = sizeof(receiver_var); /* Length of receiver
variable */
PACKED5 wait_time_p = "\x00\x00\x1D"; /* Packed value for wait time
= -1, that is, wait forever */
PACKED5 record_len; /* Length of received data queue
record */
int wait_forever = -1; /* Integer value for wait time =
/*-------------------------------------------------------------------*/
/* Start of executable code */
/*-------------------------------------------------------------------*/
sys_result = system("DLTDTAQ DTAQ(QTEMP/MSTDTAQ)"); /* Delete
previous data queue (if any) */
sys_result = system("CRTDTAQ DTAQ(QTEMP/MSTDTAQ) MAXLEN(80)"); /*
Create data queue */
QNMSTRAP (&appl_handle,
&appl_name,
&data_queue_parm,
&error_code_struc); /* Start application */
check_error_code("QNMSTRAP"); /* Check error code */
QNMREGAP (&appl_handle,
&category,
&appl_type,
&replace_reg,
&error_code_struc); /* Register the application */
check_error_code("QNMREGAP"); /* Check error code */
while (memcmp(receiver_var.received_data,
end_msg,
sizeof(end_msg)) != 0)
{ /* Loop until an ending string
has been sent by the requesting
application */
QRCVDTAQ (&data_queue_parm.data_queue_name,
&data_queue_parm.library_name,
&record_len,
¬if_record,
&wait_time_p); /* Receive indication from data
queue */
if (memcmp(notif_record.function,
incoming_data,
sizeof(incoming_data)) == 0) /* Incoming data was
received? */
{
strncpy(receiver_var.received_data,
"\0",
sizeof(receiver_var.received_data)); /* Null out the
receive buffer */
QNMRCVDT (&appl_handle,
&receiver_var,
&rcv_var_len,
¬if_record.req_id,
&qual_appl,
&data_rcvd,
&wait_forever,
&error_code_struc); /* Receive data using the
request ID in the notification*/
check_error_code("QNMRCVDT"); /* Check error code */
printf("%1.500s\n",receiver_var.received_data); /* Display
the received data */
if (memcmp(data_rcvd,
REQREPLY,
sizeof(data_rcvd)) == 0) /* Request requires
a reply? */
{
}
else
{ /* A send completion was received
for a previous send reply
operation */
QNMRCVOC (&appl_handle,
¬if_record.req_id,
&qual_appl,
&error_code_struc);/* Receive operation completion*/
check_error_code("QNMRCVOC"); /* Check error code */
printf("Reply was sent successfully.\n"); /* Error code was
OK */
}
}
QNMDRGAP (&appl_handle,
return 0;
}
/*-------------------------------------------------------------------*/
/* check_error_code - */
/* */
/* This function validates the error code parameter returned on */
/* the call to a management services transport API program. If */
/* an error occurred, it displays the error that occurred and */
/* ends this program. */
/*-------------------------------------------------------------------*/
void check_error_code (char func_name[8])
{
char *sense_ptr = error_code_struc.exception_data + 36; /*
Pointer to sense code in
exception data */
SENSECODE sense_code; /* SNA sense code */
if (error_code_struc.bytes_available != 0) /* Error occurred? */
{
printf("\nError occurred calling %1.8s.\n",func_name);
memcpy(sense_code,
sense_ptr,
sizeof(sense_code)); /* Copy sense code from exception
data */
printf("Error code is %1.7s, SNA sense code is %1.8s.\n",
error_code_struc.exception_ID,
sense_code);
if (memcmp(func_name,
"QNMSTRAP",
sizeof("QNMSTRAP")) != 0) /* Error did not occur on
start application? */
{
QNMENDAP (&appl_handle,
&error_code_struc); /* End the application */
}
exit(EXIT_FAILURE); /* Exit this program */
}
}
You might ask why this would ever be done when an ILE debugger is provided with the i5/OS
operating system. There are several reasons why an application developer might want to use these APIs
to write a different ILE debugger:
v A debugger running on a workstation can be built to debug ILE programs running on the system. This
allows a debugger to take advantage of Windows and other easy-to-use interfaces available on the
workstation. The workstation debugger can communicate with the code running on the system. The
code running on the system can use the debugger APIs.
v The writer of an ILE compiler might want to write a debugger to take advantage of the ILE languages.
The i5/OS debugger is a more general-purpose debugger that is made for all ILE languages.
v A debugger can be written with functions not available on the i5/OS ILE debugger.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
The ILE source debugger APIs can be divided into several groups. These include APIs that:
v Start and end the debug session
v Add programs and modules to debug
v Manipulate text views in a program
v Add and remove breakpoints, steps, and so on
Besides APIs, there are two user exits that get called:
v The Source Debug program gets called when the Start Debug (STRDBG), Display Module Source
(DSPMODSRC), and End Debug (ENDDBG) CL commands are entered.
v The Program Stop Handler gets called when an ILE program being debugged hits a breakpoint, step,
and so on.
To demonstrate how these APIs are used, this topic presents an example debugger with complete code
examples and an explanation of what the APIs do.
The ILE debugger that comes with i5/OS uses the debugger APIs just as a user-written debugger would.
There is nothing special about the i5/OS debugger. Its functions could be done by an application
developer using the debugger APIs and other i5/OS APIs.
Consider a simple scenario in which the user wishes to debug an ILE program.
1. From the command entry screen, the user enters the Start Debug (STRDBG) command, passing it the
name of an ILE program to debug.
STRDBG P1
2. The ILE debugger screen is displayed, showing the source of a module in the ILE program being
debugged. From this screen, the user adds a breakpoint and then exits.
3. Back at the command entry screen, the user runs the ILE program that is being debugged.
CALL P1
4. The ILE program hits the breakpoint previously set. The ILE debugger screen is displayed,
highlighting in the source where the program has stopped at the breakpoint.
5. The user displays a variable in the program being debugged.
6. The user exits the ILE debugger, allowing the ILE program to run to completion. The program ends.
7. Back at the command entry screen, the user ends the debug session.
ENDDBG
This is the simplest of debug scenarios, but it illustrates how i5/OS, the debugger user exits, and the
debugger APIs interact.
This section discusses an example ILE debugger that demonstrates the use of some of the ILE debugger
APIs. Each function in the C program is discussed along with the APIs that they call. Although the entire
program listing is printed later (see “Debugger code sample” on page 447), each function or piece of code
is printed with the section where it is discussed to make reading the code easier.
The example debugger does not use all ILE debugger APIs. Its function is limited. After the discussion of
the code, the APIs and some functions not covered are discussed.
The Create C Module (CRTCMOD) command compiles the source code of the debugger. It is compiled
into module DEBUG.
The Create Program (CRTPGM) command creates program DEBUG from module DEBUG. It is necessary
to bind to service program QTEDBGS so that the calls to the debugger APIs are resolved. It is also
important to use activation group QTEDBGAG. This is an activation group that cannot be destroyed
while the job is in debug mode. Thus, all static variables in program DEBUG remain intact throughout
the debugging of the ILE program. Only when ENDDBG is entered can the activation group be
destroyed, even if the Reclaim Resources (RCLRSC) CL command is entered.
The example debugger consists of a single program called DEBUG. The program is used as the Source
Debug program as well as the Program Stop Handler. The program determines how many parameters it
is being called with, and with this information it does the function of one or the other of the user exits.
The debugger can debug only one ILE program. This program is specified on the Start Debug CL
command. The program cannot be removed from debug until ENDDBG is done. No new programs can
be added.
To debug an ILE program P1 with this sample debugger, the following CL command could be entered:
STRDBG P1 SRCDBGPGM(DEBUG)
Note that DEBUG must be in the library list when STRDBG is done.
If the command is done, P1 is called twice, once as a Source Debug program given a reason of *START,
and again as a Source Debug program given a reason of *DISPLAY.
Other variations of the Start Debug command can be given with different results. For example, the
following CL command causes DEBUG to be called only once with a reason of *START:
STRDBG P1 SRCDBGPGM(DEBUG) DSPMODSRC(*NO)
This is because STRDBG has been told not to display the debug screen, so the *DISPLAY reason is not
given until the user does the Display Module Source (DSPMODSRC) CL command.
This is because no ILE program is specified. If an ILE program receives an unmonitored message and the
ILE debugger needs to be called, DEBUG is first called with *START as a Source Debug program. Also, if
Display Module Source is entered, the *START and then the *DISPLAY reason is passed to DEBUG.
The "list views" command shows all of the views available in the program being debugged. The text
description of the view is listed, with a sequential number. This number is used by the "switch"
command to switch to that view.
The "list text" command prints out the text of the current view. Text has a line number next to it. The line
number is used when setting breakpoints or other debug commands.
The switch command switches the current view. The current view is the view used when setting
breakpoints, displaying variables, viewing text, and so on.
Other commands are interpreted by the QteSubmitDebugCommand API. This API will be discussed later.
An example command that can be entered is "break n", where n is the line number in the current view.
These commands are similar to the ones allowed in the ILE debugger shipped with i5/OS.
Besides the normal C library header files, an API header file, qtedbgs.h is included. This file defines the
functions exported from service program QTEDBGS. This service program contains the ILE debugger
APIs.
Global variables
static _TE_VEWL0100_T *pgm_dbg_dta = NULL;
static long current_view = 0; /* current view - defaults to 1st*/
static _TE_OBJLIB_T program_lib; /* name and lib of pgm debugged */
These are global variables that hold information about the program being debugged. These variables do
not go away when program DEBUG exits, because they are stored in the activation group which is not
destroyed until the debug session has completed.
The name and library of the program are stored, as is the current view being debugged. Also, a pointer
to a structure returned by the QteRetrieveModuleViews is saved, as this information is needed when
debugging the various views of the program.
PgmList_t
typedef struct {
_TE_OBJLIB_T PgmLib; /* Name and Library of program */
_TE_NAME_T PgmType; /* program type, *PGM or *SRVPGM */
} PgmList_t;
This is the structure of the name, library, and type of the program being debugged.
main()
main (int argc, char *argv[]) {
if (argc == 4) /* called as source debug program*/
HandleSession(argv[1], (PgmList_t *)argv[2], *(int
*)argv[3]);
else if (argc == 8) /* called as program stop handler*/
Program DEBUG can be called in two ways. When it is called by the STRDBG, DSPMODSRC, and
ENDDBG CL commands, it is called as the Source Debug program user exit. It is passed three
parameters.
DEBUG can also be called when a program being debugged hits a breakpoint or step. In this case, it is
passed seven parameters.
DEBUG therefore can determine why it was called by counting the number of parameters it was passed.
Remember that argc includes the program name as the first argument passed.
If argc is 4 (three parameters passed to DEBUG), function HandleSession is called, and the three
parameters passed to DEBUG are passed to it, typecasted as needed.
If argc is 8 (seven parameters passed to DEBUG), function HandleStop is called, and the seven
parameters passed to DEBUG are passed to it, typecasted as needed.
If any other number of parameters are passed to DEBUG, it cannot have been called from the i5/OS
debug support, so DEBUG will just exit.
HandleSession()
void HandleSession(char reason[10],
PgmList_t ProgramList[],
int ProgramListCount) {
When DEBUG is called as a session handler, it is passed three parameters. The first parameter is a
10-character array containing a reason field. This contains the reason why the session handler is called.
When DEBUG is first called, it is passed a reason of *START, indicating that the debugger is to initialize
for an ILE debug session. When this reason is given, the second parameter contains a list of ILE
programs specified on the STRDBG command, and the third parameter contains the number of programs
specified on parameter two. From 0 to 10 ILE programs can be specified.
When the user wishes to see the ILE debugger screen, either from STRDBG or DSPMODSRC, a reason of
*DISPLAY is passed. When the user enters ENDDBG, the *STOP reason is passed, indicating that the ILE
debug session is ending. The second and third parameters are not used when the reason is *DISPLAY or
*STOP.
The code tests for a reason and calls the appropriate function. There is one function for each reason that
can be passed.
TearDownDebugger()
void TearDownDebugger(void) {
_TE_ERROR_CODE_T errorCode = {8}; /* errors will be ignored */
This function is called when the user enters ENDDBG. The debugger calls the QteEndSourceDebug API
which ends ILE debugging. Since an 8 is passed as the number of bytes provided, the message ID and
error data from an error are not returned to the caller. Thus, any errors from this API (there should not be
any) are ignored.
The exit() function is called, which destroys the activation group. Thus, all global data defined in the
program's variables are lost. This is ok, since the debug session is ending at this point.
StartUpDebugger()
void StartUpDebugger(PgmList_t ProgramList[],
int ProgramListCount) {
This function is passed the second and third parameters which were passed from the system when it
called DEBUG with a reason of *START. These parameters are the list of programs to be added to debug
and the number of programs in the list. This simple example debugger can only debug one program, so
if any other number of programs were specified on STRDBG, the debugger just exits.
StartUpDebugger first stores the program/library element passed to it in a global variable available to all
functions. This is the name and library of the program being debugged. It then calls the
QteStartSourceDebug API to tell the system that an ILE debug session is to begin. The name and library
of program DEBUG are passed to this API as the Program Stop Handler. Thus, whenever the program
being debugged is stopped by the debugger, program DEBUG will be called.
Finally, the function calls AddProgram to add the single program to debug.
AddProgram()
void AddProgram(void) {
The heart of this function is the two calls to the QteRetrieveModuleViews API and the call to
QteRegisterDebugView API.
The QteRetrieveModuleViews API returns information about an ILE program. It returns this information
in a structure of type _TE_VEWL0100_T. This is a fairly complex structure that has the following fields:
typedef _Packed struct { /* format VEWL0100 */
long int BytesReturned; /* number of bytes returned */
long int BytesAvailable; /* number of bytes available */
long int NumberElements; /* number of elements returned */
_Packed struct { /* one element */
_TE_NAME_T ModuleName; /* name of module in program */
_TE_NAME_T ViewType; /* type of view: */
_TE_COMPILER_ID_T CompilerID; /* compiler ID */
_TE_NAME_T MainIndicator; /* main indicator */
_TE_TIMESTAMP_T TimeStamp; /* time view was created */
_TE_TEXTDESC_T ViewDescription; /* view description */
char Reserved[3];
long int ViewNumber; /* view number within module */
long int NumViews; /* number of views in this module*/
} Element[1]; /* one element */
} _TE_VEWL0100_T;
Since there is no way to know in advance how many views a program has, the QteRetrieveModuleViews
API should be called once with only enough storage to return the number of bytes that the API needs to
return all of its information. Thus, the first call to the API provides only 8 bytes of storage for the API to
return its data. This allows the API to fill in the BytesAvailable field.
QteRetrieveModuleViews is passed a buffer to hold the receiver variable and the length of that buffer (in
this case, 8 bytes). It is also passed a format name which identifies the structure of the receiver variable.
The only allowable format name at this time is VEWL0100. A structure containing the program name and
library name of the ILE program is passed. Also, the program type is passed. In this example debugger,
only *PGM objects can be debugged, but it is possible to debug *SRVPGM objects using the ILE debugger
APIs.
The name of the module is provided, in which case information about that module is returned. *ALL
indicates that information about all modules in the program is to be returned. A return library variable is
passed. This is so that when *LIBL is passed as a library name, the real library name can be obtained,
making subsequent API calls faster because the library list won't have to be searched again.
Finally an error code structure is passed to the API. This structure is initialized with a zero, indicating
that the API is not to fill in any error code data. Instead, the API issues an exception if an error occurs.
No errors are expected, so this should not matter.
Before QteRetrieveModuleViews is called again, a buffer large enough to hold all of the information is
created. The API is called again with the same parameters, but this time the entire information will be
stored by the API in the allocated buffer.
If the API does not return any elements, this means that none of the modules has debug data. In this
case, the program cannot be debugged, so the debug session is ended.
Now that a list of views has been retrieved, it is time to register all of the views to the system, making it
possible to do debug operations against them. In a real debugger, only the views requested to be seen by
the user would be registered to save processing time, but in this example, all views will be registered at
once.
Not all of the fields in the VEWL0100 structure are needed by this debugger. However, they are described
here. The API returns one element for each view in the program. Each module in the program might
have several views. All views for a particular module are contiguous in the list.
View Description
ModuleName This is the name of the module in the program which
this particular view is for.
ViewType This indicates the type of view. A *TEXT view contains
the text retrieved from source files on the system. The
text contains sequence information from these files that
the debugger might not want to display. A *LISTING
view contains the text that is stored with the program
object itself. A *STATEMENT view contains the
information about HLL statements in the module, and
this information is not generally displayed to the user
but is used by the debugger. In the case of this debugger,
all views are displayed exactly as the text for the views
is retrieved.
A loop through all the views returned by QteRetrieveModuleViews is done, registering the view using
the QteRegisterDebugView API. The program name, program type, module name, and view number of
the module are passed as inputs to the API. The API returns the library of the program (in case *LIBL) is
passed in as the program library), the timestamp of the view (in case the program has been recompiled
between the time the view information was retrieved and the time the view was registered), the number
of lines of text in the view, and a view ID. The view ID is a handle, and it is used in identifying the
registered view to various APIs. For example, when retrieving text for a particular view, the view must
be registered, and the view ID returned when registering the view is passed to the QteRetrieveViewText
API.
The structure that held the views retrieved by QteRetrieveModuleViews is also used by the debugger.
The view number is no longer needed, since it is just a sequence number passed to
QteRegisterDebugView. Thus, this number is overwritten and will hold the view ID, which is needed by
other debugger APIs.
ProcessCommands()
void ProcessCommands(void) {
char InputBuffer[80];
char *token;
int i;
int step=0; /* do an exit for step when 1 */
step = ProcessDbgCommand(InputBuffer);
}
}
}
This function reads an input line from the user and processes it. If it is a command recognized by the
debugger, it process it. If not, it calls ProcessDebugCommand which lets QteSubmitDebugCommand
process the command.
The first test is to make sure that the pointer to the debug data is not null. This is here for safety reasons.
If program DEBUG is compiled with the wrong activation group name or no name at all, its global
variables can be destroyed when the program exits, causing problems when the program is called again.
This test prevents debug commands from being entered if the activation group has been destroyed,
wiping out the global view data.
The function loops until the quit command is entered or until a step is done. It calls the appropriate
function based on the command entered, or displays an error message if a syntax error is detected. If the
command is unknown, it is processed by ProcessDbgCommand.
The switch command is processed directly by the function. It changes the current view to a number
provided. There is no error checking in this sample debugger.
ReadLine()
void ReadLine(char *Buffer, int length) {
int i; /* loop counter */
ProcessListCommand()
void ProcessListCommand(void) {
char *token; /* pointer to next token of input*/
This routine process the list command. There are two versions of the list command, list views and list
text. The appropriate function is called depending on the type of list command entered, or a syntax error
message is issued.
PrintViews
void PrintViews(void) {
int k;
/* loop through views printing view#, module, and view desc. text */
for (k=0; k< pgm_dbg_dta->NumberElements; k++) {
printf("%d) %.10s:%.50s",
k,
pgm_dbg_dta->Element[k].ModuleName,
pgm_dbg_dta->Element[k].ViewDescription);
if (current_view == k) /* indicate if view is current */
printf("<---Current\n");
else
printf("\n");
}
}
This routine lists all of the views available in the program being debugged. The information about the
views is stored in the buffer that was passed to QteRetrieveModuleViews.
The module name and view descriptive text is printed for each view. If the current view being printed is
also the current view, this is noted by printing this fact next to the view information.
A view number is printed next to each view. This is not the view ID returned by the
QteRegisterDebugView. It is a number allowing the user to change the current view to one of the views
in the list.
PrintText()
void PrintText(void) {
This function retrieves the text associated with the current view and prints it. This text is the source of
the program and is the heart of a source debugger screen.
The text of the current view is retrieved, so the view ID of that view is determined. It is this view that is
passed to QteRetrieveViewText.
In the sample debugger, a large buffer is allocated, and as much text as will fit in this buffer is retrieved.
The QteRetrieveViewText API returns the text and the number of lines that fit in the buffer.
Once the text is retrieved, it is printed out along with the line number. The line number is needed when
setting breakpoints based on the view.
ProcessDbgCommand()
int ProcessDbgCommand(char InputBuffer[80]) {
_TE_ERROR_CODE_T errorCode = {64}; /* fill in bytes provided */
char OutputBuffer[4096];
struct _TE_RESULT_BUFFER_T *Results;
long InputBufferLength = 80;
long OutputBufferLength = sizeof(OutputBuffer);
long view_ID;
_TE_COMPILER_ID_T *CompilerID;
int i;
int return_value = 0;
view_ID = pgm_dbg_dta->Element[current_view].ViewNumber;
CompilerID = &pgm_dbg_dta->Element[current_view].CompilerID;
if (errorCode.BytesAvailable != 0) {
printf("Error = %.7s\n",errorCode.ExceptionID);
return return_value;
}
This function is called to process all commands not known by the debugger. It calls the
QteSubmitDebugCommand API which is passed a view ID, compiler ID, and a command. The API needs
the compiler ID because each programming language used in compiling a particular module has different
debug commands or command syntax, and the API needs to know which language was used when
compiling the module.
The API returns back a series of result records which indicate what was done by the API. Most of this
function reads the results of the records returned and prints an appropriate response message.
Some results records indicate that a particular function has been performed. These include:
Still other results records contain string data. In this case, the record contains an offset into the string
space returned by the API as well as a string length.
There are other kinds of results records than processed by the sample debugger. The
QteSubmitDebugCommand API discusses in detail each result record and the data it contains.
The API description also discusses the syntax of the debug command that must be passed to it. The
commands and their syntax will not be discussed in depth here, but a few example commands will be
shown:
v break 5 when x == 3
This is a conditional breakpoint. The debugger will stop the program indicated by the view ID passed
to the API when it reaches line 5 of the view and when the expression "x == 3" is true. The "when"
part of the break statement is optional, in which case an unconditional breakpoint is set.
v step 1 into
The step command instructs the debug support to stop the a program when it has executed one or
more statements. In this example, the program is stopped after 1 statement has been executed. The
"into" means that statements in procedures are counted when stepping. "over" means that statements in
called procedures are skipped over and not counted. The default step type is "into", and the default
step count is 1.
v qual 13
The qual command is necessary when there are blocks of code with the same variable name. In this
case, the user indicates where the variable is searched for in the program. Normally, this command is
not used.
v clear 8
A conditional or unconditional breakpoint is removed from line 8 of the view indicated by the view ID
parameter.
HandleStop()
void HandleStop(_TE_OBJLIB_T *ProgramLib,
_TE_NAME_T ProgramType,
_TE_NAME_T Module,
char reason[10],
long Statements[],
int StatementsCount,
char *message) {
int i;
_TE_MAPP0100_T Map_Return_Structure;
long Column = 1;
long MapLength = sizeof(Map_Return_Structure);
This function is called when program DEBUG is called as a Program Stop Handler. It is passed the name,
library, and type of the program stopped, the line number in the statement view where it has stopped, a
The first thing the function does is determine if the current view is set to the module where the program
stopped. If not, then it needs to be reset to the first view in the module where the program has stopped.
Next, the statement view ID for the module stopped needs to be determined. This is necessary because
the stopped position is given in terms of the statement view, and this position needs to be converted to a
position in the current view.
The QteMapViewPosition API maps a position in the statement view to a statement in another view in
that module. This allows the debugger to determine the source line of the current view where the
program has stopped, even though the program is only told the line number in the statement view.
Finally, the character flags are checked to see why the program was stopped. Note that the program can
be stopped for more than one reason, so every flag is checked, and if it is on, a message for that flag is
printed.
Finally, the ProcessCommands function is called, allowing the user to enter debug commands.
Other APIs
This section discusses other APIs not covered in this example debugger. Some or all of these APIs could
be used in a real ILE source-level debugger. All of them are used in the debugger shipped with i5/OS.
QteRetrieveDebugAttributes
This API allows a debugger to retrieve information about the debug session. This includes the value of
the Update Production Files, set on the Start Debug command, as well as an indication of whether the job
where the debugger is running is servicing and debugging another job.
QteSetDebugAttributes
The only attribute that can be set is the value of the Update Production Files. This can also be
accomplished using the Change Debug (CHGDBG) CL command.
QteRemoveDebugView
Views that are registered can be removed from debug. This is desirable if a program is to be removed
from debug so that it can be recompiled and added again. It is not necessary to remove views from
debug when ending the debug session, as QteEndSourceDebug will do this automatically.
QteRetrieveStoppedPosition
This indicates if a program is currently stopped and on the stack, and whether this stopped position is
anywhere in a given view. This is useful whenever a source debugger is about to put up a source screen.
If the program is stopped somewhere within the source to be displayed, this can be indicated to the user.
This is necessary because a program can be stopped by other means than the debugger. For example, an
ILE program could have put up a command entry screen, and the debugger could be displayed from
there. In this case, it is nice to indicate to the user that the program being debugged is stopped.
QteAddBreakpoint
This API performs the same function as the break n debug language command.
QteRemoveBreakpoint
This API performs the same function as the clear n debug language command.
QteRemoveAllBreakpoints
This API performs the same function as the clear pgm debug language command.
QteStep
This API performs the same function as the step n into and step n over debug language commands.
Here is the entire program listing for the ILE C program that contains the example debugger:
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: The entire program listing for the program */
/* containing the example debugger discussed in the */
/* preceding sections. */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: QteRetrieveViewText, QteSubmitDebugCommand, */
/* QteEndSourceDebug, QteRetrieveModuleViews, */
/* QteRegisterDebugView, QteStartSourceDebug, */
/* QteMapViewPosition */
/* */
/*******************************************************************/
/*******************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <qtedbgs.h>
/* PrintText: This function will print the text for the current view */
void PrintText(void) {
/* loop through views printing view#, module, and view desc. text */
for (k=0; k< pgm_dbg_dta->NumberElements; k++) {
printf("%d) %.10s:%.50s",
k,
pgm_dbg_dta->Element[k].ModuleName,
pgm_dbg_dta->Element[k].ViewDescription);
if (current_view == k) /* indicate if view is current */
printf("<---Current\n");
else
printf("\n");
}
}
view_ID = pgm_dbg_dta->Element[current_view].ViewNumber;
CompilerID = &pgm_dbg_dta->Element[current_view].CompilerID;
if (errorCode.BytesAvailable != 0) {
printf("Error = %.7s\n",errorCode.ExceptionID);
return return_value;
}
step = ProcessDbgCommand(InputBuffer);
}
}
}
See the QlgSpawn--Spawn Process (using NLS-enabled path name) API for an example of supplying
parameters in any CCSID.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
Parent program
This program acts as a parent to a child program (see “Child program” on page 460).
Use the Create C Module (CRTCMOD) and the Create Program (CRTPGM) commands to create this
program (see “Creating the parent and child programs” on page 462).
Call this program with no parameters (see “Calling the parent program” on page 463).
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: This program acts as a parent to a child program. */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: putenv(), spawn(), wait(), waitpid() */
/* */
/*******************************************************************/
/*******************************************************************/
#include <errno.h>
#include <fcntl.h>
#include <spawn.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAP_NUM 5
#define ARGV_NUM 6
#define ENVP_NUM 2
#define CHILD_PGM "QGPL/CHILD"
memset(&spw_inherit,0x00,sizeof(spw_inherit));
/* Set the spawn() child arguments that are common for each */
/* child. */
/* NOTE: The child will always get argv[0] in the */
/* LIBRARY/PROGRAM notation, but the */
/* spawn() argv[0] (spw_argv[0] */
/* in this case) must be non-NULL in order to allow additional */
/* arguments. For this example, the character pointer spw_path */
/* was chosen. */
/* NOTE: The parent pid and the parent process group are passed */
/* to the child for demonstration purposes only. */
spw_argv[0] = spw_path;
spw_argv[1] = pid_str;
spw_argv[2] = pgrp_str;
spw_argv[4] = fd_str;
spw_argv[5] = NULL;
/* The 2nd spawn() will use mapping for the file descriptor, */
/* along with the inheritance option to create a new process */
/* group for the child. */
spw_fd_count = 1;
spw_fd_map[0] = fd;
spw_inherit.pgroup = SPAWN_NEWPGROUP;
seq_num = 2;
sprintf(seq_num_str, "%d", seq_num);
spw_argv[3] = seq_num_str;
spw_envp[0] = NULL;
spw_child_pid[1] = spawn(spw_path, spw_fd_count, spw_fd_map,
&spw_inherit, spw_argv, spw_envp);
if (spw_child_pid[1] == -1)
{
printf("FAILURE: spawn() #2 with errno = %d\n",errno);
close(fd);
unlink(f_path_name);
return -1;
}
/* The 3rd spawn() will use mapping for the file descriptors */
/* with some file descriptors designated as being closed */
/* (SPAWN_FDCLOSED) and the same parent file descriptor mapped */
/* to more than one child file descriptor. In addition, an */
/* environment variable will be set and used by the child. */
spw_fd_count = 5;
spw_fd_map[0] = SPAWN_FDCLOSED;
spw_fd_map[1] = SPAWN_FDCLOSED;
spw_fd_map[2] = fd;
spw_fd_map[3] = SPAWN_FDCLOSED;
spw_fd_map[4] = fd;
spw_inherit.pgroup = 0;
seq_num = 3;
sprintf(seq_num_str, "%d", seq_num);
spw_argv[3] = seq_num_str;
strcpy(env_return_val,"return_val=3");
rc = putenv(env_return_val);
if (rc < 0)
{
printf("FAILURE: putenv() with errno = %d\n",errno);
close(fd);
unlink(f_path_name);
return -1;
}
spw_child_pid[2] = spawn(spw_path, spw_fd_count, spw_fd_map,
&spw_inherit, spw_argv, environ);
if (spw_child_pid[2] == -1)
{
printf("FAILURE: spawn() #3 with errno = %d\n",errno);
close(fd);
unlink(f_path_name);
return -1;
}
/* Open the file for read to verify what the child wrote. */
fd_read = open(f_path_name, O_RDONLY);
if (fd_read == -1)
{
printf("FAILURE: open() for read with errno = %d\n",errno);
unlink(f_path_name);
return -1;
}
This program acts as a child to a parent program (see “Parent program” on page 453). This program
demonstrates how a child program uses characteristics expressed through the use of spawn() in the
parent program. The use of file descriptors, the creation of a new process group, arguments passed from
the parent, and environment variables are demonstrated. The child program handles three distinct calls
through the use of one of its arguments.
Use the CRTCMOD and CRTPGM commands to create this program (see “Creating the parent and child
programs” on page 462).
This program is called by the spawn() function from the parent program. The program name must be
CHILD and must be created into library QGPL, as indicated by the parent program. This program is not
to be called directly.
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: This program acts as a child to a parent program. */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: getenv(), getpid(), getppid(), getpgrp() */
/* */
/*******************************************************************/
/*******************************************************************/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
The following examples show how to create the example programs (“Parent program” on page 453 and
“Child program” on page 460). These examples assume that the source for the parent program is member
PARENT in the file QGPL/QCSRC and the source for the child program is member CHILD in the file
QGPL/QCSRC.
Although an X.25 example is shown, many of the same concepts can be applied to applications running
over token-ring and Ethernet local area networks (LANs). For the purposes of the examples, the APIs are
referred to by their call names. The includes header, hexconv, and typedefs are not in QSYSINC. These
includes are only documented in the examples.
X.25 overview
In this example X.25 network, the source application on System A is responsible for establishing a
switched virtual circuit, or connection to the target application running on System B. This is done by
using the remote network address (System B's address) of X'0000652'. When the target application on
Both the source and target applications call the Query Line Description (QOLQLIND) API to obtain
information about the local X.25 line being used. This information is stored in a local control block for
use in establishing the peer connection during X.25 connection processing. Both applications also call the
Enable Link (QOLELINK) API to enable the link for future communications. The line name,
communications handle, and remote DTE address are passed to both programs as arguments to the C
function main(). For simplicity, the user space names and data queue name on the call to the QOLELINK
API are coded directly in the applications.
Note: Keyed data queue support is used by both applications. The key length is 3 and the keys used are
source (SRC) and target (TGT) for the source and target applications, respectively.
Activating filters
Once the links have been enabled and both applications have read their respective enable-complete
entries from their data queues, the target application program calls the Set Filter (QOLSETF) API to
activate a filter. The filter activated then identifies the protocol of the local X.25 service user. This filter is
used by the user-defined communications support on System B to route incoming calls. The actual filter
type activated is X'00' (for X.25 PID) and its associated value is X'21'. For more information concerning
filters, see Set Filter (QOLSETF) API. After activating the X'21' filter, the target application waits for the
source application to request a connection.
Establishing a connection
The source application calls the Send Data (QOLSEND) API with a X'B000' operation in its output data
buffer to establish a switched virtual circuit (SVC) to the target application. Included in the first byte of
the call user data is the protocol ID of the target application, or X'21'. When the user-defined
communications support on System B sees the incoming call packet with the first byte of user data equal
to a previously activated filter, the call is routed to the process responsible for activating that filter. In this
case, the target application will receive notification of an incoming call since it previously activated filter
X'21'.
While waiting for the incoming call, the target application calls the Receive Data (QOLRECV) API to
receive a X'B201' operation with incoming call data. After doing so, the target application accepts the X.25
connection by calling the QOLSEND API with a X'B400' operation in its output data buffer.
Sending data
Once the peer connection is established between the source and target applications running on System A
and System B respectively, the file transfer takes place. The source application reads records from a local
file and calls the QOLSEND API with X'0000' operations in its output data buffer to transfer the file data
to System B. This process continues until the entire contents of the source file has been sent to System B.
Receiving data
After accepting the X.25 connection, the target application waits until its data queue receives
incoming-data entries. When the first entry is read from the queue, the QOLRECV API is called to
determine which operation was received. Barring failure, the target application should receive a X'0001'
Once the entire contents of the file has been read and sent to System B, the source application calls the
QOLSEND API with a X'B100' operation in its output data buffer to clear the X.25 connection.
Afterwards, the source application closes its local file, disables its local link by calling the QOLDLINK
API, and ends.
When the source application program sends a X'B100' operation, it causes the target application to receive
a X'B301' operation. After receiving this operation, the target application program calls the QOLSEND
API with a X'B100' operation to locally close the connection between itself and the user-defined
communications support. Afterwards, the target application closes its local file, disables its local link by
calling the QOLDLINK API, and ends.
Both the source and target application programs use the user-defined communications support timer
service to manage the reception of certain operations. This is done by setting a timer before checking the
data queue for an entry. For example, the target application sets a timer to manage the reception of file
data from the source application. If the timer expires, the user-defined communications support places a
timer-expired entry on the application's data queue. The target application then assumes when receiving
this entry that the source application ended abnormally. The target application can then take the
appropriate action to end itself.
Below are the listings for the source and target applications described in the previous paragraphs. Note
the reference numbers (for example, (1)) in the listings. Detailed explanations of each reference number
block are found in “Source application program listing references” on page 476 and “Target application
program listing references” on page 488.
The target application compiler listing can be found in Target application on System B listing.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
In this example, the source application is the initiator of all meaningful work. In summary, the source
program listed on the following pages does the following:
v Calls the QOLQLIND API to get local X.25 line information
v Opens the local file
v Calls the QOLELINK API to establish a link for communications
v Calls the QOLSEND API with X'B000' operation to establish a peer (SVC) connection
v Sends the local file to the target system using X'0000' operations
v Calls the QOLSEND API with X'B100' operation to clear the peer (SVC) connection
v Calls the QOLDLINK API to disable the link
To create the program using ILE C, use the Create Bound C (CRTBNDC) command.
Program name . . . . . . . . . :
(SOURCE)
Library name . . . . .: . . . UDCS_APPLS
Source file . . . . . . .: . . . QCSRC
Library name . . . . .: . . . UDCS_APPLS
Source member name . . .: . . . SOURCE
Text Description . . . .: . . . Source Application Example
Output . . . . . . . . .: . . . *NONE
Compiler options . . . .: . . . *SOURCE *NOXREF *SHOWUSR
: *SHOWSYS *NOSHOWSKP *NOEXPMAC
: *NOAGR *NOPPONLY *NODEBUG
: *GEN *NOSECLVL *PRINT *LOGMSG
: *USRINCPATH
Checkout Options . . . . . . . : *NOAGR
Optimization . . . . . . . . . : *NONE
Inline Options:
Inliner . . . . . . . . . . . : *OFF
Mode . . . . . . . . . . . . : *NOAUTO
Threshold . . . . . . . . . . : 250
Limit . . . . . . . . . . . . : 2000
Debugging View . . . . . . . . : *NONE
Define Names . . . . . . . . . : *NONE
Language Level . . . . . . . . : *SOURCE
Source Margins:
Left margin . . . . . . . . . : 1
Right margin . . . . . . . . : 32754
Sequence columns:
Left column . . . . . . . . . : *NONE
Right column . . . . . . . . :
Message flagging level . . . . : 0
Compiler messages:
Message limit . . . . . . . . : *NOMAX
Message limit severity . . . : 30
Replace Program Object . . . . : *YES
User Profile . . . . . . . . . : *USER
Authority . . . . . . . . . . . : *LIBCRTAUT
Target Release . . . . . . . . : *CURRENT
System includes . . . . . . . . : *YES
/*******************************************************************/
/** Program Name: Source Application Program Example **/
/** **/
/** **/
/** Function: **/
/** This is the source application program example that uses **/
/** X.25 services provided by the user-defined communications **/
/** support to transfer a simple file to the target application **/
/** program running on system B. This program performs the **/
/** following: **/
/** 01. Open the source file name INFILE. **/
/** 02. Call QOLQLIND API to obtain local line information. **/
/** 03. Enable a link. **/
/** 04. Send a ’B000’X operation (call request). **/
/** 05. Receive a ’B001’X operation (call confirmation). **/
/** 06. Read record(s) from the file opened in step 1). and **/
/** send ’0001’X operation(s) to transfer the file to **/
/** the target application program. **/
/** 07. Send a ’B100’X operation (clear call request). **/
/** 08. Receive a ’B101’X operation. **/
/** 09. Disable the link enabled in step 3). **/
/** **/
/** A data queue will be actively used to manage the operation **/
/** of this program. Data queue support will be used to monitor **/
void senddata(sendparms *a, char *b, desc *c, char *d, char *e, int f);
void sndformat1(sendparms *a,char *b, char *c, char *d, qlindparms *f);
void sndformat2 (sendparms *a, char *b, char *c);
void setfilters (hdrparms *a);
void byte (char *a, int b, char *c, int d);
void printespec (espec *a);
void settimer(unsigned short *a,char *b,qentry *c,usrspace *d,char *e);
void dequeue (int a, char *b, qentry *c, usrspace *d);
void x25lind (qlindparms *a, char *b);
int getline (char *a, int b, FILE *c);
void disablelink (disableparms *a, char *b, usrspace *c);
void handler (disableparms a, usrspace *b);
(3)
signal(SIGALL,SIG_DFL);
/** Clear the command line Parameters **/
strncpy(enable.linename, " ", 10); /* Clear linename */
strncpy(commhandle, " ", 10); /* Clear Commhandle*/
strncpy(rmtdte, " ", 17); /* Clear Remote DTE*/
/** Receive command line Parameters **/
strncpy(enable.linename, argv[1], strlen(argv[1]));
strncpy(commhandle, argv[2], strlen(argv[2]));
strncpy(rmtdte, argv[3], strlen(argv[3]));
rmtdte[strlen(argv[3])] = ’\0’;
/** Initialize the user spaces **/
strncpy(inbuff.library, "UDCS_APPLS", 10); /* Input Buffer */
strncpy(inbuff.name, "SOURCEIBUF", 10);
strncpy(indesc.library, "UDCS_APPLS", 10); /* Input B Desc */
strncpy(indesc.name, "SOURCEBDSC", 10);
strncpy(outbuff.library, "UDCS_APPLS", 10); /* Output Buffer*/
strncpy(outbuff.name, "SOURCEOBUF", 10);
strncpy(outdesc.library, "UDCS_APPLS", 10); /* Output B Desc */
strncpy(outdesc.name, "SOURCEODSC", 10);
strncpy(qname.library, "UDCS_APPLS", 10); /* Data queue */
strncpy(qname.name, "X25DTAQ ", 10);
/***** retrieve the line description information ******/
x25lind (&qlind, enable.linename);
if ((qlind.retcode != 0) || (qlind.reason != 0))
{
printf("Query line description failed.\n");
(4)
/**************************************************/
/************ Enable the line *******************/
/**************************************************/
QOLELINK (&(enable.retcode), &(enable.reason), &(enable.tdusize),\
&(enable.numunits), &(enable.maxdtalan), &(enable.maxdtax25),\
(char *)&inbuff, (char *)&indesc, (char *)&outbuff,\
(char *)&outdesc, &(enable.keylength), enable.keyvalue,\
(char *)&qname, enable.linename, commhandle);
if ((enable.retcode != 0) || (enable.reason != 0))
{
printf("Line %.10s with Commhandle %.10s was NOT ENABLED.\n",\
enable.linename, commhandle);
printf("Return code = %d\n", enable.retcode);
printf("Reason code = %d\n\n", enable.reason);
return;
}
(5)
(6)
/******************************************************************/
/************** Set up a Call Request Packet *******************/
/******************************************************************/
/**** Get pointers to the user spaces. ******/
QUSPTRUS(&outbuff, &buffer);
QUSPTRUS(&outdesc, &descriptor);
send.ucep = 26; /* set the UCEP number */
send.operation = 0xB000; /* send a call request */
send.numdtaelmnts = 1; /* send one data unit */
/**----------- Send the packet ---------**/
sndformat1 (&send, buffer, rmtdte, commhandle, &qlind);
if ((send.retcode != 0) || (send.reason != 0))
{
printf("Call request packet not sent\n");
printf("Return code = %d\n", send.retcode);
printf("Reason code = %d\n", send.reason);
printf("new pcep %d\n", send.newpcep);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
(7)
/*****************************************************************/
/*********** Receive the Call CONFIRMATION packet ********/
(8)
/*****************************************************************/
/*************** Send the file to the target application *******/
/*****************************************************************/
send.pcep = send.newpcep; /* set the PCEP number */
/*************** Send the Mbr LGRF in file DOC ****************/
linesiz = getline(line, 92, fptr); /* Get first record **/
while (linesiz != 0)
{
/*************** Send a Packet of Data ***************/
/**** Get pointers to the user spaces. ******/
QUSPTRUS(&outbuff, &buffer);
QUSPTRUS(&outdesc, &descriptor);
send.operation = 0x0000;
send.numdtaelmnts = 1;
/**----- Send the packet -------------**/
senddata (&send, buffer, descriptor, commhandle, line, linesiz);
if ((send.retcode != 0) || (send.reason != 0))
{
printf("Data NOT sent for commhandle %.9s\n", commhandle);
printf("Return code = %d\n", send.retcode);
printf("Reason code = %d\n", send.reason);
printf("new pcep %d\n", send.newpcep);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
i = i + 1;
printf("Data %d Sent for commhandle %.9s.\n\n", i, commhandle);
linesiz = getline(line, 92, fptr); /** Get next record **/
} /*** End While loop ***/
/***************************************************************/
/*************** Set up a Clear Request Packet **************/
/***************************************************************/
(9)
(10)
/***************************************************************/
/*********** Receive the Clear Request Response packet *****/
/***************************************************************/
/*-------- Set a timer to receive a message ---------**/
expctid = 0xF0F3;
settimer(&expctid, "Rv Clr Rqt", &dataq, &qname, commhandle);
if (expctid != 0xF0F3)
{
disablelink (&disable, commhandle, &qname);
return;
}
/*********** Call QOLRECV to Receive the Clear Response *****/
/**** Get pointers to the user spaces. ******/
QUSPTRUS (&inbuff, &buffer);
QUSPTRUS (&indesc, &descriptor);
QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
&(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
&(recv.dataavail), &(recv.errorspecific), commhandle);
if ((recv.retcode != 0) || (recv.reason != 0))
{
printf("Recv clear response failed\n");
printf("return code %d\n", recv.retcode);
printf("reason code %d\n", recv.reason);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
/* Interpret the Received Operation */
if (recv.operation != 0xB101)
{
printf("Recvd opr %x instead of opr B101\n", recv.operation);
disablelink (&disable, commhandle, &qname);
return;
}
/***********************************************/
/*** Disable the link and end the program ****/
/***********************************************/
disablelink (&disable, commhandle, &qname);
printf("****** SOURCE completed successfully ******\n\n");
} /* End Main */
/*****************************************************************/
/************** Start Subroutine Section **********************/
/*****************************************************************/
/*************************************************************/
/*************** Send a Packet of Data ******************/
(11)
(12)
/********************************************/
/******** Routine to disable ***********/
void disablelink (disableparms *disable,
char *commhandle,
usrspace *qname)
{
unsigned short expctid;
qentry dataq;
disable->vary = 1; /* Hard coded to be varied off */
QOLDLINK (&(disable->retcode), &(disable->reason),\
commhandle, &(disable->vary));
if ((disable->retcode != 0) && (disable->reason != 00))
{
printf ("Link %.10s did not disabled.\n", commhandle);
printf ("return code = %d\n", disable->retcode);
printf ("reason code = %d\n\n", disable->reason);
}
/**------- Set a timer to receive disable complete msg --------**/
expctid = 0xF0F1;
settimer(&expctid, "Disable", &dataq, qname, commhandle);
if (expctid != 0xF0F1)
{
printf("Disable link did not complete successfully");
return;
}
printf ("%.10s link disabled \n", commhandle);
/** close the files **/
fclose(fptr);
fclose(screen);
} /* End disablelink Subroutine */
/**************************************************************/
/** Routine to convert string to Hexadecimal format ******/
void byte (char *dest,
int dlength,
char *source,
int slength)
{
register int counter;
char holder[2];
for (counter=0;counter<dlength;counter++)
dest[counter]=0;
for (counter=slength-1;counter>=0;counter--)
if (isxdigit(source[counter]))
{
holder[0]=source[counter];
holder[1]=’\0’;
if (counter % 2 == 0)
dest[counter/2] += (char) hextoint(holder)*16;
else dest[counter/2] += (char) hextoint(holder);
}
} /* End byte Subroutine */
/**************************************************************/
/** Routine to display the ErrorSpecific output ******/
void printespec(espec *errorspecific)
{
/************************************************************/
/** x25lind: Retrieve X.25 line description information **/
void x25lind (qlindparms *qlind, char *linename)
{
register int counter;
for(counter=0;counter<256;counter++)
qlind->userbuffer[counter]=0;
qlind->format = 0x01;
QOLQLIND (&(qlind->retcode), &(qlind->reason), &(qlind->nbytes),\
qlind->userbuffer, linename, &(qlind->format));
} /* End x25lind Subroutine */
/*******************************************************/
/** Getline: Read a record into line and return length **/
int getline (char *line, int max, FILE *fptr)
{
if (fgets(line, max, fptr) == NULL)
return 0;
else
return strlen(line);
} /* End getline Subroutine */
/***************************************************/
The following reference numbers and explanations correspond to the reference numbers in the source
application's program listing.
The target application waits for the source application to initiate the file transfer. The following list
summarizes the actions of the target application:
v Calls the QOLQLIND API to get local X.25 line information
v Opens the local file
v Calls the QOLELINK API to establish a link for communications
v Calls the QOLSETF API to activate an X.25 protocol ID filter
v Calls the QOLRECV API to receive the X'B201' operation (incoming call)
v Calls the QOLSEND API with a X'B400' operation to accept the SVC connection
v Receives the file from the target system using X'0001' operations
v Calls the QOLRECV API to receive the X'B301' (connection failure notification)
v Call the QOLSEND API with 'B100' operation to locally close the SVC connection
v Calls the QOLDLINK API to disable the link
v Calls the QOLTIMER API to manage the reception of data queue entries
To create the program using ILE C, use the Create Bound C (CRTBNDC) command.
Explanations of the reference numbers in the listing can be found in “Target application program listing
references” on page 488.
Program name . . . . . . . . . :
(TARGET)
Library name . . . . . .: . .
UDCS_APPLS
Source file . . . . . . . .: . .
QCSRC
Library name . . . . . .: . .
UDCS_APPLS
Source member name . . . .: . .
TARGET
Text Description . . . . . . .
: Target Application Example
Output . . . . . . . . . .: . .
*NONE
Compiler options . . . . .: . .
*SOURCE *NOXREF *NOSHOWUSR
: *NOSHOWSYS *NOSHOWSKP *NOEXPMAC
: *NOAGR *NOPPONLY *NODEBUG
: *GEN *NOSECLVL *PRINT *LOGMSG
: *USRINCPATH
Checkout Options . . . . . . . : *NOAGR
Optimization . . . . . . . . . : *NONE
Inline Options:
Inliner . . . . . . . . . . . : *OFF
Mode . . . . . . . . . . . . : *NOAUTO
Threshold . . . . . . . . . . : 250
Limit . . . . . . . . . . . . : 2000
Debugging View . . . . . . . . : *NONE
Define Names . . . . . . . . . : *NONE
Language Level . . . . . . . . : *SOURCE
Source Margins:
Left margin . . . . . . . . . : 1
Right margin . . . . . . . . : 32754
Sequence columns:
Left column . . . . . . . . . : *NONE
Right column . . . . . . . . :
Message flagging level . . . . : 0
Compiler messages:
Message limit . . . . . . . . : *NOMAX
Message limit severity . . . : 30
Replace Program Object . . . . : *YES
User Profile . . . . . . . . . : *USER
Authority . . . . . . . . . . . : *LIBCRTAUT
/*******************************************************************/
/** **/
/** Program Name: Target Application Program Example **/
/** **/
/** **/
/** Function: **/
/** This is the target application program example that uses **/
/** X.25 services provided by the user-defined communications **/
/** support to receive a simple file from the source application **/
/** program running on System A. This program performs the **/
/** following: **/
/** 01. Open the target file named OUTFILE. **/
/** 02. Call QOLQLIND to obtain local line information. **/
/** 03. Enable a link. **/
/** 04. Set a Filter on the enabled link. **/
/** 05. Receive a ’B101’X operation (incoming call). **/
/** 06. Send a ’B400’X operation (accept call). **/
/** 07. Receive ’0001’X operation(s) (incoming data) from **/
/** the source application program and write it to the **/
/** file opened in step 1). **/
/** 08. Receive a ’B301’X operation (clear call indication). **/
/** 09. Send a ’B100’X operation to respond locally to the **/
/** clearing of the connection. **/
/** 10. Disable the link enabled in step 3). **/
/** **/
/** A data queue will be actively used to manage the operation **/
/** of this program. Data queue support will be used to monitor **/
/** for the completion of the enable and disable routines, as **/
/** well as timer expirations and incoming data. Timers are **/
/** used to ensure that there will never be an infinite wait on **/
/** the data queue. If a timer expires, the link enabled will **/
/** be disabled and the program will stop. **/
/** **/
/** **/
/** Inputs: **/
/** The program expects the following input parameters: **/
/** Line Name: This is the name of the line description **/
/** that will be used to call the QOLELINK API. **/
/** The line must be an X.25 line with at least **/
/** one SVC of type *SVCBOTH or *SVCIN. **/
/** **/
/** CommHandle: This is the logical name that will be used **/
/** to identify the link enabled. **/
/** **/
/** Remote DTE Address: The is the Local Network Address **/
/** of system A. **/
/** **/
/** **/
/** Outputs: **/
/** Current status of the file transfer will be provided when **/
/** running this program. If an error should occur, then a **/
/** message will be displayed indicating where the error occurred **/
/** and the program will end. If the program completes **/
/** successfully, a "successful completion" message will be **/
/** posted. **/
/** **/
/** Language: ILE C **/
/** **/
/** APIs used: QOLELINK, QUSPTRUS, QOLRECV, QOLSEND, QOLDLINK, **/
/** QRCVDTAQ, QOLTIMER **/
/** **/
/*******************************************************************/
/*******************************************************************/
/*******************************************************************/
signal(SIGALL,SIG_DFL);
/******************************************************************/
/** Send a response to accept the call and establish a connection */
/******************************************************************/
/**** Get pointers to the user spaces. ******/
QUSPTRUS(&outbuff, &buffer);
QUSPTRUS(&outdesc, &descriptor);
/******* Set up Send Packet *********/
send.ucep = 62; /* set UCEP to be 62 */
send.pcep = recv.pcep; /* get the PCEP number */
send.operation = 0xB400; /* send a call request response*/
send.numdtaelmnts = 1; /* send one data unit */
/**----- Send the packet ----------------**/
sndformat1 (&send, buffer, rmtdte, commhandle, &qlind);
if ((send.retcode != 0) || (send.reason != 0))
{
printf("Data NOT sent for commhandle %.9s\n", commhandle);
printf("Return code = %d\n", send.retcode);
(7)
/**********************************************************/
/**** Receive Incoming Data *************************/
/**********************************************************/
/**------- Set a timer to receive data --------**/
expctid = 0xF0F3;
settimer(&expctid, "Inc Data ", &dataq, &qname, commhandle);
if (expctid != 0xF0F3)
{
disablelink (&disable, commhandle, &qname);
return;
}
/*******--- Receive the Incoming Data ----******/
/** Get pointer to user space **/
QUSPTRUS (&inbuff, &buffer);
QUSPTRUS (&indesc, &descriptor);
/** Receive the data **/
QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
&(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
&(recv.dataavail), &(recv.errorspecific), commhandle);
if ((recv.retcode != 0) || (recv.reason != 0))
{
printf("Recv op for first data unit failed\n");
printf("return code %d\n", recv.retcode);
printf("reason code %d\n", recv.reason);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
(8)
/**************************************************************/
/******* Start a loop to read in all the incoming data ***/
/**************************************************************/
i = 1;
while (recv.operation == 0x0001)
{
printf("%d Data Recvd {%.4x}.\n\n", i++, recv.operation);
/** Store all the data units in the file **/
for (j = 1; j <= recv.numdtaunits; j++) {
putdata (buffer + (j - 1)*enable.tdusize,\
descriptor->length, fptr);
descriptor = (desc *)((char *)descriptor + sizeof(desc));
} /* for */
/**------- Set a timer to wait for more data -------**/
if (recv.dataavail == 0)
{
/** Set timer **/
expctid = 0xF0F3;
settimer(&expctid, "Wt Inc Dta", &dataq, &qname, commhandle);
if (expctid != 0xF0F3)
{
disablelink (&disable, commhandle, &qname);
return;
}
}
/** Get pointer to user space **/
(9)
/****************************************************/
/*********** Receive the Clear indication ***********/
/****************************************************/
if ((recv.retcode != 83) || (recv.reason != 4002))
{
printf("Recv opr for clear request failed\n");
printf("return code %d\n", recv.retcode);
printf("reason code %d\n", recv.reason);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
/* Interpret the Received Operation */
if (recv.operation != 0xB301)
{
printf("Recvd operation %x instead of B301", recv.operation);
disablelink (&disable, commhandle, &qname);
return; /**** end the program ***/
}
(10)
/****************************************************************/
/*********** Send local response to clear indication ***********/
/****************************************************************/
/**** Get pointers to the user spaces. ******/
QUSPTRUS(&outbuff, &buffer);
QUSPTRUS(&outdesc, &descriptor);
/******* Set up the packet ****************/
send.operation = 0xB100; /* send a clear request packet */
send.numdtaelmnts = 1; /* send one data unit */
/**----- Send the packet ----------------**/
sndformat2 (&send, buffer, commhandle);
if ((send.retcode != 0) && (send.reason != 0))
{
printf("Response not sent for clear connection\n");
printf("Return code = %d\n", send.retcode);
printf("Reason code = %d\n", send.reason);
printf("new pcep %d\n\n", send.newpcep);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
/******************************************************/
/*********** Receive the Clear Confirmation **********/
/******************************************************/
/**------- Set a timer to receive data --------**/
expctid = 0xF0F3;
settimer(&expctid, "Clr Cnfrm", &dataq, &qname, commhandle);
if (expctid != 0xF0F3)
{
disablelink (&disable, commhandle, &qname);
return;
}
if ((recv.retcode != 00) || (recv.reason != 0000))
{
printf("Recv failed for clear confirmation\n");
(11)
/****************************************/
/** disable the link and end program **/
/****************************************/
disablelink (&disable, commhandle, &qname);
printf("TARGET application completed OK!\n\n");
} /* End Main */
/*****************************************************************/
/************** Start Subroutine Section **********************/
/*****************************************************************/
/*****************************************************************/
/************ Routine to fill X.25 Format I ***********/
void sndformat1 (sendparms *send,
char *buffer,
char *rmtdte,
char *commhandle,
qlindparms *qlind)
{
format1 *output = (format1 *) buffer;
register int counter;
register querydata *qd;
qd = (querydata *)&(qlind->userbuffer);
output->type = 0; /* not used */
output->logchanid = 0x0;
output->sendpacksize = qd->x25data.defsend;
output->sendwindsize = qd->x25data.windowsend;
output->recvpacksize = qd->x25data.defrecv;
output->recvwindsize = qd->x25data.windowrecv;
output->dtelength = strlen(rmtdte); /* not used */
byte(output->dte, 16, rmtdte, strlen(rmtdte)); /* not used */
output->dbit = 0;
output->cug = 0; /* not used */
output->cugid = 0; /* not used */
output->reverse = 0; /* not used */
output->fast = 0; /* not used */
output->faclength = 0;
byte(output->facilities, 109, "", 0);
output->calllength = 0;
byte(output->callud, 128, "00", 2);
output->misc[0] = 0;
output->misc[1] = 0;
output->misc[2] = 0;
output->misc[3] = 0;
output->maxasmsize = 16383;
output->autoflow = 32;
QOLSEND (&(send->retcode), &(send->reason),
&(send->errorspecific),\
&(send->newpcep), &(send->ucep), &(send->pcep),\
commhandle, &(send->operation), &(send->numdtaelmnts));
} /* End sndformat1 Subroutine */
/*****************************************************************/
/************ Routine to fill X.25 Format II ***********/
The following reference numbers and explanations correspond to the reference numbers in the target
application's program listing.
The following three includes are used by both the preceding source and target programs. They are not in
an i5/OS library.
/*******************************************************************/
/*******************************************************************/
/* Include Name: Header */
/* */
/* */
/* Function: */
/* Type define and declare the structures used to interface */
/* to the user-defined communications APIs. These structures */
/* are used by both the source and target application. */
/* */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: QOLDLINK, QOLELINK, QOLSEND, QOLRECV, QOLSETF, */
/* QOLTIMER, QUSPTRUS, QRCVDTAQ, QCLRDTAQ, QOLQLIND */
/* */
/*******************************************************************/
/*******************************************************************/
FILE *screen;
FILE *rptr;
FILE *fptr;
#include <qoldlink.h>
#include <qolelink.h>
#include <qolsend.h>
#include <qolrecv.h>
#include <qolsetf.h>
#include <qoltimer.h>
#include <qusptrus.h>
#include <qrcvdtaq.h>
#include <qclrdtaq.h>
#include <qolqlind.h>
The following typedef include has new type declarations used by both source and target programs.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <xxcvt.h>
#include <string.h>
#include <ctype.h>
#include <recio.h>
{
sprintf(hex,"%.2X",decimal);
return(hex);
}
sscanf(hex,"%x",&decimal);
return(decimal);
}
The program uses the following hierarchical file system (HFS) APIs:
v Create Directory (QHFCRTDR)
v Open Stream File (QHFOPNSF)
v Read from Stream File (QHFRDSF)
v Close Stream File (QHFCLOSF)
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/* Program Name: HFSCOPY */
/* Language : ILE C */
/* Description : This program will do the following: */
/* -- Create or replace a stream file */
/* -- Create or replace a database file */
/* -- Read from the stream file and write to the */
/* database file until EOF */
/* -- Close both files when done */
/* APIs Used : QHFCRTDR, QHFOPNSF, QHFRDSF, QHFCLOSF */
/********************************************************************/
/********************************************************************/
/* Include files */
/********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <qhfopnsf.h>
#include <qhfrdsf.h>
#include <qhfclosf.h>
#include <qhfcrtdr.h>
#include <qusec.h>
/********************************************************************/
/* Structure and variable definitions */
/********************************************************************/
#define ON 1
#define OFF 0
typedef struct error_code_struct {
Qus_EC_t EC;
char exception_data[256];
}error_code_struct;
error_code_struct error_code;
char file_handle[16];
char path_name[30];
char open_info[10];
char attrib_info;
char action;
char read_buffer[80];
int path_length;
int attrib_length = 0;
int bytes_to_read;
int bytes_read = 0;
int end_file;
int cmpgood;
FILE *FP;
/********************************************************************/
/* Start of code */
/********************************************************************/
main()
{
error_code.EC.Bytes_Provided = 116;
/********************************************************************/
/* Create the directory */
/********************************************************************/
strcpy(path_name,"/QDLS/HFSFLR");
path_length = strlen(path_name);
QHFCRTDR(path_name,path_length,&attrib_info,attrib_length,&error_code);
if ( error_code.EC.Bytes_Available != 0 )
{
if (!memcmp(error_code.EC.Exception_Id,"CPF1F04",7))
printf("Directory HFSFLR already created.\n");
else
{
printErrCode(&error_code);
exit(1);
}
}
/********************************************************************/
/* Open the stream file */
/********************************************************************/
strcpy(open_info,"210 120 "); /* Create or replace the file */
strcpy(path_name,"/QDLS/HFSFLR/SAMPLE.HFS");
path_length = strlen(path_name);
printf("OPEN STREAM FILE:\n ");
QHFOPNSF(&file_handle,
path_name,
path_length,
open_info,
&attrib_info,
attrib_length,
&action,
&error_code);
if (error_code.EC.Bytes_Available != 0)
{
printErrCode(&error_code);
exit(1);
}
/********************************************************************/
/* Open a database file */
/********************************************************************/
/* Loop through reading from the stream file and writing to the */
/* database file. */
/********************************************************************/
end_file = OFF;
while (end_file == OFF)
{
/*************************************************************/
/* Read 80 bytes from the stream file */
/*************************************************************/
bytes_to_read = 80;
printf("READ STREAM FILE:\n ");
QHFRDSF(&file_handle,
read_buffer,
bytes_to_read,
&bytes_read,
&error_code);
if (error_code.EC.Bytes_Available != 0)
{
cmpgood = strncmp("CPF1F33",error_code.EC.Exception_Id,7);
if (cmpgood != 0)
printErrCode(&error_code);
end_file = ON;
}
else
{
printf("BYTES READ: %d\n ",bytes_read);
printf("READ BUFFER: %s\n",read_buffer);
if (bytes_read < bytes_to_read)
{
end_file = ON;
/*******************************************************/
/* Write remaining bytes to the database file */
/*******************************************************/
if (bytes_read > 0)
fwrite(read_buffer,1,bytes_read,FP);
}
}
}
/********************************************************************/
/* Close the stream file */
/********************************************************************/
printf("CLOSE STREAM FILE:\n ");
QHFCLOSF(&file_handle,
&error_code);
if (error_code.EC.Bytes_Available != 0)
printErrCode(&error_code);
/********************************************************************/
/* Close the database file */
/********************************************************************/
fclose(FP);
}
Note: This example does not show the apply-temporary to apply-permanent or the not-applied to
remove-permanent cases. It is assumed that all action was taken on the moves from loaded to
apply-temporary and from apply-temporary to not-applied. If additional actions are necessary,
code could be added to handle those transitions as well.
Do not assume the default values for parameters on CL commands or for library lists. Users can change
these values. Library lists can vary from one system to another.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/**** START OF SPECIFICATIONS *************************************/
/* */
/* LANGUAGE: CL */
/* */
/* APIs USED: None */
/* */
/* FUNCTION: */
/* THIS EXIT PROGRAM IS CALLED DURING ANY */
/* OF THE FOLLOWING CASES. */
/* */
/* APPLY TEMPORARILY - (user defined) */
/* */
/* APPLY PERMANENTLY - (user defined) */
/* */
/* REMOVE TEMPORARILY - (user defined) */
/* */
/* REMOVE PERMANENTLY - (user defined) */
/* */
/* Input: PARM1 - CHAR(7) - Product ID */
/* PARM2 CHAR(7) - PTF ID */
/* PARM3 - CHAR(6) - Product release */
/* PARM4 CHAR(4) - Product option ID */
/* PARM5 CHAR(4) - Product load ID */
/* PARM6 CHAR(10) - PTF library */
/* PARM7 CHAR(50) - User data */
/* PARM8 - CHAR(1) - Current PTF Status */
/* 0 - LOADED BUT NOT APPLIED */
/* 1 - APPLIED TEMPORARILY */
/* PARM9 CHAR(1) - PTF Operation */
/* 0 - REMOVE TEMPORARILY */
/* 1 - APPLY TEMPORARILY */
/* 2 - APPLY PERMANENTLY */
/* 3 - REMOVE PERMANENTLY */
/* 4 - PRE-REMOVE TEMPORARILY */
/* 5 - PRE-APPLY TEMPORARILY */
/* 6 - PRE-APPLY PERMANENTLY */
/* 7 - PRE-REMOVE PERMANENTLY */
/* */
/* */
/******* END OF SPECIFICATIONS ************************************/
PGM PARM(&PARM1 &PARM2 &PARM3 &PARM4 &PARM5 &PARM6 &PARM7 &PARM8 &PARM9)
/*------------------------------------------------------------------*/
/* */
/* DECLARE INPUT PARAMETERS */
/* */
/*------------------------------------------------------------------*/
/*----------------------------------------------------------------*/
/* THE CURRENT STATUS OF THE PTF IS "LOADED (NOT APPLIED)" */
/*----------------------------------------------------------------*/
IF (&STATUS = ’0’) THEN(DO) /* If PTF is loaded but not applied */
IF (&ACTION = ’1’) THEN(DO) /* If action is temporarily */
/* applied then */
/*?---- TEMP APPLY - ADD YOUR STATEMENTS HERE ----- */
ENDDO
IF (&ACTION = ’5’) THEN(DO) /* If action will be temporarily */
/* apply then */
/*?---- PRE-TEMP APPLY - ADD YOUR STATEMENTS HERE ----- */
ENDDO
ENDDO /* End of loading the PTF */
/*----------------------------------------------------------------*/
/* THE CURRENT STATUS OF THE PTF IS "APPLIED TEMPORARILY" */
/*----------------------------------------------------------------*/
IF (&STATUS = ’1’) THEN(DO) /* If PTF is temporarily */
/* applied then */
IF (&ACTION = ’0’) THEN(DO) /* If action is temporarily */
/* removed then */
/*?---- TEMPORARILY REMOVE - ADD YOUR STATEMENTS HERE --- */
ENDDO
IF (&ACTION = ’4’) THEN(DO) /* If action will be temporarily */
/* remove then */
/*?---- PRE-TEMP REMOVE - ADD YOUR STATEMENTS HERE ----- */
ENDDO
ENDDO /* End of remove the PTF */
/*---------------------------------------------------------------*/
/* PTF HAS BEEN SUCCESSFULLY PROCESSED */
/*---------------------------------------------------------------*/
QSYS/SNDPGMMSG MSGID(CPC1214) MSGF(*LIBL/QCPFMSG) +
MSGDTA(*NONE) TOPGMQ(*PRV (* *NONE +
*NONE)) TOMSGQ(*TOPGMQ) MSGTYPE(*COMP)
RETURN
/*----------------------------------------------------------------*/
/* HANDLE ALL ERROR CONDITIONS */
/*----------------------------------------------------------------*/
HDLERR:
/* Try to back out any changes already made */
/* If nothing to back out or back-out operation was successful */
QSYS/SNDPGMMSG MSGID(CPF3638) MSGF(*LIBL/QCPFMSG) +
MSGDTA(*NONE) TOPGMQ(*PRV (* *NONE +
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/***************************************************************/
/***************************************************************/
/* */
/* FUNCTION: User-written exit program for doing Operational */
/* Assistant backup. */
/* */
/* LANGUAGE: CL */
/* */
/* APIs USED: None */
/* */
/***************************************************************/
/***************************************************************/
While some MI instructions are discussed within the context of how to develop MI programs, the
following information makes no attempt to review the full range of MI instructions. The goal is to
provide a sufficient base of knowledge so that you can begin to use the MI language.
| Programs and procedures are the two basic units of execution on the System i® product. Programs come
| in two flavors: the original program model (OPM) and the Integrated Language Environment (ILE). MI
| programs can be created only for the OPM environment. If you require ILE support in the development
| of your applications, use ILE C, ILE COBOL, ILE CL, or ILE RPG and the built-in MI support provided
| by the languages.
In the OPM environment, a program consists of two basic components: the object definition table (ODT)
and an instruction stream. MI programs are created by using the Create Program (QPRCRTPG) API.
The ODT is the means for defining all objects (program data elements) that are referred to by the MI
instruction stream. An ODT definition of an object does not actually allocate storage for the object. It
does, however, define when and how much storage is to be allocated and also the attributes of the
storage (for example, the data type of the object). The ODT is built from the declare (DCL) statements
found in the source used to create a program. Because DCL statements are actually instructions to the
QPRCRTPG API and not MI instructions, they are defined in the QPRCRTPG API.
The instruction stream defines the set of operations to be performed by the program. The instruction
stream is built from the MI instructions found in the source used to create a program. The various MI
instructions that you can use are defined in the machine interface information.
Within the source used to create a program, there is a type of statement called a directive. Directive
statements can be found in the QPRCRTPG API and are used to do the following:
v Control the formatting of the output listing, such as the title, page ejection, and so on.
v Define entry points within the program for external and internal calls.
v Define breakpoints within the program to associate a breakpoint name to a particular MI instruction.
v Specify the end of the program source.
The program end (PEND) directive must be the last statement in the source, and it functions as a return
external (RTX) MI instruction if logically processed as part of the instruction stream.
Noncomment source statements (declares, instructions, and directives) are always ended by a semicolon
(;). Comments always begin with a slash and asterisk (/*) and end with an asterisk and slash (*/).
First the program, MI01 in this example, needs an ENTRY directive statement to designate its external
entry point. The following directive declares an unnamed (the *) external (the EXT) entry point, which is
called with a parameter list corresponding to PARM_LIST (defined later in the source code):
ENTRY * (PARM_LIST) EXT;
i5/OS programs typically pass parameters by reference as part of the high-level language (HLL) calling
convention. Because i5/OS programs pass by reference (that is, address and not value), the program also
needs to define three space pointers (how storage is referenced) to represent the three parameters being
passed. This is accomplished by the following directives:
DCL SPCPTR ARG1@ PARM;
DCL SPCPTR ARG2@ PARM;
DCL SPCPTR RESULT@ PARM;
To associate these three space pointers with the parameters being passed to the program, the following
operand list (OL) is declared:
DCL OL PARM_LIST /* Name of OL is PARM_LIST */
(ARG1@, /* The first parameter */
ARG2@, /* The second parameter */
RESULT@) /* The third parameter */
PARM EXT; /* External parameter list */
The names ARG1@, ARG2@, RESULT@, and PARM_LIST are chosen by you and are not mandated by the
system. You can choose any valid name for any object data element. For a definition of what constitutes a
valid name, see "Name" in the Program syntax topic of the Create Program (QPRCRTPG) API.
Now that the program has established addressability (the space pointers) to the three parameters, the
program needs to declare how to map (or view) the storage addressed. The following declarations define
the storage addressed (the BAS argument) by the three space pointer parameters as being packed-decimal
(PKD) scalar data objects (DD) with 15 digits, 5 digits being to the right of the decimal point:
DCL DD ARG1 PKD(15,5) BAS(ARG1@);
DCL DD ARG2 PKD(15,5) BAS(ARG2@);
DCL DD RESULT PKD(15,5) BAS(RESULT@);
The names ARG1, ARG2, and RESULT are chosen arbitrarily, but, for ease of reading, are similar to the
basing space pointers ARG1@, ARG2@, and RESULT@. The declarations of packed 15,5 are used for
With all the needed declarations now done, the instruction stream definition, where the program will
compare the numeric values (CMPNV instruction) of parameters one and two, is started:
CMPNV(B) ARG1,ARG2 / LO(ITS2);
The program then branches (the (B) extender to CMPNV) to label ITS2 if ARG1 is less than ARG2 (the
/LO branch target).
Note: MI instructions, such as CMPNV, are defined in i5/OS Machine Interface. Pervasive instruction
extenders, such as branch (B) and target keywords (LO, HI, EQ, and so on), are defined under
Instruction Statement, which is a subheading in the Program Syntax section of the Create Program
(QPRCRTPG) API topic.
If ARG1 is not low (LO) when compared to ARG2, the next MI instruction in the source stream is run.
When the next MI instruction is run, it copies the numeric value (CPYNV instruction) of ARG1 to
RESULT and, following that, branches to label RETURN:
CPYNV RESULT,ARG1;
B RETURN;
If ARG2 was greater than ARG1, the CPYNV instruction at label ITS2 is run, setting RESULT to the value
of ARG2:
ITS2: CPYNV RESULT,ARG2;
The previous return external (RTX) instruction is not needed because it is implied by the PEND directive.
The RTX instruction is included to add clarity to the program flow.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/********************************************************************/
/* */
/* Program Name: MI01 */
/* */
/* Programming Language: MI */
/* */
/* Description: Return the larger of two packed arguments. */
/* */
/* */
/* Header Files Included: None */
/* */
/* */
/********************************************************************/
ENTRY * (PARM_LIST) EXT;
DCL SPCPTR ARG1@ PARM;
DCL SPCPTR ARG2@ PARM;
DCL SPCPTR RESULT@ PARM;
Compiling an MI program
If you enter the source into a source physical file, you can now compile the source and create an MI
program. To create the program, use the Create Program (QPRCRTPG) API. Test and debug the program
if it has errors. You can also declare an exception handler for the program.
Notes:
v The QPRCRTPG API assumes that the source statements presented to it are in code page 37.
See i5/OS Machine Interface for the specific code points that are required to build MI
programs.
v By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
Assume that the source is in a member named MI01 in the source file MISRC, which is created with a
default record length (RCDLEN) of 92. The following CLCRTPG CL program can be used to create an MI
program called MI01. (An MI program to call the Create Program (QPRCRTPG) API is developed in
Creating an MI version of CLCRTPG.)
Note: All non-MI source examples are provided in CL, because CL is the one language (other than
REXX) that is standard on all systems. Other high-level languages (HLLs) could be used in place of
the CL programs (and in many cases would have been easier).
The following program reads a source file member into a program variable (&MIPGMSRC) and then does
a CALL to the QPRCRTPG API. This program has many limitations (the major limitation is a program
variable-size limit of 2000 bytes for the source), but provides for a reasonably simple MI program creation
scenario.
/********************************************************************/
/********************************************************************/
/* */
/* Program Name: CLCRTPG */
/* */
/* Programming Language: CL */
/* */
/* Description: Create an MI program using the QPRCRTPG API. */
/* */
/* */
/* Header Files Included: None */
/* */
/* */
/********************************************************************/
PGM PARM(&SRCMBR)
DCLF FILE(MISRC)
DCL VAR(&SRCMBR) TYPE(*CHAR) LEN(10)
DCL VAR(&MIPGMSRC) TYPE(*CHAR) LEN(2000)
After creating the CL program (assumed to be called CLCRTPG), the following statements create the
previous MI program MI01:
DLTOVR MISRC
OVRDBF MISRC MBR(MI01)
CALL CLCRTPG MI01
Note: If the creation of MI01 fails, you should closely compare your source to that shown in this chapter.
In general, consider the QPRCRTPG error messages that refer to "probable compiler error" as
referring to your input source and not that the QPRCRTPG API itself is in error. (QPRCRTPG
assumes its input is probably from a high-level language (HLL) compiler.)
Further, if the error message is CPF6399 (Identifier not declared), you can get an object definition table
(ODT) listing by adding *XREF to the option template parameter (variable &PGMOPTS in the CLCRTPG
program) when calling the QPRCRTPG API. Add *XREF to the existing *LIST and *REPLACE options,
and change the number of option template entries parameter (variable &NUMOPTS) to 3.
Testing MI01
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/********************************************************************/
/* */
/* Program Name: CL01 */
This test should cause a message to be sent to your user message queue with the following value:
00000000000006.00000
The MI program (MI01) that you created is a standard *PGM object. As you would expect, you can call
MI01 from other high-level languages. You can delete MI01 with the Delete Program (DLTPGM)
command, save and restore MI01 using the standard save (SAV) and restore (RST) commands, and so on.
You can also debug it using the standard debugger on the system. To debug it, you need to look at the
listing produced by the QPRCRTPG API to determine the MI instruction number. Then use that number
with the Add Breakpoint (ADDBKP) CL command. For example, when creating MI01 in the previous
exercise, the following listing was generated by QPRCRTPG:
To view the value of RESULT at label RETURN, you first determine that RETURN corresponds to MI
instruction ((1)) 0005 ((2)) and enter the following CL commands:
Breakpoints can also be set with a directive statement. Given that the MI01 program is able to be
debugged and a break directive was not used, the purpose for which you use the directive may not be
obvious. As mentioned in “Creating the MI example program” on page 505, many expected users of the
QPRCRTPG API are compilers of HLLs. The break (BRK) directive allows users of the QPRCRTPG API to
associate an HLL statement identifier with a generated MI instruction. For example, assume that MI01
was developed to be an implementation of a fictional HLL statement such as:
RESULT = MAX(ARG1, ARG2)
This assigns the MAX (defined as the largest argument) of ARG1 or ARG2 to RESULT. Also assume that
an HLL programmer had written a program called HLLEXAMPLE with the following statements:
00001 RESULT = MAX(ARG1, ARG2)
00002 EXIT
By using break (BRK) directives, the QPRCRTPG user or compiler could associate the HLL statements
with the generated MI instructions in the following way.
/********************************************************************/
/********************************************************************/
/* */
/* Program Name: MI01 */
/* */
/* Programming Language: MI */
/* */
/* Description: Demonstrate how to associate HLL statement */
/* identifiers with MI instructions using BRK */
/* directives. */
/* */
/* Header Files Included: None */
/* */
/* */
/********************************************************************/
ENTRY * (PARM_LIST) EXT;
DCL SPCPTR ARG1@ PARM;
DCL SPCPTR ARG2@ PARM;
DCL SPCPTR RESULT@ PARM;
DCL OL PARM_LIST
(ARG1@,
ARG2@,
RESULT@)
PARM EXT;
DCL DD ARG1 PKD(15,5) BAS(ARG1@);
This allows the HLL programmer to use the following to debug the HLL program by using the statement
identifiers of the HLL:
STRDBG PGM(HLLEXAMPLE)
ADDBKP STMT(00002) PGMVAR((RESULT ()))
The following display shows that the HLL statement 00002 has been equated with MI instruction 0005
due to the use of BRK directives:
+--------------------------------------------------------------------------------+
| |
| |
| |
| Display Breakpoint |
| |
| Statement/Instruction . . . . . . . . . : 00002 /0005 |
| Program . . . . . . . . . . . . . . . . : HLLEXAMPLE |
| Recursion level . . . . . . . . . . . . : 1 |
| Start position . . . . . . . . . . . . : 1 |
| Format . . . . . . . . . . . . . . . . : *CHAR |
| Length . . . . . . . . . . . . . . . . : *DCL |
| |
| Variable . . . . . . . . . . . . . . . : RESULT |
| Type . . . . . . . . . . . . . . . . : PACKED |
| Length . . . . . . . . . . . . . . . : 15 5 |
| ’ 6.00000’ |
| |
+--------------------------------------------------------------------------------+
As coded, the MI01 program works fine when it is passed packed decimal parameters. But when the
MI01 program is passed other data types, such as in CALL CL01 (abc 6), exceptions occur. To handle
these exceptions, additional statements could be added to MI01 so that:
v A 1-character return code parameter returns a status where 0 indicates no error and 1 indicates an error
occurred.
v An exception description is defined to handle MCH1202 decimal data errors.
Note: The EXCID is the hexadecimal representation of the message identifier string 1202 where 12 =
X'0C' and 02 = X'02'. While most MCH errors follow this relationship of message ID string to
hexadecimal EXCID, always see i5/OS Machine Interface to determine what specific exception
IDs can be signaled by a given MI statement.
5. Because label M1202 is being used to indicate an error, set the return code to 1 by using copy bytes
left-justified and then end:
M1202: CPYBLA RC,’1’;
RTX *;
PEND;
A more complete example of how to handle exceptions is provided in Handling exceptions in the
MICRTPG2 program.
6. Because the non-M1202 path indicates that no error was detected, update the normal return path:
RETURN: CPYBLA RC,’0’;
7. Because M1202 was appended to the end of the MI01 source, remove the original MI01 PEND
directive.
After recompiling the MI01 program and the CL01 program, CALL CL01 (abc 6) now results in the
following message (not the previous MCH1202):
ERROR FOUND
Related reference
Create Program (QPRCRTPG) API
“Creating an MI version of the CLCRTPG program”
This topic discusses how to create an MI version of the CLCRTPG program that can be used to create MI
programs. This program is called MICRTPG.
“Creating the MICRTPG2 program” on page 524
This topic shows how to create the MICRTPG2 program and how to handle exceptions in the program.
Because the CLCRTPG program is used to create the initial version of MICRTPG and CLCRTPG can
support only as many as 2000 bytes of source in the &MIPGMSRC variable, MICRTPG is initially defined
with a minimal set of function. Significant additions to the MICRTPG program can be made after it is
used as a building block in the creation of MI programs.
In the initial design (see the program flow in “Source for the CL03 program” on page 511), there are four
programs. The first program is a CL program (CL03) that does the following:
v Creates a user space (*USRSPC) object of 64KB size to hold the MI source.
v Overrides the MISRC file to the appropriate source physical file and member (1).
v Calls a second CL program (CL04), which loads the selected MISRC member into the user space
(*USRSPC) (2).
The overall program flow for creating the MICRTPG program appears as follows:
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
You will recognize some of these statements from the MI01 example, but others are new.
The following statements, which you have seen, for example, in MI01 program complete code example,
define the entry point to this program and the parameters being passed on the call:
ENTRY * (PARM_LIST) EXT;
DCL SPCPTR MBR@ PARM;
DCL SPCPTR BINOFFSET@ PARM;
DCL OL PARM_LIST (MBR@, BINOFFSET@) PARM EXT;
DCL DD MBR CHAR(10) BAS(MBR@);
DCL DD BINOFFSET BIN(4) BAS(BINOFFSET@);
These statements declare a structure named RSLVOBJ that comprises four subelements defined within it.
The subelements specify their position relative to the start of the structure RSLVOBJ. In the cases of the
RSLVTYPE, RSLVSUBTYPE, and RSLVAUTH data elements, they initialize the associated storage.
The RSLVOBJ structure is used later in the program as input to the Resolve System Pointer (RSLVSP) MI
instruction. The RSLVSP instruction resolves (establishes addressability) to a user space (*USRSPC) (the
X'1934' object type and subtype) named RSLVNAME. The RSLVNAME user space is assigned from the
Note: In the declare (DCL) statement of RSLVOBJ, the leading blanks used to indent the subelements (for
example, RSLVTYPE and RSLVSUBTYPE) are strictly to enhance the readability of the source. They
are not a requirement of the QPRCRTPG API. In general, you can use strings of blanks of any
length in the source of a program. Blanks, one or more, are simply used as delimiters in
identifying tokens. The major exception is the INIT argument of a DCL statement where the
number of blanks is important. For example, the previous declare statement could have been
written as follows and other than readability, nothing would have been lost:
DCL DD RSLVOBJ CHAR(34); DCL DD RSLVTYPE CHAR(1)
DEF(RSLVOBJ) POS(1) INIT(X’19’); DCL DD RSLVSUBTYPE CHAR(1)
DEF(RSLVOBJ) POS(2)
INIT(X’34’); DCL DD RSLVNAME CHAR(30) DEF(RSLVOBJ) POS(3); DCL
DD RSLVAUTH CHAR(2) DEF(RSLVOBJ) POS(33) INIT(X’0000’);
Declaring pointers
The next statements declare a system pointer named USRSPCOBJ and a space pointer named USRSPC.
USRSPCOBJ contains the address of the *USRSPC object after the execution of the RSLVSP instruction
later in the instruction stream. USRSPC addresses the first byte of the *USRSPC:
DCL SYSPTR USRSPCOBJ;
DCL SPCPTR USRSPC;
Because this program also uses the call external (CALLX) instruction to call the CL program CL05, define
a system pointer for CL05:
DCL SYSPTR CL05 INIT("CL05", TYPE(PGM));
The preceding statement causes the QPRCRTPG API to initialize the system pointer CL05 to the name of
the PGM CL05. The CL05 pointer is not set to the address of the CL05 object–this happens the first time
the CL05 pointer is referred to in the instruction stream. If you review the declare statement in the
QPRCRTPG API, notice that the context (CTX) argument uses the default. Using the context default
(better known as library to most programmers) is equivalent to specifying *LIBL. *LIBL is referred to as
the process name resolution list in the machine interface instructions.
Because this program calls the CL05 program (CALLX CL05) with parameters, it now defines an operand
list CL05OL, which specifies the arguments to be passed on the CALLX:
DCL OL CL05OL (MBR@, USRSPC, BINOFFSET@) ARG;
When you get to the instruction stream of MICRTPG, copy the passed parameter MBR to the data
structure element RSLVNAME. As RSLVNAME is defined as CHAR(30) and MBR is CHAR(10), the
program uses the copy bytes left-justified with pad (CPYBLAP) instruction to set the rightmost 20 bytes
of RSLVNAME to the value of the third argument (in this case, blanks):
CPYBLAP RSLVNAME, MBR, ’ ’;
Having established the *USRSPC name, use the RSLVSP instruction to get addressability to the object
itself:
RSLVSP USRSPCOBJ, RSLVOBJ, *, *;
Note: Similar to how the *USRSPC name was resolved, RSLVSP could be used with a type of X'02' and a
subtype of X'01' to resolve a system pointer to the CL05 *PGM object. The two different approaches
were used to demonstrate the different styles (RSLVSP is clearly more flexible) and also to stay
within the 2000-byte limit of the program source size imposed by the CLCRTPG program.
Now the program will call the CL05 program (CALLX CL05) and pass the address of the *USRSPC as a
parameter (along with the member name, program name, and the size of the source stream). When you
call CL05 with the operand list CL05OL, CL05 passes the actual space pointer USRSPC. CL05 does not
pass a space pointer that refers to the space pointer USRSPC (as opposed to how MBR@ and
BINOFFSET@ are passed to refer to MBR and BINOFFSET, respectively). This has the effect of having the
CL05 program treat the *USRSPC storage as the parameter:
CALLX CL05, CL05OL, *;
Finally, as the program comes to an end, this is the return external instruction and pend directive for the
initial version of MICRTPG:
RTX *;
PEND;
Assuming a successful creation, the CLCRTPG program is not used again because of the MI base with
which to work (for example, MICRTPG is used as a boot-strap for further compiler enhancement).
Related tasks
“Enhanced version of the MICRTPG program”
The enhanced version of the MICRTPG program (named MICRTPG2) incorporates the functions of the
CL03 program and the CL05 program.
Related reference
“Compiling an MI program” on page 504
If you enter the source into a source physical file, you can now compile the source and create an MI
program. To create the program, use the Create Program (QPRCRTPG) API. Test and debug the program
if it has errors. You can also declare an exception handler for the program.
“Example: Writing an MI program” on page 502
This example shows how to write a simple MI program that receives two packed-decimal parameters and
returns the larger value through a third parameter.
i5/OS Machine Interface
“Internal object types” on page 64
Internal objects are used to store the information needed to perform some system functions. The table
shows the predefined values for all the i5/OS internal object types.
Create Program (QPRCRTPG) API
A modified form of CL04 (renamed to CL06) is used in these examples to read the MISRC source
physical file because MI instruction support for database access is beyond the scope of this topic
collection.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
Related reference
“Creating an MI version of the CLCRTPG program” on page 510
This topic discusses how to create an MI version of the CLCRTPG program that can be used to create MI
programs. This program is called MICRTPG.
Note: In the following declare (DCL) statement for CLOVRCMD, the 3 strings of '1234567890' are
used strictly so that you can see that 10 bytes are being used. The strings themselves are
overridden by the subsequent subelement DCLs for FILNAM, MBRNAM, and RECNUM, and
could be replaced by 10 blanks:
DCL DD CLOVRCMD CHAR(65);
DCL DD OVRSTR CHAR(39) DEF(CLOVRCMD) POS(1)
INIT("OVRDBF MISRC 1234567890 MBR(1234567890)");
DCL DD OVRSTR2 CHAR(26) DEF(CLOVRCMD) POS(40)
INIT(" POSITION(*RRN 1234567890)");
DCL DD FILNAM CHAR(10) DEF(CLOVRCMD) POS(14);
DCL DD MBRNAM CHAR(10) DEF(CLOVRCMD) POS(29);
DCL DD RECNUM ZND(10,0) DEF(CLOVRCMD) POS(55);
Resuming the program flow of the MICRTPG2 program from “Beginning the instruction stream” on page
519, you should have the program perform the following:
1. Fall into a loop (the MORE label) until all source records are loaded as the source physical file
member position is overridden:
MORE: CALLX QCMDEXC, QCMDOVROL, *;
2. Instruct the CL06 program to load source records from the start of the input buffer, which is actually
the BINOFFSET into the space created earlier:
CPYNV OFFSET,1;
CALLX CL06, CL06OL, *;
3. Back out (subtract) the base-1 nature of CL using the short (the (S) extender) form of the subtract
numeric (SUBN) instruction:
SUBN(S) OFFSET, 1;
4. Add the number of MI source bytes processed by CL06 to the offset into the space (for the next call):
ADDN(S) BINOFFSET, OFFSET;
SETSPPO USRSPC, BINOFFSET;
5. Update the Override with Database File (OVRDBF) position parameter for the next call to CL06:
ADDN(S) RECNUM, 20;
6. Delete the previous OVRDBF:
CALLX QCMDEXC, QCMDDLTOL, *;
7. Check to see if all records were processed, and if not, branch to label MORE to load more source
records:
STPLLEN NUM_PARMS;
CMPNV(B) NUM_PARMS, 2 / EQ(PARM2);
CPYBLAP FILNAM, ’MISRC’, ’ ’;
B PARM1;
PARM2: CPYBLA FILNAM, FIL;
PARM1: CPYBLA MBRNAM,MBR;
CMPBLA(B) READY, ’1’ / EQ(SKIP);
CPYBWP CONTEXT, QTEMP@;
CRTS USRSPC@, CRTSTMPLT@;
SETSPPFP USRSPC,USRSPC@;
CPYBLA READY, ’1’;
SKIP: CPYNV RECNUM, 1;
MORE: CALLX QCMDEXC, QCMDOVROL, *;
CPYNV OFFSET,1;
CALLX CL06, CL06OL, *;
SUBN(S) OFFSET, 1;
ADDN(S) BINOFFSET, OFFSET;
SETSPPO USRSPC, BINOFFSET;
ADDN(S) RECNUM, 20;
CALLX QCMDEXC, QCMDDLTOL, *;
CMPNV(B) OFFSET, 1600 /EQ(MORE);
CPYBLA PGMNAM, MBR;
SETSPPO USRSPC, 0;
CALLX QPRCRTPG, QPRCRTPGOL, *;
RTX *;
PEND;
After the successful creation of MICRTPG2, you can create any new MI programs by entering the
following, where SourceFileName is an optional parameter:
CALL MICRTPG2 (MemberName SourceFileName)
Some exceptions that are not being handled by the MICRTPG2 program might occur. For example, if you
used MICRTPG2 to compile MICRTPG2 two times in succession, the exception MCH1401 occurs. This
occurs because the most recent activation of the MICRTPG2 program has its own static storage and is not
aware of the earlier instances of MICRTPG2 creating the space named MICRTPG2 in QTEMP.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/********************************************************************/
/* */
/* program Name: MICRTPG2 */
/* */
/* programming Language: MI */
/* */
/* Description: Enhanced version of MI program MICRTPG2, */
/* which provides for exception handling. */
/* */
/* */
/* Header Files Included: None */
/* */
/* */
/********************************************************************/
/* Entry point and associated parameters */
STPLLEN NUM_PARMS;
CMPNV(B) NUM_PARMS, 2 / EQ(PARM2);
CPYBLAP FILNAM, ’MISRC’, ’ ’;
B PARM1;
PARM2: CPYBLA FILNAM, FIL;
PARM1: CPYBLA MBRNAM,MBR;
CMPBLA(B) READY, ’1’ / EQ(SKIP);
CPYBWP CONTEXT, QTEMP@;
CRTS USRSPC@, CRTSTMPLT@;
SETSPPFP USRSPC,USRSPC@;
CPYBLA READY, ’1’;
SKIP: CPYNV RECNUM, 1;
MORE: CALLX QCMDEXC, QCMDOVROL, *;
CPYNV OFFSET,1;
CALLX CL06, CL06OL, *;
SUBN(S) OFFSET, 1;
ADDN(S) BINOFFSET, OFFSET;
SETSPPO USRSPC, BINOFFSET;
ADDN(S) RECNUM, 20;
CALLX QCMDEXC, QCMDDLTOL, *;
CMPNV(B) OFFSET, 1600 /EQ(MORE);
CPYBLA PGMNAM, MBR;
SETSPPO USRSPC, 0;
CALLX QPRCRTPG, QPRCRTPGOL, *;
RTX *;
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
/********************************************************************/
/********************************************************************/
/* */
/* Program Name: MISC1 */
/* */
/* Programming Language: MI */
/* */
/* Description: This program materializes the objects found */
/* within the QTEMP library (context). For each */
/* object found, a message is sent to the */
/* interactive user message queue showing the */
/* name of the object and the object’s type and */
/* subtype. */
/* */
ENTRY * EXT;
/* Allocate the necessary storage (we could also have used CRTS
to allocate the storage and a SPCPTR to the space for the
large receiver variable) */
MORE:
OVRPGATR 1,4;
ADDN(S) NUM_DONE, 1;
ADDSPP OBJ_ENTRY@, OBJ_ENTRY@, 32;
CMPNV(B) NUM_DONE, SIZE / LO(MORE);
DONE: RTX *;
PEND;
Program storage
Program activation and program invocation are needed to run an MI program.
Program activation is the process of allocating and initializing static storage for the program. Program
invocation is the process of allocating and initializing automatic storage.
Program activation can be done explicitly through the Activate Program (ACTPG) instruction or
implicitly by using a call external (CALLX) instruction when the called program has not been previously
activated. Program activation typically occurs only once within a job or process. Program activation is not
reset by an RTX instruction within the called program (the program is still considered to be in an
activated state). This means that all static storage on subsequent calls (CALLXs) to the program are found
in a last-used state, not in a reinitialized state. If a programmer wants to reinitialize the static storage
associated with a program activation, this can be accomplished through the deactivate program
(DEACTPG) instruction so that the next call (CALLX or ACTPG) causes a new activation of the program.
Program invocation, on the other hand, occurs every time a program is called with a CALLX instruction.
Automatic storage is reinitialized if a discrete INIT value was specified on the declare (DCL) statement.
(If the INIT was allowed to be the default, then whether initialization occurs for the field is determined
by an option of the QPRCRTPG API when the program was created.) If you have not already done so,
review all of the option template values available on the QPRCRTPG API before developing your MI
applications.
The examples in this topic present a program that is used for creating a user space.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
The common error shown in this example is the use of the error code structure to indicate to the API not
to send exception messages for errors found. Additionally, the example does not examine the error code
structure to determine if the API call was successful or not. To demonstrate the improper use of the error
code structure, an incorrect value is used on the replace parameter of the QUSCRTUS API. The replace
parameter is a required parameter. The coded error (*XXXXXXX) is shown at location (1) in the incorrect
and also at location (2) in the correct coding.
Both the incorrect (3) and correct coding (4) show the program monitoring for any error from the call to
the API. However, the program does not examine the bytes available field after calling the QUSCRTUS
API.
Because of the error on the replace parameter, the requested user space is not created. The calling
program, however, is not aware of this as shown at (5).
*****************************************************************
*
*Program Name: PGM1
*
*Program Language: RPG
*
*Description: This sample program illustrates the incorrect
* way of using the error code parameter.
*
*Header Files Included: QUSEC - Error Code Parameter
*
*APIs Used: QUSCRTUS - Create User Space
*
*****************************************************************
* BRING IN THE ERROR STRUCTURE FROM QSYSINC
I/COPY QSYSINC/QRPGSRC,QUSEC
**
ISPCNAM DS
I I ’SPCNAME ’ 1 10 SPC
I I ’PAM ’ 11 20 LIB
** OTHER ASSORTED VARIABLES
I DS
I I 2000 B 1 40SIZ
I B 5 80START
I I X’00’ 9 9 INTVAL
*
* Initialize the bytes provided field (QUSBNDB) of the error code
* structure. Languages such as RPG and CL tend to initialize the bytes
* provided field to blanks, which when passed to an API is viewed as a
* very large (and incorrect) binary value. If you receive CPF3CF1 when
* calling an API, the bytes provided field should be the first field
* you examine as part of problem determination.
C Z-ADD16 QUSBNB (3)
You can add code to help you discover what errors might be in a program. In the following example
program, code has been added to monitor error information that is passed back in the error code
parameter (QUSBN). The code at (6) has been added to check the error code parameter for any messages
and to display the exception identifier to the user if any errors are found. The incorrectly coded program
does no checking for the error code parameter, as shown at (5).
*****************************************************************
*
*Program Name: PGM2
*
*Program Language: RPG
*
*Description: This sample program illustrates the correct
* way of using the error code parameter.
*
*Header Files Included: QUSEC - Error Code Parameter
*
*APIs Used: QUSCRTUS - Create User Space
*
*****************************************************************
* BRING IN THE ERROR STRUCTURE FROM QSYSINC
I/COPY QSYSINC/QRPGSRC,QUSEC
**
ISPCNAM DS
I I ’SPCNAME ’ 1 10 SPC
I I ’QTEMP ’ 11 20 LIB
** OTHER ASSORTED VARIABLES
I DS
I I 2000 B 1 40SIZ
I B 5 80START
I I X’00’ 9 9 INTVAL
*
C Z-ADD16 QUSBNB (4)
*
* CREATE THE SPACE TO HOLD THE DATA
C CALL ’QUSCRTUS’
C PARM SPCNAM
C PARM ’EXT_ATTR’EXTATR 10
C PARM SIZ
C PARM INTVAL
C PARM ’*ALL ’PUBAUT 10
C PARM ’NO TEXT ’TXTDSC 50
C PARM ’*XXXXXXX’REPLAC 10 (2)
C PARM QUSBN
**
* DISPLAY EXCEPTION IDENTIFIER TO THE USER
C QUSBNC IFGT *ZEROS (6)
C EXSR DSPERR
C END
For information about IBM-supplied data structures that are contained in the QSYSINC library, see
“Include files and the QSYSINC library” on page 62.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
When the program that defines a data structure is run, it does the following:
v Creates a user space
v Retrieves a list of active jobs
v Displays the first part of a job name
v Deletes the user space that held the data
In this example, the data structure to be used with the QUSLJOB API has been defined incorrectly. The
incorrectly defined variables are JNAME and USRNAM. The JNAME length is defined as 1 through 12
and the USRNAM length as 13 through 20. This is shown at (1). The data displayed (JNAME variable)
will be incorrect. The correct coding is shown at (2).
*****************************************************************
*
*Program Name: PGM1
*
*Program Language: RPG
*
*Description: This sample program illustrates the incorrect
* way of defining data structures.
*
*Header Files Included: QUSEC - Error Code Parameter
* QUSGEN - User Space Format for Generic Header
*
*APIs Used: QUSCRTUS - Create User Space
* QUSLJOB - List Job
* QUSRTVUS - Retrieve User Space
* QUSDLTUS - Delete User Space
*****************************************************************
* THIS PROGRAM WILL CREATE THE NECESSARY SPACE AND THEN CALL
* THE QUSLJOB API TO GET A LIST OF ALL ACTIVE JOBS ON THE SYSTEM.
* THE FIRST JOB NAME/USER WILL BE DISPLAYED TO THE USER.
*
* BRING IN THE USER SPACE GENERIC HEADER
I/COPY QSYSINC/QRPGSRC,QUSGEN
* BRING IN THE ERROR STRUCTURE FROM QSYSINC
I/COPY QSYSINC/QRPGSRC,QUSEC
** JOB NAME STRUCTURE FOR CALLING QUSLJOB
IJOBNAM DS
I I ’*ALL ’ 1 10 JOB
I I ’*ALL ’ 11 20 USER
The following program uses a data structure that is supplied from the QSYSINC library. When you use
this data structure, you can prevent errors in data structure creation from happening. If the data
structures change from release to release, updates to programs do not have to be done. The application
program would have to be updated only if a new field was added to the data structure and you wanted to
use the field. The copying of the QSYSINC data structure is shown at (2).
*
*
*****************************************************************
*
*Program Name: PGM2
*
*Program Language: RPG
*
*Description: This sample program illustrates the correct
* way of defining data structures.
*
*Header Files Included: QUSEC - Error Code Parameter
* QUSGEN - User Space Format for Generic Header
* QUSLJOB - List Job API
*
*APIs Used: QUSCRTUS - Create User Space
* QUSLJOB - List Job
* QUSRTVUS - Retrieve User Space
* QUSDLTUS - Delete User Space
*
*
* THIS PROGRAM WILL CREATE THE NECESSARY SPACE AND THEN CALL
* THE QUSLJOB API TO GET A LIST OF ALL ACTIVE JOBS ON THE SYSTEM.
* THE FIRST JOB NAME/USER WILL BE DISPLAYED TO THE USER.
*
I/COPY QSYSINC/QRPGSRC,QUSGEN
I/COPY QSYSINC/QRPGSRC,QUSEC
I/COPY QSYSINC/QRPGSRC,QUSLJOB (2)
** JOB NAME STRUCTURE FOR CALLING QUSLJOB
IJOBNAM DS
I I ’*ALL ’ 1 10 JOB
I I ’*ALL ’ 11 20 USER
I I ’*ALL’ 21 26 JOBNUM
** JOBL0100 FORMAT RETURNED FROM QUSLJOB API
**
**
ISPCNAM DS
I I ’SPCNAME ’ 1 10 SPC
I I ’QTEMP ’ 11 20 LIB
** OTHER ASSORTED VARIABLES
I DS
I I 2000 B 1 40SIZ
I I B 5 80START
I I B 9 120LENDTA
I I X’00’ 13 13 INTVAL
*
The following example program might fail because the receiver variable has been defined as 50 bytes, as
shown at (1), but 60 bytes are being requested to be passed back from the API, as shown at (2) in the
incorrect program and at (3) in the correct program. The correct coding is shown at (4).
When this situation happens, other variables are overwritten with unintended data. This causes the other
variables to be incorrect. For example, the first 10 characters of the QUSBN parameter can be written over
with these extra characters. On the call to the next API, the error code parameter might appear to contain
meaningless characters that can cause the next call to an API to fail.
*****************************************************************
*
*Program Name: PGM1
*
*Program Language: RPG
*
*Description: This sample program illustrates the incorrect
* way of defining receiver variables.
*
*Header Files Included: QUSEC - Error Code Parameter
* QUSLJOB - List Job API
* QUSGEN - User Space Format for Generic Header
*
*APIs Used: QUSCRTUS - Create User Space
* QUSLJOB - List Job
* QUSRTVUS - Retrieve User Space
* QUSDLTUS - Delete User Space
*****************************************************************
* THIS PROGRAM WILL CREATE THE NECESSARY SPACE AND THEN CALL
* THE QUSLJOB API TO GET A LIST OF ALL ACTIVE JOBS ON THE SYSTEM.
* BRING IN THE GENERIC USER SPACE HEADER FROM QSYSINC
I/COPY QSYSINC/QRPGSRC,QUSGEN
*
* BRING IN THE ERROR STRUCTURE FROM QSYSINC
I/COPY QSYSINC/QRPGSRC,QUSEC
*
** JOBL0100 FORMAT RETURNED FROM QUSLJOB API
I/COPY QSYSINC/QRPGSRC,QUSLJOB
*
** JOB NAME STRUCTURE FOR CALLING QUSLJOB
IJOBNAM DS
I I ’*ALL ’ 1 10 JOB
I I ’*ALL ’ 11 20 USER
I I ’*ALL’ 21 26 JOBNUM
ISPCNAM DS
I I ’SPCNAME ’ 1 10 SPC
I I ’QTEMP ’ 11 20 LIB
** OTHER ASSORTED VARIABLES
I DS
I I 2000 B 1 40SIZ
I B 5 80START
I B 9 120LENDTA
I I X’00’ 13 13 INTVAL
*
* SET UP TO ACCEPT EXCEPTIONS
C Z-ADD*ZEROS QUSBNB
*
* CREATE THE SPACE TO HOLD THE DATA
C CALL ’QUSCRTUS’
C PARM SPCNAM
C PARM ’EXT_ATTR’EXTATR 10
The following example program defines a larger receiver variable: 60 bytes. This is shown at position (4).
This increase in the receiver variable allows up to 60 bytes of data to be received.
*****************************************************************
*
*Program Name: PGM2
*
*Program Language: RPG
*
*Description: This sample program illustrates the correct
* way of defining receiver variables.
*
*Header Files Included: QUSEC - Error Code Parameter
* QUSLJOB - List Job API
The program uses the format length to advance to the next list entry in the user space. The length of the
format might change from release to release. Therefore, when the format length changes, your program
could be susceptible to being pointed at an incorrect position in the user space and nonsensical data
could be placed in the receiver variable.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
The program has the length of the list entry format hard coded. This is shown at (1). If your program
runs on a Version 2 Release 2 system, that value would work. However, with Version 2 Release 3, the
format size increased from 52 to 56 bytes. The correct coding is shown at (2).
*****************************************************************
*
*Program Name: PGM1
*
*Program Language: RPG
*
*Description: This sample program illustrates the incorrect
* way of using list entry length formats.
*
*Header Files Included: QUSEC - Error Code Parameter
* QUSLJOB - List Job API
* QUSGEN - User Space Format for Generic Header
*
*APIs Used: QUSCRTUS - Create User Space
* QUSLJOB - List Job
* QUSRTVUS - Retrieve User Space
* QUSDLTUS - Delete User Space
*****************************************************************
*
* THIS PROGRAM WILL CREATE THE NECESSARY SPACE AND THEN CALL
* THE QUSLJOB API TO GET A LIST OF ALL ACTIVE JOBS ON THE SYSTEM.
* THE FIRST JOB NAME/USER WILL BE DISPLAYED TO THE USER.
The following program correctly uses the list entry length that is defined in the space header for the
QUSRTVUS API to advance from one entry to the next. This is shown at (2). If you use this value in your
program, you will always have the correct list entry length regardless of the version or release level of
the API.
*****************************************************************
*
*Program Name: PGM2
*
*Program Language: RPG
*
*Description: This sample program illustrates the correct
* way of using list entry length formats.
*
*Header Files Included: QUSEC - Error Code Parameter
* QUSLJOB - List Job API
* QUSGEN - User Space Format for Generic Header
*
*APIs Used: QUSCRTUS - Create User Space
* QUSLJOB - List Job
* QUSRTVUS - Retrieve User Space
* QUSDLTUS - Delete User Space
*****************************************************************
*
* THIS PROGRAM WILL CREATE THE NECESSARY SPACE AND THEN CALL
* THE QUSLJOB API TO GET A LIST OF ALL ACTIVE JOBS ON THE SYSTEM.
*
I/COPY QSYSINC/QRPGSRC,QUSGEN
I/COPY QSYSINC/QRPGSRC,QUSLJOB
I/COPY QSYSINC/QRPGSRC,QUSEC
*
** JOB NAME STRUCTURE FOR CALLING QUSLJOB
IJOBNAM DS
I I ’*ALL ’ 1 10 JOB
I I ’*ALL ’ 11 20 USER
I I ’*ALL’ 21 26 JOBNUM
*
** DATA STRUCTURE TO HOLD SPACE NAME
ISPCNAM DS
I I ’SPCNAME ’ 1 10 SPC
I I ’QTEMP ’ 11 20 LIB
** OTHER ASSORTED VARIABLES
I DS
I I 2000 B 1 40SIZ
Note: Using NULL with ignored parameters is primarily a consideration with program-based APIs. APIs
based on service programs allow you to pass NULL parameters to indicate omitted parameter
values.
Even though the value assigned to a parameter is not used, the parameter itself must be addressable.
When you use NULL for a parameter value, the system conceptually passes an address that can be
equated with 0, where 0 indicates that the parameter cannot be addressed. This lack of addressability
often results in a function check (MCH3601). Additionally, other error messages might also occur.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
The following program has two parameter values coded as NULL. They are the ignored parameters of
the member and record format used in the List Database Relations (QDBLDBR) API, which is shown at
(1). The correct coding is shown at (2).
When the program is called, a machine function check of MCH3601 is reported because the address of
the required parameters member and record format are specified as NULL.
/**************************************************************/
/* */
/*Program Name: PGM1 */
/* */
/*Program Language: ILE C */
/* */
/*Description: This sample program illustrates the incorrect */
/* use of ignored and null parameters. */
/* */
/*Header Files Included: <stdio.h> */
/* <qusec.h> */
/* <qusgen.h> */
/* <qdbldbr.h> */
/* <quscrtus.h> */
/* <qusptrus.h> */
/* <qliept.h> */
/* */
/*APIs Used: QUSCRTUS - Create User Space */
/* QDBLDBR - List Database Relations */
/* QUSPTRUS - Retrieve Pointer to User Space */
/**************************************************************/
#include <stdio.h>
#include <qusec.h>
#include <qusgen.h>
#include <qdbldbr.h>
#include <quscrtus.h>
#include <qusptrus.h>
#include <qliept.h>
main()
{
/***********************************************************/
/* Get pointer to user space which contains dependencies */
/***********************************************************/
/***********************************************************/
/* and display number of entries generated */
/***********************************************************/
The following program specifies that blanks be used as the values for both the member and record
format parameters. This coding is shown at (2) in the example program. By using blanks, the storage or
address location of those parameters is identified and passed when needed.
/**************************************************************/
/* */
/*Program Name: PGM2 */
/* */
/*Program Language: ILE C */
/* */
/*Description: This sample program illustrates the correct */
/* use of ignored and null parameters. */
/* */
/*Header Files Included: <stdio.h> */
/***********************************************************/
/* initialize program data elements */
/***********************************************************/
/***********************************************************/
/* Create the user space to hold API results */
/***********************************************************/
/***********************************************************/
/* Get list of file dependencies in current library */
/* */
/* Note that in this API call, blank characters are being */
/* used for the "ignored" parameters Member and */
/* Record_Format. While the value is ignored, a valid */
/* parameter storage location must still be passed */
/***********************************************************/
/***********************************************************/
/* Get pointer to user space which contains dependencies */
/***********************************************************/
/***********************************************************/
/* and display number of entries generated */
/***********************************************************/
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
This program illustrates byte alignment while defining a structure. This is shown at (1). Four-byte
alignment is required when using this program.
Variable-length records must begin on a 4-byte boundary. As shown at (1), the variable-length record
CCSID_rec is not beginning on a 4-byte boundary. When the API accesses the CCSID_rec record, 4-byte
alignment is forced by padding the first 3 bytes of the CCSID_rec between the replace field and the start
of the CCSID_rec record. (2) shows that the variable-length record is not 4-byte aligned (the value is 13,
which is not divisible by 4). The correct coding is shown at (3).
Note: Not all APIs require a 4-byte boundary. APIs based on service programs, such as the Add Exit
Program (QusAddExitProgram) API, require a 4-byte boundary.
/***************************************************************** */
/* */
/*Program Name: PGM1 */
/* */
/*Program Language: ILE C */
/* */
/*Description: This program illustrates improper byte */
/* alignment when using variable length */
/* records. */
/* */
/* */
/*Header Files Included: <stdio.h> */
/* <signal.h> */
/* <string.h> */
/* <stdlib.h> */
/* <qusrgfa1.h> */
/* <qusec.h> */
/* <qliept.h> */
/* */
/* APIs Used: QusAddExitProgram - Add an exit program */
/* */
/********************************************************************/
/********************************************************************/
/* Includes */
/********************************************************************/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <qusrgfa1.h>
#include <qusec.h>
#include <qliept.h>
/********************************************************************/
/* Structures */
/* */
/********************************************************************/
/******************************************************************/
/* Initialize the error code parameter. */
/******************************************************************/
error_code.ec_fields.Bytes_Provided=sizeof(error_code_struct);
/******************************************************************/
/* Set the total number of exit program attributes that we are */
/* specifying on the call. We will let the API take the default */
/* for the attributes that we are not specifying. */
/******************************************************************/
attrib_keys.num_rec=3;
/******************************************************************/
/* Set the values for the three attributes that we will be */
/* specifying: */
/* Replace exit program = 1 (CHAR(1) field) */
/* Exit program data CCSID = 37 (BIN(4) field) */
/* Exit program description=’THIS IS A TEST EXIT PROGRAM’ */
/* (CHAR(50) field) */
/* */
/* The structure for the exit program attributes defined above is */
/* as follows: */
/* */
/* typedef struct { */
/* int num_rec; */
/* Qus_Vlen_Rec_4_t replace_rec; */
/* char replace; */
/* Qus_Vlen_Rec_4_t CCSID_rec; */
/* int CCSID; */
/* Qus_Vlen_Rec_4_t desc_rec; */
/* char desc[50]; */
/* } addep_attributes; */
/* */
/* and the Qus_Vlen_Rec_4_t structure is defined in */
/* qus.h (included by qusrgfa1) as: */
/* */
/* typedef _Packed struct Qus_Vlen_Rec_4 { */
/* int Length_Vlen_Record; */
/* int Control_Key; */
/* int Length_Data; */
/* **char Data[];-> this field is supplied by */
/* the user */
/* } Qus_Vlen_Rec_4_t; */
/* */
/* This structure is mapped in bytes as follows: */
/* { */
/* BIN(4) - num_rec */
/* BIN(4) - length variable length record for replace key */
attrib_keys.CCSID_rec.Length_Vlen_Record=16;
attrib_keys.CCSID_rec.Control_Key=3;
attrib_keys.CCSID_rec.Length_Data=4;
attrib_keys.CCSID=37;
attrib_keys.desc_rec.Length_Vlen_Record=39;
attrib_keys.desc_rec.Control_Key=2;
attrib_keys.desc_rec.Length_Data=27;
memcpy(&attrib_keys.desc,
"THIS IS A TEST EXIT PROGRAM",27);
/******************************************************************/
/* Call the API to add the exit program. */
/******************************************************************/
QusAddExitProgram("EXAMPLE_EXIT_POINT ",
"EXMP0100",
1,
"EXAMPLEPGMEXAMPLELIB",
"EXAMPLE EXIT PROGRAM DATA",
25,
&attrib_keys,
&error_code);
if (error_code.ec_fields.Bytes_Available != 0)
{
printf("ATTEMPT TO ADD AN EXIT PROGRAM FAILED WITH EXCEPTION:%.7s",
error_code.ec_fields.Exception_Id);
exit(1);
}
} /* end program */
The following example program shows a CHAR(3) bytes reserved field being added to the structure to
maintain 4-byte alignment as shown at (4). This corresponds to (1) in the incorrect coding example. The 3
reserved bytes are included in the length of the replace variable-length record. (3) shows the
variable-length record is now 4-byte aligned (record length of 16 is divisible by 4). This corresponds to (2)
in the incorrect coding example.
/********************************************************************/
/* */
/*Program Name: PGM2 */
/* */
/*Program Language: ILE C */
/********************************************************************/
/* */
/* main */
/* */
/********************************************************************/
int main()
{
error_code_struct error_code;
addep_attributes attrib_keys;
/******************************************************************/
/* Initialize the error code parameter. */
/******************************************************************/
error_code.ec_fields.Bytes_Provided=sizeof(error_code_struct);
/******************************************************************/
/* Set the total number of exit program attributes that we are */
/* specifying on the call. We will let the API take the default */
/* for the attributes that we are not specifying. */
/******************************************************************/
/******************************************************************/
/* Set the values for the three attributes that we will be */
/* specifying: */
/* Replace exit program = 1 (CHAR(1) field) */
/* Exit program data CCSID = 37 (BIN(4) field) */
/* Exit program description=’THIS IS A TEST EXIT PROGRAM’ */
/* (CHAR(50) field) */
/******************************************************************/
attrib_keys.replace_rec.Length_Vlen_Record=16; (3)
attrib_keys.replace_rec.Control_Key=4;
attrib_keys.replace_rec.Length_Data=1;
attrib_keys.replace=’1’;
attrib_keys.CCSID_rec.Length_Vlen_Record=16;
attrib_keys.CCSID_rec.Control_Key=3;
attrib_keys.CCSID_rec.Length_Data=4;
attrib_keys.CCSID=37;
attrib_keys.desc_rec.Length_Vlen_Record=39;
attrib_keys.desc_rec.Control_Key=2;
attrib_keys.desc_rec.Length_Data=27;
memcpy(&attrib_keys.desc,"THIS IS A TEST EXIT PROGRAM",27);
/******************************************************************/
/* Call the API to add the exit program. */
/******************************************************************/
QusAddExitProgram("EXAMPLE_EXIT_POINT ",
"EXMP0100",
1,
"EXAMPLEPGMEXAMPLELIB",
"EXAMPLE EXIT PROGRAM DATA",
25,
&attrib_keys,
&error_code);
if (error_code.ec_fields.Bytes_Available != 0)
{
printf("ATTEMPT TO ADD AN EXIT PROGRAM FAILED WITH EXCEPTION: %.7s",
error_code.ec_fields.Exception_Id);
exit(1);
}
} /* end program */
Related concepts
“Receiver variables” on page 76
A receiver variable is a program variable that is used as an output field to contain information that is
returned from a retrieve API.
Using offsets incorrectly can produce errors when coding in a base 1 language such as RPG and COBOL.
One way to determine the base of a language is to determine how the first element of an array is
specified. In a base 0 language, the first element is number 0. In base 1 languages, the first element is
number 1.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
The beginning point for reading a user space is shown at (1). The data is read and placed into a user
space. However, the data in the user space is incorrect because the position to start was off by 1. This
program started to retrieve the data one character (or position) too soon. The correct coding is shown at
(2).
I*****************************************************************
I*****************************************************************
I*
I*Program Name: APIUG1
I*
I*Programming Language: RPG
I*
I*Description: This sample program illustrates the incorrect
I* way of using the offset in a user space.
I*
I*Header Files Included: QUSGEN - Generic Header of a User Space
I* QUSEC - Error Code Parameter
I* (Copied into Program)
I* QUSLOBJ - List Objects API
I*
I*APIs Used: QUSCRTUS - Create User Space
I* QUSLOBJ - List Objects
I* QUSRTVUS - Retrieve User Space
I* QUSDLTUS - Delete User Space
I*****************************************************************
I*****************************************************************
I*
I* Generic Header of a User Space Include
I*
I/COPY QSYSINC/QRPGSRC,QUSGEN
I*
I* Error Code Parameter Include for the APIs
I*
I* The following QUSEC include is copied into this program
I* so that the variable length field can be defined as a
I* fixed length.
I*
I*** START HEADER FILE SPECIFICATIONS ****************************
I*
I*Header File Name: H/QUSEC
I*
I*Descriptive Name: Error Code Parameter.
I*
I*5763-SS1, 5722-SS1 (C) Copyright IBM Corp. 1994, 2001
I*All rights reserved.
I*US Government Users Restricted Rights -
I*Use, duplication or disclosure restricted
I*by GSA ADP Schedule Contract with IBM Corp.
I*
I*Licensed Materials-Property of IBM
I*
I*
I*Description: Include header file for the error code parameter.
I*
I*Header Files Included: None.
I*
The following example program has code in it that compensates for the API offset convention of that
starts at 0. The code adds 1 to the starting position (STRPOS) offset. This is shown at (2).
I*
I*Program Name: APIUG2
I*
I*Programming Language: RPG
I*
I*Description: This sample program illustrates the correct
I* way of using offsets in user space.
I*
I*Header Files Included: QUSGEN - Generic Header of a User Space
I* QUSEC - Error Code Parameter
I* (Copied into Program)
I* QUSLOBJ - List Objects API
I*
I*APIs Used: QUSCRTUS - Create User Space
I* QUSLOBJ - List Objects
I* QUSRTVUS - Retrieve User Space
Suppose that a new object type *SRVPGM is introduced, which can adopt owner authority.
A general theme of this example is never to assume that the values returned by an API are static. The
i5/OS operating system is continually evolving. While the example is based on the addition of a new
object type, this philosophy should be applied to any output of an API. For example, if an API today can
return *YES or *NO, you need to discretely check for these values because *MAYBE might be valid in the
future. Similarly, if your application assumes that a particular integer output has a positive nonzero value
(an offset for instance), you need to check for a positive nonzero value because future releases might
return a negative value to indicate the new function.
Note: By using the code examples, you agree to the terms of the “Code license and disclaimer
information” on page 574.
In this example program, a check is made to determine the object type. This is shown at (1). The example
program considers only object types of *SQLPKG or *PGMs. This is because they are the only object types
that could adopt owner authority before Version 2 Release 3. Since that time, a new object type of
*SRVPGM has been introduced. *SRVPGM can adopt owner authority. Hence, this example program
processes *SRVPGM objects as if they were *PGM objects. The correct coding is shown at (2).
D*****************************************************************
D*
D*Program Name: PGM1
D*
D*Program Language: ILE RPG
D*
D*Description: This example program demonstrates how a program can
D* be "broken" by new functions introduced on the system.
D*
D*
D*
D*Header Files Included: QUSGEN - Generic Header of a User Space
D* (Copied Into Program)
D* QUSEC - Error Code Parameter
D* (Copied Into Program)
D* QSYLOBJP - List Objects API
D* (Copied Into Program)
D*
D*APIs Used: QUSCRTUS - Create User Space
D* QSYLOBJP - List Objects That Adopt Owner Authority
D* QUSROBJD - Retrieve Object Description
D* QUSPTRUS - Retrieve Pointer to User Space
In the following example program, code has been written that checks for object types *SRVPGM, *PGM,
and *SQLPKG. If an object type is encountered that is unknown (it does not match *SRVPGM, *PGM, or
*SQLPKG), an error is logged and an exit from the program takes place.
The coding to handle the integration of new function (in this case the new object type that can adopt
owner authority) is shown at (2).
C*****************************************************************
C*
C*Program Name: PGM2
C*
C*Program Language: ILE RPG
C*
C*Description: This example program demonstrates how a program can
C* be coded to accept new functions introduced on the system.
IBM may not offer the products, services, or features discussed in this document in other countries.
Consult your local IBM representative for information on the products and services currently available in
your area. Any reference to an IBM product, program, or service is not intended to state or imply that
only that IBM product, program, or service may be used. Any functionally equivalent product, program,
or service that does not infringe any IBM intellectual property right may be used instead. However, it is
the user's responsibility to evaluate and verify the operation of any non-IBM product, program, or
service.
IBM may have patents or pending patent applications covering subject matter described in this
document. The furnishing of this document does not grant you any license to these patents. You can send
license inquiries, in writing, to:
IBM Director of Licensing
IBM Corporation
North Castle Drive
Armonk, NY 10504-1785
U.S.A.
For license inquiries regarding double-byte (DBCS) information, contact the IBM Intellectual Property
Department in your country or send inquiries, in writing, to:
IBM World Trade Asia Corporation
Licensing
2-31 Roppongi 3-chome, Minato-ku
Tokyo 106-0032, Japan
The following paragraph does not apply to the United Kingdom or any other country where such
provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION
PROVIDES THIS PUBLICATION “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some
states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this
statement may not apply to you.
This information could include technical inaccuracies or typographical errors. Changes are periodically
made to the information herein; these changes will be incorporated in new editions of the publication.
IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this
publication at any time without notice.
Any references in this information to non-IBM Web sites are provided for convenience only and do not in
any manner serve as an endorsement of those Web sites. The materials at those Web sites are not part of
the materials for this IBM product and use of those Web sites is at your own risk.
IBM may use or distribute any of the information you supply in any way it believes appropriate without
incurring any obligation to you.
Licensees of this program who wish to have information about it for the purpose of enabling: (i) the
exchange of information between independently created programs and other programs (including this
one) and (ii) the mutual use of the information which has been exchanged, should contact:
IBM Corporation
Such information may be available, subject to appropriate terms and conditions, including in some cases,
payment of a fee.
The licensed program described in this document and all licensed material available for it are provided
by IBM under terms of the IBM Customer Agreement, IBM International Program License Agreement,
IBM License Agreement for Machine Code, or any equivalent agreement between us.
Any performance data contained herein was determined in a controlled environment. Therefore, the
results obtained in other operating environments may vary significantly. Some measurements may have
been made on development-level systems and there is no guarantee that these measurements will be the
same on generally available systems. Furthermore, some measurements may have been estimated through
extrapolation. Actual results may vary. Users of this document should verify the applicable data for their
specific environment.
Information concerning non-IBM products was obtained from the suppliers of those products, their
published announcements or other publicly available sources. IBM has not tested those products and
cannot confirm the accuracy of performance, compatibility or any other claims related to non-IBM
products. Questions on the capabilities of non-IBM products should be addressed to the suppliers of
those products.
All statements regarding IBM's future direction or intent are subject to change or withdrawal without
notice, and represent goals and objectives only.
This information contains examples of data and reports used in daily business operations. To illustrate
them as completely as possible, the examples include the names of individuals, companies, brands, and
products. All of these names are fictitious and any similarity to the names and addresses used by an
actual business enterprise is entirely coincidental.
COPYRIGHT LICENSE:
This information contains sample application programs in source language, which illustrate programming
techniques on various operating platforms. You may copy, modify, and distribute these sample programs
in any form without payment to IBM, for the purposes of developing, using, marketing or distributing
application programs conforming to the application programming interface for the operating platform for
which the sample programs are written. These examples have not been thoroughly tested under all
conditions. IBM, therefore, cannot guarantee or imply reliability, serviceability, or function of these
programs.
Each copy or any portion of these sample programs or any derivative work, must include a copyright
notice as follows:
© (your company name) (year). Portions of this code are derived from IBM Corp. Sample Programs. ©
Copyright IBM Corp. _enter the year or years_. All rights reserved.
If you are viewing this information softcopy, the photographs and color illustrations may not appear.
COBOL/400
i5/OS
IBM
IBM (logo)
Integrated Language Environment
REXX
System i
System/36
VisualAge
Adobe, the Adobe logo, PostScript, and the PostScript logo are either registered trademarks or trademarks
of Adobe Systems Incorporated in the United States, and/or other countries.
Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the
United States, other countries, or both.
Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other
countries, or both.
UNIX is a registered trademark of The Open Group in the United States and other countries.
Other company, product, or service names may be trademarks or service marks of others.
Personal Use: You may reproduce these publications for your personal, noncommercial use provided that
all proprietary notices are preserved. You may not distribute, display or make derivative works of these
publications, or any portion thereof, without the express consent of IBM.
Commercial Use: You may reproduce, distribute and display these publications solely within your
enterprise provided that all proprietary notices are preserved. You may not make derivative works of
these publications, or reproduce, distribute or display these publications or any portion thereof outside
your enterprise, without the express consent of IBM.
Except as expressly granted in this permission, no other permissions, licenses or rights are granted, either
express or implied, to the publications or any information, data, software or other intellectual property
contained therein.
IBM reserves the right to withdraw the permissions granted herein whenever, in its discretion, the use of
the publications is detrimental to its interest or, as determined by IBM, the above instructions are not
being properly followed.
You may not download, export or re-export this information except in full compliance with all applicable
laws and regulations, including all United States export laws and regulations.
Printed in USA