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. |