add cfg_effective_config() API

Add the entry point of the logic to merge the user and the default
configuration, called cfg_effective_config(). This function takes a user
configuration and a default configuration. It internally clones the user
configuration tree, then walks through the clauses recursively applying
default values if they are missing.

The newly built configuration tree, called the effective configuration
tree, is then returned.

Currently this is just the basic mechanism which is implemented (i.e.
enable to walk from clause to clause, goes into a nested clause, and so
on). The next commits will introduce the implementation of
clause-specific merge functions in order to preserve the existing
named.conf semantics.
This commit is contained in:
Colin Vidal 2025-10-16 15:48:05 +02:00 committed by Evan Hunt
parent a9a0af4359
commit 9d477aa3d4
2 changed files with 80 additions and 0 deletions

View file

@ -49,3 +49,14 @@ extern cfg_type_t cfg_type_zoneopts;
/*%< DNSSEC Key and Signing Policy options */
extern cfg_type_t cfg_type_dnssecpolicyopts;
/*%<
* Build the effective configuration, by cloning the user configuration then
* applying (merging) the default configuration on top of it (based on various
* specific rules regarding how a default statement is used/overridden when the
* user provides it, and possibly how some user provided statement might be
* internally changed).
*/
cfg_obj_t *
cfg_effective_config(const cfg_obj_t *userconfig,
const cfg_obj_t *defaultconfig);

View file

@ -1142,6 +1142,60 @@ static cfg_type_t cfg_type_fetchesper = { "fetchesper", cfg_parse_tuple,
cfg_print_tuple, cfg_doc_tuple,
&cfg_rep_tuple, fetchesper_fields };
static void
map_merge(cfg_obj_t *effectivemap, const cfg_obj_t *defaultmap) {
const void *clauses = NULL;
const cfg_clausedef_t *clause = NULL;
unsigned int i = 0;
for (clause = cfg_map_firstclause(effectivemap->type, &clauses, &i);
clause != NULL;
clause = cfg_map_nextclause(effectivemap->type, &clauses, &i))
{
isc_result_t defaultres;
isc_result_t effectiveres;
cfg_obj_t *effectiveobj = NULL;
const cfg_obj_t *defaultobj = NULL;
defaultres = cfg_map_get(defaultmap, clause->name, &defaultobj);
INSIST(defaultres == ISC_R_NOTFOUND ||
defaultres == ISC_R_SUCCESS);
effectiveres = cfg_map_get(effectivemap, clause->name,
(const cfg_obj_t **)&effectiveobj);
INSIST(effectiveres == ISC_R_NOTFOUND ||
effectiveres == ISC_R_SUCCESS);
/*
* If the clause has a specific case, let's delegate to its
* merge callback
*/
if (effectiveobj != NULL && defaultobj != NULL &&
clause->merge != NULL)
{
clause->merge(effectiveobj, defaultobj);
continue;
}
/*
* If the clause is defined in the default but not in the user
* config, let's clone it inside the user config
*/
if (effectiveres == ISC_R_NOTFOUND &&
defaultres == ISC_R_SUCCESS)
{
INSIST(cfg_map_addclone(effectivemap, defaultobj,
clause) == ISC_R_SUCCESS);
continue;
}
/*
* Otherwise, the clause is defined in user, so the default (if
* it exists) is ignored
*/
}
}
/*%
* Clauses that can be found within the top level of the named.conf
* file only.
@ -4070,3 +4124,18 @@ static cfg_type_t cfg_type_http_description = {
"http_desc", cfg_parse_named_map, cfg_print_map,
cfg_doc_map, &cfg_rep_map, http_description_clausesets
};
cfg_obj_t *
cfg_effective_config(const cfg_obj_t *userconfig,
const cfg_obj_t *defaultconfig) {
cfg_obj_t *effective = NULL;
REQUIRE(defaultconfig != NULL &&
defaultconfig->type == &cfg_type_namedconf);
REQUIRE(userconfig != NULL && userconfig->type == &cfg_type_namedconf);
cfg_obj_clone(userconfig, &effective);
map_merge(effective, defaultconfig);
return effective;
}