home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


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 / Implementing Configuration Directives in C
"Hello World" with All the Bells and Whistles

To show you how custom configuration directives work in practice, let's go ahead and add a directive handler table to the mod_hello example that we introduced long, long ago in Chapter 2, A First Module.

We start simply by adding the ability for users to configure mod_hello to say hello to something other than "world," which you are surely tired of by now. Once we are done making the required modifications to mod_hello.c, the message "Hello world" can be changed to "Hello Dolly" by adding this line to any configuration file:

HelloTo Dolly

The complete source code for the modified mod_hello.c is shown in Example 11-3.

The first change over the original version is to declare the module structure at the top of the source file. This is to allow the C compiler to handle forward references to the structure within the calls to ap_get_module_config(). The new line immediately follows the #include lines:

module hello_module;

Next, we declare a new data type to hold our per-directory configuration data, hello_dir_config. Its definition has a single field only, a char* named to, which will hold the argument of the HelloTo directive:

typedef struct {
   char *to;
} hello_dir_config;

Now we need to add a function to create a new per-directory structure. This will be called each time Apache notices one of the module's directives in a directory or location configuration section. Our function simply allocates a new hello_dir_config structure and initializes the to field to contain the default string world:

static void *hello_create_dir_config(pool *p, char *path)
{
  hello_dir_config *cfg =
    (hello_dir_config *)ap_pcalloc(p, sizeof(hello_dir_config));
  cfg->to = "world";
  return (void *)cfg;
}

Now we must modify the module structure so that the per-directory config creator slot is occupied by our new per-directory config creator function:

   hello_create_dir_config,  /* per-directory config creator  */

In this case, our configuration data is so simple that there's no need to write a directory config merge function. In the absence of a merge function, Apache will use the most specific configuration record, giving us the most recent value of Hello-To. This is exactly what we want.

What's next? We need a function to actually handle the directive. Once Apache hits the HelloTo directive, it will call this function, passing it a cmd_parms pointer, a pointer to our newly initialized hello_dir_config structure, and the directive argument. The hello_cmd_to() directive handling function is nice and simple. It makes a copy of the argument and stores it into the configuration structure. We return NULL to indicate that all went well:

static const char *hello_cmd_to(cmd_parms *parms,
                               void *mconfig, char *to)
{
   hello_dir_config *cfg = (hello_dir_config *)mconfig;
   cfg->to = (char*)ap_pstrdup(parms->pool, to);
   return NULL;
}

In order for Apache to know about our new directive, we need to create a command_rec table to register it with the module structure. The table declares a single directive named HelloTo whose command handler is hello_cmd_to(). The directive will be available anywhere in the configuration files and will take a single argument. There's no static information to pass to the handler, so this field is NULL:

static command_rec hello_cmds[] =
{
   {
       "HelloTo",                   /* directive name */
       hello_cmd_to,                /* config action routine */
       NULL,                        /* argument to include in call */
       OR_ALL,                      /* where available */
       TAKE1,                       /* arguments */
       "Who we say hello to, default is 'world'" /* description */
   },
   {NULL}
};

Notice that the command_rec table is terminated by a NULL record.

We can now add the hello_cmds array to the command table slot of the module structure. The complete module structure looks like this:

module MODULE_VAR_EXPORT hello_module =
{
   STANDARD_MODULE_STUFF,
   NULL,               /* module initializer                 */
   hello_create_dir_config,  /* per-directory config creator       */
   NULL,               /* dir config merger                  */
   NULL,               /* server config creator              */
   NULL,               /* server config merger               */
   hello_cmds,         /* command table                      */
   hello_handlers,     /* [9]  content handlers              */
   NULL,               /* [2]  URI-to-filename translation   */
   NULL,               /* [5]  check/validate user_id        */
   NULL,               /* [6]  check user_id is valid *here* */
   NULL,               /* [4]  check access by host address  */
   NULL,               /* [7]  MIME type checker/setter      */
   NULL,               /* [8]  fixups                        */
   NULL,               /* [10] logger                        */
   NULL,               /* [3]  header parser                 */
   NULL,               /* process initialization             */
   NULL,               /* process exit/cleanup               */
   NULL                /* [1]  post read_request handling    */
};

The last thing we need to do is to actually put the configuration data to use. In the content handler function hello_handler(), we add the following line to retrieve the configuration structure:

   hello_dir_config *cfg =  (hello_dir_config *)
      ap_get_module_config(r->per_dir_config, &hello_module);

Now we change the call to rputs(), where we used to print out "Hello world", into a call to rprintf() that uses the configuration information to print out a customized message:

  rprintf(r, "say \"hello %s\"?\n", cfg->to);

Recompile the module, restart the server, and start saying "Hello" to whomever you choose!

Example 11-3. mod_hello with a Custom Configuration Directive

/* file: mod_hello.c */
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
/* Forward declaration so that ap_get_module_config() can find us. */
module hello_module;
/* Here's our per-directory configuration data */
typedef struct {
   char *to;
} hello_dir_config;
/* This function is called to create the default per-directory
  configuration */
static void *hello_create_dir_config(pool *p, char *path)
{
   hello_dir_config *cfg =
       (hello_dir_config *)ap_pcalloc(p, sizeof(hello_dir_config));
   cfg->to = "world";
   return (void *)cfg;
}
/* This is the handler for the HelloTo directive */
static const char *hello_cmd_to(cmd_parms *parms, void *mconfig, char *to)
{
   hello_dir_config *cfg = (hello_dir_config *)mconfig;
   cfg->to = (char *)ap_pstrdup(parms->pool, to);
   return NULL;
}
/* Make the name of the content handler known to Apache */
static command_rec hello_cmds[] =
{
   {
       "HelloTo",              /* directive name */
       hello_cmd_to,           /* config action routine */
       NULL,                   /* argument to include in call */
       OR_ALL,                 /* where available */
       TAKE1,                  /* arguments */
       "Who we say hello to, default is 'world'" /* description */
   },
   {NULL}
};
/* here's the content handler */
static int hello_handler(request_rec *r) {
 const char* hostname;
 hello_dir_config *cfg;
 r->content_type = "text/html";
  ap_send_http_header(r);
 hostname = ap_get_remote_host(r->connection,
                               r->per_dir_config, REMOTE_NAME);
 cfg = (hello_dir_config *)
          ap_get_module_config(r->per_dir_config, &hello_module);
  ap_rputs("<HTML>\n", r);
 ap_rputs("<HEAD>\n", r);
 ap_rputs("<TITLE>Hello There</TITLE>\n", r);
 ap_rputs("</HEAD>\n", r);
 ap_rputs("<BODY>\n", r);
 ap_rprintf(r, "<H1>Hello %s</H1>\n", hostname);
 ap_rputs("Here we go again...", r);
 ap_rprintf(r, "\"Hello %s\"!\n", cfg->to);
 ap_rputs("</BODY>\n", r);
 ap_rputs("</HTML>\n", r);
  return OK;
}
/* Make the name of the content handler known to Apache */
static handler_rec hello_handlers[] =
{
   {"hello-handler", hello_handler},
   {NULL}
};
/* Tell Apache what phases of the transaction we handle */
module MODULE_VAR_EXPORT hello_module =
{
   STANDARD_MODULE_STUFF,
   NULL,               /* module initializer                 */
   hello_create_dir_config,  /* per-directory config creator       */
   NULL,               /* dir config merger                  */
   NULL,               /* server config creator              */
  NULL,               /* server config merger               */
   hello_cmds,         /* command table                      */
   hello_handlers,     /* [9]  content handlers              */
   NULL,               /* [2]  URI-to-filename translation   */
   NULL,               /* [5]  check/validate user_id        */
   NULL,               /* [6]  check user_id is valid *here* */
   NULL,               /* [4]  check access by host address  */
   NULL,               /* [7]  MIME type checker/setter      */
   NULL,               /* [8]  fixups                        */
   NULL,               /* [10] logger                        */
   NULL,               /* [3]  header parser                 */
   NULL,               /* process initialization             */
   NULL,               /* process exit/cleanup               */
   NULL                /* [1]  post read_request handling    */
};

   Show Contents   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.