]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add cfg_effective_config() API
authorColin Vidal <colin@isc.org>
Thu, 16 Oct 2025 13:48:05 +0000 (15:48 +0200)
committerEvan Hunt <each@isc.org>
Wed, 29 Oct 2025 20:55:04 +0000 (13:55 -0700)
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.

lib/isccfg/include/isccfg/namedconf.h
lib/isccfg/namedconf.c

index 2150b3b44b87e33f0d069e9c0cd6a9bb1b72f141..d2fa34b58edfa642f243112641f25af432570d21 100644 (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);
index 4b7a203ddb4a619eb9f5171bf92edfa16ceb835b..d28387549c9f2ed71f0a21676d5aa8b1d72af19a 100644 (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;
+}