CGI Programs and Activation Groups

The following section is intended to give a brief overview of Activation Groups.

Note: It is very important to become familiar with the details of activation groups prior to developing or porting a CGI application that will use this support.

Activation Groups

Program activation is the process that is used to prepare a program to run. The system must activate ILE programs before they can be run. Program activation includes the allocation and initialization of static storage for the program in addition to completing the binding of programs to service programs. Named activation groups must be used when running persistent CGI.

Program activation is not a unique concept. All modern computer operating systems must perform program initialization and load. What is unique to CGI programs on the iSeries™ server is the concept of Activation Groups. All ILE programs and service programs are activated within an activation group. This substructure contains the resources necessary to run the program. The resources that are contained and are managed with an activation group include:

Run-time creation of ILE activation groups is controlled by specifying an activation group attribute when your program or service program is created. The attribute is specified by using the ACTGRP parameter on the CRTPGM or CRTSRVPGM command. The valid options for this attribute include user-named, *NEW, and *CALLER. The following is a brief description of these options:

user-named
A named activation group allows you to manage a collection of ILE programs and ILE service programs as one application. The activation group is created when it is first needed. All programs and service programs that specify the same activation group name use it then.
*NEW
The name for this activation group is selected by ILE and will always be unique. System-named activation groups are always deleted when the high level language returns. *NEW is the standard behavior that can be expected on other systems such as UNIX®.
*CALLER
Specifying *CALLER causes the ILE program or service program to be activated within the activation group of the calling program. A new activation group is never created with this attribute.
Notes:
  1. When you create a persistent CGI program, you must specify a named activation group.
  2. CGI programs that are not persistent should not refer to job-level scoped resources.

For additional information about activation groups see the ILE Concepts ILE Concepts, SC41-5606 book.

CGI Considerations

There are advantages to running CGI programs in either a user-named or *CALLER activation group. The performance overhead associated with activating a CGI every time that is requested can be drastically reduced. It is important to understand that because the system does not delete user-named activation groups, normal high level language end verbs cannot provide complete end processing. For example, the system will not close open files, and the system will not return the static and heap storage that are allocated by a program. The program must manage these resources explicitly. This will be especially important when changing the activation group of CGI programs that rely on their end processing functions to run properly.

Note: When you activate multi-threaded CGI on your web server, you get multiple thread support for your CGI application Your CGI application must end all of its threads before returning to the server. When using multi-thread capable CGI, you need to put the CGI program in a new or named activation group.

The following section shows examples which will work fine running in a *NEW activation group, however will cause problems if run in a user-named or *CALLER activation group.

Activation Group Problem Examples

Note: The following examples are not general CGI programming examples. For general CGI programming examples, see Chapter 7, “Sample programs (in Java™, ILE C, and ILE RPG)” on page 49.
Note: CGI programming examples are also available on the IBM® HTTP Server for i5/OS™ website.

In the following example a CGI program when run in a *NEW activation group, would write Hello World to the browser. What is important to understand is that this application is taking advantage of job end processing to delete the stdio buffers that are used to buffer the stdout data.

You could build the following CGI program to run in either a user-named or *CALLER activation group. In such an instance, the server will not process the information that was written to stdout. This will cause the web browser to display a ″Document Contains No Data″ error message. Another application could run again in the same activation group that properly erased stdout. In this instance, the data that has been buffered from previous calls would be sent.

#include <stdio.h>
void main(void) {
/***************************************************/
/* Write header information. */
/***************************************************/
printf("Content-type: text/html\n\n");
/***************************************************/
/* Write header information. */
/***************************************************/
printf("Hello World\n");
}

End processing may not erase stdio buffers so the application must erase the stdout with a fflush(stdout) call. The following example will work regardless of the activation group specification:

#include <stdio.h>
void main(void) {
/***************************************************/
/* Write header information. */
/***************************************************/
printf("Content-type: text/html\n\n");
/***************************************************/
/* Write header information. */
/***************************************************/
printf("Hello World\n");
/*-------------------------------------------------*/
/* FIX: Flush stdout. */
/*-------------------------------------------------*/
fflush(stdout);
}

When run in a *NEW activation group, this example CGI would read CONTENT_LENGTH bytes of data from stdin and write this back out to stdout. The system has allocated the buffer that is used to hold the data with a malloc. Like the example that is previously shown, this application is relying on several aspects of job end processing to function properly.

If this CGI program were built to run in either a user-named or *CALLER activation group, the following problems would occur:

/*************************************************************************/
/* */
/* CGI Example program. */
/* */
/*************************************************************************/
#include
void main(void)
{
char* stdinBuffer;
char* contentLength;
int numBytes;
int bytesRead;
FILE* pStdin;
/**********************************************************************/
/* Write the header. */
/**********************************************************************/
printf("Content-type: text/html\n\n");
/**********************************************************************/
/* Get the length of data on stdin. */
/**********************************************************************/
contentLength = getenv("CONTENT_LENGTH");
if (contentLength != NULL) {
/*******************************************************************/
/* Allocate storage and clear the storage to hold the data. */
/*******************************************************************/
numBytes = atoi(contentLength);
stdinBuffer = (char*)malloc(numBytes+1);
if ( stdinBuffer )
memset(stdinBuffer, 0x00, numBytes+1);
/*******************************************************************/
/* Read the data from stdin and write back to stdout. */
/*******************************************************************/
bytesRead = fread(stdinBuffer, 1, numBytes, pStdin);
stdinBuffer[bytesRead+1] = ’\0’;
printf("%s", stdinBuffer);
} else
printf("Error getting content length\n");
return;
}

The following example shows the changes that would be required to this application to allow it to run in a user-named or *CALLER activation group:

/*************************************************************************/
/* */
/* CGI Example program with changes to support user-named */
/* and *CALLER ACTGRP. */
/* */
/*************************************************************************/
#include
void main(void)
{
char* stdinBuffer;
char* contentLength;
int numBytes;
int bytesRead;
FILE* pStdin;
/**********************************************************************/
/* Write the header. */
/**********************************************************************/
printf("Content-type: text/html\n\n");
/**********************************************************************/
/* Get the length of data on stdin. */
/**********************************************************************/
contentLength = getenv("CONTENT_LENGTH");
if (contentLength != NULL) {
/*******************************************************************/
/* Allocate storage and clear the storage to hold the data. */
/*******************************************************************/
numBytes = atoi(contentLength);
stdinBuffer = (char*)malloc(numBytes+1);
if ( stdinBuffer )
memset(stdinBuffer, 0x00, numBytes+1);
/*-----------------------------------------------------------------*/
/* FIX 2: Reset stdin buffers. */
/*-----------------------------------------------------------------*/
pStdin = freopen("", "r", stdin);
/*******************************************************************/
/* Read the data from stdin and write back to stdout. */
/*******************************************************************/
bytesRead = fread(stdinBuffer, 1, numBytes, pStdin);
stdinBufferþbytesRead+1þ = ’\0’;
printf("%s", stdinBuffer);
/*-----------------------------------------------------------------*/
/* FIX 3: Free allocated memory. */
/*-----------------------------------------------------------------*/
free(stdinBuffer);
} else
printf("Error getting content length\n");
/*-------------------------------------------------------------------*/
/* FIX 1: Flush stdout. */
/*-------------------------------------------------------------------*/
fflush(stdout);
return;
}