mod_annot editor

Annotate Section

The Configuration Hierarchy

We have now dealt with creating the configuration structures and populating them using configuration directives. The final topic we need to deal with is managing the configuration hierarchy: how directives set at different levels interact with each other. This is the purpose of the merge functions in the module struct.

A merge function is called whenever there are directives at more than one level in a hierarchy, starting at the top level of httpd.conf. In the case of the per-directory config there may be several levels and thus several calls to a merge function, incorporating htaccess files (if applicable) as well as sections in httpd.conf.

A merge function may also be NULL. In that case all directives in the less-specific container are discarded, so incremental configuration is not possible. Nevertheless, it is perfectly adequate for some modules.

More typically, we want the merge function to honour directives set in the more specific container, but inherit values that are not explicitly set. This is where we need a merge function. Consider the following example:

typedef struct {
  int a , b , c :
} my_dir_cfg;

with directives to set each of these, and a configuration

<Location />
	SetMyA	123
	SetMyC	321
<Location /somewhere/>
	SetMyB	456
<Location /somewhere/else/again/>
	SetMyC	789

Here the most specific section is /somewhere/else/again/, so in the absence of a directory merge function, c will be set to 789 but the values of a and b are unset. We need a merge function, which takes the generic form:

static void* my_merge_dir_conf(apr_pool_t* pool, void* BASE, void* ADD) {
    my_dir_cfg* base = BASE ;
    my_dir_cfg* add = ADD ;
    my_dir_cfg* conf = apr_palloc(pool, sizeof(my_dir_cfg)) ;
    conf->a = ( add->a == UNSET ) ? base->a : add->a ;
    conf->b = ( add->b == UNSET ) ? base->b : add->b ;
    conf->c = ( add->c == UNSET ) ? base->c : add->c ;
    return conf ;

To make this effective, we define the value of UNSET to some value that won't be used (e.g. -1 if our integers will always be positive), and initialise them to that in our create_config function. Now our configuration is processed as follows:

  1. At the top level, a is set to 123 and c to 321 while b is unset.
  2. The first merge sets b to 456. Since a and c are not set (overridden) at this level, the previous values are inherited in the merge.
  3. There are no configuration directives at /somewhere/else/, so this level simply inherits from /somewhere/ without any need for a merge.
  4. The second merge sets the value of c overriding the previous setting, while inheriting the previous values of a and b. Now we have a=123, b=456, c=789.

This is obviously a trivial merge function. Often we may need to do something a little more interesting: for example to merge nontrivial structures, or to deal with cases where there is no meaningful UNSET value to test. When merging structures involving pointers, take care about modifying the originals: it's usually safer to make a copy unless you're using a standard APR datatype with its merge functions. You'll just have to deal with each case on its merits.