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>
<Location /somewhere/>
SetMyB 456
</Location>
<Location /somewhere/else/again/>
SetMyC 789
</Location>
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:
- At the top level, a is set to 123 and c to 321 while b is unset.
- 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.
- There are no configuration directives at /somewhere/else/,
so this level simply inherits from /somewhere/ without
any need for a merge.
- 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.