From 148a6abbc2fed7ecacd2a30eb89e9e84ef1b7ff1 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Fri, 14 Feb 2025 11:47:11 +0100 Subject: [PATCH] introduce isc_cfgmgr_foreachnodes isc_cfgmgr_foreachnodes/foreachproperties enable to list each direct sub-nodes/properties form within a node. This is needed in general, and will make it easier to have a dump API which also will be needed to make materialization testing easier. The implementation is not great because it's likely slow (lot of LMDB lookups and string manipulation). But at least it enables to expose the API so we can built up on top of it, and come back to a better implementation later. --- lib/isc/cfgmgr.c | 244 ++++++++++++++++++++---------- lib/isc/include/isc/cfgmgr.h | 9 ++ tests/isc/cfgmgr_test.c | 286 ++++++++++++++++++++++++++++++++++- 3 files changed, 461 insertions(+), 78 deletions(-) diff --git a/lib/isc/cfgmgr.c b/lib/isc/cfgmgr.c index 51e79f4945..610549eb1e 100644 --- a/lib/isc/cfgmgr.c +++ b/lib/isc/cfgmgr.c @@ -51,6 +51,9 @@ typedef struct { MDB_cursor *builtincursor; MDB_cursor *usercursor; MDB_cursor *runningcursor; + MDB_dbi builtindbi; + MDB_dbi userdbi; + MDB_dbi runningdbi; MDB_txn *txn; bool readonly; isc_cfgmgr_mode_t mode; @@ -75,17 +78,16 @@ static thread_local char isc__cfgmgr_lasterror[LASTERRORLEN]; static bool isc__cfgmgr_openlmdbcursor(const char *dbname, MDB_cursor **cursor, - bool readonly) { - MDB_dbi dbi; + MDB_dbi *dbi, bool readonly) { int result; result = mdb_dbi_open(isc__cfgmgr_ctx.txn, dbname, - readonly ? 0 : MDB_CREATE, &dbi); + readonly ? 0 : MDB_CREATE, dbi); if (result != 0) { return false; } - result = mdb_cursor_open(isc__cfgmgr_ctx.txn, dbi, cursor); + result = mdb_cursor_open(isc__cfgmgr_ctx.txn, *dbi, cursor); if (result != 0) { return false; } @@ -117,11 +119,13 @@ isc__cfgmgr_startlmdbtransaction(bool readonly) { INSIST(isc__cfgmgr_ctx.txn != NULL); if (!isc__cfgmgr_openlmdbcursor( - "builtin", &isc__cfgmgr_ctx.builtincursor, readonly) || + "builtin", &isc__cfgmgr_ctx.builtincursor, + &isc__cfgmgr_ctx.builtindbi, readonly) || !isc__cfgmgr_openlmdbcursor("user", &isc__cfgmgr_ctx.usercursor, - readonly) || - !isc__cfgmgr_openlmdbcursor( - "running", &isc__cfgmgr_ctx.runningcursor, readonly)) + &isc__cfgmgr_ctx.userdbi, readonly) || + !isc__cfgmgr_openlmdbcursor("running", + &isc__cfgmgr_ctx.runningcursor, + &isc__cfgmgr_ctx.runningdbi, readonly)) { isc__cfgmgr_closelmdbcursors(); mdb_txn_abort(isc__cfgmgr_ctx.txn); @@ -268,6 +272,22 @@ isc__cfgmgr_lmdbcursor(void) { UNREACHABLE(); } +static MDB_dbi +isc__cfgmgr_lmdbdbi(void) { + switch (isc__cfgmgr_ctx.mode) { + case ISC_CFGMGR_MODEUNKNOWN: + UNREACHABLE(); + case ISC_CFGMGR_MODEBUILTIN: + return isc__cfgmgr_ctx.builtindbi; + case ISC_CFGMGR_MODEUSER: + return isc__cfgmgr_ctx.userdbi; + case ISC_CFGMGR_MODERUNNING: + return isc__cfgmgr_ctx.runningdbi; + } + + UNREACHABLE(); +} + static void isc__cfgmgr_setlasterror(const char *fmt, ...) { va_list ap; @@ -391,18 +411,34 @@ isc_cfgmgr_rollback(void) { isc__cfgmgr_checkofftransaction(); } -static void -isc__cfgmgr_buildkey(const char *name, bool addleading, char *b, size_t len) { - const char *prefix = "/"; - const char *leading = addleading ? "/" : ""; - - REQUIRE(name != NULL); - - if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openednodes) == false) { - prefix = ISC_LIST_TAIL(isc__cfgmgr_ctx.openednodes)->prefix; +static isc__cfgmgr_node_t * +isc__cfgmgr_curnode(void) { + if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openednodes)) { + return NULL; } - REQUIRE(snprintf(b, len, "%s%s%s", prefix, name, leading) <= (int)len); + return ISC_LIST_TAIL(isc__cfgmgr_ctx.openednodes); +} + +static const char * +isc__cfgmgr_curprefix(void) { + const isc__cfgmgr_node_t *node = isc__cfgmgr_curnode(); + + if (node) { + return node->prefix; + } + + return "/"; +} + +static void +isc__cfgmgr_buildkey(const char *name, const char *term, char *b, size_t len) { + REQUIRE(name != NULL); + REQUIRE(term != NULL); + REQUIRE(b != NULL); + + REQUIRE(snprintf(b, len, "%s%s%s", isc__cfgmgr_curprefix(), name, + term) <= (int)len); } void @@ -421,7 +457,7 @@ isc_cfgmgr_open(const char *name) { }; strcpy(node->name, name); - isc__cfgmgr_buildkey(name, true, node->prefix, BUFLEN); + isc__cfgmgr_buildkey(name, "/", node->prefix, BUFLEN); ISC_LIST_APPEND(isc__cfgmgr_ctx.openednodes, node, link); } @@ -431,7 +467,7 @@ isc_cfgmgr_close(void) { isc__cfgmgr_checkopenednodes(); - node = ISC_LIST_TAIL(isc__cfgmgr_ctx.openednodes); + node = isc__cfgmgr_curnode(); ISC_LIST_UNLINK(isc__cfgmgr_ctx.openednodes, node, link); isc_mem_free(isc__cfgmgr_mctx, node->name); isc_mem_put(isc__cfgmgr_mctx, node->prefix, BUFLEN); @@ -440,19 +476,19 @@ isc_cfgmgr_close(void) { void isc_cfgmgr_delnode(void) { - MDB_val dbkey; - char *prefix; + const char *prefix; isc__cfgmgr_checkopenednodes(); REQUIRE(isc__cfgmgr_ctx.readonly == false); - prefix = ISC_LIST_TAIL(isc__cfgmgr_ctx.openednodes)->prefix; - dbkey = (MDB_val){ .mv_size = strlen(prefix) + 1, .mv_data = prefix }; - + prefix = isc__cfgmgr_curprefix(); do { /* * Recursively delete nested nodes and properties. */ + MDB_val dbkey = { .mv_size = strlen(prefix) + 1, + .mv_data = (void *)prefix }; + int result = mdb_cursor_get(isc__cfgmgr_lmdbcursor(), &dbkey, NULL, MDB_SET_RANGE); @@ -481,7 +517,7 @@ isc_cfgmgr_getval(const char *name, isc_cfgmgr_val_t *value) { REQUIRE(name != NULL); REQUIRE(value != NULL); - isc__cfgmgr_buildkey(name, false, key, sizeof(key)); + isc__cfgmgr_buildkey(name, "", key, sizeof(key)); dbkey = (MDB_val){ .mv_size = strlen(key) + 1, .mv_data = key }; if (mdb_cursor_get(isc__cfgmgr_lmdbcursor(), &dbkey, &dbval, MDB_SET) != @@ -519,7 +555,7 @@ isc_cfgmgr_setval(const char *name, const isc_cfgmgr_val_t *value) { REQUIRE((value != NULL && value->type != ISC_CFGMGR_UNDEFINED) || value == NULL); - isc__cfgmgr_buildkey(name, false, key, sizeof(key)); + isc__cfgmgr_buildkey(name, "", key, sizeof(key)); dbkey = (MDB_val){ .mv_size = strlen(key) + 1, .mv_data = key }; if (value == NULL) { @@ -568,61 +604,115 @@ out: return result; } -void -isc_cfgmgr_foreachnodes(void (*action)(void *state, const char *name), - void *state) { - UNUSED(state); - UNUSED(action); +static void +isc__cfgmgr_foreach(void (*actionnode)(void *state, const char *name), + void (*actionproperty)(void *state, const char *name, + const isc_cfgmgr_val_t *val), + void *state) { + MDB_cursor *cursor; + MDB_val dbkey; + const char *prefix = isc__cfgmgr_curprefix(); + size_t prefixlen = strlen(prefix); + size_t i; + char name[BUFLEN]; + int result; + bool foundnode; + + isc__cfgmgr_checkontransaction(); + REQUIRE(mdb_cursor_open(isc__cfgmgr_ctx.txn, isc__cfgmgr_lmdbdbi(), + &cursor) == 0); + + name[0] = 0; + dbkey.mv_data = (void *)prefix; + dbkey.mv_size = prefixlen; + result = mdb_cursor_get(cursor, &dbkey, NULL, MDB_SET_RANGE); /* - * Not implemented yet, but here's the plan. Let's consider the - * following keys: + * The current implementation is slow: first, it looked up the first key + * which has the same prefix than the current one. Then, the code find + * the first "/" after the prefix to find the next sub-node name. If + * there none, the current entry is a property and it retry on the next + * one. * - * /foo/bar/gee/f - * /foo/bar/gff/g - * /foo/bar/gdd - * /foo/prop - * /foo/zz/a - * /foo/zz/c + * When a sub-node name is found, it checks if it matches the previous + * found name, if it does, it moves on to the next entry (so this is + * typically _lot_ of LMDB lookup to go to the next sub-node, + * especially with zone with lot of properties). * - * Then action will be called once, with "bar" as name, then another - * time with "zz" as name. To be fast, the implementation consist to - * have a internal sub-node in "foo" (or any node with sub-nodes) which - * contains its direct sub-nodes. So here, we would actually also have - * the following key,values: + * When a sub-node name is found and does not match the previous one, + * the action function is called, passing the sub-node name as "name". * - * __internal_idx__/foo/bar/,bar - * __internal_idx__/foo/zz/,zz + * This also enable to iterate over direct properties of the currently + * opened node. This seems particularly useful as base-API to build + * custom configuration printer. (Other approach I attempted was to + * provide a "configuration format" that cfgmgr was reading to validate + * or call user-provided function to build configuration blocks (when a + * node is found) or properties. I might come back to this, but in + * meantime this seems handy to write tests/validate approaches). * - * What _foreachnodes is doing is: - * - * - create a new (local) cursor - * - make a search for "__internal_idx__/foo/" (so it takes the - * current prefix and prefix "__internal_idx__" on it) - * - then, for each entry until we reach the end of entries starting - * by "__internal_idex__/foo/", we call _foreachnodes, passing the - * value as "name". - * - * The caller is free to open the sub-node, or do whathever needed, i.e. - * also call _foreachnodes on its own sub-nodes, because _foreachnodes - * call has its own cursor, it won't mess it up. - * - * In order to maintains this internal __internal__idx__ node, every - * time a property is added to a node "bar", its prefix is taken, then - * "__internal_idx__" is appened to it, making a key. This key is added - * with the value "bar" (current node name). Then this key,val is added - * to current LMDB DB. - * - * The similar logic is applied if the node is removed (taking the node - * prefix, prefixing it with "__internal_idx__" and delete all entries - * starting with this key. However, it is a little bit more subtle: it - * also needs to verify, anytime a property is removed, if there are - * remaining properties for the current node. If not, then it also need - * to remove the "__internal_idx__/nodeprefix" as well. - * - * Because the API enforces the fact that a regular key is always - * starting with "/", the "__internal_idx__" key,values are indeed - * internal and can't never be accessed/overridden by API misuse, nor - * clash with view/zones/any user provided names. + * I tried various alternatives and tricks (i.e. adding "indexes" to + * have a list of sub-nodes), but this is tricky and very time consuming + * to get right, so I leave this problem on the side for now, at least + * the API work and make it possible to move on/experiment with it. */ +again: + if (result != MDB_SUCCESS || + strncmp(prefix, dbkey.mv_data, prefixlen) != 0) + { + goto out; + } + + i = prefixlen; + foundnode = false; + while (i < dbkey.mv_size) { + if (((char *)dbkey.mv_data)[i] == '/') { + foundnode = true; + break; + } + i++; + } + + if (foundnode && actionnode) { + char newname[BUFLEN]; + size_t namelen = i - prefixlen; + + INSIST(namelen < sizeof(newname)); + strncpy(newname, (char *)dbkey.mv_data + prefixlen, namelen); + newname[namelen] = 0; + + if (strcmp(name, newname) != 0) { + actionnode(state, newname); + strcpy(name, newname); + } + } else if (!foundnode && actionproperty) { + char propname[BUFLEN]; + size_t namelen = i - prefixlen; + isc_cfgmgr_val_t val; + + INSIST(namelen < sizeof(propname)); + strncpy(propname, (char *)dbkey.mv_data + prefixlen, namelen); + propname[namelen] = 0; + + REQUIRE(isc_cfgmgr_getval(propname, &val) == ISC_R_SUCCESS); + actionproperty(state, propname, &val); + } + + result = mdb_cursor_get(cursor, &dbkey, NULL, MDB_NEXT); + goto again; + +out: + mdb_cursor_close(cursor); +} + +void +isc_cfgmgr_foreachnodes(void(*action)(void *state, const char *name), + void *state) { + isc__cfgmgr_foreach(action, NULL, state); +} + +void +isc_cfgmgr_foreachproperties(void (*action)(void *state, const char *name, + const isc_cfgmgr_val_t *val), + void *state) { + isc__cfgmgr_foreach(NULL, action, state); } diff --git a/lib/isc/include/isc/cfgmgr.h b/lib/isc/include/isc/cfgmgr.h index a850bed4d6..3b8f7a3f3d 100644 --- a/lib/isc/include/isc/cfgmgr.h +++ b/lib/isc/include/isc/cfgmgr.h @@ -153,6 +153,15 @@ void isc_cfgmgr_foreachnodes(void(*action)(void *state, const char *name), void *state); +/* + * Calls function "action" for each direct property of the currently opened + * node. Must be called under a transaction. + */ +void +isc_cfgmgr_foreachproperties(void (*action)(void *state, const char *name, + const isc_cfgmgr_val_t *val), + void *state); + /* * Starts a read-only transaction and returns ISC_R_SUCCESS. If there is an * issue creating a transaction, ISC_R_FAILURE is returned. Prior calling this diff --git a/tests/isc/cfgmgr_test.c b/tests/isc/cfgmgr_test.c index b27e9eb6dc..55143c074f 100644 --- a/tests/isc/cfgmgr_test.c +++ b/tests/isc/cfgmgr_test.c @@ -772,7 +772,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_delete) { isc_cfgmgr_open("foo"); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_getval("prop1", &val); +result = isc_cfgmgr_getval("prop1", &val); assert_int_equal(result, ISC_R_SUCCESS); result = isc_cfgmgr_getval("prop2", &val); @@ -1319,6 +1319,288 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_opentests) { isc_cfgmgr_deinit(); } +static void +isc__cfgmgr_foreachnodesaction(void *state, const char *name) { + isc_buffer_t *b = state; + + isc_buffer_printf(b, "%s ", name); +} + +static void +isc__cfgmgr_foreachpropertiesaction(void *state, const char *name, + const isc_cfgmgr_val_t *val) { + isc_buffer_t *b = state; + + isc_buffer_printf(b, "%s=", name); + switch (val->type) { + case ISC_CFGMGR_UINT32: + isc_buffer_printf(b, "%u ", val->uint32); + break; + case ISC_CFGMGR_STRING: + isc_buffer_printf(b, "%s ", val->string); + break; + default: + UNREACHABLE(); + } +} + +static void +isc__cfgmgr_foreachnodesactionreentrant(void *state, const char *name) { + isc_buffer_t *b = state; + + isc_buffer_printf(b, "%s[", name); + isc_cfgmgr_open(name); + isc_cfgmgr_foreachnodes(isc__cfgmgr_foreachnodesactionreentrant, state); + isc_cfgmgr_close(); + isc_buffer_printf(b, "] "); +} + +ISC_RUN_TEST_IMPL(isc_cfgmgr_foreachnodes) { + isc_result_t result; + isc_cfgmgr_val_t val; + + result = isc_cfgmgr_init(mctx, TEST_DBPATH); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_cfgmgr_rwtransaction(); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_cfgmgr_open("bar"); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 42 }; + result = isc_cfgmgr_setval("propbar1", &val); + assert_int_equal(result, ISC_R_SUCCESS); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "barstr" }; + result = isc_cfgmgr_setval("another-stuff", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_open("foo"); + + isc_cfgmgr_open("foo2"); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "foo2str" }; + result = isc_cfgmgr_setval("another-foo-stuff", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_open("foo1"); + + isc_cfgmgr_open("foo11"); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 123 }; + result = isc_cfgmgr_setval("fooprop", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_open("foo12"); + result = isc_cfgmgr_setval("prop", &val); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 456 }; + result = isc_cfgmgr_setval("fooprop", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_open("foo13"); + result = isc_cfgmgr_setval("prop", &val); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 789 }; + result = isc_cfgmgr_setval("fooprop", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_close(); /* foo1 */ + isc_cfgmgr_close(); /* foo */ + + isc_buffer_t b; + char data[512]; + data[0] = 0; + + /* + * bar doesn't have direct sub-nodes + */ + isc_cfgmgr_open("bar"); + isc_buffer_init(&b, data, sizeof(data)); + isc_cfgmgr_foreachnodes(isc__cfgmgr_foreachnodesaction, &b); + assert_string_equal(b.base, ""); + isc_cfgmgr_close(); + + /* + * foo1 has 3 direct sub-nodes + */ + isc_cfgmgr_open("foo"); + isc_cfgmgr_open("foo1"); + isc_buffer_init(&b, data, sizeof(data)); + isc_cfgmgr_foreachnodes(isc__cfgmgr_foreachnodesaction, &b); + assert_string_equal(b.base, "foo11 foo12 foo13 "); + isc_cfgmgr_close(); + isc_cfgmgr_close(); + + /* + * isc_cfgmgr_foreachnodes is re-entrant + */ + isc_cfgmgr_open("foo"); + isc_buffer_init(&b, data, sizeof(data)); + isc_cfgmgr_foreachnodes(isc__cfgmgr_foreachnodesactionreentrant, &b); + assert_string_equal(b.base, "foo1[foo11[] foo12[] foo13[] ] foo2[] "); + isc_cfgmgr_close(); + + /* + * foreachproperties + */ + isc_cfgmgr_open("bar"); + isc_buffer_init(&b, data, sizeof(data)); + isc_cfgmgr_foreachproperties(isc__cfgmgr_foreachpropertiesaction, &b); + assert_string_equal(b.base, "another-stuff=barstr propbar1=42 "); + isc_cfgmgr_close(); + + isc_cfgmgr_rollback(); + isc_cfgmgr_deinit(); +} + +typedef struct isc__cfgmgr_dump isc__cfgmgr_dump_t; +struct isc__cfgmgr_dump { + size_t indent; + size_t lvl; + isc_buffer_t buffer; + char data[512]; +}; + +static void +isc__cfgmgr_dumptestsprop(void *state, const char *name, + const isc_cfgmgr_val_t *val) { + isc__cfgmgr_dump_t *dump = state; + + for (size_t i = 0; i < dump->lvl * dump->indent; i++) { + isc_buffer_putstr(&dump->buffer, " "); + } + + isc_buffer_printf(&dump->buffer, "%s ", name); + switch (val->type) { + case ISC_CFGMGR_UINT32: + isc_buffer_printf(&dump->buffer, "%u", val->uint32); + break; + case ISC_CFGMGR_STRING: + isc_buffer_printf(&dump->buffer, "%s", val->string); + break; + default: + UNREACHABLE(); + } + isc_buffer_putstr(&dump->buffer, ";\n"); +} + +static void +isc__cfgmgr_dumptestnode(void *state, const char *name) { + isc__cfgmgr_dump_t *dump = state; + + for (size_t i = 0; i < dump->lvl * dump->indent; i++) { + isc_buffer_putstr(&dump->buffer, " "); + } + + isc_buffer_printf(&dump->buffer, "%s {\n", name); + + dump->lvl++; + isc_cfgmgr_open(name); + isc_cfgmgr_foreachproperties(isc__cfgmgr_dumptestsprop, state); + isc_cfgmgr_foreachnodes(isc__cfgmgr_dumptestnode, state); + isc_cfgmgr_close(); + dump->lvl--; + + for (size_t i = 0; i < dump->lvl * dump->indent; i++) { + isc_buffer_putstr(&dump->buffer, " "); + } + isc_buffer_printf(&dump->buffer, "};\n"); +} + +ISC_RUN_TEST_IMPL(isc_cfgmgr_dumptests) { + isc_result_t result; + isc_cfgmgr_val_t val; + + result = isc_cfgmgr_init(mctx, TEST_DBPATH); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_cfgmgr_rwtransaction(); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_cfgmgr_open("bar"); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 42 }; + result = isc_cfgmgr_setval("propbar1", &val); + assert_int_equal(result, ISC_R_SUCCESS); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "barstr" }; + result = isc_cfgmgr_setval("another-stuff", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_open("foo"); + + isc_cfgmgr_open("foo2"); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "foo2str" }; + result = isc_cfgmgr_setval("another-foo-stuff", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_open("foo1"); + + isc_cfgmgr_open("foo11"); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 123 }; + result = isc_cfgmgr_setval("fooprop", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_open("foo12"); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 123 }; + result = isc_cfgmgr_setval("prop", &val); + assert_int_equal(result, ISC_R_SUCCESS); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 456 }; + result = isc_cfgmgr_setval("fooprop", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_open("foo13"); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 123 }; + result = isc_cfgmgr_setval("prop", &val); + assert_int_equal(result, ISC_R_SUCCESS); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 789 }; + result = isc_cfgmgr_setval("fooprop", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_close(); /* foo1 */ + isc_cfgmgr_close(); /* foo */ + + isc__cfgmgr_dump_t dump = { + .lvl = 0, + .indent = 8, + }; + isc_buffer_init(&dump.buffer, dump.data, sizeof(dump.data)); + dump.data[0] = 0; + + /* TO BE FIXED... */ + isc_cfgmgr_foreachnodes(isc__cfgmgr_dumptestnode, &dump); + + const char expected[] = "bar {\n" + " another-stuff barstr;\n" + " propbar1 42;\n" + "};\n" + "foo {\n" + " foo1 {\n" + " foo11 {\n" + " fooprop 123;\n" + " };\n" + " foo12 {\n" + " fooprop 456;\n" + " prop 123;\n" + " };\n" + " foo13 {\n" + " fooprop 789;\n" + " prop 123;\n" + " };\n" + " };\n" + " foo2 {\n" + " another-foo-stuff foo2str;\n" + " };\n" + "};\n"; + assert_string_equal(dump.buffer.base, expected); + + isc_cfgmgr_rollback(); + isc_cfgmgr_deinit(); +} + // ISC_RUN_TEST_IMPL(isc_cfgmgr_builtintests) { // isc_result_t result; // isc_cfgmgr_val_t val; @@ -1786,6 +2068,8 @@ ISC_TEST_ENTRY(isc_cfgmgr_delete) ISC_TEST_ENTRY(isc_cfgmgr_nested_nodes) ISC_TEST_ENTRY(isc_cfgmgr_threads) ISC_TEST_ENTRY(isc_cfgmgr_opentests) +ISC_TEST_ENTRY(isc_cfgmgr_foreachnodes) +ISC_TEST_ENTRY(isc_cfgmgr_dumptests) // ISC_TEST_ENTRY(isc_cfgmgr_validationtest) // ISC_TEST_ENTRY(isc_cfgmgr_getparent)