diff --git a/lib/isc/cfgmgr.c b/lib/isc/cfgmgr.c index b91b09a6e8..51e79f4945 100644 --- a/lib/isc/cfgmgr.c +++ b/lib/isc/cfgmgr.c @@ -27,22 +27,27 @@ /* * See MDB_MAXKEYSIZE documentation, but not accessible as defined in * internal implementation. Having key with longer size won't work - * with LMDB. The value is 511 by default. + * with LMDB. The value is 511 by default. So this is used to specify LMDB + * buffer key sizes, as well random temporary buffers when needed. */ #define BUFLEN 511 -typedef struct openedclause openedclause_t; -struct openedclause { +/* + * Enough room to support big enough error message, especially if there are long + * domain names in the key + */ +#define LASTERRORLEN 4092 + +typedef struct isc__cfgmgr_node isc__cfgmgr_node_t; +struct isc__cfgmgr_node { char *name; - uint32_t id; - ISC_LINK(openedclause_t) link; + char *prefix; + ISC_LINK(isc__cfgmgr_node_t) link; }; -typedef ISC_LIST(openedclause_t) openedclauses_t; +typedef ISC_LIST(isc__cfgmgr_node_t) isc__cfgmgr_nodes_t; typedef struct { - openedclauses_t openedclauses; - char *prefix; - char *buffer; + isc__cfgmgr_nodes_t openednodes; MDB_cursor *builtincursor; MDB_cursor *usercursor; MDB_cursor *runningcursor; @@ -51,13 +56,10 @@ typedef struct { isc_cfgmgr_mode_t mode; } context_t; -static const isc_cfgmgr_clause_t **isc__cfgmgr_configformat = NULL; static isc_mem_t *isc__cfgmgr_mctx = NULL; static MDB_env *isc__cfgmgr_env = NULL; static thread_local context_t isc__cfgmgr_ctx = - (context_t){ .openedclauses = ISC_LIST_INITIALIZER, - .prefix = NULL, - .buffer = NULL, + (context_t){ .openednodes = ISC_LIST_INITIALIZER, .builtincursor = NULL, .usercursor = NULL, .runningcursor = NULL, @@ -69,66 +71,25 @@ static thread_local context_t isc__cfgmgr_ctx = * transaction (i.e. a commit error occurs, the context is flushed, but user * still needs to know what was the error) */ -static thread_local char isc__cfgmgr_lasterror[BUFLEN]; - -static unsigned long -isc__cfgmgr_parseid(const char *dbkey) { - uint32_t id = 0; - size_t idstarts; - size_t idends; - size_t keylen; - - REQUIRE(isc__cfgmgr_ctx.buffer != NULL); - REQUIRE(dbkey != NULL); - - /* - * starts checking after the prefix dot delimiter, i.e. if - * prefix is "foo" then the key will be "foo.1235..." so the - * start of the id (character 1) is at character index 4 - */ - idstarts = strlen(isc__cfgmgr_ctx.buffer); - INSIST(idstarts > 0 && isc__cfgmgr_ctx.buffer[idstarts - 1] == '.'); - idends = idstarts; - - /* - * Cutting the key form the dot after the identifier - */ - keylen = strlen(dbkey); - REQUIRE(keylen > idends); - while (dbkey[idends] != '.') { - idends++; - INSIST(keylen > idends); - } - - /* - * strtoul stops as soon as it doesn't encounter a non-digit - * number, so no need to get an extra buffer, copy the dbkey - * and add a null byte after the last digit. - */ - id = strtoul(dbkey + idstarts, NULL, 10); - ENSURE(id > 0); - return id; -} +static thread_local char isc__cfgmgr_lasterror[LASTERRORLEN]; static bool -isc__cfgmgr_openlmdbcursor(const char *dbname, MDB_cursor **cursor) { +isc__cfgmgr_openlmdbcursor(const char *dbname, MDB_cursor **cursor, + bool readonly) { MDB_dbi dbi; + int result; - /* - * There might be a bug here: LMDB doc says MDB_CREATE can't be used - * for read-only transaction. It might be simply ignored. But we - * shouldn't put it eitherway in such case (and assert in the DB aren't - * already created - */ - if (mdb_dbi_open(isc__cfgmgr_ctx.txn, dbname, MDB_CREATE | MDB_DUPSORT, - &dbi) != 0) - { + result = mdb_dbi_open(isc__cfgmgr_ctx.txn, dbname, + readonly ? 0 : MDB_CREATE, &dbi); + if (result != 0) { return false; } - if (mdb_cursor_open(isc__cfgmgr_ctx.txn, dbi, cursor) != 0) { + result = mdb_cursor_open(isc__cfgmgr_ctx.txn, dbi, cursor); + if (result != 0) { return false; } + INSIST(*cursor != NULL); return true; } @@ -155,13 +116,17 @@ isc__cfgmgr_startlmdbtransaction(bool readonly) { INSIST(isc__cfgmgr_ctx.txn != NULL); - if (!isc__cfgmgr_openlmdbcursor("builtin", - &isc__cfgmgr_ctx.builtincursor) || - !isc__cfgmgr_openlmdbcursor("user", &isc__cfgmgr_ctx.usercursor) || - !isc__cfgmgr_openlmdbcursor("running", - &isc__cfgmgr_ctx.runningcursor)) + if (!isc__cfgmgr_openlmdbcursor( + "builtin", &isc__cfgmgr_ctx.builtincursor, readonly) || + !isc__cfgmgr_openlmdbcursor("user", &isc__cfgmgr_ctx.usercursor, + readonly) || + !isc__cfgmgr_openlmdbcursor( + "running", &isc__cfgmgr_ctx.runningcursor, readonly)) { isc__cfgmgr_closelmdbcursors(); + mdb_txn_abort(isc__cfgmgr_ctx.txn); + isc__cfgmgr_ctx.txn = NULL; + return false; } @@ -171,9 +136,7 @@ isc__cfgmgr_startlmdbtransaction(bool readonly) { static void isc__cfgmgr_checkofftransaction(void) { REQUIRE(isc__cfgmgr_env != NULL); - REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses)); - REQUIRE(isc__cfgmgr_ctx.prefix == NULL); - REQUIRE(isc__cfgmgr_ctx.buffer == NULL); + REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openednodes)); REQUIRE(isc__cfgmgr_ctx.txn == NULL); REQUIRE(isc__cfgmgr_ctx.builtincursor == NULL); REQUIRE(isc__cfgmgr_ctx.usercursor == NULL); @@ -181,8 +144,7 @@ isc__cfgmgr_checkofftransaction(void) { } isc_result_t -isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath, - const isc_cfgmgr_clause_t **configformat) { +isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath) { int result = ISC_R_SUCCESS; char dbname[BUFLEN]; char dblockname[BUFLEN]; @@ -190,13 +152,6 @@ isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath, bool persist = getenv("NAMED_CFGMGR_PERSIST") != NULL; uint32_t openflags = MDB_NOSUBDIR; - REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses)); - REQUIRE(isc__cfgmgr_ctx.prefix == NULL); - REQUIRE(isc__cfgmgr_ctx.buffer == NULL); - REQUIRE(isc__cfgmgr_ctx.builtincursor == NULL); - REQUIRE(isc__cfgmgr_ctx.usercursor == NULL); - REQUIRE(isc__cfgmgr_ctx.runningcursor == NULL); - REQUIRE(isc__cfgmgr_ctx.txn == NULL); REQUIRE(isc__cfgmgr_mctx == NULL); REQUIRE(isc__cfgmgr_env == NULL); REQUIRE(mctx != NULL); @@ -251,8 +206,6 @@ isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath, remove(dblockname); } - isc__cfgmgr_configformat = configformat; - /* * Let's do a read-write transaction, so LMDB can create the DBs (and it * avoid to explain that the very first transaction must be RW, even if @@ -282,50 +235,23 @@ out: void isc_cfgmgr_deinit(void) { /* - * Well, I'm on the fence about those context checks for the - * deinit function: it's good to have, but because the context - * data is thread specific, it doesn't means there isn't a - * thread somewhere which haven't released its opened clauses - * and not the one calling isc_cfgmgr_deinit, then we'll leak - * those - even if unlikely as this function should be call - * late in shutdown flow. (That said it's just an extra clue, - * because destroying the context will assert anyway, as some - * memory would not be released yet). + * This won't guard agains't opened transaction on a different thread, + * so there is still a risk of leak that wouldn't be caught here. Though + * a crash would likely come quickly after as using LMDB object when the + * environment is closed lead to SIGSEGV (according to the doc). */ - REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses)); - REQUIRE(isc__cfgmgr_ctx.prefix == NULL); - REQUIRE(isc__cfgmgr_ctx.buffer == NULL); - REQUIRE(isc__cfgmgr_ctx.builtincursor == NULL); - REQUIRE(isc__cfgmgr_ctx.usercursor == NULL); - REQUIRE(isc__cfgmgr_ctx.runningcursor == NULL); - REQUIRE(isc__cfgmgr_ctx.txn == NULL); + isc__cfgmgr_checkofftransaction(); + REQUIRE(isc__cfgmgr_mctx != NULL); REQUIRE(isc__cfgmgr_env != NULL); + mdb_env_close(isc__cfgmgr_env); isc__cfgmgr_env = NULL; + isc_mem_detach(&isc__cfgmgr_mctx); INSIST(isc__cfgmgr_mctx == NULL); } -static void -isc__cfgmgr_buildkey(const char *name, bool isclause) { - size_t written; - const char *prefix = ""; - const char *dot = isclause ? "." : ""; - - REQUIRE(name != NULL); - REQUIRE(isc__cfgmgr_ctx.buffer != NULL); - REQUIRE(isc__cfgmgr_ctx.prefix != NULL); - - if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false) { - prefix = isc__cfgmgr_ctx.prefix; - } - - written = snprintf(isc__cfgmgr_ctx.buffer, BUFLEN, "%s%s%s", prefix, - name, dot); - INSIST(written <= BUFLEN); -} - static MDB_cursor * isc__cfgmgr_lmdbcursor(void) { switch (isc__cfgmgr_ctx.mode) { @@ -342,98 +268,6 @@ isc__cfgmgr_lmdbcursor(void) { UNREACHABLE(); } -static isc_result_t -isc__cfgmgr_findclause(const char *name, unsigned long *id) { - isc_result_t result = ISC_R_SUCCESS; - MDB_val dbkey; - size_t prefixlen = 0; - - REQUIRE(name != NULL); - - isc__cfgmgr_buildkey(name, true); - prefixlen = strlen(isc__cfgmgr_ctx.buffer); - dbkey = (MDB_val){ .mv_size = strlen(isc__cfgmgr_ctx.buffer) + 1, - .mv_data = (char *)isc__cfgmgr_ctx.buffer }; - - /* - * Let's use LMDB prefix search because the first clause - * key/val won't just have the "name.id" prefix, but also the - * id and the first property name (so "name.id.prop"). - */ - if (mdb_cursor_get(isc__cfgmgr_lmdbcursor(), &dbkey, NULL, - MDB_SET_RANGE) != 0) - { - result = ISC_R_NOTFOUND; - goto out; - } - - /* - * LMDB found a key with "prefix", so let's make - * sure it's actually the same prefix by checking the found - * key has the exact same prefix (including the leading dot) - */ - if (dbkey.mv_size <= prefixlen || - strncmp(dbkey.mv_data, isc__cfgmgr_ctx.buffer, prefixlen)) - { - result = ISC_R_NOTFOUND; - goto out; - } - - /* - * We found the clause. Let's extract its ID - */ - *id = isc__cfgmgr_parseid(dbkey.mv_data); - -out: - return result; -} - -static void -isc__cfgmgr_updateprefix(void) { - size_t written = 0; - - REQUIRE(isc__cfgmgr_ctx.prefix != NULL); - - if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses)) { - return; - } - - for (openedclause_t *clause = - ISC_LIST_TAIL(isc__cfgmgr_ctx.openedclauses); - clause != NULL; clause = ISC_LIST_PREV(clause, link)) - { - written += snprintf(isc__cfgmgr_ctx.prefix + written, - BUFLEN - written, "%s.%" PRIu32 ".", - clause->name, clause->id); - INSIST(written <= BUFLEN); - } -} - -static void -isc__cfgmgr_pushclause(const char *name, unsigned long id) { - openedclause_t *clause = isc_mem_get(isc__cfgmgr_mctx, sizeof(*clause)); - - *clause = (openedclause_t){ - .name = isc_mem_allocate(isc__cfgmgr_mctx, strlen(name) + 1), - .id = id, - .link = ISC_LINK_INITIALIZER, - }; - strcpy(clause->name, name); - ENSURE(clause->id > 0); - ISC_LIST_PREPEND(isc__cfgmgr_ctx.openedclauses, clause, link); - isc__cfgmgr_updateprefix(); -} - -static void -isc__cfgmgr_freectx(void) { - REQUIRE(isc__cfgmgr_ctx.buffer != NULL && - isc__cfgmgr_ctx.prefix != NULL); - - isc_mem_free(isc__cfgmgr_mctx, isc__cfgmgr_ctx.buffer); - isc_mem_free(isc__cfgmgr_mctx, isc__cfgmgr_ctx.prefix); - isc__cfgmgr_ctx.txn = NULL; -} - static void isc__cfgmgr_setlasterror(const char *fmt, ...) { va_list ap; @@ -446,7 +280,6 @@ isc__cfgmgr_setlasterror(const char *fmt, ...) { static void isc__cfgmgr_checkontransaction(void) { REQUIRE(isc__cfgmgr_env != NULL); - REQUIRE(isc__cfgmgr_ctx.buffer != NULL); REQUIRE(isc__cfgmgr_ctx.txn != NULL); REQUIRE(isc__cfgmgr_ctx.builtincursor != NULL); REQUIRE(isc__cfgmgr_ctx.usercursor != NULL); @@ -454,10 +287,9 @@ isc__cfgmgr_checkontransaction(void) { } static void -isc__cfgmgr_checkopenedclause(void) { +isc__cfgmgr_checkopenednodes(void) { isc__cfgmgr_checkontransaction(); - REQUIRE(isc__cfgmgr_ctx.prefix != NULL); - REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false); + REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openednodes) == false); } void @@ -474,16 +306,10 @@ isc__cfgmgr_starttransaction(bool readonly) { isc__cfgmgr_setlasterror(""); if (isc__cfgmgr_startlmdbtransaction(readonly) == false) { - if (isc__cfgmgr_ctx.txn) { - mdb_txn_abort(isc__cfgmgr_ctx.txn); - isc__cfgmgr_ctx.txn = NULL; - } return ISC_R_FAILURE; } isc__cfgmgr_ctx.readonly = readonly; - isc__cfgmgr_ctx.buffer = isc_mem_allocate(isc__cfgmgr_mctx, BUFLEN); - isc__cfgmgr_ctx.prefix = isc_mem_allocate(isc__cfgmgr_mctx, BUFLEN); isc__cfgmgr_ctx.mode = ISC_CFGMGR_MODERUNNING; isc__cfgmgr_checkontransaction(); @@ -505,263 +331,27 @@ isc_cfgmgr_lasterror(void) { return isc__cfgmgr_lasterror; } -static const char * -isc__cfgmgr_typetostring(isc_cfgmgr_type_t type) { - switch (type) { - case ISC_CFGMGR_UNDEFINED: - return "undefined"; - case ISC_CFGMGR_STRING: - return "string"; - case ISC_CFGMGR_BOOLEAN: - return "boolean"; - case ISC_CFGMGR_NONE: - return "none"; - case ISC_CFGMGR_SOCKADDR: - return "sockaddr"; - case ISC_CFGMGR_UINT32: - return "uint32"; - } - - UNREACHABLE(); -} - -static void -isc__cfgmgr_validate_buildprefix(const char *name) { - isc_buffer_t b; - const openedclause_t *clause; - - isc_buffer_init(&b, isc__cfgmgr_ctx.buffer, BUFLEN); - clause = ISC_LIST_TAIL(isc__cfgmgr_ctx.openedclauses); - - while (clause != NULL) { - isc_buffer_putstr(&b, clause->name); - clause = ISC_LIST_PREV(clause, link); - - if (clause != NULL || name != NULL) { - isc_buffer_putstr(&b, "."); - } - } - - if (name != NULL) { - isc_buffer_putstr(&b, name); - } - - isc_buffer_putuint8(&b, 0); -} - -static isc_result_t -isc__cfgmgr_validate_prop(const isc_cfgmgr_prop_t *prop) { - isc_result_t result; - isc_cfgmgr_val_t val; - - result = isc_cfgmgr_getval(prop->name, &val); - if (result != ISC_R_SUCCESS) { - if (result == ISC_R_NOTFOUND) { - if (prop->optional) { - result = ISC_R_SUCCESS; - } else { - isc__cfgmgr_validate_buildprefix(prop->name); - isc__cfgmgr_setlasterror( - "mandatory property %s is " - "missing", - isc__cfgmgr_ctx.buffer); - } - } - goto out; - } - - if (val.type != prop->type) { - isc__cfgmgr_validate_buildprefix(prop->name); - isc__cfgmgr_setlasterror( - "property %s has wrong type. given %s, " - "expected %s", - isc__cfgmgr_ctx.buffer, - isc__cfgmgr_typetostring(val.type), - isc__cfgmgr_typetostring(prop->type)); - result = ISC_R_UNEXPECTED; - } - -out: - return result; -} - -static isc_result_t -isc__cfgmgr_validate_clause(const isc_cfgmgr_clause_t *clause) { - isc_result_t result; - - result = isc_cfgmgr_open(clause->name); - if (result == ISC_R_SUCCESS) { - again: - for (size_t i = 0; clause->props[i] != NULL; i++) { - result = isc__cfgmgr_validate_prop(clause->props[i]); - if (result != ISC_R_SUCCESS) { - break; - } - } - - if (clause->clauses != NULL) { - for (size_t i = 0; clause->clauses[i] != NULL; i++) { - result = isc__cfgmgr_validate_clause( - clause->clauses[i]); - if (result != ISC_R_SUCCESS) { - break; - } - } - } - - if (result == ISC_R_SUCCESS) { - result = isc_cfgmgr_nextclause(); - - if (result == ISC_R_SUCCESS) { - if (clause->repeat == false) { - result = ISC_R_MULTIPLE; - isc__cfgmgr_validate_buildprefix(NULL); - isc__cfgmgr_setlasterror( - "clause %s is repeated but " - "it's not " - "repeatable", - isc__cfgmgr_ctx.buffer); - } else { - goto again; - } - } else if (result == ISC_R_NOMORE) { - result = ISC_R_SUCCESS; - } - } - isc_cfgmgr_close(); - } else if (result == ISC_R_NOTFOUND) { - if (clause->optional) { - result = ISC_R_SUCCESS; - } else { - isc__cfgmgr_validate_buildprefix(clause->name); - isc__cfgmgr_setlasterror("mandatory clause %s is " - "missing", - isc__cfgmgr_ctx.buffer); - } - } - - return result; -} - -static isc_result_t -isc__cfgmgr_validate(void) { - isc_result_t result = ISC_R_SUCCESS; - - if (isc__cfgmgr_configformat != NULL) { - for (size_t i = 0; isc__cfgmgr_configformat[i] != NULL; i++) { - result = isc__cfgmgr_validate_clause( - isc__cfgmgr_configformat[i]); - if (result != ISC_R_SUCCESS) { - break; - } - } - } - - /* - * An empty format bypass the validation, so it's a success in this case - * as well. - */ - return result; -} - -static isc_result_t -isc__cfgmgr_getval(const char *name, isc_cfgmgr_val_t *value); - -static isc_result_t -isc__cfgmgr_dumpprop(const isc_cfgmgr_dumpcfg_t *cfg, - const isc_cfgmgr_prop_t *prop, size_t lvl) { - isc_result_t result; - isc_cfgmgr_val_t val; - - result = isc__cfgmgr_getval(prop->name, &val); - if (result != ISC_R_SUCCESS) { - if (result == ISC_R_NOTFOUND) { - result = ISC_R_SUCCESS; - } - } else { - cfg->property(cfg->state, prop->name, &val, cfg->indent * lvl); - } - - return result; -} - -static isc_result_t -isc__cfgmgr_dumpclause(const isc_cfgmgr_dumpcfg_t *cfg, - const isc_cfgmgr_clause_t *clause, size_t lvl) { - isc_result_t result; - - result = isc_cfgmgr_open(clause->name); - if (result != ISC_R_SUCCESS) { - if (result == ISC_R_NOTFOUND) { - result = ISC_R_SUCCESS; - } - goto out; - } - -again: - cfg->clausestarts(cfg->state, clause->name, cfg->indent * lvl); - - for (size_t i = 0; clause->props[i] != NULL; i++) { - result = isc__cfgmgr_dumpprop(cfg, clause->props[i], lvl + 1); - if (result != ISC_R_SUCCESS) { - break; - } - } - - if (result == ISC_R_SUCCESS && clause->clauses != NULL) { - for (size_t i = 0; clause->clauses[i] != NULL; i++) { - result = isc__cfgmgr_dumpclause(cfg, clause->clauses[i], - lvl + 1); - if (result != ISC_R_SUCCESS) { - break; - } - } - } - - if (result == ISC_R_SUCCESS && isc_cfgmgr_nextclause() == ISC_R_SUCCESS) - { - cfg->clauseends(cfg->state, clause->name, cfg->indent * lvl); - goto again; - } - - cfg->clauseends(cfg->state, clause->name, cfg->indent * lvl); - isc_cfgmgr_close(); -out: - return result; -} - -isc_result_t -isc_cfgmgr_dump(const isc_cfgmgr_dumpcfg_t *cfg) { - isc_result_t result = ISC_R_SUCCESS; - size_t lvl = 0; - - isc__cfgmgr_checkofftransaction(); - REQUIRE(cfg != NULL); - REQUIRE(cfg->dumpmode != ISC_CFGMGR_MODEUNKNOWN); - - if (isc__cfgmgr_configformat == NULL) { - result = ISC_R_FAILURE; - goto out; - } - - result = isc_cfgmgr_transaction(); - isc_cfgmgr_setmode(cfg->dumpmode); - if (result != ISC_R_SUCCESS) { - goto out; - } - - for (size_t i = 0; isc__cfgmgr_configformat[i] != NULL; i++) { - result = isc__cfgmgr_dumpclause( - cfg, isc__cfgmgr_configformat[i], lvl); - if (result != ISC_R_SUCCESS) { - break; - } - } - - isc_cfgmgr_rollback(); -out: - return result; -} +/* + * static const char * + * isc__cfgmgr_typetostring(isc_cfgmgr_type_t type) { + * switch (type) { + * case ISC_CFGMGR_UNDEFINED: + * return "undefined"; + * case ISC_CFGMGR_STRING: + * return "string"; + * case ISC_CFGMGR_BOOLEAN: + * return "boolean"; + * case ISC_CFGMGR_NONE: + * return "none"; + * case ISC_CFGMGR_SOCKADDR: + * return "sockaddr"; + * case ISC_CFGMGR_UINT32: + * return "uint32"; + * } + * + * UNREACHABLE(); + * } + */ isc_result_t isc_cfgmgr_commit(void) { @@ -769,23 +359,18 @@ isc_cfgmgr_commit(void) { isc__cfgmgr_checkontransaction(); - while (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false) { + while (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openednodes) == false) { isc_cfgmgr_close(); } - result = isc__cfgmgr_validate(); + /* configuration validation would occurs here */ - if (result == ISC_R_SUCCESS) { - isc__cfgmgr_closelmdbcursors(); - if (mdb_txn_commit(isc__cfgmgr_ctx.txn) != 0) { - isc__cfgmgr_setlasterror( - "configuration database error"); - result = ISC_R_FAILURE; - } - isc__cfgmgr_freectx(); - } else { - isc_cfgmgr_rollback(); + isc__cfgmgr_closelmdbcursors(); + if (mdb_txn_commit(isc__cfgmgr_ctx.txn) != 0) { + isc__cfgmgr_setlasterror("configuration database error"); + result = ISC_R_FAILURE; } + isc__cfgmgr_ctx.txn = NULL; isc__cfgmgr_checkofftransaction(); return result; @@ -795,195 +380,114 @@ void isc_cfgmgr_rollback(void) { isc__cfgmgr_checkontransaction(); - while (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false) { + while (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openednodes) == false) { isc_cfgmgr_close(); } isc__cfgmgr_closelmdbcursors(); mdb_txn_abort(isc__cfgmgr_ctx.txn); - isc__cfgmgr_freectx(); + isc__cfgmgr_ctx.txn = NULL; isc__cfgmgr_checkofftransaction(); } -isc_result_t -isc_cfgmgr_open(const char *name) { - isc_result_t result = ISC_R_SUCCESS; - unsigned long id = 0; +static void +isc__cfgmgr_buildkey(const char *name, bool addleading, char *b, size_t len) { + const char *prefix = "/"; + const char *leading = addleading ? "/" : ""; - isc__cfgmgr_checkontransaction(); - REQUIRE(isc__cfgmgr_env != NULL); REQUIRE(name != NULL); - REQUIRE(isc__cfgmgr_ctx.txn != NULL); - REQUIRE(isc__cfgmgr_lmdbcursor() != NULL); - result = isc__cfgmgr_findclause(name, &id); - if (result == ISC_R_SUCCESS) { - isc__cfgmgr_pushclause(name, id); + if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openednodes) == false) { + prefix = ISC_LIST_TAIL(isc__cfgmgr_ctx.openednodes)->prefix; } - return result; + REQUIRE(snprintf(b, len, "%s%s%s", prefix, name, leading) <= (int)len); +} + +void +isc_cfgmgr_open(const char *name) { + isc__cfgmgr_node_t *node = NULL; + + isc__cfgmgr_checkontransaction(); + REQUIRE(name != NULL); + + + node = isc_mem_get(isc__cfgmgr_mctx, sizeof(*node)); + *node = (isc__cfgmgr_node_t){ + .name = isc_mem_allocate(isc__cfgmgr_mctx, strlen(name) + 1), + .prefix = isc_mem_get(isc__cfgmgr_mctx, BUFLEN), + .link = ISC_LINK_INITIALIZER, + }; + + strcpy(node->name, name); + isc__cfgmgr_buildkey(name, true, node->prefix, BUFLEN); + ISC_LIST_APPEND(isc__cfgmgr_ctx.openednodes, node, link); } void isc_cfgmgr_close(void) { - openedclause_t *clause; + isc__cfgmgr_node_t *node; - isc__cfgmgr_checkopenedclause(); + isc__cfgmgr_checkopenednodes(); - clause = ISC_LIST_HEAD(isc__cfgmgr_ctx.openedclauses); - ISC_LIST_UNLINK(isc__cfgmgr_ctx.openedclauses, clause, link); - isc_mem_free(isc__cfgmgr_mctx, clause->name); - isc_mem_put(isc__cfgmgr_mctx, clause, sizeof(*clause)); - isc__cfgmgr_updateprefix(); + node = ISC_LIST_TAIL(isc__cfgmgr_ctx.openednodes); + 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); + isc_mem_put(isc__cfgmgr_mctx, node, sizeof(*node)); } void -isc_cfgmgr_delclause(void) { +isc_cfgmgr_delnode(void) { MDB_val dbkey; + char *prefix; - isc__cfgmgr_checkopenedclause(); + isc__cfgmgr_checkopenednodes(); REQUIRE(isc__cfgmgr_ctx.readonly == false); - dbkey = (MDB_val){ .mv_size = strlen(isc__cfgmgr_ctx.prefix) + 1, - .mv_data = isc__cfgmgr_ctx.prefix }; + prefix = ISC_LIST_TAIL(isc__cfgmgr_ctx.openednodes)->prefix; + dbkey = (MDB_val){ .mv_size = strlen(prefix) + 1, .mv_data = prefix }; + do { /* - * even though the key is modified by mdb_cursor_get - * on each run (and is the exact current key) we're - * good: MDB_SET_RANGE of the current key will point - * to the next one with the same prefix as soon it - * gets deleted + * Recursively delete nested nodes and properties. */ - int mdbres = mdb_cursor_get(isc__cfgmgr_lmdbcursor(), &dbkey, + int result = mdb_cursor_get(isc__cfgmgr_lmdbcursor(), &dbkey, NULL, MDB_SET_RANGE); - if (mdbres == MDB_NOTFOUND) { + + if (result == MDB_NOTFOUND) { break; } - if (strncmp(isc__cfgmgr_ctx.prefix, dbkey.mv_data, - strlen(isc__cfgmgr_ctx.prefix)) != 0) - { + if (strncmp(prefix, dbkey.mv_data, strlen(prefix)) != 0) { break; } - /* - * NDB_NODUPDATA not strictly needed here, but we - * avoid extra iterations if there are lists in the - * clause - */ - REQUIRE(mdb_cursor_del(isc__cfgmgr_lmdbcursor(), - MDB_NODUPDATA) == 0); + REQUIRE(mdb_cursor_del(isc__cfgmgr_lmdbcursor(), 0) == 0); } while (1); isc_cfgmgr_close(); } -void -isc_cfgmgr_newclause(const char *name) { - isc__cfgmgr_checkontransaction(); - REQUIRE(isc__cfgmgr_ctx.readonly == false); - - isc__cfgmgr_pushclause(name, isc_random32()); - INSIST(isc__cfgmgr_ctx.prefix != NULL); -} - isc_result_t -isc_cfgmgr_nextclause(void) { - isc_result_t result = ISC_R_SUCCESS; - MDB_val dbkey; - uint32_t id; - size_t idstarts = 0; - size_t written = 0; - - isc__cfgmgr_checkopenedclause(); - - /* - * Let's pick the very next id (even if doesn't exists) of the - * current clause - */ - id = ISC_LIST_HEAD(isc__cfgmgr_ctx.openedclauses)->id + 1; - - /* - * Variant of updateprefix, but this time we put a incremented - * key for the currently opened clause. We also keep track of - * when the current clause id starts. - */ - for (openedclause_t *clause = - ISC_LIST_TAIL(isc__cfgmgr_ctx.openedclauses); - clause != NULL; clause = ISC_LIST_PREV(clause, link)) - { - bool last = ISC_LIST_PREV(clause, link) == NULL; - - if (last) { - idstarts = written + strlen(clause->name) + 1; - } - written += snprintf(isc__cfgmgr_ctx.buffer + written, - BUFLEN - written, "%s.%" PRIu32 ".", - clause->name, last ? id : clause->id); - INSIST(written <= BUFLEN); - } - INSIST(idstarts > 0); - dbkey = (MDB_val){ .mv_size = strlen(isc__cfgmgr_ctx.buffer) + 1, - .mv_data = isc__cfgmgr_ctx.buffer }; - - /* - * Looking for similar prefix name but with a bigger - * id. Thanks for LMDB sort and MDB_SET_RANGE, we'll bump to - * the first key/val of the next clause of the same type - */ - if (mdb_cursor_get(isc__cfgmgr_lmdbcursor(), &dbkey, NULL, - MDB_SET_RANGE) != 0) - { - result = ISC_R_NOMORE; - goto out; - } - - /* - * Let's check if next found clause is same name - */ - REQUIRE(idstarts < BUFLEN); - if (strncmp(isc__cfgmgr_ctx.buffer, dbkey.mv_data, idstarts) != 0) { - result = ISC_R_NOMORE; - goto out; - } - - /* - * Gets the actual id of the next clause (so let's get rid of - * the fake id part from the prefix). Instead of pop/push a - * new clause, let's simply replace the id and update the - * prefix. - */ - isc__cfgmgr_ctx.buffer[idstarts] = 0; - ISC_LIST_HEAD(isc__cfgmgr_ctx.openedclauses)->id = - isc__cfgmgr_parseid(dbkey.mv_data); - isc__cfgmgr_updateprefix(); - -out: - return result; -} - -static isc_result_t -isc__cfgmgr_getval(const char *name, isc_cfgmgr_val_t *value) { +isc_cfgmgr_getval(const char *name, isc_cfgmgr_val_t *value) { isc_result_t result = ISC_R_SUCCESS; MDB_val dbkey; MDB_val dbval; - const int opt = name == NULL ? MDB_NEXT_DUP : MDB_SET; + char key[BUFLEN]; - isc__cfgmgr_checkopenedclause(); + isc__cfgmgr_checkopenednodes(); + REQUIRE(name != NULL); REQUIRE(value != NULL); - if (name != NULL) { - isc__cfgmgr_buildkey(name, false); - } - dbkey = (MDB_val){ - .mv_size = name == NULL ? 0 - : strlen(isc__cfgmgr_ctx.buffer) + 1, - .mv_data = name == NULL ? NULL : isc__cfgmgr_ctx.buffer - }; - if (mdb_cursor_get(isc__cfgmgr_lmdbcursor(), &dbkey, &dbval, opt) != 0) + isc__cfgmgr_buildkey(name, false, 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) != + 0) { - result = opt == MDB_NEXT_DUP ? ISC_R_NOMORE : ISC_R_NOTFOUND; + result = ISC_R_NOTFOUND; goto out; } @@ -1003,30 +507,21 @@ out: } isc_result_t -isc_cfgmgr_getval(const char *name, isc_cfgmgr_val_t *value) { - return isc__cfgmgr_getval(name, value); -} - -isc_result_t -isc_cfgmgr_getnextlistval(isc_cfgmgr_val_t *value) { - return isc__cfgmgr_getval(NULL, value); -} - -static isc_result_t -isc__cfgmgr_setval(const char *name, const isc_cfgmgr_val_t *value, bool list) { +isc_cfgmgr_setval(const char *name, const isc_cfgmgr_val_t *value) { isc_result_t result = ISC_R_SUCCESS; MDB_val dbkey; MDB_val dbval; + char key[BUFLEN]; - isc__cfgmgr_checkopenedclause(); + isc__cfgmgr_checkopenednodes(); REQUIRE(isc__cfgmgr_ctx.readonly == false); REQUIRE(name != NULL); REQUIRE((value != NULL && value->type != ISC_CFGMGR_UNDEFINED) || - (value == NULL && list == false)); + value == NULL); + + isc__cfgmgr_buildkey(name, false, key, sizeof(key)); + dbkey = (MDB_val){ .mv_size = strlen(key) + 1, .mv_data = key }; - isc__cfgmgr_buildkey(name, false); - dbkey = (MDB_val){ .mv_size = strlen(isc__cfgmgr_ctx.buffer) + 1, - .mv_data = isc__cfgmgr_ctx.buffer }; if (value == NULL) { if (mdb_cursor_get(isc__cfgmgr_lmdbcursor(), &dbkey, NULL, MDB_SET) == MDB_NOTFOUND) @@ -1063,21 +558,6 @@ isc__cfgmgr_setval(const char *name, const isc_cfgmgr_val_t *value, bool list) { .mv_data = (void *)value }; } - if (list == false) { - /* - * Can't use MDB_NOOVERWRITE as it would override the - * data if the key/value already exists so it wouldn't work with - * list. Making a value copy ahead just in case is likely more - * expensive than an extra lookup - */ - if (mdb_cursor_get(isc__cfgmgr_lmdbcursor(), &dbkey, NULL, - MDB_SET) != MDB_NOTFOUND) - { - REQUIRE(mdb_cursor_del(isc__cfgmgr_lmdbcursor(), 0) == - 0); - } - } - REQUIRE(mdb_cursor_put(isc__cfgmgr_lmdbcursor(), &dbkey, &dbval, 0) == 0); if (value->type == ISC_CFGMGR_STRING) { @@ -1088,12 +568,61 @@ out: return result; } -isc_result_t -isc_cfgmgr_setval(const char *name, const isc_cfgmgr_val_t *value) { - return isc__cfgmgr_setval(name, value, false); -} +void +isc_cfgmgr_foreachnodes(void (*action)(void *state, const char *name), + void *state) { + UNUSED(state); + UNUSED(action); -isc_result_t -isc_cfgmgr_setnextlistval(const char *name, const isc_cfgmgr_val_t *value) { - return isc__cfgmgr_setval(name, value, true); + /* + * Not implemented yet, but here's the plan. Let's consider the + * following keys: + * + * /foo/bar/gee/f + * /foo/bar/gff/g + * /foo/bar/gdd + * /foo/prop + * /foo/zz/a + * /foo/zz/c + * + * 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: + * + * __internal_idx__/foo/bar/,bar + * __internal_idx__/foo/zz/,zz + * + * 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. + */ } diff --git a/lib/isc/include/isc/cfgmgr.h b/lib/isc/include/isc/cfgmgr.h index 7b69a12091..a850bed4d6 100644 --- a/lib/isc/include/isc/cfgmgr.h +++ b/lib/isc/include/isc/cfgmgr.h @@ -25,18 +25,38 @@ * * The materialization of the running mode is based on the following rules: * - * - the "options" clause gets all the values from the builtin - * "option" clause, then all the values from the user "options" clause + * - the "options" node gets all the values from the builtin + * "options" node, then all the values from the user "options" node * (potentially overriding those from the builtin mode) * - * - the "view" clauses gets all the values of the running "options" - * clause, then can override those. It also means that an update in the - * running "options" clause automatically applies to all "view" clauses as + * - the "views" sub-nodes gets all the values of the running "options" + * node, then can override those. It also means that an update in the + * running "options" node automatically applies to all "views" sub-nodes as * well. * - * - the "zone" clauses gets all the values of its parent running "view" clause, + * - the "zones" sub-nodes gets all the values of its parent view, * then can override those. It also means that an update in the running parent - * "view" clause propagates to all its zones. + * view propagates to all its zones. + * + * To clarify what "views sub-nodes" or "parent view" means, here is a tree + * representation how things are represented in cfgmgr: + * + * / + * ├── options/ + * │ ├── allow-query + * │ └── listen-on + * └── views/ + * ├── zones/ + * │ └── isc.org + * │ └── allow-query + * ├── allow-query + * └── another-prop + * + * Of course, other node than "options", "views" and "zones" (as well as the + * zone sub-nodes named after a domain name) can be used as there is no + * restriction on the node names or properties names. However the + * materialization logic will apply only on running version of "options" and + * "zones" sub-nodes. */ typedef enum isc_cfgmgr_mode isc_cfgmgr_mode_t; enum isc_cfgmgr_mode { @@ -58,31 +78,6 @@ typedef enum isc_cfgmgr_type { ISC_CFGMGR_UINT32 } __attribute__((__packed__)) isc_cfgmgr_type_t; -/* - * Configuration validation and printing. The validator is able to verify that - * mandatory properties (or subclauses) of a clause are present, as well as - * their respective types, as well as a non-repeatable clause is not repeated or - * a mandatory clause is not present. However it won't verify if a non-expected - * clause does exists. (This actually might handy if non-user configurable data - * needs to be stored in cfgmgr.) - */ -typedef struct isc_cfgmgr_clause isc_cfgmgr_clause_t; -typedef struct isc_cfgmgr_property isc_cfgmgr_prop_t; - -struct isc_cfgmgr_clause { - const char *name; - bool optional; - bool repeat; - const isc_cfgmgr_clause_t **clauses; - const isc_cfgmgr_prop_t **props; -}; - -struct isc_cfgmgr_property { - const char *name; - bool optional; - isc_cfgmgr_type_t type; -}; - /* * Generic value holding the actual value and type value for * read/write from/to cfgmgr. @@ -107,96 +102,62 @@ void isc_cfgmgr_setmode(isc_cfgmgr_mode_t mode); /* - * Get the property "name" in the opened clause into the caller + * Get the property "name" in the opened node into the caller * allocated "value" and returns ISC_R_SUCCESS. Returns ISC_R_NOTFOUND - * and "*value" is not mutated if "name" is not found. If "name" is a list - * property, get its head. + * and "*value" is not mutated if "name" is not found. Must be called under a + * transaction. */ isc_result_t isc_cfgmgr_getval(const char *name, isc_cfgmgr_val_t *value); /* - * Write "value" into the property "name" in the opened clause and + * Write "value" into the property "name" in the opened node and * returns ISC_R_SUCCESS. If the property already exists, it is * overridden and even if the type is different. If "value" is NULL - * and the property exists, it will be deleted (applies for list - * properties as well), otherwise, it returns ISC_R_NOTFOUND. + * and the property exists, it will be deleted, otherwise it returns + * ISC_R_NOTFOUND. Must be called under a write transaction. */ isc_result_t isc_cfgmgr_setval(const char *name, const isc_cfgmgr_val_t *value); /* - * Same as isc_cfgmgr_getval but applies for elements after the head - * of a list property. The head is read using isc_cfgmgr_getval as any - * other value, then subsequents calls to isc_cfgmgr_getnextlistval - * will get the next elements in the list. When the end of the list is - * reached, ISC_R_NOMORE is returned. Calls to - * isc_cfgmgr_getnextlistval name has to be made in immediate sequence - * (without intermediate isc_cfgmgr_{set,get}val calls) to retrieve - * each list element. - */ -isc_result_t -isc_cfgmgr_getnextlistval(isc_cfgmgr_val_t *value); - -/* - * Same as isc_cfgmgr_setval but applies for a list property. Writes - * by appending "*value" at the end of the list property "name" in the - * opened clause and returns ISC_R_SUCCESS. If "name" property wasn't - * existing before (or wasn't a list) it's overriden. It is not - * possible to delete individual list element, only the whole list can - * be removed using isc_cfgmgr_setval. - */ -isc_result_t -isc_cfgmgr_setnextlistval(const char *name, const isc_cfgmgr_val_t *value); - -/* - * If the opened clause is a repeatable clause (i.e. view, acl, etc.), - * internally closes the opened clause and open the next clause of the - * same type and returns ISC_R_SUCCESS. When there is no next clause - * of the same type, ISC_R_NOMORE is returned. - */ -isc_result_t -isc_cfgmgr_nextclause(void); - -/* - * Create and open a new clause. If not clause is currently opened, the newly - * created clause is a top-level one. Otherwise, it is nested in the clause - * opened when this function is called. Note that in order to have the new - * clause actually written in cfgmgr, at least one property needs to be set to - * that clause. + * Close and delete the opened node. (And thus all its properties, + * including nested nodes). If the node was nested, the currently + * opened node is now the parent node. Can't be called from the root + * node. Must be called under a write transaction. */ void -isc_cfgmgr_newclause(const char *name); +isc_cfgmgr_delnode(void); /* - * Delete and close the opened clause. (And thus all its properties, - * including nested clauses). If the clause was nested, the currently - * opened clause is now the parent clause. Otherwise, no clause is - * opened. + * Open the node "name". Must be called under a transaction. */ void -isc_cfgmgr_delclause(void); - -/* - * Open the clause "name" and returns ISC_R_SUCCES or ISC_R_NOTFOUND - * is the clause is not found. Must be called under a transaction. - */ -isc_result_t isc_cfgmgr_open(const char *name); /* - * Close the currently opened clause and returns ISC_R_SUCCESS. If the - * closed clause was nested, the currently opened clause is now the - * parent clause. Must be called under a transaction. + * Close the currently opened node. If the closed node was nested, the currently + * opened node is now the parent node. Can't be called from the root node. Must + * be called under a transaction. */ void isc_cfgmgr_close(void); +/* + * Calls function "action" for each direct sub-nodes of the currently opened + * node. The caller is free to do whatever needed inside "action", in + * particular, opening "name" (and its sub-nodes) to read or write properties + * inside. Must be called under a transaction. + */ +void +isc_cfgmgr_foreachnodes(void(*action)(void *state, const char *name), + 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 callimg this + * issue creating a transaction, ISC_R_FAILURE is returned. Prior calling this * function, there must be no currently transaction started from the current - * thread. + * thread. By default, the root node is opened. */ isc_result_t isc_cfgmgr_transaction(void); @@ -206,7 +167,8 @@ isc_cfgmgr_transaction(void); * issue creating a transaction, ISC_R_FAILURE is returned. Prior callimg this * function, there must be no currently transaction started from the current * thread. If another thread already started a read-write transaction, - * this call will block until the other transaction is terminated. + * this call will block until the other transaction is terminated. By default, + * the root node is opened. */ isc_result_t isc_cfgmgr_rwtransaction(void); @@ -216,8 +178,7 @@ isc_cfgmgr_rwtransaction(void); * transaction visible to all transaction started after this call returns. If * something goes wrong while applying the changes, ISC_R_FAILURE is returned * and the changes made during the transaction are discareded. ISC_R_SUCCESS is - * returned otherwise. If a clause is opened when this function is called, it is - * automatically closed. + * returned otherwise. * * If the validation of the new configuration fails, the transaction is * rolled-backed and all the changes are discarded. It is possible to get @@ -237,47 +198,9 @@ isc_cfgmgr_commit(void); const char * isc_cfgmgr_lasterror(void); -/* - * Configuration of the dump provided by the user. The three function pointers - * are provided by the user which is responsible of implementing the dump output - * format. "clausestarts" and "clauseends" functions are called at the entry and - * exit of each clause, "property" is called whenever a property is reached, and - * "indent" is the number of spaces that cfgmgr must calculate each time it - * enters a clause. (The actual indentation level is provided to each of those 3 - * functions). - * - * The "state" pointer to each user provided function can be anything and is - * simply passed from the isc_cfgmgr_dumpcfg to each function when they are - * called. - * - * isc_cfgmgr_dump must be called outside of any transaction (it internally uses - * a read-only transaction). - * - * The dump mode indicates if only the builtin configuration, or only the user - * provided configuration, or the actual running configuration (builtin + user - * overriding builtin data) should be dumped. It can't be unknown. - * - * A configuration format must be provided to cfgmgr (when isc_cfgmgr_init is - * called) in order to build a dump, otherwise, ISC_R_FAILURE will be returned. - */ -typedef struct isc_cfgmgr_dumpcfg isc_cfgmgr_dumpcfg_t; -struct isc_cfgmgr_dumpcfg { - void (*clausestarts)(void *state, const char *name, size_t indent); - void (*clauseends)(void *state, const char *name, size_t indent); - void (*property)(void *state, const char *name, - const isc_cfgmgr_val_t *val, size_t indent); - void *state; - size_t indent; - isc_cfgmgr_mode_t dumpmode; -}; - -isc_result_t -isc_cfgmgr_dump(const isc_cfgmgr_dumpcfg_t *cfg); - /* * Discard all the changes made during transaction and terminate the - * transaction. If a clause is opened when this function is called, it is - * automatically closed. + * transaction. */ void isc_cfgmgr_rollback(void); @@ -288,15 +211,9 @@ isc_cfgmgr_rollback(void); * isc_cfgmgr_deinit (this drops all the data written in * cfgmgr). Returns ISC_R_SUCCESS or ISC_R_FAILURE if there is an * issue initializing the internal database. - * - * "configformat" is a NULL-terminated array and its life-cycle must outlive - * cfgmgr (it is not copied). It is used to validate and to dump the - * configuration. NULL can be passed, in which case the validation is bypassed - * and the dump functions won't return anything. */ isc_result_t -isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath, - const isc_cfgmgr_clause_t **configformat); +isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath); /* * Destroy all cfgmgr data and free memory. Must be called only after diff --git a/tests/isc/cfgmgr_test.c b/tests/isc/cfgmgr_test.c index 31082d3870..b27e9eb6dc 100644 --- a/tests/isc/cfgmgr_test.c +++ b/tests/isc/cfgmgr_test.c @@ -30,41 +30,34 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_assertions) { isc_cfgmgr_val_t dummyval; + isc_result_t result; /* * Invalid cfgmgr initialization */ - expect_assert_failure(isc_cfgmgr_init(NULL, TEST_DBPATH, NULL)); - expect_assert_failure(isc_cfgmgr_init(mctx, NULL, NULL)); + expect_assert_failure(isc_cfgmgr_init(NULL, TEST_DBPATH)); + expect_assert_failure(isc_cfgmgr_init(mctx, NULL)); expect_assert_failure(isc_cfgmgr_close()); expect_assert_failure(isc_cfgmgr_getval("foo", &dummyval)); - expect_assert_failure(isc_cfgmgr_getnextlistval(&dummyval)); expect_assert_failure(isc_cfgmgr_setval("foo", &dummyval)); - expect_assert_failure(isc_cfgmgr_setnextlistval("foo", &dummyval)); - expect_assert_failure(isc_cfgmgr_nextclause()); expect_assert_failure(isc_cfgmgr_transaction()); expect_assert_failure(isc_cfgmgr_rwtransaction()); expect_assert_failure(isc_cfgmgr_commit()); expect_assert_failure(isc_cfgmgr_rollback()); expect_assert_failure(isc_cfgmgr_open("foo")); - expect_assert_failure(isc_cfgmgr_newclause("foo")); - expect_assert_failure(isc_cfgmgr_delclause()); + expect_assert_failure(isc_cfgmgr_delnode()); expect_assert_failure(isc_cfgmgr_deinit()); /* * operations which must be used under transaction */ - (void)isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - expect_assert_failure(isc_cfgmgr_delclause()); + (void)isc_cfgmgr_init(mctx, TEST_DBPATH); + expect_assert_failure(isc_cfgmgr_delnode()); expect_assert_failure(isc_cfgmgr_getval("foo", &dummyval)); - expect_assert_failure(isc_cfgmgr_getnextlistval(&dummyval)); expect_assert_failure(isc_cfgmgr_setval("foo", &dummyval)); - expect_assert_failure(isc_cfgmgr_setnextlistval("foo", &dummyval)); - expect_assert_failure(isc_cfgmgr_nextclause()); expect_assert_failure(isc_cfgmgr_commit()); expect_assert_failure(isc_cfgmgr_rollback()); expect_assert_failure(isc_cfgmgr_open("foo")); - expect_assert_failure(isc_cfgmgr_newclause("foo")); expect_assert_failure(isc_cfgmgr_close()); assert_int_equal(isc_cfgmgr_transaction(), ISC_R_SUCCESS); @@ -82,82 +75,110 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_assertions) { /* * One close for one open */ - (void)isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - assert_int_equal(isc_cfgmgr_rwtransaction(), ISC_R_SUCCESS); - isc_cfgmgr_newclause("foo"); - isc_cfgmgr_close(); - expect_assert_failure(isc_cfgmgr_close()); - assert_int_equal(isc_cfgmgr_commit(), ISC_R_SUCCESS); - isc_cfgmgr_deinit(); + result = isc_cfgmgr_init(mctx, TEST_DBPATH); + assert_int_equal(result, ISC_R_SUCCESS); - /* - * One close for one open (and failed open as not existent) - */ - (void)isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - assert_int_equal(isc_cfgmgr_rwtransaction(), ISC_R_SUCCESS); - isc_cfgmgr_newclause("foo"); - assert_int_equal(isc_cfgmgr_open("bar"), ISC_R_NOTFOUND); + result = isc_cfgmgr_rwtransaction(); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_cfgmgr_open("foo"); isc_cfgmgr_close(); expect_assert_failure(isc_cfgmgr_close()); - assert_int_equal(isc_cfgmgr_commit(), ISC_R_SUCCESS); + + result = isc_cfgmgr_commit(); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_deinit(); /* * (Implicit) two closes for two open */ - (void)isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - assert_int_equal(isc_cfgmgr_rwtransaction(), ISC_R_SUCCESS); - isc_cfgmgr_newclause("foo"); - isc_cfgmgr_newclause("bar"); - assert_int_equal(isc_cfgmgr_commit(), ISC_R_SUCCESS); + 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("foo"); + isc_cfgmgr_open("bar"); + + result = isc_cfgmgr_commit(); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_deinit(); /* - * can't write on non-write open clause nor open a subclause as write + * can't write on read-only transaction */ - (void)isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - assert_int_equal(isc_cfgmgr_rwtransaction(), ISC_R_SUCCESS); - (void)isc_cfgmgr_newclause("foo"); - (void)isc_cfgmgr_setval("bar", - &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }); - (void)isc_cfgmgr_newclause("subfoo"); - (void)isc_cfgmgr_setval("subfoobar", - &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }); - (void)isc_cfgmgr_close(); - (void)isc_cfgmgr_close(); + 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("foo"); + result = isc_cfgmgr_setval( + "bar", &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_cfgmgr_open("subfoo"); + result = isc_cfgmgr_setval( + "subfoobar", &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_cfgmgr_close(); + isc_cfgmgr_close(); assert_int_equal(isc_cfgmgr_commit(), ISC_R_SUCCESS); assert_int_equal(isc_cfgmgr_transaction(), ISC_R_SUCCESS); - (void)isc_cfgmgr_open("foo"); + isc_cfgmgr_open("foo"); expect_assert_failure(isc_cfgmgr_setval( "bar", &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE })); expect_assert_failure(isc_cfgmgr_setval( "baz", &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE })); - expect_assert_failure(isc_cfgmgr_newclause("gee")); - expect_assert_failure(isc_cfgmgr_delclause()); + + isc_cfgmgr_open("subfoo"); + expect_assert_failure(isc_cfgmgr_setval( + "subfoobar", &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE })); assert_int_equal(isc_cfgmgr_commit(), ISC_R_SUCCESS); + isc_cfgmgr_deinit(); /* * reading values parameters */ - (void)isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - assert_int_equal(isc_cfgmgr_rwtransaction(), ISC_R_SUCCESS); - isc_cfgmgr_newclause("foo"); - (void)isc_cfgmgr_setval("bar", - &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }); + 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("foo"); + result = isc_cfgmgr_setval( + "bar", &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }); + assert_int_equal(result, ISC_R_SUCCESS); expect_assert_failure(isc_cfgmgr_getval("bar", NULL)); - assert_int_equal(isc_cfgmgr_commit(), ISC_R_SUCCESS); + + result = isc_cfgmgr_commit(); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_deinit(); /* * can't de-init while having opened transaction */ - (void)isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - assert_int_equal(isc_cfgmgr_rwtransaction(), ISC_R_SUCCESS); - isc_cfgmgr_newclause("foo"); + 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("foo"); expect_assert_failure(isc_cfgmgr_deinit()); - assert_int_equal(isc_cfgmgr_commit(), ISC_R_SUCCESS); + + result = isc_cfgmgr_commit(); + assert_int_equal(result, ISC_R_SUCCESS); + (void)isc_cfgmgr_deinit(); /* @@ -171,16 +192,13 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rw) { isc_cfgmgr_val_t val1; isc_cfgmgr_val_t val2 = { .type = ISC_CFGMGR_UNDEFINED }; - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); + 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); - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_NOTFOUND); - - isc_cfgmgr_newclause("foo"); + isc_cfgmgr_open("foo"); val1 = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 4058304 }; @@ -258,12 +276,11 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rw) { result = isc_cfgmgr_transaction(); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); /* * Everything still there when closing and re-opening - * (read-only) the clause + * (read-only) the node */ result = isc_cfgmgr_getval("prop3", &val2); assert_int_equal(result, ISC_R_SUCCESS); @@ -283,14 +300,14 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rw) { assert_int_equal(result, ISC_R_SUCCESS); /* - * Adding other clause (intentionally with a different name, - * but a common prefix in the name - those are still different - * clauses + * Adding other node, intentionally with a different name, + * but a common prefix in the name, those are still different + * nodes */ result = isc_cfgmgr_rwtransaction(); assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_newclause("foo1"); + isc_cfgmgr_open("foo1"); val1 = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 1234 }; result = isc_cfgmgr_setval("prop1", &val1); @@ -310,7 +327,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rw) { assert_int_equal(val2.type, ISC_CFGMGR_NONE); /* - * Make sure we don't mixes clause properties + * Make sure we don't mixes nodes properties */ result = isc_cfgmgr_getval("prop2", &val2); assert_int_equal(result, ISC_R_NOTFOUND); @@ -327,8 +344,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rw) { result = isc_cfgmgr_rwtransaction(); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); result = isc_cfgmgr_getval("prop1", &val2); assert_int_equal(result, ISC_R_SUCCESS); @@ -346,8 +362,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rw) { result = isc_cfgmgr_rwtransaction(); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("foo1"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo1"); result = isc_cfgmgr_getval("prop1", &val2); assert_int_equal(result, ISC_R_SUCCESS); @@ -384,17 +399,16 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rw) { ISC_RUN_TEST_IMPL(isc_cfgmgr_rollback) { isc_cfgmgr_val_t val; - assert_int_equal(isc_cfgmgr_init(mctx, TEST_DBPATH, NULL), - ISC_R_SUCCESS); + assert_int_equal(isc_cfgmgr_init(mctx, TEST_DBPATH), ISC_R_SUCCESS); assert_int_equal(isc_cfgmgr_rwtransaction(), ISC_R_SUCCESS); - isc_cfgmgr_newclause("foo"); + isc_cfgmgr_open("foo"); val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 42 }; assert_int_equal(isc_cfgmgr_setval("prop", &val), ISC_R_SUCCESS); assert_int_equal(isc_cfgmgr_commit(), ISC_R_SUCCESS); assert_int_equal(isc_cfgmgr_rwtransaction(), ISC_R_SUCCESS); - assert_int_equal(isc_cfgmgr_open("foo"), ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 9999 }; assert_int_equal(isc_cfgmgr_setval("prop", &val), ISC_R_SUCCESS); val.uint32 = 0; @@ -407,7 +421,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rollback) { isc_cfgmgr_rollback(); assert_int_equal(isc_cfgmgr_transaction(), ISC_R_SUCCESS); - assert_int_equal(isc_cfgmgr_open("foo"), ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); assert_int_equal(isc_cfgmgr_getval("prop", &val), ISC_R_SUCCESS); assert_int_equal(val.type, ISC_CFGMGR_UINT32); /* @@ -419,128 +433,18 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rollback) { isc_cfgmgr_deinit(); } -ISC_RUN_TEST_IMPL(isc_cfgmgr_parseid) { - isc_result_t result; - isc_cfgmgr_val_t val; - - /* - * Excercise the fact that even if properties/clause names are - * number, this doesn't confuse the id parser (in particular, - * validates the usage of strtoul is correct). This also - * exercise the nested clause and repeatable clauses with such - * odd names - */ - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("123"); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 666666 }; - result = isc_cfgmgr_setval("123123", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("456"); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 777777 }; - result = isc_cfgmgr_setval("456456", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_close(); - - isc_cfgmgr_newclause("456"); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 888888 }; - result = isc_cfgmgr_setval("456456", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_close(); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 9999 }; - result = isc_cfgmgr_setval("456456", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_transaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_open("123"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("123123", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_UINT32); - assert_int_equal(val.uint32, 666666); - - result = isc_cfgmgr_getval("456456", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_UINT32); - assert_int_equal(val.uint32, 9999); - - /* - * Because of the internal randomly generated id for each - * clause and the lexicographical order of LMDB, we don't know - * which one will be first, hance this little "danse" to - * figure out which one we get - */ - bool found_777777 = false; - bool found_888888 = false; - - result = isc_cfgmgr_open("456"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("456456", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_UINT32); - if (val.uint32 == 777777) { - found_777777 = true; - } else if (val.uint32 == 888888) { - found_888888 = true; - } else { - assert_true(false); - } - - result = isc_cfgmgr_nextclause(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("456456", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_UINT32); - if (val.uint32 == 777777) { - found_777777 = true; - } else if (val.uint32 == 888888) { - found_888888 = true; - } else { - assert_true(false); - } - - assert_true(found_777777); - assert_true(found_888888); - - /* - * or _commit, whatever, it's not a rwtransaction anyway... - */ - isc_cfgmgr_rollback(); - - isc_cfgmgr_deinit(); -} - ISC_RUN_TEST_IMPL(isc_cfgmgr_override) { isc_result_t result; isc_cfgmgr_val_t val1; isc_cfgmgr_val_t val2; - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); + 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_newclause("foo"); + isc_cfgmgr_open("foo"); val1 = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 4058304 }; @@ -593,13 +497,13 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rw_string) { isc_cfgmgr_val_t val1; isc_cfgmgr_val_t val2 = { .type = ISC_CFGMGR_UNDEFINED }; - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); + 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_newclause("foo"); + isc_cfgmgr_open("foo"); val1 = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "hey there!" }; @@ -655,221 +559,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_rw_string) { isc_cfgmgr_deinit(); } -ISC_RUN_TEST_IMPL(isc_cfgmgr_list) { - isc_result_t result; - isc_cfgmgr_val_t val; - - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("foo"); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "lst1" }; - result = isc_cfgmgr_setnextlistval("proplist", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - val.string = "lst2"; - result = isc_cfgmgr_setnextlistval("proplist", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - val.string = "lst3"; - result = isc_cfgmgr_setnextlistval("proplist", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - val.string = "lst4"; - result = isc_cfgmgr_setnextlistval("proplist", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - val.string = "otherpropval"; - result = isc_cfgmgr_setval("otherprop", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - val.string = "zzzval"; - result = isc_cfgmgr_setval("zzz", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_transaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("proplist", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - assert_string_equal(val.string, "lst1"); - - /* - * calling it again, we stick to the head - */ - result = isc_cfgmgr_getval("proplist", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - assert_string_equal(val.string, "lst1"); - - /* - * now we're moving on... - */ - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - assert_string_equal(val.string, "lst2"); - - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - assert_string_equal(val.string, "lst3"); - - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - assert_string_equal(val.string, "lst4"); - - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_NOMORE); - - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_NOMORE); - - /* - * and start from the begining again - */ - result = isc_cfgmgr_getval("proplist", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - assert_string_equal(val.string, "lst1"); - - /* - * move on in the list but re-start again - */ - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - assert_string_equal(val.string, "lst2"); - - result = isc_cfgmgr_getval("proplist", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - assert_string_equal(val.string, "lst1"); - - /* - * calling after reading a non-list property - */ - result = isc_cfgmgr_getval("zzz", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_NOMORE); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_deinit(); -} - -ISC_RUN_TEST_IMPL(isc_cfgmgr_repeatable_clauses) { - isc_result_t result; - isc_cfgmgr_val_t val1; - isc_cfgmgr_val_t val2 = { .type = ISC_CFGMGR_UNDEFINED }; - - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("view"); - - val1 = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, - .string = "view1 p1 val" }; - result = isc_cfgmgr_setval("p1", &val1); - assert_int_equal(result, ISC_R_SUCCESS); - - val1 = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_BOOLEAN, - .boolean = false }; - result = isc_cfgmgr_setval("p2", &val1); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("view"); - - val1 = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, - .string = "view2 p2 val" }; - result = isc_cfgmgr_setval("p1", &val1); - assert_int_equal(result, ISC_R_SUCCESS); - - val1 = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_BOOLEAN, - .boolean = true }; - result = isc_cfgmgr_setval("p2", &val1); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_transaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_open("view"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("p2", &val2); - assert_int_equal(val2.type, ISC_CFGMGR_BOOLEAN); - - val1.type = ISC_CFGMGR_UNDEFINED; - if (val2.boolean) { - result = isc_cfgmgr_getval("p1", &val1); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val1.type, ISC_CFGMGR_STRING); - assert_string_equal(val1.string, "view2 p2 val"); - } else { - result = isc_cfgmgr_getval("p1", &val1); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val1.type, ISC_CFGMGR_STRING); - assert_string_equal(val1.string, "view1 p1 val"); - } - - result = isc_cfgmgr_nextclause(); - assert_int_equal(result, ISC_R_SUCCESS); - - val2.type = ISC_CFGMGR_UNDEFINED; - result = isc_cfgmgr_getval("p2", &val2); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val2.type, ISC_CFGMGR_BOOLEAN); - - val1.type = ISC_CFGMGR_UNDEFINED; - if (val2.boolean) { - result = isc_cfgmgr_getval("p1", &val1); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val1.type, ISC_CFGMGR_STRING); - assert_string_equal(val1.string, "view2 p2 val"); - } else { - result = isc_cfgmgr_getval("p1", &val1); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val1.type, ISC_CFGMGR_STRING); - assert_string_equal(val1.string, "view1 p1 val"); - } - - result = isc_cfgmgr_nextclause(); - assert_int_equal(result, ISC_R_NOMORE); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_deinit(); -} - -ISC_RUN_TEST_IMPL(isc_cfgmgr_nested_clauses) { +ISC_RUN_TEST_IMPL(isc_cfgmgr_nested_nodes) { isc_result_t result; isc_cfgmgr_val_t val; @@ -877,15 +567,15 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_nested_clauses) { * Let's start by writting then reading * foo { bar { baz { gee: none; }; }; }; */ - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); + 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_newclause("foo"); - isc_cfgmgr_newclause("bar"); - isc_cfgmgr_newclause("baz"); + isc_cfgmgr_open("foo"); + isc_cfgmgr_open("bar"); + isc_cfgmgr_open("baz"); val.type = ISC_CFGMGR_NONE; result = isc_cfgmgr_setval("gee", &val); @@ -897,14 +587,9 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_nested_clauses) { result = isc_cfgmgr_transaction(); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_open("bar"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_open("baz"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); + isc_cfgmgr_open("bar"); + isc_cfgmgr_open("baz"); val.type = ISC_CFGMGR_UNDEFINED; result = isc_cfgmgr_getval("gee", &val); @@ -916,23 +601,23 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_nested_clauses) { /* * then let's delete bar and add some properties in foo and - * another nested clause + * another nested node */ result = isc_cfgmgr_rwtransaction(); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("foo"); + isc_cfgmgr_open("foo"); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("bar"); + isc_cfgmgr_open("bar"); assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_delclause(); + isc_cfgmgr_delnode(); - isc_cfgmgr_newclause("foonewsubclause"); + isc_cfgmgr_open("foonewsubnode"); val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "abc" }; - result = isc_cfgmgr_setval("propsubclause", &val); + result = isc_cfgmgr_setval("propsubnode", &val); assert_int_equal(result, ISC_R_SUCCESS); isc_cfgmgr_close(); @@ -948,8 +633,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_nested_clauses) { result = isc_cfgmgr_transaction(); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); val.type = ISC_CFGMGR_UNDEFINED; result = isc_cfgmgr_getval("propfoo", &val); @@ -957,13 +641,9 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_nested_clauses) { assert_int_equal(val.type, ISC_CFGMGR_STRING); assert_string_equal(val.string, "propfooval"); - result = isc_cfgmgr_open("bar"); - assert_int_equal(result, ISC_R_NOTFOUND); + isc_cfgmgr_open("foonewsubnode"); - result = isc_cfgmgr_open("foonewsubclause"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("propsubclause", &val); + result = isc_cfgmgr_getval("propsubnode", &val); assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(val.type, ISC_CFGMGR_STRING); assert_string_equal(val.string, "abc"); @@ -971,71 +651,6 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_nested_clauses) { result = isc_cfgmgr_commit(); assert_int_equal(result, ISC_R_SUCCESS); - /* - * Let's mix nested and repeatable clauses - */ - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("foonewsubclause"); - - bool abc_found = false; - bool def_found = false; - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "def" }; - result = isc_cfgmgr_setval("propsubclause", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_transaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UNDEFINED, - .string = NULL }; - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_open("foonewsubclause"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("propsubclause", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - if (strncmp(val.string, "abc", 3) == 0) { - abc_found = true; - } else if (strncmp(val.string, "def", 3) == 0) { - def_found = true; - } else { - assert_true(false); - } - - result = isc_cfgmgr_nextclause(); - assert_int_equal(result, ISC_R_SUCCESS); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UNDEFINED, - .string = NULL }; - result = isc_cfgmgr_getval("propsubclause", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_STRING); - if (strncmp(val.string, "abc", 3) == 0) { - abc_found = true; - } else if (strncmp(val.string, "def", 3) == 0) { - def_found = true; - } else { - assert_true(false); - } - - assert_true(abc_found); - assert_true(def_found); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_deinit(); } @@ -1043,28 +658,13 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_delete) { isc_result_t result; isc_cfgmgr_val_t val; - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); + 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_newclause("foo"); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - /* - * foo is not found because properties has been written in the - * clause. - */ - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_NOTFOUND); - - isc_cfgmgr_newclause("foo"); + isc_cfgmgr_open("foo"); val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }; result = isc_cfgmgr_setval("prop1", &val); @@ -1077,7 +677,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_delete) { isc_cfgmgr_close(); - result = isc_cfgmgr_open("foo"); + isc_cfgmgr_open("foo"); assert_int_equal(result, ISC_R_SUCCESS); result = isc_cfgmgr_getval("prop1", &val); @@ -1089,26 +689,21 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_delete) { isc_cfgmgr_close(); /* - * let's delete prop1 and add a list as prop3 + * let's delete prop1 and add prop3 */ - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); result = isc_cfgmgr_setval("prop1", NULL); assert_int_equal(result, ISC_R_SUCCESS); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 123 }; - result = isc_cfgmgr_setnextlistval("prop3", &val); + val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }; + result = isc_cfgmgr_setval("prop3", &val); assert_int_equal(result, ISC_R_SUCCESS); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 456 }; - result = isc_cfgmgr_setnextlistval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); isc_cfgmgr_close(); - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); result = isc_cfgmgr_getval("prop1", &val); assert_int_equal(result, ISC_R_NOTFOUND); @@ -1120,22 +715,14 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_delete) { result = isc_cfgmgr_getval("prop3", &val); assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_UINT32); - assert_int_equal(val.uint32, 123); - - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_SUCCESS); - ; - assert_int_equal(val.type, ISC_CFGMGR_UINT32); - assert_int_equal(val.uint32, 456); + assert_int_equal(val.type, ISC_CFGMGR_NONE); isc_cfgmgr_close(); /* - * let's delete prop2 and prop3, the whole close disappears + * let's delete prop2 and prop3 */ - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); result = isc_cfgmgr_setval("prop2", NULL); assert_int_equal(result, ISC_R_SUCCESS); @@ -1145,15 +732,22 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_delete) { isc_cfgmgr_close(); - result = isc_cfgmgr_open("foo"); + isc_cfgmgr_open("foo"); + + result = isc_cfgmgr_getval("prop2", &val); assert_int_equal(result, ISC_R_NOTFOUND); + result = isc_cfgmgr_getval("prop3", &val); + assert_int_equal(result, ISC_R_NOTFOUND); + + isc_cfgmgr_close(); + /* - * let's now delete a clause in one go (w/o explicitely - * deleting its properties. Another clause exists as well, it - * is not deleted. + * let's now delete a node in one go (w/o explicitely + * deleting its properties as well as its subnode. Another node exists + * as well, it is not deleted. */ - isc_cfgmgr_newclause("foo"); + isc_cfgmgr_open("foo"); val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }; result = isc_cfgmgr_setval("prop1", &val); @@ -1164,79 +758,69 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_delete) { result = isc_cfgmgr_setval("prop2", &val); assert_int_equal(result, ISC_R_SUCCESS); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 123 }; - result = isc_cfgmgr_setnextlistval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 456 }; - result = isc_cfgmgr_setnextlistval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_close(); - - isc_cfgmgr_newclause("fooo"); - + isc_cfgmgr_open("subfoo"); val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }; result = isc_cfgmgr_setval("prop1", &val); assert_int_equal(result, ISC_R_SUCCESS); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, - .string = "prop2val" }; - result = isc_cfgmgr_setval("prop2", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 123 }; - result = isc_cfgmgr_setnextlistval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 456 }; - result = isc_cfgmgr_setnextlistval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_close(); - - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("prop2", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_delclause(); - - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_NOTFOUND); - - result = isc_cfgmgr_open("fooo"); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("prop2", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_getval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_UINT32); - assert_int_equal(val.uint32, 123); - - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(val.type, ISC_CFGMGR_UINT32); - assert_int_equal(val.uint32, 456); - - result = isc_cfgmgr_getnextlistval(&val); - assert_int_equal(result, ISC_R_NOMORE); - result = isc_cfgmgr_commit(); assert_int_equal(result, ISC_R_SUCCESS); + result = isc_cfgmgr_rwtransaction(); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_cfgmgr_open("foo"); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_cfgmgr_getval("prop1", &val); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_cfgmgr_getval("prop2", &val); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_cfgmgr_open("subfoo"); + result = isc_cfgmgr_getval("prop1", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_delnode(); + + isc_cfgmgr_open("foo"); + + result = isc_cfgmgr_getval("prop1", &val); + assert_int_equal(result, ISC_R_NOTFOUND); + + result = isc_cfgmgr_getval("prop2", &val); + assert_int_equal(result, ISC_R_NOTFOUND); + + isc_cfgmgr_open("subfoo"); + result = isc_cfgmgr_getval("prop1", &val); + assert_int_equal(result, ISC_R_NOTFOUND); + isc_cfgmgr_close(); + + /* + * but let's rollback! "/foo/prop1" and "/foo/prop2" will still be there + * then, as well as "/foo/subfoo/prop1". + */ + isc_cfgmgr_rollback(); + + result = isc_cfgmgr_rwtransaction(); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_cfgmgr_open("foo"); + + result = isc_cfgmgr_getval("prop1", &val); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_cfgmgr_getval("prop2", &val); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_cfgmgr_open("subfoo"); + result = isc_cfgmgr_getval("prop1", &val); + assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_close(); + + isc_cfgmgr_rollback(); isc_cfgmgr_deinit(); } @@ -1251,8 +835,7 @@ cfgmgr_threads_worker(void *arg) { result = isc_cfgmgr_transaction(); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); sem_wait(&sems[0]); sem_post(&sems[1]); @@ -1268,7 +851,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_threads) { pthread_t thread; sem_t sems[2]; - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); + result = isc_cfgmgr_init(mctx, TEST_DBPATH); assert_int_equal(result, ISC_R_SUCCESS); REQUIRE(sem_init(&sems[0], 0, 0) == 0); @@ -1277,7 +860,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_threads) { result = isc_cfgmgr_rwtransaction(); assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_newclause("foo"); + isc_cfgmgr_open("foo"); result = isc_cfgmgr_setval( "p", &(isc_cfgmgr_val_t){ .type = ISC_CFGMGR_NONE }); @@ -1291,8 +874,7 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_threads) { result = isc_cfgmgr_rwtransaction(); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("foo"); - assert_int_equal(result, ISC_R_SUCCESS); + isc_cfgmgr_open("foo"); REQUIRE(sem_post(&sems[0]) == 0); REQUIRE(sem_wait(&sems[1]) == 0); @@ -1305,206 +887,206 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_threads) { isc_cfgmgr_deinit(); } -ISC_RUN_TEST_IMPL(isc_cfgmgr_validationtest) { - isc_result_t result; - isc_cfgmgr_val_t val; - - /* - * foo clause is mandatory and non-repeatable, it has one property prop1 - * (string, mandatory) - */ - const isc_cfgmgr_prop_t prop1 = { "prop1", false, ISC_CFGMGR_STRING }; - const isc_cfgmgr_prop_t prop2 = { "prop2", true, ISC_CFGMGR_UINT32 }; - const isc_cfgmgr_prop_t *foo_props[] = { &prop1, &prop2, NULL }; - const isc_cfgmgr_clause_t foo = { "foo", false, false, NULL, - foo_props }; - - /* - * bar clause is optional and repratable, it has one property prop3 - * (string, mandatory), another prop4 (string, optional) - */ - const isc_cfgmgr_prop_t prop3 = { "prop3", false, ISC_CFGMGR_STRING }; - const isc_cfgmgr_prop_t prop4 = { "prop4", true, ISC_CFGMGR_STRING }; - const isc_cfgmgr_prop_t *bar_props[] = { &prop3, &prop4, NULL }; - const isc_cfgmgr_clause_t bar = { "bar", true, true, NULL, bar_props }; - - const isc_cfgmgr_clause_t *format[] = { &foo, &bar, NULL }; - - result = isc_cfgmgr_init(mctx, TEST_DBPATH, format); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - /* - * let's add just the mandatory property to foo first - */ - isc_cfgmgr_newclause("foo"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "ab" }; - result = isc_cfgmgr_setval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_close(); - - /* - * two instances of bar as it's a repeatable clause - */ - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "cd" }; - isc_cfgmgr_newclause("bar"); - result = isc_cfgmgr_setval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_close(); - - isc_cfgmgr_newclause("bar"); - val.string = "ef"; - result = isc_cfgmgr_setval("prop4", &val); - assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_close(); - - /* - * validation fails, as the second instance of bar doesn't have the - * mandatory property prop3. (the first instance doesn't have prop4, but - * it doesn't matter as it's optional) - */ - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_NOTFOUND); - assert_string_equal(isc_cfgmgr_lasterror(), - "mandatory property bar.prop3 is missing"); - - /* - * another attempt, with only foo - */ - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("foo"); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 42 }; - result = isc_cfgmgr_setval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - /* - * validation fails here as well, because the type of prop1 is wrong - */ - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_UNEXPECTED); - assert_string_equal(isc_cfgmgr_lasterror(), - "property foo.prop1 has wrong type. given uint32, " - "expected string"); - - /* - * validation fails again, because foo is not repeatable - */ - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("foo"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; - result = isc_cfgmgr_setval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_close(); - - isc_cfgmgr_newclause("foo"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; - result = isc_cfgmgr_setval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_close(); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_MULTIPLE); - assert_string_equal(isc_cfgmgr_lasterror(), - "clause foo is repeated but it's not repeatable"); - - /* - * validation fails again, becuase foo is mandatory clause but not - * provided - */ - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("bar"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; - result = isc_cfgmgr_setval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_NOTFOUND); - assert_string_equal(isc_cfgmgr_lasterror(), - "mandatory clause foo is missing"); - - /* - * Test validation of subclauses. Now bar is a mandatory child clause of - * foo. - */ - const isc_cfgmgr_clause_t bar2 = { "bar", false, true, NULL, - bar_props }; - const isc_cfgmgr_clause_t *foo2subclauses[] = { &bar2, NULL }; - const isc_cfgmgr_clause_t foo2 = { "foo", false, false, foo2subclauses, - foo_props }; - const isc_cfgmgr_clause_t *format2[] = { &foo2, NULL }; - - isc_cfgmgr_deinit(); - result = isc_cfgmgr_init(mctx, TEST_DBPATH, format2); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("foo"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; - result = isc_cfgmgr_setval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - /* - * validation fails because bar subclause is missing from foo - */ - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_NOTFOUND); - assert_string_equal(isc_cfgmgr_lasterror(), - "mandatory clause foo.bar is missing"); - - /* - * Let's try again... This time it fails because of a missing mandatory - * property in bar - */ - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("foo"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; - result = isc_cfgmgr_setval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("bar"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; - result = isc_cfgmgr_setval("prop4", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_NOTFOUND); - assert_string_equal(isc_cfgmgr_lasterror(), - "mandatory property foo.bar.prop3 is missing"); - - /* - * And again... This time validation passes. - */ - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("foo"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; - result = isc_cfgmgr_setval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("bar"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; - result = isc_cfgmgr_setval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_deinit(); -} +//ISC_RUN_TEST_IMPL(isc_cfgmgr_validationtest) { +// isc_result_t result; +// isc_cfgmgr_val_t val; +// +// /* +// * foo clause is mandatory and non-repeatable, it has one property prop1 +// * (string, mandatory) +// */ +// const isc_cfgmgr_prop_t prop1 = { "prop1", false, ISC_CFGMGR_STRING }; +// const isc_cfgmgr_prop_t prop2 = { "prop2", true, ISC_CFGMGR_UINT32 }; +// const isc_cfgmgr_prop_t *foo_props[] = { &prop1, &prop2, NULL }; +// const isc_cfgmgr_clause_t foo = { "foo", false, false, NULL, +// foo_props }; +// +// /* +// * bar clause is optional and repratable, it has one property prop3 +// * (string, mandatory), another prop4 (string, optional) +// */ +// const isc_cfgmgr_prop_t prop3 = { "prop3", false, ISC_CFGMGR_STRING }; +// const isc_cfgmgr_prop_t prop4 = { "prop4", true, ISC_CFGMGR_STRING }; +// const isc_cfgmgr_prop_t *bar_props[] = { &prop3, &prop4, NULL }; +// const isc_cfgmgr_clause_t bar = { "bar", true, true, NULL, bar_props }; +// +// const isc_cfgmgr_clause_t *format[] = { &foo, &bar, NULL }; +// +// result = isc_cfgmgr_init(mctx, TEST_DBPATH, format); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// result = isc_cfgmgr_rwtransaction(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// /* +// * let's add just the mandatory property to foo first +// */ +// isc_cfgmgr_newclause("foo"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "ab" }; +// result = isc_cfgmgr_setval("prop1", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// isc_cfgmgr_close(); +// +// /* +// * two instances of bar as it's a repeatable clause +// */ +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "cd" }; +// isc_cfgmgr_newclause("bar"); +// result = isc_cfgmgr_setval("prop3", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// isc_cfgmgr_close(); +// +// isc_cfgmgr_newclause("bar"); +// val.string = "ef"; +// result = isc_cfgmgr_setval("prop4", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// isc_cfgmgr_close(); +// +// /* +// * validation fails, as the second instance of bar doesn't have the +// * mandatory property prop3. (the first instance doesn't have prop4, but +// * it doesn't matter as it's optional) +// */ +// result = isc_cfgmgr_commit(); +// assert_int_equal(result, ISC_R_NOTFOUND); +// assert_string_equal(isc_cfgmgr_lasterror(), +// "mandatory property bar.prop3 is missing"); +// +// /* +// * another attempt, with only foo +// */ +// result = isc_cfgmgr_rwtransaction(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_newclause("foo"); +// +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 42 }; +// result = isc_cfgmgr_setval("prop1", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// /* +// * validation fails here as well, because the type of prop1 is wrong +// */ +// result = isc_cfgmgr_commit(); +// assert_int_equal(result, ISC_R_UNEXPECTED); +// assert_string_equal(isc_cfgmgr_lasterror(), +// "property foo.prop1 has wrong type. given uint32, " +// "expected string"); +// +// /* +// * validation fails again, because foo is not repeatable +// */ +// result = isc_cfgmgr_rwtransaction(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_newclause("foo"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; +// result = isc_cfgmgr_setval("prop1", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// isc_cfgmgr_close(); +// +// isc_cfgmgr_newclause("foo"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; +// result = isc_cfgmgr_setval("prop1", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// isc_cfgmgr_close(); +// +// result = isc_cfgmgr_commit(); +// assert_int_equal(result, ISC_R_MULTIPLE); +// assert_string_equal(isc_cfgmgr_lasterror(), +// "clause foo is repeated but it's not repeatable"); +// +// /* +// * validation fails again, becuase foo is mandatory clause but not +// * provided +// */ +// result = isc_cfgmgr_rwtransaction(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_newclause("bar"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; +// result = isc_cfgmgr_setval("prop3", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// result = isc_cfgmgr_commit(); +// assert_int_equal(result, ISC_R_NOTFOUND); +// assert_string_equal(isc_cfgmgr_lasterror(), +// "mandatory clause foo is missing"); +// +// /* +// * Test validation of subclauses. Now bar is a mandatory child clause of +// * foo. +// */ +// const isc_cfgmgr_clause_t bar2 = { "bar", false, true, NULL, +// bar_props }; +// const isc_cfgmgr_clause_t *foo2subclauses[] = { &bar2, NULL }; +// const isc_cfgmgr_clause_t foo2 = { "foo", false, false, foo2subclauses, +// foo_props }; +// const isc_cfgmgr_clause_t *format2[] = { &foo2, NULL }; +// +// isc_cfgmgr_deinit(); +// result = isc_cfgmgr_init(mctx, TEST_DBPATH, format2); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// result = isc_cfgmgr_rwtransaction(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_newclause("foo"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; +// result = isc_cfgmgr_setval("prop1", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// /* +// * validation fails because bar subclause is missing from foo +// */ +// result = isc_cfgmgr_commit(); +// assert_int_equal(result, ISC_R_NOTFOUND); +// assert_string_equal(isc_cfgmgr_lasterror(), +// "mandatory clause foo.bar is missing"); +// +// /* +// * Let's try again... This time it fails because of a missing mandatory +// * property in bar +// */ +// result = isc_cfgmgr_rwtransaction(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_newclause("foo"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; +// result = isc_cfgmgr_setval("prop1", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_newclause("bar"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; +// result = isc_cfgmgr_setval("prop4", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// result = isc_cfgmgr_commit(); +// assert_int_equal(result, ISC_R_NOTFOUND); +// assert_string_equal(isc_cfgmgr_lasterror(), +// "mandatory property foo.bar.prop3 is missing"); +// +// /* +// * And again... This time validation passes. +// */ +// result = isc_cfgmgr_rwtransaction(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_newclause("foo"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; +// result = isc_cfgmgr_setval("prop1", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_newclause("bar"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; +// result = isc_cfgmgr_setval("prop3", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// result = isc_cfgmgr_commit(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_deinit(); +//} // ISC_RUN_TEST_IMPL(isc_cfgmgr_getparent) { // isc_result_t result; @@ -1646,26 +1228,22 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_opentests) { isc_result_t result; isc_cfgmgr_val_t val = { .type = ISC_CFGMGR_UINT32, .uint32 = 1234 }; - result = isc_cfgmgr_init(mctx, TEST_DBPATH, NULL); + 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); - assert_int_equal(isc_cfgmgr_open("foo"), ISC_R_NOTFOUND); - assert_int_equal(isc_cfgmgr_open("bar"), ISC_R_NOTFOUND); - assert_int_equal(isc_cfgmgr_open("baz"), ISC_R_NOTFOUND); - - isc_cfgmgr_newclause("foo"); - isc_cfgmgr_setval("prop", &val); + isc_cfgmgr_open("foo"); + result = isc_cfgmgr_setval("prop", &val); assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_newclause("bar"); - isc_cfgmgr_setval("prop", &val); + isc_cfgmgr_open("bar"); + result = isc_cfgmgr_setval("prop", &val); assert_int_equal(result, ISC_R_SUCCESS); - isc_cfgmgr_newclause("baz"); - isc_cfgmgr_setval("prop", &val); + isc_cfgmgr_open("baz"); + result = isc_cfgmgr_setval("prop", &val); assert_int_equal(result, ISC_R_SUCCESS); result = isc_cfgmgr_commit(); @@ -1675,47 +1253,69 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_opentests) { assert_int_equal(result, ISC_R_SUCCESS); /* - * Can't open directly bar or baz! + * Directly going into bar or baz doesn't bring us in "/foo/bar" or + * "/foo/bar/baz"! */ - result = isc_cfgmgr_open("bar"); + isc_cfgmgr_open("bar"); + result = isc_cfgmgr_getval("prop", &val); assert_int_equal(result, ISC_R_NOTFOUND); + isc_cfgmgr_close(); - result = isc_cfgmgr_open("baz"); + isc_cfgmgr_open("baz"); + result = isc_cfgmgr_getval("prop", &val); assert_int_equal(result, ISC_R_NOTFOUND); + isc_cfgmgr_close(); - result = isc_cfgmgr_open("foo"); + isc_cfgmgr_open("foo"); + result = isc_cfgmgr_getval("prop", &val); assert_int_equal(result, ISC_R_SUCCESS); /* - * And can't open directly baz (not open foo, which doesn't exists there - * either)! + * can't go directly to "/foo/bar/baz" from "/foo" */ - result = isc_cfgmgr_open("baz"); + isc_cfgmgr_open("baz"); + result = isc_cfgmgr_getval("prop", &val); assert_int_equal(result, ISC_R_NOTFOUND); + isc_cfgmgr_close(); - result = isc_cfgmgr_open("foo"); + /* + * can't go directly to "/foo" from "/foo" w/o closing it first + */ + isc_cfgmgr_open("foo"); + result = isc_cfgmgr_getval("prop", &val); assert_int_equal(result, ISC_R_NOTFOUND); + isc_cfgmgr_close(); - result = isc_cfgmgr_open("bar"); + /* + * but we can access "/foo/bar" from "/foo" + */ + isc_cfgmgr_open("bar"); + result = isc_cfgmgr_getval("prop", &val); assert_int_equal(result, ISC_R_SUCCESS); /* - * Finally open baz (but then can't open bar or foo already opened - * anyway, but the reason is because they doesn't exists here) + * Finally open "/foo/bar/baz" (but then can't read properties from + * "/foo/bar/bar" or "/foo/bar/foo" because they doesn't exists here) */ - result = isc_cfgmgr_open("baz"); + isc_cfgmgr_open("baz"); + result = isc_cfgmgr_getval("prop", &val); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_cfgmgr_open("foo"); + isc_cfgmgr_open("foo"); + result = isc_cfgmgr_getval("prop", &val); + assert_int_equal(result, ISC_R_NOTFOUND); + isc_cfgmgr_close(); + + isc_cfgmgr_open("bar"); + result = isc_cfgmgr_getval("prop", &val); + assert_int_equal(result, ISC_R_NOTFOUND); + isc_cfgmgr_close(); + + isc_cfgmgr_open("baz"); + result = isc_cfgmgr_getval("prop", &val); assert_int_equal(result, ISC_R_NOTFOUND); - result = isc_cfgmgr_open("bar"); - assert_int_equal(result, ISC_R_NOTFOUND); - - result = isc_cfgmgr_open("baz"); - assert_int_equal(result, ISC_R_NOTFOUND); - - (void)isc_cfgmgr_commit(); + isc_cfgmgr_rollback(); isc_cfgmgr_deinit(); } @@ -1897,284 +1497,284 @@ ISC_RUN_TEST_IMPL(isc_cfgmgr_opentests) { // isc_cfgmgr_deinit(); // } -static void -cfgmgr_dumptests_clausestarts(void *state, const char *name, size_t indent) { - isc_buffer_t *b = state; - - for (size_t i = 0; i < indent; i++) { - isc_buffer_putstr(b, " "); - } - isc_buffer_printf(b, "%s {\n", name); -} - -static void -cfgmgr_dumptests_clauseends(void *state, const char *name, size_t indent) { - isc_buffer_t *b = state; - - for (size_t i = 0; i < indent; i++) { - isc_buffer_putstr(b, " "); - } - isc_buffer_printf(b, "}; #%s\n", name); -} - -static void -cfgmgr_dumptests_property(void *state, const char *name, - const isc_cfgmgr_val_t *val, size_t indent) { - isc_buffer_t *b = state; - - for (size_t i = 0; i < indent; i++) { - isc_buffer_putstr(b, " "); - } - isc_buffer_putstr(b, name); - isc_buffer_putstr(b, " "); - - switch (val->type) { - case ISC_CFGMGR_UINT32: - isc_buffer_printf(b, "%" PRIu32, val->uint32); - break; - case ISC_CFGMGR_STRING: - isc_buffer_putstr(b, val->string); - break; - default: - UNREACHABLE(); - } - - isc_buffer_putstr(b, ";\n"); -} - -ISC_RUN_TEST_IMPL(isc_cfgmgr_dumptests) { - isc_result_t result; - isc_cfgmgr_val_t val; - - const isc_cfgmgr_prop_t prop3 = { "prop3", false, ISC_CFGMGR_STRING }; - const isc_cfgmgr_prop_t prop4 = { "prop4", true, ISC_CFGMGR_STRING }; - const isc_cfgmgr_prop_t *bar_props[] = { &prop3, &prop4, NULL }; - const isc_cfgmgr_clause_t bar = { "bar", true, true, NULL, bar_props }; - - const isc_cfgmgr_prop_t prop1 = { "prop1", false, ISC_CFGMGR_STRING }; - const isc_cfgmgr_prop_t prop2 = { "prop2", true, ISC_CFGMGR_UINT32 }; - const isc_cfgmgr_prop_t prop2bis = { "prop2bis", false, - ISC_CFGMGR_UINT32 }; - const isc_cfgmgr_prop_t *foo_props[] = { &prop1, &prop2, &prop2bis, - NULL }; - const isc_cfgmgr_clause_t *foo_clauses[] = { &bar, NULL }; - const isc_cfgmgr_clause_t foo = { "foo", false, false, foo_clauses, - foo_props }; - - const isc_cfgmgr_prop_t prop5 = { "prop5", false, ISC_CFGMGR_STRING }; - const isc_cfgmgr_prop_t *baz_props[] = { &prop5, NULL }; - const isc_cfgmgr_clause_t baz = { "baz", false, true, NULL, baz_props }; - - const isc_cfgmgr_clause_t *format[] = { &foo, &baz, NULL }; - - result = isc_cfgmgr_init(mctx, TEST_DBPATH, format); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_setmode(ISC_CFGMGR_MODEUSER); - - isc_cfgmgr_newclause("foo"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "a" }; - result = isc_cfgmgr_setval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 43 }; - result = isc_cfgmgr_setval("prop2bis", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - /* - * Yes, "prop4" has nothing to do in "foo" (according to the format). - * But let's add it anyway: this won't be visible in foo (because it's - * not as part of its format) and... Won't be visible in bar, because - * we'll iniitally make a ISC_CFGMGR_MODEUSER, so a dump of the user - * configuration only, which does have any kind of materialization - */ - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "zz" }; - result = isc_cfgmgr_setval("prop4", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_newclause("bar"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; - result = isc_cfgmgr_setval("prop3", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_close(); - isc_cfgmgr_close(); - - isc_cfgmgr_newclause("baz"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "c" }; - result = isc_cfgmgr_setval("prop5", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_close(); - - isc_cfgmgr_newclause("baz"); - /* - * Let's use the exact internal value, as there is no ordering garantee - * of the repeatable clauses (depending the random clause instance - * identifier, one might be before the other depending the runs...) - */ - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "c" }; - result = isc_cfgmgr_setval("prop5", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - char data[512]; - isc_buffer_t buffer; - isc_cfgmgr_dumpcfg_t cfg = { .clausestarts = - cfgmgr_dumptests_clausestarts, - .clauseends = cfgmgr_dumptests_clauseends, - .property = cfgmgr_dumptests_property, - .state = &buffer, - .indent = 8, - .dumpmode = ISC_CFGMGR_MODEUSER }; - - isc_buffer_init(&buffer, data, sizeof(data)); - result = isc_cfgmgr_dump(&cfg); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_buffer_putuint8(&buffer, 0); - - const char expected[] = "foo {\n" - " prop1 a;\n" - " prop2bis 43;\n" - " bar {\n" - " prop3 b;\n" - " }; #bar\n" - "}; #foo\n" - "baz {\n" - " prop5 c;\n" - "}; #baz\n" - "baz {\n" - " prop5 c;\n" - "}; #baz\n"; - - assert_string_equal(buffer.base, expected); - - // /* - // * Let's now run a ISC_CFGMGR_DUMPRUNNING dump. The only - //difference is - // * that prop4 will now be visible in bar, as the inheritance is - //enabled. - // */ - // cfg.dumpmode = ISC_CFGMGR_MODERUNNING; - // isc_buffer_init(&buffer, data, sizeof(data)); - // result = isc_cfgmgr_dump(&cfg); - // assert_int_equal(result, ISC_R_SUCCESS); - // - // isc_buffer_putuint8(&buffer, 0); - // - // const char expected2[] = "foo {\n" - // " prop1 a;\n" - // " prop2bis 43;\n" - // " bar {\n" - // " prop3 b;\n" - // " prop4 zz;\n" - // " }; #bar\n" - // "}; #foo\n" - // "baz {\n" - // " prop5 c;\n" - // "}; #baz\n" - // "baz {\n" - // " prop5 c;\n" - // "}; #baz\n"; - // - // assert_string_equal(buffer.base, expected2); - - /* - * Dump with ISC_CFGMGR_UNKNOWN is a coding error - */ - cfg.dumpmode = ISC_CFGMGR_MODEUNKNOWN; - isc_buffer_init(&buffer, data, sizeof(data)); - expect_assert_failure(isc_cfgmgr_dump(&cfg)); - - /* - * Let's add some builtin data. - */ - result = isc_cfgmgr_rwtransaction(); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_setmode(ISC_CFGMGR_MODEBUILTIN); - isc_cfgmgr_newclause("foo"); - - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, - .string = "abuiltin" }; - result = isc_cfgmgr_setval("prop1", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_cfgmgr_close(); - isc_cfgmgr_newclause("baz"); - val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "c" }; - result = isc_cfgmgr_setval("prop5", &val); - assert_int_equal(result, ISC_R_SUCCESS); - - result = isc_cfgmgr_commit(); - assert_int_equal(result, ISC_R_SUCCESS); - - /* - * let's dump in ISC_CFGMGR_DUMPBUILTIN only - */ - cfg.dumpmode = ISC_CFGMGR_MODEBUILTIN; - isc_buffer_init(&buffer, data, sizeof(data)); - result = isc_cfgmgr_dump(&cfg); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_buffer_putuint8(&buffer, 0); - - const char expected3[] = "foo {\n" - " prop1 abuiltin;\n" - "}; #foo\n" - "baz {\n" - " prop5 c;\n" - "}; #baz\n"; - - assert_string_equal(buffer.base, expected3); - - // /* - // * let's make a new running dump - foo.prop1 will override the - // * builtin value. - // */ - // cfg.dumpmode = ISC_CFGMGR_MODERUNNING; - // isc_buffer_init(&buffer, data, sizeof(data)); - // result = isc_cfgmgr_dump(&cfg); - // assert_int_equal(result, ISC_R_SUCCESS); - // - // isc_buffer_putuint8(&buffer, 0); - // - // const char expected4[] = "foo {\n" - // " prop1 a;\n" - // " prop2bis 43;\n" - // " bar {\n" - // " prop3 b;\n" - // " prop4 zz;\n" - // " }; #bar\n" - // "}; #foo\n" - // "baz {\n" - // " prop5 c;\n" - // "}; #baz\n" - // "baz {\n" - // " prop5 c;\n" - // "}; #baz\n"; - // - // assert_string_equal(buffer.base, expected4); - - /* - * And another user only dump, which will be as the first dump (do the - * builtin value in foo is ignored) - */ - cfg.dumpmode = ISC_CFGMGR_MODEUSER; - isc_buffer_init(&buffer, data, sizeof(data)); - result = isc_cfgmgr_dump(&cfg); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_buffer_putuint8(&buffer, 0); - assert_string_equal(buffer.base, expected); - - isc_cfgmgr_deinit(); -} +//static void +//cfgmgr_dumptests_clausestarts(void *state, const char *name, size_t indent) { +// isc_buffer_t *b = state; +// +// for (size_t i = 0; i < indent; i++) { +// isc_buffer_putstr(b, " "); +// } +// isc_buffer_printf(b, "%s {\n", name); +//} +// +//static void +//cfgmgr_dumptests_clauseends(void *state, const char *name, size_t indent) { +// isc_buffer_t *b = state; +// +// for (size_t i = 0; i < indent; i++) { +// isc_buffer_putstr(b, " "); +// } +// isc_buffer_printf(b, "}; #%s\n", name); +//} +// +//static void +//cfgmgr_dumptests_property(void *state, const char *name, +// const isc_cfgmgr_val_t *val, size_t indent) { +// isc_buffer_t *b = state; +// +// for (size_t i = 0; i < indent; i++) { +// isc_buffer_putstr(b, " "); +// } +// isc_buffer_putstr(b, name); +// isc_buffer_putstr(b, " "); +// +// switch (val->type) { +// case ISC_CFGMGR_UINT32: +// isc_buffer_printf(b, "%" PRIu32, val->uint32); +// break; +// case ISC_CFGMGR_STRING: +// isc_buffer_putstr(b, val->string); +// break; +// default: +// UNREACHABLE(); +// } +// +// isc_buffer_putstr(b, ";\n"); +//} +// +//ISC_RUN_TEST_IMPL(isc_cfgmgr_dumptests) { +// isc_result_t result; +// isc_cfgmgr_val_t val; +// +// const isc_cfgmgr_prop_t prop3 = { "prop3", false, ISC_CFGMGR_STRING }; +// const isc_cfgmgr_prop_t prop4 = { "prop4", true, ISC_CFGMGR_STRING }; +// const isc_cfgmgr_prop_t *bar_props[] = { &prop3, &prop4, NULL }; +// const isc_cfgmgr_clause_t bar = { "bar", true, true, NULL, bar_props }; +// +// const isc_cfgmgr_prop_t prop1 = { "prop1", false, ISC_CFGMGR_STRING }; +// const isc_cfgmgr_prop_t prop2 = { "prop2", true, ISC_CFGMGR_UINT32 }; +// const isc_cfgmgr_prop_t prop2bis = { "prop2bis", false, +// ISC_CFGMGR_UINT32 }; +// const isc_cfgmgr_prop_t *foo_props[] = { &prop1, &prop2, &prop2bis, +// NULL }; +// const isc_cfgmgr_clause_t *foo_clauses[] = { &bar, NULL }; +// const isc_cfgmgr_clause_t foo = { "foo", false, false, foo_clauses, +// foo_props }; +// +// const isc_cfgmgr_prop_t prop5 = { "prop5", false, ISC_CFGMGR_STRING }; +// const isc_cfgmgr_prop_t *baz_props[] = { &prop5, NULL }; +// const isc_cfgmgr_clause_t baz = { "baz", false, true, NULL, baz_props }; +// +// const isc_cfgmgr_clause_t *format[] = { &foo, &baz, NULL }; +// +// result = isc_cfgmgr_init(mctx, TEST_DBPATH, format); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// result = isc_cfgmgr_rwtransaction(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_setmode(ISC_CFGMGR_MODEUSER); +// +// isc_cfgmgr_newclause("foo"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "a" }; +// result = isc_cfgmgr_setval("prop1", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_UINT32, .uint32 = 43 }; +// result = isc_cfgmgr_setval("prop2bis", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// /* +// * Yes, "prop4" has nothing to do in "foo" (according to the format). +// * But let's add it anyway: this won't be visible in foo (because it's +// * not as part of its format) and... Won't be visible in bar, because +// * we'll iniitally make a ISC_CFGMGR_MODEUSER, so a dump of the user +// * configuration only, which does have any kind of materialization +// */ +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "zz" }; +// result = isc_cfgmgr_setval("prop4", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_newclause("bar"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "b" }; +// result = isc_cfgmgr_setval("prop3", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_close(); +// isc_cfgmgr_close(); +// +// isc_cfgmgr_newclause("baz"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "c" }; +// result = isc_cfgmgr_setval("prop5", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_close(); +// +// isc_cfgmgr_newclause("baz"); +// /* +// * Let's use the exact internal value, as there is no ordering garantee +// * of the repeatable clauses (depending the random clause instance +// * identifier, one might be before the other depending the runs...) +// */ +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "c" }; +// result = isc_cfgmgr_setval("prop5", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// result = isc_cfgmgr_commit(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// char data[512]; +// isc_buffer_t buffer; +// isc_cfgmgr_dumpcfg_t cfg = { .clausestarts = +// cfgmgr_dumptests_clausestarts, +// .clauseends = cfgmgr_dumptests_clauseends, +// .property = cfgmgr_dumptests_property, +// .state = &buffer, +// .indent = 8, +// .dumpmode = ISC_CFGMGR_MODEUSER }; +// +// isc_buffer_init(&buffer, data, sizeof(data)); +// result = isc_cfgmgr_dump(&cfg); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_buffer_putuint8(&buffer, 0); +// +// const char expected[] = "foo {\n" +// " prop1 a;\n" +// " prop2bis 43;\n" +// " bar {\n" +// " prop3 b;\n" +// " }; #bar\n" +// "}; #foo\n" +// "baz {\n" +// " prop5 c;\n" +// "}; #baz\n" +// "baz {\n" +// " prop5 c;\n" +// "}; #baz\n"; +// +// assert_string_equal(buffer.base, expected); +// +// // /* +// // * Let's now run a ISC_CFGMGR_DUMPRUNNING dump. The only +// //difference is +// // * that prop4 will now be visible in bar, as the inheritance is +// //enabled. +// // */ +// // cfg.dumpmode = ISC_CFGMGR_MODERUNNING; +// // isc_buffer_init(&buffer, data, sizeof(data)); +// // result = isc_cfgmgr_dump(&cfg); +// // assert_int_equal(result, ISC_R_SUCCESS); +// // +// // isc_buffer_putuint8(&buffer, 0); +// // +// // const char expected2[] = "foo {\n" +// // " prop1 a;\n" +// // " prop2bis 43;\n" +// // " bar {\n" +// // " prop3 b;\n" +// // " prop4 zz;\n" +// // " }; #bar\n" +// // "}; #foo\n" +// // "baz {\n" +// // " prop5 c;\n" +// // "}; #baz\n" +// // "baz {\n" +// // " prop5 c;\n" +// // "}; #baz\n"; +// // +// // assert_string_equal(buffer.base, expected2); +// +// /* +// * Dump with ISC_CFGMGR_UNKNOWN is a coding error +// */ +// cfg.dumpmode = ISC_CFGMGR_MODEUNKNOWN; +// isc_buffer_init(&buffer, data, sizeof(data)); +// expect_assert_failure(isc_cfgmgr_dump(&cfg)); +// +// /* +// * Let's add some builtin data. +// */ +// result = isc_cfgmgr_rwtransaction(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_setmode(ISC_CFGMGR_MODEBUILTIN); +// isc_cfgmgr_newclause("foo"); +// +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, +// .string = "abuiltin" }; +// result = isc_cfgmgr_setval("prop1", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_cfgmgr_close(); +// isc_cfgmgr_newclause("baz"); +// val = (isc_cfgmgr_val_t){ .type = ISC_CFGMGR_STRING, .string = "c" }; +// result = isc_cfgmgr_setval("prop5", &val); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// result = isc_cfgmgr_commit(); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// /* +// * let's dump in ISC_CFGMGR_DUMPBUILTIN only +// */ +// cfg.dumpmode = ISC_CFGMGR_MODEBUILTIN; +// isc_buffer_init(&buffer, data, sizeof(data)); +// result = isc_cfgmgr_dump(&cfg); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_buffer_putuint8(&buffer, 0); +// +// const char expected3[] = "foo {\n" +// " prop1 abuiltin;\n" +// "}; #foo\n" +// "baz {\n" +// " prop5 c;\n" +// "}; #baz\n"; +// +// assert_string_equal(buffer.base, expected3); +// +// // /* +// // * let's make a new running dump - foo.prop1 will override the +// // * builtin value. +// // */ +// // cfg.dumpmode = ISC_CFGMGR_MODERUNNING; +// // isc_buffer_init(&buffer, data, sizeof(data)); +// // result = isc_cfgmgr_dump(&cfg); +// // assert_int_equal(result, ISC_R_SUCCESS); +// // +// // isc_buffer_putuint8(&buffer, 0); +// // +// // const char expected4[] = "foo {\n" +// // " prop1 a;\n" +// // " prop2bis 43;\n" +// // " bar {\n" +// // " prop3 b;\n" +// // " prop4 zz;\n" +// // " }; #bar\n" +// // "}; #foo\n" +// // "baz {\n" +// // " prop5 c;\n" +// // "}; #baz\n" +// // "baz {\n" +// // " prop5 c;\n" +// // "}; #baz\n"; +// // +// // assert_string_equal(buffer.base, expected4); +// +// /* +// * And another user only dump, which will be as the first dump (do the +// * builtin value in foo is ignored) +// */ +// cfg.dumpmode = ISC_CFGMGR_MODEUSER; +// isc_buffer_init(&buffer, data, sizeof(data)); +// result = isc_cfgmgr_dump(&cfg); +// assert_int_equal(result, ISC_R_SUCCESS); +// +// isc_buffer_putuint8(&buffer, 0); +// assert_string_equal(buffer.base, expected); +// +// isc_cfgmgr_deinit(); +//} ISC_TEST_LIST_START ISC_TEST_ENTRY(isc_cfgmgr_assertions) @@ -2182,16 +1782,14 @@ ISC_TEST_ENTRY(isc_cfgmgr_rw) ISC_TEST_ENTRY(isc_cfgmgr_rollback) ISC_TEST_ENTRY(isc_cfgmgr_override) ISC_TEST_ENTRY(isc_cfgmgr_rw_string) -ISC_TEST_ENTRY(isc_cfgmgr_list) ISC_TEST_ENTRY(isc_cfgmgr_delete) -ISC_TEST_ENTRY(isc_cfgmgr_repeatable_clauses) -ISC_TEST_ENTRY(isc_cfgmgr_nested_clauses) +ISC_TEST_ENTRY(isc_cfgmgr_nested_nodes) ISC_TEST_ENTRY(isc_cfgmgr_threads) -ISC_TEST_ENTRY(isc_cfgmgr_parseid) -ISC_TEST_ENTRY(isc_cfgmgr_validationtest) -// ISC_TEST_ENTRY(isc_cfgmgr_getparent) ISC_TEST_ENTRY(isc_cfgmgr_opentests) + +// ISC_TEST_ENTRY(isc_cfgmgr_validationtest) +// ISC_TEST_ENTRY(isc_cfgmgr_getparent) // ISC_TEST_ENTRY(isc_cfgmgr_builtintests) -ISC_TEST_ENTRY(isc_cfgmgr_dumptests) +// ISC_TEST_ENTRY(isc_cfgmgr_dumptests) ISC_TEST_LIST_END ISC_TEST_MAIN