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

In this section...

Introduction
Overview
Creating and Merging Configuration Data Blocks
The command_rec Structure
Constants for the override Field
Constants for the args_how Field
The cmd_parms Structure
Accessing Module Configuration Data
"Hello World" with All the Bells and Whistles
Handy Built-in Directive Handlers
Accessing Other Modules' Configuration Information

Introduction

   Show Contents   Go to Top   Previous Page   Next Page

The C-language API allows modules to install their own configuration directives. The directives' syntax and usage information are defined in a command_rec data structure and processed by directive handler callback routines defined within the module.

Whereas the configuration API is optional in Perl modules due to the catchall PerlSetVar directive, C-language programmers don't have this luxury. You'll have to create custom configuration directives in order to write any module that requires runtime configuration information.

Overview

   Show Contents   Go to Top   Previous Page   Next Page

Modules are responsible for managing their own configuration data. There are two general types of configuration data: data that apply to a particular server or virtual host and data that apply to a directory or URI. A module can maintain both server-specific and directory-specific configuration information or just one or the other, depending on its needs. Because there may be dozens of virtual hosts and hundreds of directories, the Apache API allows modules to juggle thousands of configuration records. During configuration file processing, Apache will call your module's configuration allocation and processing routines at the right time and in the right order to create the configuration data. Then, at request time, Apache will choose the correct configuration record to return to your handler when the handler requests it.

The work of creating and maintaining configuration records is done in several steps. In the first step, the module is given an opportunity to allocate storage for its private configuration settings and to create a reasonable set of defaults, if it chooses. The content of the configuration data is entirely private to your module. Apache sees it only as an opaque void*.

During the second step, which occurs during configuration file processing, the module's directives are parsed and passed back to your code for processing, along with the initialized configuration settings from the previous phase. There is one directive handler for each custom directive that your module declares. The directive handler will alter the configuration block in some way to record the meaning of the directive. Typically the handler will change the contents of a field or add an entry to a table.

The third step is the mysterious-sounding "merging" process. The idea is that configuration information is often nested. For example, a particular directive could appear in the main part of the configuration file, in a <VirtualHost> section, in a <Directory> section, and in a .htaccess file. When the directive appears in a nested scope, your module needs to handle the potential contradiction in some way, either by letting the nested directive take precedence over the parent directive or by merging the contents of the two somehow. Apache handles this process by calling your merge routines. These routines take the base configuration (the configuration that belongs to the parent scope) and the new configuration (the configuration that belongs to the nested section) and merge them into a new configuration block that combines them both.

The last step actually occurs during the transaction. Handlers that need access to their module's per-server or per-directory configuration settings request it from Apache. The ap_get_module_config() API function is able to perform a quick, one-step lookup of your module's configuration data relevant to the current transaction, no matter how many configuration blocks were constructed during server startup.

Creating and Merging Configuration Data Blocks

   Show Contents   Go to Top   Previous Page   Next Page

Your module declares its intent to maintain configuration information by filling in one or more of the slots in the module record labeled config creator or config merger. There are four such slots: one each for functions to create per-directory and per-server configuration settings and one each for merging per-directory and per-server data. The four functions have the following prototypes:

void *create_dir_config(pool *p, char *dir)
void *merge_dir_config(pool *p, void *base_conf, void *new_conf)
void *create_server_config(pool *p, server_rec *s)
void *merge_server_config(pool *p, void *base_conf, void *new_conf)

The create_server_config() function is an opportunity for the module to allocate per-server configuration data. It is passed a resource pool and a server_rec server structure. It may, if it chooses, allocate a data structure from within the resource pool, initialize it, and return it to Apache as a void*.

create_dir_config() is similar, except that it is called to create per-directory configuration information (directives contained within <Directory>, <Location>, or .htaccess files). In this case, the subroutine is called with a resource pool and the name of the current directory or URI. The routine may, if it chooses, allocate a data structure for the per-directory information and return it to Apache as a void*.

As a concrete example, consider a "traffic cop" module that regulates the flow of traffic on a server. It has two configuration settings: one which sets the maximum speed limit on the server (in bits per second, say) and one which contains a list of domains that have "right-of-way" through the server and can fetch documents at a higher maximum speed. This module could store the information in the following per-server configuration record:

typedef struct {
  int    speed_limit;
  table *right_of_way;
} traffic_server_config;

The following definition of traffic_create_server_config() allocates the storage for the per-server configuration information and sets up appropriate defaults. speed_limit is set to 55 (whatever that means in this context!) and the right_of_way field is initialized to point to a new empty table.

static void *traffic_create_server_config (pool *p, server_rec *s) {
  traffic_server_config *cfg =
     (traffic_server_config *)ap_pcalloc(p, sizeof(traffic_server_config));
  cfg->speed_limit = 55;
  cfg->right_of_way = ap_make_table(p, 0);
  return (void *)cfg;
}

This initial data block will be passed back to your module's directive handlers as a void* when the time comes to process a directive. The handler should typecast the pointer back to the correct data type, then make the appropriate change to the affected field.

A create_dir_config() routine will look almost identical to this, but instead of receiving a server_rec in the second argument, it receives a string containing the path to the relevant directory or URI.

Later on in the process, Apache may be called upon to process a directive that needs to be merged into the parent configuration. You can define up to two such merge routines. The merge_server_config() routine is called at server startup time to merge directives in <VirtualHost> blocks with the configuration for the parent server. It receives a pointer to a resource pool, a pointer to the parent server configuration, and a pointer to the child server configuration. The merge routine's job is to create a new configuration structure that combines the two and to return it to Apache as a void*.

merge_dir_config() is similar, but it happens at request time and operates on two per-directory structures: the parent directory's configuration and the current directory's configuration. It is expected to merge the two and return a new per-directory configuration structure that combines the configurations in some sensible way.

For example, here is a plausible server merge routine for the traffic cop configuration. We want to overwrite the speed_limit field so that the current virtual host's setting supersedes that of the base host. However, instead of allowing the virtual host's right_of_way settings to supersede those of the parent server, we merge the two using ap_overlay_tables():

static void *traffic_merge_server_config (pool *p, void* base, void* new) {
  traffic_server_config *merged =
     (traffic_server_config*)ap_pcalloc(p, sizeof(traffic_server_config));
  traffic_server_config *parent = (traffic_server_config*)base;
  traffic_server_config *child  = (traffic_server_config*)new;
   merged->speed_limit = child->speed_limit ?
                        child->speed_limit : parent->speed_limit;
  merged->right_of_way = ap_overlay_tables(p, parent->right_of_way,
                                              child->right_of_way);
  return (void*)merged;
}

If your module does not define any merge routines, then Apache will use the configuration of the most recent server or directory configuration, ignoring any directives which previously have been defined for a block's ancestors. If your module defines no create_server_config() or create_dir_config() routine, then it will have no runtime configuration blocks. However, this doesn't mean that the module can't maintain any configuration information at all; it can still maintain some state in static variables. However, this information will be global to the module, rather than server-specific or directory-specific. This rarely works out the way you want it, nor is it thread-safe.

Rather than having the create_server_config() and create_dir_config() fill in the configuration records' fields with default values, it is often useful to have the two routines fill in the configuration fields with explicit UNSET values. This allows you to distinguish between fields that are unset and fields that just happen to have been set to the default value. It also simplifies merging because the assignment logic now becomes the following:

merged->attr = base->attr == UNSET ? base->attr : new->attr;

There is one major trap in the current Apache configuration API. If your module depends on per-server records and <VirtualHost> sections are in use, then at least one of your module's configuration directives must be present in the <VirtualHost> section or your module's create_server_config() routine will never be called. As a result, your module will have no chance to create its per-server configuration before its handlers are called at transaction time. There are two ways around this problem. You can simply DECLINE to handle the transaction if the per-server configuration block is NULL, or you can try to fill in the values of the configuration block on the fly.

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