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
Customizing the Configuration Process

In this section...

Introduction
The configfile_t Structure
Using Configuration Streams
Writing Container Directives

Introduction

   Show Contents   Go to Top   Previous Page   Next Page

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.

The configfile_t Structure

   Show Contents   Go to Top   Previous Page   Next Page

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.

Using Configuration Streams

   Show Contents   Go to Top   Previous Page   Next Page

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

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 0 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
   Show Contents   Go to Top   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.