Writing Apache Modules with Perl and C
By:   Lincoln Stein and Doug MacEachern
Published:   O'Reilly & Associates, Inc.  - March 1999

Copyright © 1999 by O'Reilly & Associates, Inc.


 


   Show Contents   Previous Page   Next Page

Chapter 11 - C API Reference Guide, Part II / Launching Subprocesses
A Practical Example

We are going to say "Goodbye World" now but this time in a very big way. We will add a "goodbye-banner" handler to mod_hello. This handler will run the Unix banner command to print out a large, vertically oriented "Goodbye World" message. Although this is a very simple example compared to what happens inside mod_cgi, it does show you everything you need to write basic fork/exec code. For advanced tricks and subtleties, we recommend you peruse the source code for mod_cgi and mod_include.

The additions to mod_hello.c are shown in Example 11-6. At the top, we add util_script.h to the list of included files and hardcode the absolute path to the banner program in the #define BANNER_PGM.

Example 11-6. Additions to mod_hello.c to Launch a Child Process

#include "util_script.h"
#define BANNER_PGM "/usr/bin/banner"
/* Forward declaration so that ap_get_module_config() can find us. */
module hello_module;
static int banner_child(void *rp, child_info *pinfo)
{
  char **env;
  int child_pid;
  request_rec *r = (request_rec *)rp;
   env = ap_create_environment(r->pool, r->subprocess_env);
  ap_error_log2stderr(r->server);
  r->filename = BANNER_PGM;
  r->args = "-w80+Goodbye%20World";
  ap_cleanup_for_exec();
  child_pid = ap_call_exec(r, pinfo, r->filename, env, 0);
#ifdef WIN32
  return(child_pid);
#else
  ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed", r->filename); 
exit(0); /*NOT REACHED*/ return(0); #endif }
static int goodbye_banner_handler(request_rec *r)
{
  BUFF *pipe_output;
  if (!ap_bspawn_child(r->pool, banner_child,
                       (void *) r, kill_after_timeout,
                       NULL, &pipe_output, NULL)) {
      ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
                   "couldn't spawn child process: %s", BANNER_PGM);
      return HTTP_INTERNAL_SERVER_ERROR;
  }
  r->content_type = "text/plain";
  ap_send_http_header(r);
  ap_send_fb(pipe_output, r);
  ap_bclose(pipe_output);
  return OK;
}
static handler_rec hello_handlers[] =
{
  {"hello-handler", hello_handler},
  {"goodbye-banner-handler", goodbye_banner_handler},
  {NULL}
};

Skipping over the definition of banner_child() for now, look at goodbye_banner_handler(). This is the content handler for the request. We are going to access the output of the banner command, so we declare a BUFF pointer for its standard output. Now we attempt to fork by calling ap_bspawn_child(). We pass the request record's resource pool as the first argument and the address of the banner_child() subroutine as the second. For the third argument, we use a copy of the request_rec, cast to a void*. We use kill_after_timeout for the kill conditions argument, which is the usual choice. We don't care about the banner program's standard input or standard error, so we pass NULL for the fifth and seventh arguments, but we do want to recover the program's output, so we pass the address of the pipe_output BUFF* for the sixth argument.

If ap_bspawn_child() succeeds, there will now be two processes. In the child process, ap_bspawn_child() immediately invokes the banner_child() function, which we will examine momentarily. In the parent process, ap_bspawn_child() returns the process ID of the child. If it encounters an error it will return 0, and the parent logs an error and returns HTTP_INTERNAL_SERVER_ERROR.

The remainder of what we have to do in the handler is simple. We set the outgoing response's content type to text/plain and send the HTTP header with ap_send_http_header(). Next we forward the child process's output to the browser by calling ap_send_fb(), which reads from the child and sends to the client in a single step. When this is done, we clean up by closing pipe_output and return OK.

The banner_child() function is called within the child spawned by ap_bspawn_child(). We're going to set up the environment, do a little cleanup, and then replace the process with the banner program. We begin by recovering the request record and passings its pool and subprocess_env fields to ap_create_environment(), obtaining an environment pointer. We then open the child's standard error stream to the error log by invoking ap_error_log2stderr().

We want to call banner as if it had been invoked by this command at the shell:

% banner -w80 "Goodbye World"

This specifies a banner 80 characters wide with a message of "Goodbye World". To do this, we place the command's full path in the request record's filename field, and set the args field to contain the string -w80+Goodbye%20World. Individual command arguments are separated by + symbols, and any character that would have special meaning to the shell, such as the space character, is replaced with a URL hex escape.

Before we launch banner we should invoke any cleanup handlers that have been registered for the current request. We do so by calling ap_cleanup_for_exec(). Now we call ap_call_exec() to run banner, passing the routine the request record, the pinfo pointer passed to the routine by Apache, the name of the banner program, and the environment array created by ap_create_environment(). We want Apache to pass arguments to banner, so we specify a shellcmd argument of false.

If all goes well, the next line is never reached on Unix platforms. But if for some reason Apache couldn't exec the banner program, we log an error and immediately exit. The return statement at the end of the routine is never reached but is there to keep the C compiler from generating a warning. As noted above, ap_call_exec() behaves differently on Win32 platforms because the function launches a new process rather than overlaying the current one. We handle this difference with conditional compilation. If the Win32 define is present, banner_child() returns the process ID generated by ap_call_exec(). We do this even though it isn't likely that the banner program will ever be ported to Windows platforms!

There's only one thing more to do to make the goodbye_banner_handler() available for use, which is to add it and a symbolic handler name to the hello_handlers[] array. We chose "goodbye-banner-handler" for this purpose. Now, by creating a <Location> section like this one, you can give the handler a whirl:

<Location /goodbye>
 SetHandler goodbye-banner-handler
</Location>

Figure 11-1 shows our handler in action, and this seems to be a good place to say goodbye as well.

Figure 11-1. "goodbye-banner-handler" re-creates a burst page from a circa-1960 line printer.

Footnotes

4 Note that the source code for ap_call_exec() refers to the return value as the "pid." This is misleading.    Show Contents   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.

HIVE: All information for read only. Please respect copyright!
Hosted by hive КГБ: Киевская городская библиотека