11.2. Customizing the Configuration Process

If Apache's standard configuration mechanism isn't sufficient for you, or you want to do something wild like generating dynamic configuration files on the fly (as mod_perl does in its <Perl> sections), you can reach into the internals of Apache's configuration machinery and make it do what you want. This section covers the more obscure parts of Apache's configuration system and shows you how to achieve advanced effects such as redirecting the configuration process from your own data source.

11.2.1. The configfile_t Structure

Apache uses a clever abstraction for its configuration process. Instead of reading the text of the configuration file directly from a FILE* or file descriptor, Apache interposes the concept of an abstract "configuration stream," the configfile_t pointer. Configuration streams are much like ordinary files, allowing your programs to read from them character by character or line by line, and they are often attached to real files. However, a configfile_t pointer may just as easily be attached to another process or even to a set of internal subroutines that read configuration information from a database. By creating a custom configfile_t pointer, your module can dynamically generate configuration text to feed directly into the Apache configuration machinery.

The configfile_t struct is defined in httpd.h. Its definition is reproduced in Example 11.4. The most important fields are the first three, which are pointers to callback functions that act like the getc(), fgets(), and close() standard I/O library functions. These three functions are used to implement the routines that fetch data from whatever file, process, or internal routine is attached to the data stream. The fourth field, param, is a void* that holds stream-specific data. In a configuration stream attached to a file, this might be the FILE*. In a stream attached to routines that read data from a database, this might be the database handle. This field is passed to the callback functions at runtime. The last two fields, name and line_number, contain a description of the data source and the number of the last-read line. These fields are used for reporting configuration syntax errors.

Example 11.4. The configfile_t Struct (from httpd.h)
typedef struct {
   int (*getch) (void *param); /* a getc()-like function */
                               /* an fgets()-like function */
   void *(*getstr) (void *buf, size_t bufsiz, void *param);
   int (*close) (void *param); /* a close() function */
   void *param;                /* the argument passed to getch/getstr/close */
   const char *name;           /* the filename / description */
   unsigned line_number;       /* current line number, starting at 1 */
} configfile_t;

Directive processing handlers can find a pointer to the currently active configfile_t stream by examining the config_file field of the passed parms argument.

11.2.2. Using Configuration Streams

The API calls listed in this section allow you to open configfile_t pointers on files, to read configuration data from them, and to create custom configfile_t streams that fetch their data from arbitrary sources. For the most part, you should access the configfile_t fields via the appropriate API functions listed in this section. Do not attempt to modify any of the fields directly.

The following short code fragment shows the basic outline for opening a configuration file, reading it line by line, then closing it:

char line[MAX_STRING_LEN];
configfile_t *cfg = ap_pcfg_openfile(p, file);

if(!cfg) {
    ap_log_error(APLOG_MARK, APLOG_CRIT, s,
                  "unable to open config file %s", file);
    exit(1);
}

while (!(ap_cfg_getline(line, sizeof(line), cfg))) {
   if(*line == '#' || !*line) {
       continue; /* skip comments and empty lines */
   }
   /* ... do something with the line ... */
}

ap_pcfg_closefile(cfg);


configfile_t *ap_pcfg_openfile (pool *p, const char *name)

The most common type of configfile_t is one that is opened on an ordinary text file. Examples include Apache's httpd.conf, srm.conf, and access.conf configuration files.

The ap_pcfg_openfile() function takes a resource pool pointer and the path of the configuration file to open. If successful, the function fills in the stream's param field with a FILE* opened on the requested file and sets up the first three fields so as to call back to functions that read data from the FILE*.[2]

[2] Actually, the FILE* is not stored directly into the param field. Instead, it is stored into an intermediate data type called a poolfile_t that contains both the FILE* and a resource pool pointer. It is this poolfile_t that gets stored into the param field.

If an error occurs while opening the file, the function logs an error message and returns NULL.


int ap_cfg_getline (char *buf, size_t bufsize, configfile_t *cfp)

The ap_cfg_getline() function reads a line of data from the given configfile_t pointer. The arguments consist of a character buffer, (buf), the maximum size of the buffer (bufsize), and the configfile_t pointer itself. The function fills the buffer with a line of configuration file data up to the maximum specified in bufsize, and returns the number of characters read as the function result. The function returns if no more data is left to read or if an error occurred.

The definition of a configuration file "line" is different from the usual one. The returned line is stripped of leading and trailing whitespace, and runs of space characters and other whitespace are replaced with single spaces, unless they are enclosed within quotes. The ap_cfg_getline() function also correctly handles continuation lines. Lines ending with the backslash character () are merged into single lines, and the newlines replaced with single spaces. For each line read, the configfile_t's line_number field is incremented by one.


int ap_cfg_getc ( configfile_t *cfp)

The ap_cfg_getc() function acts like getc() to return a single character from the configfile_t stream. The character is returned as the function result, or EOF is returned when there is no more data to be read. The line_number field is incremented when a linefeed character is seen, but continuation characters do not receive special treatment.


int ap_cfg_closefile (configfile_t *cfp)

Once you are done reading from a configfile_t, call ap_cfg_closefile() to close the file or release other resources.


configfile_t *ap_pcfg_open_custom (pool *p, const char *descr, void *param, int(*getc_ func)(void*), void *(*gets_ func) (void*, size_t, void*), int(*close_ func)(void*))

The ap_pcfg_open_custom() function can be used to open and initialize a new configuration stream. The long prototype for this function may seem intimidating, but it's actually straightforward. The first argument is a resource pool pointer, typically the pool pointer located in the server_rec passed to your module initialization handler. The second argument is a char* containing a description of the stream for use in error reporting. The third argument, param, is a generic pointer to any data you want to pass to the three callbacks. The fourth, fifth, and sixth arguments are function pointers to the callbacks themselves, corresponding to the routines that implement getc_ func(), fgets_ func(), and close_ func() behavior.

The prototypes for these three callbacks are as follows:

int getc_func (void *param);
int gets_func (void *buffer, size_t bufsize, void *param);
int close_func (void *param);

The getc_ func() should return a single character from the data stream. gets_ func() should return a whole line or the number of characters specified in bufsize, whichever is smaller. close_ func() should do whatever is necessary to close and deallocate the stream. Apache's core configuration routines only use ap_cfg_getline() to read from configuration streams, so it is possible in some circumstances to pass NULL for the getc_ func() pointer.

The only example of using ap_pcfg_open_custom() in the standard distribution is in http_config.c, where it is used to process the Apache -C and -c command line arguments. mod_perl also uses this function during processing of <Perl> sections. You'll see an example of using this function shortly.


const char *ap_srm_command_loop (cmd_parms *parms, void *cfgvector)

The ap_srm_command_loop() function is the core of Apache's internal configuration process. The function operates on the configuration stream contained within the passed cmd_parms pointer and the vector of per-directory module-specific configuration pointers contained within the server record's lookup_defaults field. The return value is NULL if the entire configuration stream was parsed correctly or a character string indicating the error if the loop terminated prematurely because of a syntax error.

Within the function, Apache reads one line of configuration data after another with ap_cfg_getline(). It parses each line into directive name and arguments, searches through the modules' command tables for the handler for this directive, then locates the correct per-directory configuration pointer within the configuration vector. The command parameters, configuration pointer, and directive arguments are then passed to the handler for processing.

If your module wishes to take over Apache's configuration process and configure everything from information stored within, say, a database, it can do so. For instance, your module might declare a ConfigFromDatabase directive that takes a single argument, the data source from which to read the configuration information:

ConfigFromDatabase ODBC:ApacheConfig

Then, to implement this directive, the directive handler can be written like this:

static const char *cfg_from_db_cmd(cmd_parms *parms, db_cfg *cfg,
                                    char *dsn)
{
    db *dbhandle = db_open(dsn);
    configfile_t old_cfg = parms->config_file; /*save old config stream */

    parms->config_file =
        ap_pcfg_open_custom(p,
                            "Database config",
                            (void *)dbhandle,
                            NULL,
                            db_getline,
                            db_close);

    char *errmsg = ap_srm_command_loop(parms,
                                       parms->server->lookup_defaults);
    if (errmsg) {
        ap_log_error(APLOG_MARK, APLOG_CRIT, s,
                     "unable to config from database %s" );
        return errmsg;
    }
    ap_cfg_closefile(parms->config_file);

    parms->config_file = old_cfg; /* restore configuration stream */
    return NULL;
}

Your code has to supply the db_open() and db_close() routines to open and close the database handle, as well as the db_getline() routine. This last routine must return directive strings in exactly the same way they would appear in an ordinary configuration file, such as:

Group apache
					

11.2.3. Writing Container Directives

cmd_parms->config_file is also useful for implementing your own container-style directives. The logic is the same as described in Chapter 7. In the command table, declare the start-section directive as a RAW_ARGS style directive and omit the trailing > from the directive name. You should also declare the end-section directive as a NO_ARGS command:

static command_rec traffic_cmds[] = 
{ 
    {"<TrafficCopSpeedLimits", spdlimit_container_cmd, NULL, 
     RSRC_CONF, RAW_ARGS, "a district speed limit container"}, 
    {"</TrafficCopSpeedLimits>", spdlimit_container_cmd_end, NULL, 
     RSRC_CONF, NO_ARGS, "end of speed limit container"}, 
    { NULL }, 
};

The command handler for the start-section directive uses a three-argument prototype similar to this one:

const char *spdlimit_container_cmd(cmd_parms *parms,
                                   void *mconfig, const char *args)

Everything to the right of the directive name will be passed in args as an unprocessed string. This string will include the terminal > symbol, so the command handler should be careful to strip off the character. Something like this will do the trick:

char *endp = strrchr(args, '>'),
if (!endp) {
   return "Syntax error: no terminal ">" sign";
}
*endp = '';

The routine should then call ap_getword_conf() (or one of the other ap_getword_ variants) in order to parse out the arguments and take the appropriate actions:

const char *pos = args;
char *nextword;
while (*pos && (nextword = ap_getword_conf(parms->pool, &pos))) {
    /* do something */
}

Now the directive handler will process the contents of the container. It does this by reading directly from parms->config_file until it finds the string that terminates the container. For each line it reads, it parses out the line and takes whatever action is appropriate:

char line[MAX_STRING_LEN];
while (!ap_cfg_getline(line, sizeof(line), parms->config_file)) {
   if (!strcasecmp(line, "</TrafficCopSpeedLimits>")) {
      break;
   }
   /* otherwise parse the line and do something with it */
}

(MAX_STRING_LEN, defined in httpd.h, is used for static string buffers in various parts of the Apache core.)

Because this loop swallows the container terminator, Apache will normally never even see it. The reason for including the end-section directive in the module's command table is to catch configuration errors in which the end-section directive appears without being preceded by a matching start-section directive. The handler for this directive returns an error string:

static const char *spdlimit_container_cmd_end(cmd_parms *parms,
                                               void *mconfig)
{
  return "</TrafficCopSpeedLimits> without matching
          <TrafficCopSpeedLimits> section";
}

You can also write a completely generic end-section directive handler by taking advantage of the information stored in parms:

static const char *end_section(cmd_parms *parms, void *mconfig) {
    return ap_pstrcat(parms->pool, parms->cmd->name,
         " without matching <", parms->cmd->name + 2, " section", NULL);
}

We now turn to utility functions for manipulating strings, URIs, dates, and files.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.47.144