From: Colin Vidal Date: Thu, 16 Oct 2025 13:48:05 +0000 (+0200) Subject: add cfg_effective_config() API X-Git-Tag: v9.21.15~22^2~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9d477aa3d4d2b1ad12f07605543be8b916381120;p=thirdparty%2Fbind9.git 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. --- diff --git a/lib/isccfg/include/isccfg/namedconf.h b/lib/isccfg/include/isccfg/namedconf.h index 2150b3b44b8..d2fa34b58ed 100644 --- a/lib/isccfg/include/isccfg/namedconf.h +++ b/lib/isccfg/include/isccfg/namedconf.h @@ -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); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 4b7a203ddb4..d28387549c9 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -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; +}