Compare commits

...
Author SHA1 Message Date
Colin Vidal 31be352cb2 Attempt to compare rwlock and LMDB internal locking mechanism.
The benchmark first writes 1000 key/values (actually, the same key is
re-used) in LMDB and read it 1000 times. Then it does the same thing
using concurrency, on a 16 cores machine, with the following
configurations:

- 32 threads (29 readers and 3 writer)
- 8 threads (6 readers and 2 writer)
- 8 threads (8 readers and 0 writer)

See [3] for LMDB internal locking result and [4] for rwlock result.

What the result seems to say is that LMDB internal locking mechanism
is doing way better (100x) when there are lot of concurrency
involved. I'm kinda surprised by such factor and tried various things
in case I've done something wrong, but eventually always end up with
similar difference. The only explanation I have is that LMDB actually
does uses an userspace RCU mechanism internally, so readers are never
blocked.

Using rwlock would block the readers while a writer lock is
alive. LMDB doc seems to say [1] that if we use our own
synchronization mechanism, we should only have one writer at the time,
and hold a write lock from the beginning of the transaction up to the
commit/revert (I tried holding a read lock on a write transaction then
upgrade to a write lock only at commit time... but of course I end up
in lot of odd crashes). I think this explain the massive congestion
when using 32 threads, as lot of readers are waiting.

When there is no thread congestion (the 2 later configuration with 8
threads) using the rwlock actually seems a little bit faster (same
order of magnitude though). I guess this might be explained by the
fact that, while LMDB doesn't lock readers, there is still a quick
lock-unlock to maintains a reader table (and eventually free unused
old versions once a writer commit). [2]

[1] http://www.lmdb.tech/doc/group__mdb.html#:~:text=must%20ensure%20that%20no%20readers%20are%20using%20old%20transactions%20while%20a%20writer%20is%20active

[2] http://www.lmdb.tech/doc/group__readers.html

[3] two runs using LMDB internal locking (HEAD~2)

    sequential writes and read
    avg write 0.005878 ms
    avg read 0.001640 ms
    concurrent 29 readers and 3 writers
    avg write 0.746217 ms
    avg read 6.644507 ms
    concurrent 6 readers and 2 writers
    avg write 0.077576 ms
    avg read 0.177316 ms
    concurrent 8 readers and 0 writer
    avg write 0.000000 ms
    avg read 0.273563 ms

    sequential writes and read
    avg write 0.005799 ms
    avg read 0.001656 ms
    concurrent 29 readers and 3 writers
    avg write 0.737782 ms
    avg read 6.616162 ms
    concurrent 6 readers and 2 writers
    avg write 0.075893 ms
    avg read 0.178631 ms
    concurrent 8 readers and 0 writer
    avg write 0.000000 ms
    avg read 0.303209 ms

[4] two runs using rwlock (HEAD~1)

    sequential writes and read
    avg write 0.005931 ms
    avg read 0.001658 ms
    concurrent 29 readers and 3 writers
    avg write 17.249417 ms
    avg read 163.805094 ms
    concurrent 6 readers and 2 writers
    avg write 0.040559 ms
    avg read 0.168439 ms
    concurrent 8 readers and 0 writer
    avg write 0.000000 ms
    avg read 0.200699 ms

    sequential writes and read
    avg write 0.007423 ms
    avg read 0.001656 ms
    concurrent 29 readers and 3 writers
    avg write 20.312584 ms
    avg read 190.565366 ms
    concurrent 6 readers and 2 writers
    avg write 0.026039 ms
    avg read 0.147365 ms
    concurrent 8 readers and 0 writer
    avg write 0.000000 ms
    avg read 0.174471 ms
2025-01-02 10:59:03 +01:00
Colin Vidal 27fab42e6f using rwlock 2025-01-02 10:50:37 +01:00
Colin Vidal a6daf19d0b benchmark 2025-01-02 10:50:37 +01:00
Colin Vidal b50bf757f2 fixup! Introduction of cfgmgr 2024-12-20 12:37:44 +01:00
Colin Vidal b3fba18b3c fixup! Introduction of cfgmgr 2024-12-20 12:26:34 +01:00
Colin Vidal a4f5395184 Move LMDB common definitions in a dedicated header
As several places uses custom LMDB macros, this centralize those
definitions inside a dedicated `lib/isc/include/isc/lmdb.h` header
file, so future uses cases not DNS-centrics could fit into there as
well.

MDB_CREATE is removed as well because:

- it was useless (it's needed only for named DB, which is not the case
  for all use case so far)

- it was used in `mdb_env_open` which is not expecting this flag: it
  should be passed (if needed) to `mdb_dbi_open`. It was likely
  ignored so far.
2024-12-20 12:26:34 +01:00
Colin Vidal e512cd66f7 Makes LMDB a mandatory dependency 2024-12-20 11:51:46 +01:00
Colin Vidal e9352e195d Add implementation doc 2024-12-20 08:36:02 +01:00
Colin Vidal 6fc47b3724 fixup! Introduction of cfgmgr 2024-12-19 15:35:26 +01:00
Colin Vidal 828c7ad87c fixup! Introduction of cfgmgr 2024-12-19 15:00:00 +01:00
Colin Vidal a125158762 fixup! Introduction of cfgmgr 2024-12-19 14:33:01 +01:00
Colin Vidal 5ec5650763 fixup! Introduction of cfgmgr 2024-12-19 11:53:28 +01:00
Colin Vidal 66ac1b2845 fixup! Introduction of cfgmgr 2024-12-19 11:40:22 +01:00
Colin Vidal f8ab917471 fixup! Introduction of cfgmgr 2024-12-18 08:53:44 +01:00
Colin Vidal e729f5bf50 fixup! Introduction of cfgmgr 2024-12-16 20:16:00 +01:00
Colin Vidal a243999f4c fixup! Introduction of cfgmgr 2024-12-16 15:57:12 +01:00
Colin Vidal 62da04fd76 fixup! Introduction of cfgmgr 2024-12-16 10:50:22 +01:00
Colin Vidal 03d2b4a670 Introduction of cfgmgr
Few points to note (and possibly discuss):

- cfgmgr is build on top of LMDB, as it brings transaction (and so
  thread safe) support out of the box

- LMDB keys are build in a way that we can support repeatable (see
  https://pad.isc.org/p/cfgmgr-proposal-v2 even if most of it is
  outdated, the section 2.0.2 about the way it's build is still
  relevant, otherwise I hope the code changes here are clear enough)

- Each thread own its own cfgmgr context (which basically means LMDB
  transaction, and where the API points to inside the configuration)

- In order to avoid allocations everytime we get/set a value (as well
  as few other operations) a single buffer is pre-allocated per-thread
  and per-transaction. Now, few internal helper functions directly use
  it (instead or, let's say, work on a parameter) and this might be
  confusing and error prone, I'm happy to change that if it is a
  worry.

- The data type which can be read/wrote from cfgmgr is not exhaustive,
  more data type will be added (i.e. duration type, uint64 if needed,
  etc.)

- This current implementation does not support inheritance (i.e. a
  non-specified view option won't use the option one). It's something
  we need to discuss, I see some options that could be put on top of
  that's here.

- The "default" values, however, should be fine out-of-the-box: I
  think the default configuration in bin/named/config.c can be
  parse/"added" in cfgmgr first then user one on top of that (because
  writting an existing value override it). Obviously there would be
  no-way to "go back" to the default config only, but I don't see such
  use case in existing code. Even if we'd need such thing, that would
  be quite invasive and a full config reload would be advisable. (so
  we could add an API to entirely drop the cfgmgr data and start from
  scratch. But I don't see such use case right now anyway, so it's not
  there)

- I hope the things a user of cfgmgr must know should be clearly
   explained in the cfgmgr.h file (if it's not the case, then I need
   to fix it -- That said I'll likely re-work the doc anyway).

- I initially implemented a mechanism which would dynamically re-size
  key buffers in case keys are very long, but I remove this as LMDB
  doesn't supports key more than 511 bytes anyway. Instead I made
  assertions every time we build a key to make sure we don't exceed
  this value.
2024-12-16 10:00:33 +01:00
25 changed files with 2585 additions and 635 deletions
+2 -4
View File
@@ -1029,13 +1029,12 @@ unit:gcc:ossl3:amd64:
artifacts: true
# Jobs for regular GCC builds on Debian "sid" (amd64)
# Also tests configration option: --without-lmdb.
gcc:sid:amd64:
variables:
CC: gcc
CFLAGS: "${CFLAGS_COMMON} -O3"
EXTRA_CONFIGURE: "--with-libidn2 --without-lmdb ${WITH_READLINE}"
EXTRA_CONFIGURE: "--with-libidn2 ${WITH_READLINE}"
RUN_MAKE_INSTALL: 1
<<: *debian_sid_amd64_image
<<: *build_job
@@ -1056,14 +1055,13 @@ unit:gcc:sid:amd64:
artifacts: true
# Job for out-of-tree GCC build on Debian 12 "bookworm" (amd64)
# Also tests configration option: --with-lmdb.
gcc:out-of-tree:
variables:
CC: gcc
CFLAGS: "${CFLAGS_COMMON} -Og"
CONFIGURE: "${CI_PROJECT_DIR}/configure"
EXTRA_CONFIGURE: "--with-libidn2 --with-lmdb"
EXTRA_CONFIGURE: "--with-libidn2"
RUN_MAKE_INSTALL: 1
OUT_OF_TREE_WORKSPACE: workspace
<<: *base_image
+3 -5
View File
@@ -159,11 +159,9 @@ options {\n\
fetch-quota-params 100 0.1 0.3 0.7;\n\
fetches-per-server 0;\n\
fetches-per-zone 0;\n\
lame-ttl 0;\n"
#ifdef HAVE_LMDB
" lmdb-mapsize 32M;\n"
#endif /* ifdef HAVE_LMDB */
" max-cache-size 90%;\n\
lame-ttl 0;\n\
lmdb-mapsize 32M;\n\
max-cache-size 90%;\n\
max-cache-ttl 604800; /* 1 week */\n\
max-clients-per-query 100;\n\
max-ncache-ttl 10800; /* 3 hours */\n\
+11 -392
View File
@@ -45,6 +45,7 @@
#include <isc/httpd.h>
#include <isc/job.h>
#include <isc/lex.h>
#include <isc/lmdb.h>
#include <isc/loop.h>
#include <isc/meminfo.h>
#include <isc/netmgr.h>
@@ -140,15 +141,6 @@
#include <named/smf_globals.h>
#endif /* ifdef HAVE_LIBSCF */
#ifdef HAVE_LMDB
#include <lmdb.h>
#define configure_newzones configure_newzones_db
#define dumpzone dumpzone_db
#else /* HAVE_LMDB */
#define configure_newzones configure_newzones_file
#define dumpzone dumpzone_file
#endif /* HAVE_LMDB */
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif /* ifndef SIZE_MAX */
@@ -503,7 +495,6 @@ putuint8(isc_buffer_t **b, uint8_t val);
static isc_result_t
putnull(isc_buffer_t **b);
#ifdef HAVE_LMDB
static isc_result_t
nzd_writable(dns_view_t *view);
@@ -518,10 +509,6 @@ nzd_env_close(dns_view_t *view);
static isc_result_t
nzd_close(MDB_txn **txnp, bool commit);
#else /* ifdef HAVE_LMDB */
static isc_result_t
nzf_append(dns_view_t *view, const cfg_obj_t *zconfig);
#endif /* ifdef HAVE_LMDB */
static isc_result_t
load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg);
@@ -7361,7 +7348,6 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
dns_view_setnewzonedir(view, dir);
}
#ifdef HAVE_LMDB
result = named_config_get(maps, "lmdb-mapsize", &obj);
if (result == ISC_R_SUCCESS && obj != NULL) {
mapsize = cfg_obj_asuint64(obj);
@@ -7381,9 +7367,6 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
return ISC_R_FAILURE;
}
}
#else /* ifdef HAVE_LMDB */
UNUSED(obj);
#endif /* HAVE_LMDB */
/*
* A non-empty catalog-zones statement implies allow-new-zones
@@ -7478,54 +7461,6 @@ configure_zone_setviewcommit(isc_result_t result, const cfg_obj_t *zconfig,
dns_view_detach(&pview);
}
#ifndef HAVE_LMDB
static isc_result_t
configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
cfg_aclconfctx_t *actx) {
isc_result_t result;
ns_cfgctx_t *nzctx;
const cfg_obj_t *zonelist;
const cfg_listelt_t *element;
nzctx = view->new_zone_config;
if (nzctx == NULL || nzctx->nzf_config == NULL) {
return ISC_R_SUCCESS;
}
isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
ISC_LOG_INFO, "loading additional zones for view '%s'",
view->name);
zonelist = NULL;
cfg_map_get(nzctx->nzf_config, "zone", &zonelist);
for (element = cfg_list_first(zonelist); element != NULL;
element = cfg_list_next(element))
{
const cfg_obj_t *zconfig = cfg_listelt_value(element);
CHECK(configure_zone(config, zconfig, vconfig, view,
&named_g_server->viewlist,
&named_g_server->kasplist,
&named_g_server->keystorelist, actx, true,
false, false, false));
}
result = ISC_R_SUCCESS;
cleanup:
for (element = cfg_list_first(zonelist); element != NULL;
element = cfg_list_next(element))
{
const cfg_obj_t *zconfig = cfg_listelt_value(element);
configure_zone_setviewcommit(result, zconfig, view);
}
return result;
}
#else /* HAVE_LMDB */
static isc_result_t
data_to_cfg(dns_view_t *view, MDB_val *key, MDB_val *data, isc_buffer_t **text,
cfg_obj_t **zoneconfig) {
@@ -7823,8 +7758,6 @@ cleanup:
return result;
}
#endif /* HAVE_LMDB */
static isc_result_t
load_configuration(const char *filename, named_server_t *server,
bool first_time) {
@@ -8832,12 +8765,11 @@ load_configuration(const char *filename, named_server_t *server,
server->sctx->tkeyctx = tkeyctx;
}
#ifdef HAVE_LMDB
/*
* If we're using LMDB, we may have created newzones databases
* as root, making it impossible to reopen them later after
* switching to a new userid. We close them now, and reopen
* after relinquishing privileges them.
* We may have created newzones LMDB databases as root, making
* it impossible to reopen them later after switching to a new
* userid. We close them now, and reopen after relinquishing
* privileges them.
*/
if (first_time) {
for (dns_view_t *view = ISC_LIST_HEAD(server->viewlist);
@@ -8846,7 +8778,6 @@ load_configuration(const char *filename, named_server_t *server,
nzd_env_close(view);
}
}
#endif /* HAVE_LMDB */
/*
* Switch to the effective UID for setting up files.
@@ -8868,7 +8799,6 @@ load_configuration(const char *filename, named_server_t *server,
goto cleanup_cachelist;
}
#ifdef HAVE_LMDB
/*
* Reopen NZD databases.
*/
@@ -8879,7 +8809,6 @@ load_configuration(const char *filename, named_server_t *server,
nzd_env_reopen(view);
}
}
#endif /* HAVE_LMDB */
/*
* Configure the logging system.
@@ -12345,163 +12274,6 @@ named_smf_add_message(isc_buffer_t **text) {
}
#endif /* HAVE_LIBSCF */
#ifndef HAVE_LMDB
/*
* Emit a comment at the top of the nzf file containing the viewname
* Expects the fp to already be open for writing
*/
#define HEADER1 "# New zone file for view: "
#define HEADER2 \
"\n# This file contains configuration for zones added by\n" \
"# the 'rndc addzone' command. DO NOT EDIT BY HAND.\n"
static isc_result_t
add_comment(FILE *fp, const char *viewname) {
isc_result_t result;
CHECK(isc_stdio_write(HEADER1, sizeof(HEADER1) - 1, 1, fp, NULL));
CHECK(isc_stdio_write(viewname, strlen(viewname), 1, fp, NULL));
CHECK(isc_stdio_write(HEADER2, sizeof(HEADER2) - 1, 1, fp, NULL));
cleanup:
return result;
}
static void
dumpzone(void *arg, const char *buf, int len) {
FILE *fp = arg;
(void)isc_stdio_write(buf, len, 1, fp, NULL);
}
static isc_result_t
nzf_append(dns_view_t *view, const cfg_obj_t *zconfig) {
isc_result_t result;
off_t offset;
FILE *fp = NULL;
bool offsetok = false;
LOCK(&view->new_zone_lock);
CHECK(isc_stdio_open(view->new_zone_file, "a", &fp));
CHECK(isc_stdio_seek(fp, 0, SEEK_END));
CHECK(isc_stdio_tell(fp, &offset));
offsetok = true;
if (offset == 0) {
CHECK(add_comment(fp, view->name));
}
CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp);
CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL));
CHECK(isc_stdio_flush(fp));
result = isc_stdio_close(fp);
fp = NULL;
cleanup:
if (fp != NULL) {
(void)isc_stdio_close(fp);
if (offsetok) {
isc_result_t result2;
result2 = isc_file_truncate(view->new_zone_file,
offset);
if (result2 != ISC_R_SUCCESS) {
isc_log_write(NAMED_LOGCATEGORY_GENERAL,
NAMED_LOGMODULE_SERVER,
ISC_LOG_ERROR,
"Error truncating NZF file '%s' "
"during rollback from append: "
"%s",
view->new_zone_file,
isc_result_totext(result2));
}
}
}
UNLOCK(&view->new_zone_lock);
return result;
}
static isc_result_t
nzf_writeconf(const cfg_obj_t *config, dns_view_t *view) {
const cfg_obj_t *zl = NULL;
cfg_list_t *list;
const cfg_listelt_t *elt;
FILE *fp = NULL;
char tmp[1024];
isc_result_t result;
result = isc_file_template(view->new_zone_file, "nzf-XXXXXXXX", tmp,
sizeof(tmp));
if (result == ISC_R_SUCCESS) {
result = isc_file_openunique(tmp, &fp);
}
if (result != ISC_R_SUCCESS) {
return result;
}
cfg_map_get(config, "zone", &zl);
if (!cfg_obj_islist(zl)) {
CHECK(ISC_R_FAILURE);
}
list = UNCONST(&zl->value.list);
CHECK(add_comment(fp, view->name)); /* force a comment */
for (elt = ISC_LIST_HEAD(*list); elt != NULL;
elt = ISC_LIST_NEXT(elt, link))
{
const cfg_obj_t *zconfig = cfg_listelt_value(elt);
CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp);
CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL));
}
CHECK(isc_stdio_flush(fp));
result = isc_stdio_close(fp);
fp = NULL;
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
CHECK(isc_file_rename(tmp, view->new_zone_file));
return result;
cleanup:
if (fp != NULL) {
(void)isc_stdio_close(fp);
}
(void)isc_file_remove(tmp);
return result;
}
static isc_result_t
load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg) {
isc_result_t result;
/* The new zone file may not exist. That is OK. */
if (!isc_file_exists(view->new_zone_file)) {
return ISC_R_SUCCESS;
}
/*
* Parse the configuration in the NZF file. This may be called in
* multiple views, so we reset the parser each time.
*/
cfg_parser_reset(named_g_addparser);
result = cfg_parse_file(named_g_addparser, view->new_zone_file,
&cfg_type_addzoneconf, &nzcfg->nzf_config);
if (result != ISC_R_SUCCESS) {
isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
ISC_LOG_ERROR, "Error parsing NZF file '%s': %s",
view->new_zone_file, isc_result_totext(result));
}
return result;
}
#else /* HAVE_LMDB */
static void
nzd_setkey(MDB_val *key, dns_name_t *name, char *namebuf, size_t buflen) {
dns_fixedname_t fixed;
@@ -12983,7 +12755,6 @@ cleanup:
return result;
}
#endif /* HAVE_LMDB */
static isc_result_t
newzone_parse(named_server_t *server, char *command, dns_view_t **viewp,
@@ -13168,21 +12939,14 @@ cleanup:
static isc_result_t
do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
dns_name_t *name, cfg_obj_t *zoneconf, const cfg_obj_t *zoneobj,
bool redirect, isc_buffer_t **text) {
dns_name_t *name, const cfg_obj_t *zoneobj, bool redirect,
isc_buffer_t **text) {
isc_result_t result, tresult;
dns_zone_t *zone = NULL;
#ifndef HAVE_LMDB
FILE *fp = NULL;
bool cleanup_config = false;
#else /* HAVE_LMDB */
MDB_txn *txn = NULL;
MDB_dbi dbi;
bool locked = false;
UNUSED(zoneconf);
#endif
/* Zone shouldn't already exist */
if (redirect) {
result = (view->redirect == NULL) ? ISC_R_NOTFOUND
@@ -13198,24 +12962,6 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
}
isc_loopmgr_pause(named_g_loopmgr);
#ifndef HAVE_LMDB
/*
* Make sure we can open the configuration save file
*/
result = isc_stdio_open(view->new_zone_file, "a", &fp);
if (result != ISC_R_SUCCESS) {
isc_loopmgr_resume(named_g_loopmgr);
TCHECK(putstr(text, "unable to create '"));
TCHECK(putstr(text, view->new_zone_file));
TCHECK(putstr(text, "': "));
TCHECK(putstr(text, isc_result_totext(result)));
goto cleanup;
}
(void)isc_stdio_close(fp);
fp = NULL;
#else /* HAVE_LMDB */
LOCK(&view->new_zone_lock);
locked = true;
/* Make sure we can open the NZD database */
@@ -13228,7 +12974,6 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
result = ISC_R_FAILURE;
goto cleanup;
}
#endif /* HAVE_LMDB */
/* Mark view unfrozen and configure zone */
dns_view_thaw(view);
@@ -13263,22 +13008,6 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
}
}
#ifndef HAVE_LMDB
/*
* If there wasn't a previous newzone config, just save the one
* we've created. If there was a previous one, merge the new
* zone into it.
*/
if (cfg->nzf_config == NULL) {
cfg_obj_attach(zoneconf, &cfg->nzf_config);
} else {
cfg_obj_t *z = UNCONST(zoneobj);
CHECK(cfg_parser_mapadd(cfg->add_parser, cfg->nzf_config, z,
"zone"));
}
cleanup_config = true;
#endif /* HAVE_LMDB */
/*
* Load the zone from the master file. If this fails, we'll
* need to undo the configuration we've done already.
@@ -13307,34 +13036,17 @@ do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
/* Flag the zone as having been added at runtime */
dns_zone_setadded(zone, true);
#ifdef HAVE_LMDB
/* Save the new zone configuration into the NZD */
CHECK(nzd_open(view, 0, &txn, &dbi));
CHECK(nzd_save(&txn, dbi, zone, zoneobj));
#else /* ifdef HAVE_LMDB */
/* Append the zone configuration to the NZF */
result = nzf_append(view, zoneobj);
#endif /* HAVE_LMDB */
cleanup:
#ifndef HAVE_LMDB
if (fp != NULL) {
(void)isc_stdio_close(fp);
}
if (result != ISC_R_SUCCESS && cleanup_config) {
tresult = delete_zoneconf(view, cfg->add_parser,
cfg->nzf_config, name, NULL);
RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
}
#else /* HAVE_LMDB */
if (txn != NULL) {
(void)nzd_close(&txn, false);
}
if (locked) {
UNLOCK(&view->new_zone_lock);
}
#endif /* HAVE_LMDB */
if (zone != NULL) {
dns_zone_detach(&zone);
@@ -13350,14 +13062,9 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
isc_result_t result, tresult;
dns_zone_t *zone = NULL;
bool added;
#ifndef HAVE_LMDB
FILE *fp = NULL;
cfg_obj_t *z;
#else /* HAVE_LMDB */
MDB_txn *txn = NULL;
MDB_dbi dbi;
bool locked = false;
#endif /* HAVE_LMDB */
/* Zone must already exist */
if (redirect) {
@@ -13377,30 +13084,8 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
added = dns_zone_getadded(zone);
dns_zone_detach(&zone);
#ifndef HAVE_LMDB
cfg = (ns_cfgctx_t *)view->new_zone_config;
if (cfg == NULL) {
TCHECK(putstr(text, "new zone config is not set"));
CHECK(ISC_R_FAILURE);
}
#endif /* ifndef HAVE_LMDB */
isc_loopmgr_pause(named_g_loopmgr);
#ifndef HAVE_LMDB
/* Make sure we can open the configuration save file */
result = isc_stdio_open(view->new_zone_file, "a", &fp);
if (result != ISC_R_SUCCESS) {
TCHECK(putstr(text, "unable to open '"));
TCHECK(putstr(text, view->new_zone_file));
TCHECK(putstr(text, "': "));
TCHECK(putstr(text, isc_result_totext(result)));
isc_loopmgr_resume(named_g_loopmgr);
goto cleanup;
}
(void)isc_stdio_close(fp);
fp = NULL;
#else /* HAVE_LMDB */
LOCK(&view->new_zone_lock);
locked = true;
/* Make sure we can open the NZD database */
@@ -13413,7 +13098,6 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
isc_loopmgr_resume(named_g_loopmgr);
goto cleanup;
}
#endif /* HAVE_LMDB */
/* Reconfigure the zone */
dns_view_thaw(view);
@@ -13441,21 +13125,6 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
CHECK(dns_view_findzone(view, name, DNS_ZTFIND_EXACT, &zone));
}
#ifndef HAVE_LMDB
/* Remove old zone from configuration (and NZF file if applicable) */
if (added) {
result = delete_zoneconf(view, cfg->add_parser, cfg->nzf_config,
dns_zone_getorigin(zone),
nzf_writeconf);
if (result != ISC_R_SUCCESS) {
TCHECK(putstr(text, "former zone configuration "
"not deleted: "));
TCHECK(putstr(text, isc_result_totext(result)));
goto cleanup;
}
}
#endif /* HAVE_LMDB */
if (!added) {
if (cfg->vconfig == NULL) {
result = delete_zoneconf(
@@ -13512,24 +13181,9 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
goto cleanup;
}
#ifndef HAVE_LMDB
/* Store the new zone configuration; also in NZF if applicable */
z = UNCONST(zoneobj);
CHECK(cfg_parser_mapadd(cfg->add_parser, cfg->nzf_config, z, "zone"));
#endif /* HAVE_LMDB */
if (added) {
#ifdef HAVE_LMDB
CHECK(nzd_open(view, 0, &txn, &dbi));
CHECK(nzd_save(&txn, dbi, zone, zoneobj));
#else /* ifdef HAVE_LMDB */
result = nzf_append(view, zoneobj);
if (result != ISC_R_SUCCESS) {
TCHECK(putstr(text, "\nNew zone config not saved: "));
TCHECK(putstr(text, isc_result_totext(result)));
goto cleanup;
}
#endif /* HAVE_LMDB */
TCHECK(putstr(text, "zone '"));
TCHECK(putstr(text, zname));
@@ -13543,18 +13197,12 @@ do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
cleanup:
#ifndef HAVE_LMDB
if (fp != NULL) {
(void)isc_stdio_close(fp);
}
#else /* HAVE_LMDB */
if (txn != NULL) {
(void)nzd_close(&txn, false);
}
if (locked) {
UNLOCK(&view->new_zone_lock);
}
#endif /* HAVE_LMDB */
if (zone != NULL) {
dns_zone_detach(&zone);
@@ -13594,12 +13242,7 @@ named_server_changezone(named_server_t *server, char *command,
&redirect, text));
/* Are we accepting new zones in this view? */
#ifdef HAVE_LMDB
if (view->new_zone_db == NULL)
#else /* ifdef HAVE_LMDB */
if (view->new_zone_file == NULL)
#endif /* HAVE_LMDB */
{
if (view->new_zone_db == NULL) {
(void)putstr(text, "Not allowing new zones in view '");
(void)putstr(text, view->name);
(void)putstr(text, "'");
@@ -13629,8 +13272,8 @@ named_server_changezone(named_server_t *server, char *command,
}
if (addzone) {
CHECK(do_addzone(server, cfg, view, dnsname, zoneconf, zoneobj,
redirect, text));
CHECK(do_addzone(server, cfg, view, dnsname, zoneobj, redirect,
text));
} else {
CHECK(do_modzone(server, cfg, view, dnsname, zonename, zoneobj,
redirect, text));
@@ -13693,10 +13336,8 @@ rmzone(void *arg) {
dns_db_t *dbp = NULL;
bool added;
isc_result_t result;
#ifdef HAVE_LMDB
MDB_txn *txn = NULL;
MDB_dbi dbi;
#endif /* ifdef HAVE_LMDB */
REQUIRE(dz != NULL);
@@ -13718,7 +13359,6 @@ rmzone(void *arg) {
catz = dns_zone_get_parentcatz(zone);
if (added && catz == NULL && cfg != NULL) {
#ifdef HAVE_LMDB
/* Make sure we can open the NZD database */
LOCK(&view->new_zone_lock);
result = nzd_open(view, 0, &txn, &dbi);
@@ -13742,17 +13382,6 @@ rmzone(void *arg) {
(void)nzd_close(&txn, false);
}
UNLOCK(&view->new_zone_lock);
#else /* ifdef HAVE_LMDB */
result = delete_zoneconf(view, cfg->add_parser, cfg->nzf_config,
dns_zone_getorigin(zone),
nzf_writeconf);
if (result != ISC_R_SUCCESS) {
isc_log_write(NAMED_LOGCATEGORY_GENERAL,
NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
"unable to delete zone configuration: %s",
isc_result_totext(result));
}
#endif /* HAVE_LMDB */
}
if (!added && cfg != NULL) {
@@ -14061,9 +13690,7 @@ named_server_showzone(named_server_t *server, isc_lex_t *lex,
dns_view_t *view = NULL;
dns_zone_t *zone = NULL;
ns_cfgctx_t *cfg = NULL;
#ifdef HAVE_LMDB
cfg_obj_t *nzconfig = NULL;
#endif /* HAVE_LMDB */
bool added, redirect;
ns_dzarg_t dzarg;
@@ -14103,12 +13730,6 @@ named_server_showzone(named_server_t *server, isc_lex_t *lex,
redirect);
}
#ifndef HAVE_LMDB
if (zconfig == NULL && cfg->nzf_config != NULL) {
zconfig = find_name_in_list_from_map(cfg->nzf_config, "zone",
zonename, redirect);
}
#else /* HAVE_LMDB */
if (zconfig == NULL) {
const cfg_obj_t *zlist = NULL;
CHECK(get_newzone_config(view, zonename, &nzconfig));
@@ -14119,7 +13740,6 @@ named_server_showzone(named_server_t *server, isc_lex_t *lex,
zconfig = cfg_listelt_value(cfg_list_first(zlist));
}
#endif /* HAVE_LMDB */
if (zconfig == NULL) {
CHECK(ISC_R_NOTFOUND);
@@ -14137,11 +13757,10 @@ named_server_showzone(named_server_t *server, isc_lex_t *lex,
result = ISC_R_SUCCESS;
cleanup:
#ifdef HAVE_LMDB
if (nzconfig != NULL) {
cfg_obj_destroy(named_g_addparser, &nzconfig);
}
#endif /* HAVE_LMDB */
if (isc_buffer_usedlength(*text) > 0) {
(void)putnull(text);
}
+7 -8
View File
@@ -133,14 +133,13 @@ Currently supported commands are:
string specified on the command line is the zone configuration text
that would ordinarily be placed in :iscman:`named.conf`.
The configuration is saved in a file called ``viewname.nzf`` (or, if
:iscman:`named` is compiled with liblmdb, an LMDB database file called
``viewname.nzd``). ``viewname`` is the name of the view, unless the view
name contains characters that are incompatible with use as a file
name, in which case a cryptographic hash of the view name is used
instead. When :iscman:`named` is restarted, the file is loaded into
the view configuration so that zones that were added can persist
after a restart.
The configuration is saved in an LMDB database called
``viewname.nzf``. ``viewname`` is the name of the view, unless the
view name contains characters that are incompatible with use as a
file name, in which case a cryptographic hash of the view name is
used instead. When :iscman:`named` is restarted, the file is loaded
into the view configuration so that zones that were added can
persist after a restart.
This sample ``addzone`` command adds the zone ``example.com`` to
the default view:
+25 -85
View File
@@ -36,8 +36,8 @@ n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
# When LMDB support is compiled in, this tests that migration from
# NZF to NZD occurs during named startup
# This tests that migration from NZF to NZD occurs during named
# startup
echo_i "checking previously added zone ($n)"
ret=0
$DIG $DIGOPTS @10.53.0.2 a.previous.example a >dig.out.ns2.$n || ret=1
@@ -47,13 +47,11 @@ n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if $FEATURETEST --with-lmdb; then
echo_i "checking that existing NZF file was renamed after migration ($n)"
[ -e ns2/3bf305731dd26307.nzf~ ] || ret=1
n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
fi
echo_i "checking that existing NZF file was renamed after migration ($n)"
[ -e ns2/3bf305731dd26307.nzf~ ] || ret=1
n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
echo_i "adding new zone ($n)"
ret=0
@@ -126,16 +124,6 @@ n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if ! $FEATURETEST --with-lmdb; then
echo_i "verifying no comments in NZF file ($n)"
ret=0
hcount=$(grep "^# New zone file for view: _default" ns2/3bf305731dd26307.nzf | wc -l)
[ $hcount -eq 0 ] || ret=1
n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
fi
echo_i "checking rndc showzone with previously added zone ($n)"
ret=0
$RNDCCMD 10.53.0.2 showzone previous.example >rndc.out.ns2.$n
@@ -145,13 +133,11 @@ n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if $FEATURETEST --with-lmdb; then
echo_i "checking zone is present in NZD ($n)"
ret=0
$NZD2NZF ns2/_default.nzd | grep previous.example >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
fi
echo_i "checking zone is present in NZD ($n)"
ret=0
$NZD2NZF ns2/_default.nzd | grep previous.example >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
echo_i "deleting previously added zone ($n)"
ret=0
@@ -171,22 +157,10 @@ check_nzd2nzf() (
&& ! grep previous.example nzd2nzf.out.$n >/dev/null
)
if $FEATURETEST --with-lmdb; then
echo_i "checking zone was deleted from NZD ($n)"
retry_quiet 10 check_nzd2nzf || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
fi
if ! $FEATURETEST --with-lmdb; then
echo_i "checking NZF file now has comment ($n)"
ret=0
hcount=$(grep "^# New zone file for view: _default" ns2/3bf305731dd26307.nzf | wc -l)
[ $hcount -eq 1 ] || ret=1
n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
fi
echo_i "checking zone was deleted from NZD ($n)"
retry_quiet 10 check_nzd2nzf || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
echo_i "deleting newly added zone added.example ($n)"
ret=0
@@ -508,24 +482,12 @@ n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if ! $FEATURETEST --with-lmdb; then
echo_i "checking new NZF file has comment ($n)"
ret=0
hcount=$(grep "^# New zone file for view: external" ns2/external.nzf | wc -l)
[ $hcount -eq 1 ] || ret=1
n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
fi
if $FEATURETEST --with-lmdb; then
echo_i "verifying added.example in external view created an external.nzd DB ($n)"
ret=0
[ -e ns2/external.nzd ] || ret=1
n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
fi
echo_i "verifying added.example in external view created an external.nzd DB ($n)"
ret=0
[ -e ns2/external.nzd ] || ret=1
n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
echo_i "checking rndc reload causes named to reload the external view's new zone config ($n)"
ret=0
@@ -544,11 +506,7 @@ status=$((status + ret))
echo_i "checking rndc showzone with newly added zone ($n)"
_check_rndc_showzone_newly_added() (
if ! $FEATURETEST --with-lmdb; then
expected='zone "added.example" in external { type primary; file "added.db"; };'
else
expected='zone "added.example" { type primary; file "added.db"; };'
fi
expected='zone "added.example" { type primary; file "added.db"; };'
$RNDCCMD 10.53.0.2 showzone added.example in external >rndc.out.ns2.$n 2>/dev/null \
&& [ "$(cat rndc.out.ns2.$n)" = "$expected" ]
)
@@ -651,13 +609,8 @@ n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if $FEATURETEST --with-lmdb; then
echo_i "checking NZD file was created in new-zones-directory ($n)"
expect=ns2/new-zones/directory.nzd
else
echo_i "checking NZF file was created in new-zones-directory ($n)"
expect=ns2/new-zones/directory.nzf
fi
echo_i "checking NZD file was created in new-zones-directory ($n)"
expect=ns2/new-zones/directory.nzd
$RNDCCMD 10.53.0.2 sync 'added.example IN directory' 2>&1 | sed 's/^/I:ns2 /'
sleep 2
[ -e "$expect" ] || ret=1
@@ -693,19 +646,6 @@ n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if ! $FEATURETEST --with-lmdb; then
echo_i "check that addzone is fully reversed on failure (--with-lmdb=no) ($n)"
ret=0
$RNDCCMD 10.53.0.3 addzone "test1.baz" '{ type primary; file "e.db"; };' >/dev/null 2>&1 || ret=1
$RNDCCMD 10.53.0.3 addzone "test2.baz" '{ type primary; file "dne.db"; };' >/dev/null 2>&1 && ret=1
$RNDCCMD 10.53.0.3 addzone "test3.baz" '{ type primary; file "e.db"; };' >/dev/null 2>&1 || ret=1
$RNDCCMD 10.53.0.3 delzone "test3.baz" >/dev/null 2>&1 || ret=1
grep test2.baz ns3/_default.nzf >/dev/null && ret=1
n=$((n + 1))
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
fi
_check_version_bind() (
$DIG $DIGOPTS @10.53.0.3 version.bind txt ch >dig.out.test$n \
&& grep "status: NOERROR" dig.out.test$n >/dev/null
-9
View File
@@ -54,7 +54,6 @@ usage(void) {
fprintf(stderr, "\t--tsan\n");
fprintf(stderr, "\t--with-dlz-filesystem\n");
fprintf(stderr, "\t--with-libidn2\n");
fprintf(stderr, "\t--with-lmdb\n");
fprintf(stderr, "\t--with-libnghttp2\n");
fprintf(stderr, "\t--with-zlib\n");
}
@@ -238,14 +237,6 @@ main(int argc, char **argv) {
#endif /* ifdef HAVE_LIBIDN2 */
}
if (strcmp(argv[1], "--with-lmdb") == 0) {
#ifdef HAVE_LMDB
return 0;
#else /* ifdef HAVE_LMDB */
return 1;
#endif /* ifdef HAVE_LMDB */
}
if (strcmp(argv[1], "--with-libnghttp2") == 0) {
#ifdef HAVE_LIBNGHTTP2
return 0;
+1 -4
View File
@@ -13,8 +13,5 @@
. ../conf.sh
$FEATURETEST --with-lmdb || {
echo_i "This test requires LMDB support." >&2
exit 255
}
$FEATURETEST
exit 0
+6 -17
View File
@@ -2,18 +2,21 @@ include $(top_srcdir)/Makefile.top
AM_CPPFLAGS += \
$(LIBISC_CFLAGS) \
$(LIBDNS_CFLAGS)
$(LIBDNS_CFLAGS) \
$(LMDB_CFLAGS)
LDADD += \
$(LIBDNS_LIBS) \
$(LIBISC_LIBS)
$(LIBISC_LIBS) \
$(LMDB_LIBS)
bin_PROGRAMS = \
arpaname \
mdig \
named-journalprint \
named-rrchecker \
nsec3hash
nsec3hash \
named-nzd2nzf
arpaname_LDADD = \
$(LIBISC_LIBS)
@@ -32,17 +35,3 @@ dnstap_read_LDADD = \
$(LIBISC_LIBS) \
$(DNSTAP_LIBS)
endif
if HAVE_LMDB
bin_PROGRAMS += \
named-nzd2nzf
named_nzd2nzf_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(LMDB_CFLAGS)
named_nzd2nzf_LDADD = \
$(LIBISC_LIBS) \
$(LMDB_LIBS)
endif
+2 -5
View File
@@ -11,14 +11,11 @@
* information regarding copyright ownership.
*/
#ifndef HAVE_LMDB
#error This program requires the LMDB library.
#endif /* ifndef HAVE_LMDB */
#include <lmdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <isc/lmdb.h>
#include <dns/view.h>
int
+7 -37
View File
@@ -749,43 +749,13 @@ AC_SUBST([KRB5_CFLAGS])
AC_SUBST([KRB5_LIBS])
#
# was --with-lmdb specified?
# LMDB is mandatory
#
PKG_CHECK_MODULES([LMDB], [lmdb])
AX_SAVE_FLAGS([lmdb])
# [pairwise: --with-lmdb=auto, --with-lmdb=yes, --without-lmdb]
AC_ARG_WITH([lmdb],
[AS_HELP_STRING([--with-lmdb=@<:@PATH@:>@],
[use LMDB library @<:@default=auto@:>@, optionally specify the prefix for lmdb library])],
[:],
[with_lmdb="auto"])
ac_lib_lmdb_found=no
AS_CASE([$with_lmdb],
[no],[],
[auto|yes], [PKG_CHECK_MODULES([LMDB], [lmdb],
[ac_lib_lmdb_found=yes],
[for ac_lib_lmdb_path in /usr /usr/local /opt /opt/local; do
AX_LIB_LMDB([$ac_lib_lmdb_path],
[ac_lib_lmdb_found=yes
break])
done
])],
[AX_LIB_LMDB([$with_lmdb],[ac_lib_lmdb_found=yes])])
# don't fail when in automatic mode
AS_IF([test "$with_lmdb" = "auto" && test "$ac_lib_lmdb_found" = "no"],
[with_lmdb=no])
# hard fail when LMDB requested, but not found
AS_IF([test "$with_lmdb" != "no" && test "$ac_lib_lmdb_found" != "yes"],
[AC_MSG_ERROR([LMDB requested, but not found])])
AS_IF([test "$ac_lib_lmdb_found" = "yes"],
[AC_DEFINE([HAVE_LMDB], [1], [Use lmdb library])])
AC_SUBST([LMDB_CFLAGS])
AC_SUBST([LMDB_LIBS])
AM_CONDITIONAL([HAVE_LMDB], [test -n "$LMDB_LIBS"])
CFLAGS="$CFLAGS $LMDB_CFLAGS"
LIBS="$LIBS $LMDB_LIBS"
#
# was --with-libxml2 specified?
@@ -1512,7 +1482,7 @@ report() {
test -z "$LIBXML2_LIBS" || echo " XML statistics (--with-libxml2)"
test -z "$JSON_C_LIBS" || echo " JSON statistics (--with-json-c): $JSON_C_CFLAGS $JSON_C_LIBS"
test -z "$ZLIB_LIBS" || echo " HTTP zlib compression (--with-zlib)"
test -z "$LMDB_LIBS" || echo " LMDB database to store configuration for 'addzone' zones (--with-lmdb)"
test -z "$LMDB_LIBS" || echo " LMDB database"
test -z "$LIBIDN2_LIBS" || echo " IDN support (--with-libidn2)"
fi
@@ -1574,7 +1544,7 @@ report() {
test -z "$LIBXML2_LIBS" && echo " XML statistics (--with-libxml2)"
test -z "$JSON_C_LIBS" && echo " JSON statistics (--with-json-c)"
test -z "$ZLIB_LIBS" && echo " HTTP zlib compression (--with-zlib)"
test -z "$LMDB_LIBS" && echo " LMDB database to store configuration for 'addzone' zones (--with-lmdb)"
test -z "$LMDB_LIBS" && echo " LMDB database"
test -z "$LIBIDN2_LIBS" && echo " IDN support (--with-libidn2)"
echo "-------------------------------------------------------------------------------"
-5
View File
@@ -116,11 +116,6 @@ be linked against ``zlib`` (https://zlib.net/). If this is installed in
a nonstandard location, specify the prefix using
``--with-zlib=/prefix``.
To support storing configuration data for runtime-added zones in an LMDB
database, the server must be linked with ``liblmdb``
(https://github.com/LMDB/lmdb). If this is installed in a nonstandard
location, specify the prefix using ``--with-lmdb=/prefix``.
To support MaxMind GeoIP2 location-based ACLs, the server must be linked
with ``libmaxminddb`` (https://maxmind.github.io/libmaxminddb/). This is
turned on by default if the library is found; if the library is
+13 -16
View File
@@ -1360,12 +1360,11 @@ default is used.
:tags: server
:short: Sets a maximum size for the memory map of the new-zone database in LMDB database format.
When :iscman:`named` is built with liblmdb, this option sets a maximum size
for the memory map of the new-zone database (NZD) in LMDB database
format. This database is used to store configuration information for
zones added using :option:`rndc addzone`. Note that this is not the NZD
database file size, but the largest size that the database may grow
to.
This option sets a maximum size for the memory map of the new-zone
database (NZD) in LMDB database format. This database is used to
store configuration information for zones added using :option:`rndc
addzone`. Note that this is not the NZD database file size, but the
largest size that the database may grow to.
Because the database file is memory-mapped, its size is limited by
the address space of the :iscman:`named` process. The default of 32 megabytes
@@ -1994,17 +1993,15 @@ Boolean Options
Newly added zones' configuration parameters are stored so that they
can persist after the server is restarted. The configuration
information is saved in a file called ``viewname.nzf`` (or, if
:iscman:`named` is compiled with liblmdb, in an LMDB database file called
``viewname.nzd``). "viewname" is the name of the view, unless the view
name contains characters that are incompatible with use as a file
name, in which case a cryptographic hash of the view name is used
instead.
information is saved in an LMDB database file called
``viewname.nzf``. "viewname" is the name of the view, unless the
view name contains characters that are incompatible with use as a
file name, in which case a cryptographic hash of the view name is
used instead.
Configurations for zones added at runtime are stored either in
a new-zone file (NZF) or a new-zone database (NZD), depending on
whether :iscman:`named` was linked with liblmdb at compile time. See
:ref:`man_rndc` for further details about :option:`rndc addzone`.
Configurations for zones added at runtime are stored either in a
new-zone database (NZD). See :ref:`man_rndc` for further details
about :option:`rndc addzone`.
.. namedconf:statement:: auth-nxdomain
:tags: query
+173
View File
@@ -0,0 +1,173 @@
<!--
Copyright (C) Internet Systems Consortium, Inc. ("ISC")
SPDX-License-Identifier: MPL-2.0
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, you can obtain one at https://mozilla.org/MPL/2.0/.
See the COPYRIGHT file distributed with this work for additional
information regarding copyright ownership.
-->
## cfgmgr
### Concepts
cfgmgr is a library to (in-memory) store and to interact with
configuration data in a hierarchical way. A configuration clause can
be seen as a "box" holding properties accessible by name. A property
can be of various types (boolean, unsigned integer, string, sockaddr,
etc.) as well as a clause, making possible to have nested
clauses. Furthermore, a clause can be repeated: it is possible to have
several clauses of the same name (or "several instances") at the same
level (i.e. in the same parent clause).
### API
Public API usage and concepts are documented in
lib/isc/include/isc/cfgmgr.h. The API is thread-safe and transaction
based. In order to access to a clause, it must be opened (which starts
a read or read-write transaction) and eventually closed (which commit
the transaction, if read-write). Changes made during a transaction are
visible by the current thread only. Opening nested clauses doesn't
create sub-transactions: there is only one transaction at the time for
a given thread opening a top-level clause.
### Implementation
cfgmgr is essentially a wrapper abstracting LMDB, which is providing
the storage, thread-safe and transactional access. Because LMDB also
write data on disk and cfgmgr intents to be in-memory only, the LMDB
files are immediately deleted during the initialization of cfgmgr.
#### Nested and repeatable clauses
LDBM itself is "flat": it stores key-values associations. In order to
model nested clauses, cfgmgr dynamically build the key for each value
using a namespace dot based notation. For example:
```
foo {
bar {
gee: 55;
};
baz: "abc";
};
```
is implemented with the following key-values:
```
foo.XXX.bar.YYY.gee: 55
foo.XXX.baz: "abc"
```
The "XXX" and "YYY" are random numbers identifying a specific clause
instance. Let's make the configuration a bit more complex:
```
foo {
bar {
gee: 55;
};
bar {
gee: 234;
};
baz: "abc";
};
```
This is implemented with the following key-values:
```
foo.XXX.bar.YYY.gee: 55
foo.XXX.bar.ZZZ.gee: 234
foo.XXX.baz: "abc"
```
This makes it easy to differentiate between the two instances of the
"bar" clause: one is "bar.YYY" while the other is "bar.ZZZ".
The reason a number is used (instead of a name or something else) is
because LMDB sort the key in a lexicographical way. Also, given a key,
LMDB is able to give the first key which is equal or bigger. This is
the crux of how cfgmgr can jump to the next instance of a same clause
(function `isc_cfgmgr_nextclause`) in one lookup. It takes the key of
the current clause, increments it by one (which is way simpler than
incrementing a "string by one"), and asks LMDB to give it back the
next key equal or bigger than the new one. Example:
- current clause is `foo.1234`
- cfgmgr then creates the key prefix `foo.1235.`
- LMDB gives the first key bigger or equals to `foo.1235.`
No matter what's inside `foo.1234` (so no matter the keys starting by
`foo.1234.`, LMDB returns the first key starting by _at least_
`foo.1235`, which would be the first key of the next instance of the
same clause. (If there is no other "foo" clause, nothing is found).
### List properties
named configuration format supports properties holding a list of
values. cfgmgr approach to implement lists uses the fact that LMDB
allows duplicate keys and it stores a newly duplicated key after the
previous ones. A clause "foo" with a property "bar" holding a list of
integers is implemented by cfgmgr in the following way in LMDB:
```
foo.XXX.bar: 23
foo.XXX.bar: 43
```
(Note there would be another way as LMDB enables to store several
values for a given key, but it's not flexible enough for cfgmgr: it
requires providing ahead the size used by all the values. It means
that adding a new list element would require to allocate a buffer of
the existing size plus the size of the new element, delete the
existing key-value from LMDB, and add it again. Dup keys seems to be a
better fit here.)
### Inheritance (proposal/speculative)
Some named configuration clauses have a concept of inheritance. For
instance, if a looked-up property is not found in a "view" clause, the
property will be looked-up in the "options" clause. cfgmgr currently
doesn't support inheritance, so the following is an API/implementation
proposal to support-ish it:
- a new API `isc_cfgmgr_fallback("A")` could be called while a clause
"B" is opened. Then, cfgmgr would know that if a looked-up property
of a clause "B" is not found, then it would internally and
synchronously try to lookup the same property from within the clause
"A". So this can be seen as "B inherits A properties" from the
caller perspective. This API could be used once-per clause (no
"multiple inheritance"), and it would be possible to break this
relation if needed by calling `isc_cfgmgr_fallback(NULL)`.
- internally, `isc_cfgmgr_fallback` would create a new LMDB key-value
`B.0.fallback: "A"`. So when cfgmgr opens a clause it can figure out
if it needs to lookup for another clause in case a looked-up
property is not found.
- From performance perspective in would make one extra LMDB lookup
only if a property name is not found for a clause "B" (so this is
likely acceptable). (And of course one extra lookup when opening a
clause to find the "clausename.0.fallback" key, but again, that's
acceptable.)
- limitation 1: if "B" has multiple instances, only the first instance
values would be used. (Which I think is fine for named use case:
"options" is not a repeatable clause).
- limitation 2: There won't be any check to make sure "A" actually
exists when `isc_cfgmgr_fallback` is called, so can be done at any
tine even if "A" doesn't exists yet. It makes initialization time
order-independent (so actually not really a limitation), and more
importantly it avoids falling into a rabbit hole, for instance, to
make sure that if we delete "A" it would remove all the
`"other-clause-names.0.fallback: "A"` keys. This is actually the
reason I want to call it "fallback" in the API and not
"inheritance". It's basically just a "second-chance" if a value is
not found, but nothing more.
+1 -5
View File
@@ -100,6 +100,7 @@ man_MANS = \
named-checkzone.1 \
named-compilezone.1 \
named-journalprint.1 \
named-nzd2nzf.1 \
named.8 \
nsec3hash.1 \
rndc-confgen.8 \
@@ -111,11 +112,6 @@ man_MANS += \
dnstap-read.1
endif HAVE_DNSTAP
if HAVE_LMDB
man_MANS += \
named-nzd2nzf.1
endif HAVE_LMDB
MANPAGES_IN = \
$(man_MANS:=in) \
dnstap-read.1in \
+2 -2
View File
@@ -165,7 +165,7 @@ options {
lame-ttl <duration>;
listen-on [ port <integer> ] [ proxy <string> ] [ tls <string> ] [ http <string> ] { <address_match_element>; ... }; // may occur multiple times
listen-on-v6 [ port <integer> ] [ proxy <string> ] [ tls <string> ] [ http <string> ] { <address_match_element>; ... }; // may occur multiple times
lmdb-mapsize <sizeval>; // optional (only available if configured)
lmdb-mapsize <sizeval>;
managed-keys-directory <quoted_string>;
masterfile-format ( raw | text );
masterfile-style ( full | relative );
@@ -449,7 +449,7 @@ view <string> [ <class> ] {
}; // may occur multiple times
key-directory <quoted_string>;
lame-ttl <duration>;
lmdb-mapsize <sizeval>; // optional (only available if configured)
lmdb-mapsize <sizeval>;
masterfile-format ( raw | text );
masterfile-style ( full | relative );
match-clients { <address_match_element>; ... };
+2 -5
View File
@@ -267,6 +267,7 @@ libdns_la_CPPFLAGS = \
$(LIBISC_CFLAGS) \
$(LIBURCU_CFLAGS) \
$(LIBUV_CFLAGS) \
$(LMDB_CFLAGS) \
$(OPENSSL_CFLAGS)
libdns_la_LDFLAGS = \
@@ -277,6 +278,7 @@ libdns_la_LIBADD = \
$(LIBISC_LIBS) \
$(LIBURCU_LIBS) \
$(LIBUV_LIBS) \
$(LMDB_LIBS) \
$(OPENSSL_LIBS)
if HAVE_JSON_C
@@ -326,11 +328,6 @@ libdns_la_CPPFLAGS += $(DNSTAP_CFLAGS)
libdns_la_LIBADD += $(DNSTAP_LIBS)
endif
if HAVE_LMDB
libdns_la_CPPFLAGS += $(LMDB_CFLAGS)
libdns_la_LIBADD += $(LMDB_LIBS)
endif
if !HAVE_SYSTEMTAP
DTRACE_DEPS = libdns_la-xfrin.lo
DTRACE_OBJS = .libs/libdns_la-xfrin.$(OBJEXT)
-13
View File
@@ -249,19 +249,6 @@ struct dns_view {
#define DNS_VIEWATTR_ADBSHUTDOWN 0x02
#define DNS_VIEWATTR_REQSHUTDOWN 0x04
#ifdef HAVE_LMDB
#define DNS_LMDB_COMMON_FLAGS (MDB_CREATE | MDB_NOSUBDIR | MDB_NOLOCK)
#ifndef __OpenBSD__
#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS)
#else /* __OpenBSD__ */
/*
* OpenBSD does not have a unified buffer cache, which requires both reads and
* writes to be performed using mmap().
*/
#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS | MDB_WRITEMAP)
#endif /* __OpenBSD__ */
#endif /* HAVE_LMDB */
isc_result_t
dns_view_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
dns_dispatchmgr_t *dispmgr, dns_rdataclass_t rdclass,
+2 -18
View File
@@ -17,15 +17,12 @@
#include <limits.h>
#include <stdbool.h>
#ifdef HAVE_LMDB
#include <lmdb.h>
#endif /* ifdef HAVE_LMDB */
#include <isc/atomic.h>
#include <isc/dir.h>
#include <isc/file.h>
#include <isc/hash.h>
#include <isc/lex.h>
#include <isc/lmdb.h>
#include <isc/md.h>
#include <isc/result.h>
#include <isc/stats.h>
@@ -392,7 +389,6 @@ destroy(dns_view_t *view) {
isc_mem_free(view->mctx, view->new_zone_dir);
view->new_zone_dir = NULL;
}
#ifdef HAVE_LMDB
if (view->new_zone_dbenv != NULL) {
mdb_env_close((MDB_env *)view->new_zone_dbenv);
view->new_zone_dbenv = NULL;
@@ -401,7 +397,6 @@ destroy(dns_view_t *view) {
isc_mem_free(view->mctx, view->new_zone_db);
view->new_zone_db = NULL;
}
#endif /* HAVE_LMDB */
dns_fwdtable_destroy(&view->fwdtable);
dns_aclenv_detach(&view->aclenv);
if (view->failcache != NULL) {
@@ -1787,14 +1782,8 @@ dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
void (*cfg_destroy)(void **), uint64_t mapsize) {
isc_result_t result = ISC_R_SUCCESS;
char buffer[1024];
#ifdef HAVE_LMDB
MDB_env *env = NULL;
int status;
#endif /* ifdef HAVE_LMDB */
#ifndef HAVE_LMDB
UNUSED(mapsize);
#endif /* ifndef HAVE_LMDB */
REQUIRE(DNS_VIEW_VALID(view));
REQUIRE((cfgctx != NULL && cfg_destroy != NULL) || !allow);
@@ -1804,7 +1793,6 @@ dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
view->new_zone_file = NULL;
}
#ifdef HAVE_LMDB
if (view->new_zone_dbenv != NULL) {
mdb_env_close((MDB_env *)view->new_zone_dbenv);
view->new_zone_dbenv = NULL;
@@ -1814,7 +1802,6 @@ dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
isc_mem_free(view->mctx, view->new_zone_db);
view->new_zone_db = NULL;
}
#endif /* HAVE_LMDB */
if (view->new_zone_config != NULL) {
view->cfg_destroy(&view->new_zone_config);
@@ -1830,7 +1817,6 @@ dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
view->new_zone_file = isc_mem_strdup(view->mctx, buffer);
#ifdef HAVE_LMDB
CHECK(nz_legacy(view->new_zone_dir, view->name, "nzd", buffer,
sizeof(buffer)));
@@ -1866,7 +1852,6 @@ dns_view_setnewzones(dns_view_t *view, bool allow, void *cfgctx,
view->new_zone_dbenv = env;
env = NULL;
#endif /* HAVE_LMDB */
view->new_zone_config = cfgctx;
view->cfg_destroy = cfg_destroy;
@@ -1878,7 +1863,6 @@ cleanup:
view->new_zone_file = NULL;
}
#ifdef HAVE_LMDB
if (view->new_zone_db != NULL) {
isc_mem_free(view->mctx, view->new_zone_db);
view->new_zone_db = NULL;
@@ -1886,7 +1870,7 @@ cleanup:
if (env != NULL) {
mdb_env_close(env);
}
#endif /* HAVE_LMDB */
view->new_zone_config = NULL;
view->cfg_destroy = NULL;
}
+5
View File
@@ -14,6 +14,7 @@ libisc_la_HEADERS = \
include/isc/base32.h \
include/isc/base64.h \
include/isc/buffer.h \
include/isc/cfgmgr.h \
include/isc/commandline.h \
include/isc/condition.h \
include/isc/counter.h \
@@ -44,6 +45,7 @@ libisc_la_HEADERS = \
include/isc/lang.h \
include/isc/lex.h \
include/isc/list.h \
include/isc/lmdb.h \
include/isc/log.h \
include/isc/loop.h \
include/isc/magic.h \
@@ -122,6 +124,7 @@ libisc_la_SOURCES = \
backtrace.c \
base32.c \
base64.c \
cfgmgr.c \
commandline.c \
condition.c \
counter.c \
@@ -214,6 +217,7 @@ libisc_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(LIBISC_CFLAGS) \
$(LIBUV_CFLAGS) \
$(LMDB_CFLAGS) \
$(OPENSSL_CFLAGS) \
$(ZLIB_CFLAGS)
@@ -223,6 +227,7 @@ libisc_la_LDFLAGS = \
libisc_la_LIBADD = \
$(LIBUV_LIBS) \
$(LMDB_LIBS) \
$(OPENSSL_LIBS) \
$(ZLIB_LIBS)
+765
View File
@@ -0,0 +1,765 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <isc/cfgmgr.h>
#include <isc/list.h>
#include <isc/lmdb.h>
#include <isc/mem.h>
#include <isc/random.h>
#include <isc/rwlock.h>
#include <isc/thread.h>
#include <isc/util.h>
/*
* 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.
*/
#define BUFLEN 511
typedef struct openedclause openedclause_t;
struct openedclause {
char *name;
uint32_t id;
ISC_LINK(openedclause_t) link;
};
typedef ISC_LIST(openedclause_t) openedclauses_t;
typedef struct {
openedclauses_t openedclauses;
char *prefix;
char *buffer;
MDB_cursor *cursor;
MDB_txn *txn;
bool readonly;
} context_t;
static isc_rwlock_t isc__cfgmgr_lock;
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,
.cursor = NULL,
.txn = NULL,
.readonly = false };
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;
}
isc_result_t
isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath) {
int result = ISC_R_SUCCESS;
char dbname[BUFLEN];
char dblockname[BUFLEN];
uint32_t random;
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses));
REQUIRE(isc__cfgmgr_ctx.prefix == NULL);
REQUIRE(isc__cfgmgr_ctx.buffer == NULL);
REQUIRE(isc__cfgmgr_ctx.cursor == NULL);
REQUIRE(isc__cfgmgr_ctx.txn == NULL);
REQUIRE(isc__cfgmgr_mctx == NULL);
REQUIRE(isc__cfgmgr_env == NULL);
REQUIRE(mctx != NULL);
REQUIRE(dbpath != NULL);
isc_rwlock_init(&isc__cfgmgr_lock);
isc_mem_attach(mctx, &isc__cfgmgr_mctx);
INSIST(isc__cfgmgr_mctx != NULL);
result = mdb_env_create(&isc__cfgmgr_env);
if (result != 0) {
result = ISC_R_FAILURE;
goto cleanup;
}
INSIST(isc__cfgmgr_env != NULL);
random = isc_random32();
/*
* Because LMDB also create a <dbname>-lock file, we also need
* to build it (even if not passed to LMDB itself) so we can
* immediately delete it
*/
REQUIRE(snprintf(dbname, BUFLEN, "%s-%" PRIu32, dbpath, random) <
BUFLEN);
REQUIRE(snprintf(dblockname, BUFLEN, "%s-%" PRIu32 "-lock", dbpath,
random) < BUFLEN);
/*
* Using MDB_NOSYNC as it avoids force disk flush after a
* transaction. It's quicker and in our case we don't need it
* as we delete the only link to the inode right away (so disk
* corruption doesn't matter: as soon as the process is dead,
* the disk data is dead as well)
*/
result = mdb_env_open(isc__cfgmgr_env, dbname,
MDB_NOSYNC | MDB_NOSUBDIR | MDB_NOLOCK, 0600);
if (result != 0) {
result = ISC_R_FAILURE;
goto cleanup;
}
remove(dbname);
remove(dblockname);
goto out;
cleanup:
if (isc__cfgmgr_env != NULL) {
mdb_env_close(isc__cfgmgr_env);
isc__cfgmgr_env = NULL;
}
out:
return result;
}
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).
*/
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses));
REQUIRE(isc__cfgmgr_ctx.prefix == NULL);
REQUIRE(isc__cfgmgr_ctx.buffer == NULL);
REQUIRE(isc__cfgmgr_ctx.cursor == NULL);
REQUIRE(isc__cfgmgr_ctx.txn == NULL);
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 trailingdot) {
size_t written;
const char *prefix = "";
const char *dot = trailingdot ? "." : "";
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 isc_result_t
isc__cfgmgr_findclause(const char *name, unsigned long *id) {
isc_result_t result = ISC_R_SUCCESS;
MDB_val dbkey;
size_t dotpos = 0;
REQUIRE(name != NULL);
isc__cfgmgr_buildkey(name, true);
dotpos = strlen(isc__cfgmgr_ctx.buffer) - 1;
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_ctx.cursor, &dbkey, NULL,
MDB_SET_RANGE) != 0)
{
result = ISC_R_NOTFOUND;
goto out;
}
/*
* LMDB found a key which starts by "prefix", so let's make
* sure it's actually the same prefix by checking the found
* key has an immediate leading dot
*/
if (dbkey.mv_size <= dotpos || ((char *)dbkey.mv_data)[dotpos] != '.') {
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;
isc__cfgmgr_ctx.cursor = NULL;
}
static isc_result_t
isc__cfgmgr_starttransaction(bool readonly) {
MDB_dbi dbi;
isc_rwlock_lock(&isc__cfgmgr_lock,
readonly ? isc_rwlocktype_read : isc_rwlocktype_write);
if (mdb_txn_begin(isc__cfgmgr_env, NULL, readonly ? MDB_RDONLY : 0,
&isc__cfgmgr_ctx.txn) != 0)
{
goto failure;
}
INSIST(isc__cfgmgr_ctx.txn != NULL);
if (mdb_dbi_open(isc__cfgmgr_ctx.txn, NULL, MDB_CREATE | MDB_DUPSORT,
&dbi) != 0)
{
goto failure;
}
if (mdb_cursor_open(isc__cfgmgr_ctx.txn, dbi,
&isc__cfgmgr_ctx.cursor) != 0)
{
goto failure;
}
INSIST(isc__cfgmgr_ctx.cursor != NULL);
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);
return ISC_R_SUCCESS;
failure:
if (isc__cfgmgr_ctx.txn) {
mdb_txn_abort(isc__cfgmgr_ctx.txn);
isc__cfgmgr_freectx();
isc_rwlock_unlock(&isc__cfgmgr_lock,
isc__cfgmgr_ctx.readonly
? isc_rwlocktype_read
: isc_rwlocktype_write);
}
ENSURE(isc__cfgmgr_ctx.buffer == NULL &&
isc__cfgmgr_ctx.prefix == NULL);
return ISC_R_FAILURE;
}
static isc_result_t
isc__cfgmgr_opentoplevel(const char *name, bool readonly) {
isc_result_t result = ISC_R_SUCCESS;
unsigned long id = 0;
REQUIRE(isc__cfgmgr_env != NULL);
REQUIRE(name != NULL);
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses));
REQUIRE(isc__cfgmgr_ctx.prefix == NULL);
REQUIRE(isc__cfgmgr_ctx.buffer == NULL);
REQUIRE(isc__cfgmgr_ctx.txn == NULL);
REQUIRE(isc__cfgmgr_ctx.cursor == NULL);
/*
* We're opening a clause at top-level, so let's start a
* transaction
*/
result = isc__cfgmgr_starttransaction(readonly);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* Now let's try to find the clause...
*/
result = isc__cfgmgr_findclause(name, &id);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* The clause is found, let's enqueue the clause in
* context. the clause is now opened
*/
isc__cfgmgr_pushclause(name, id);
goto out;
cleanup:
if (isc__cfgmgr_ctx.txn) {
mdb_cursor_close(isc__cfgmgr_ctx.cursor);
mdb_txn_abort(isc__cfgmgr_ctx.txn);
isc__cfgmgr_freectx();
isc_rwlock_unlock(&isc__cfgmgr_lock,
isc__cfgmgr_ctx.readonly
? isc_rwlocktype_read
: isc_rwlocktype_write);
}
out:
return result;
}
static isc_result_t
isc__cfgmgr_opennested(const char *name) {
isc_result_t result = ISC_R_SUCCESS;
unsigned long id = 0;
REQUIRE(isc__cfgmgr_env != NULL);
REQUIRE(name != NULL);
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
result = isc__cfgmgr_findclause(name, &id);
if (result != ISC_R_SUCCESS) {
goto out;
}
isc__cfgmgr_pushclause(name, id);
out:
return result;
}
isc_result_t
isc_cfgmgr_openrw(const char *name) {
return isc__cfgmgr_opentoplevel(name, false);
}
isc_result_t
isc_cfgmgr_open(const char *name) {
if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses)) {
return isc__cfgmgr_opentoplevel(name, true);
}
return isc__cfgmgr_opennested(name);
}
static void
popclause(void) {
openedclause_t *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();
}
isc_result_t
isc_cfgmgr_close(void) {
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(isc__cfgmgr_env != NULL);
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
popclause();
if (ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses)) {
mdb_cursor_close(isc__cfgmgr_ctx.cursor);
if (mdb_txn_commit(isc__cfgmgr_ctx.txn) != 0) {
result = ISC_R_FAILURE;
}
isc__cfgmgr_freectx();
isc_rwlock_unlock(&isc__cfgmgr_lock,
isc__cfgmgr_ctx.readonly
? isc_rwlocktype_read
: isc_rwlocktype_write);
}
return result;
}
isc_result_t
isc_cfgmgr_delclause(void) {
MDB_val dbkey;
REQUIRE(isc__cfgmgr_env != NULL);
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
REQUIRE(isc__cfgmgr_ctx.readonly == false);
dbkey = (MDB_val){ .mv_size = strlen(isc__cfgmgr_ctx.prefix) + 1,
.mv_data = isc__cfgmgr_ctx.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
*/
int mdbres = mdb_cursor_get(isc__cfgmgr_ctx.cursor, &dbkey,
NULL, MDB_SET_RANGE);
if (mdbres == MDB_NOTFOUND) {
break;
}
if (strncmp(isc__cfgmgr_ctx.prefix, dbkey.mv_data,
strlen(isc__cfgmgr_ctx.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_ctx.cursor, MDB_NODUPDATA) ==
0);
} while (1);
return isc_cfgmgr_close();
}
isc_result_t
isc_cfgmgr_newclause(const char *name) {
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(name != NULL);
REQUIRE(isc__cfgmgr_env != NULL);
if (isc__cfgmgr_ctx.txn == NULL || isc__cfgmgr_ctx.cursor == NULL) {
REQUIRE(isc__cfgmgr_ctx.prefix == NULL);
REQUIRE(isc__cfgmgr_ctx.buffer == NULL);
REQUIRE(isc__cfgmgr_ctx.txn == NULL);
REQUIRE(isc__cfgmgr_ctx.cursor == NULL);
result = isc__cfgmgr_starttransaction(false);
}
if (result == ISC_R_SUCCESS) {
INSIST(isc__cfgmgr_ctx.txn != NULL);
INSIST(isc__cfgmgr_ctx.buffer != NULL);
INSIST(isc__cfgmgr_ctx.cursor != NULL);
INSIST(isc__cfgmgr_ctx.readonly == false);
isc__cfgmgr_pushclause(name, isc_random32());
INSIST(isc__cfgmgr_ctx.prefix != NULL);
}
return result;
}
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;
REQUIRE(isc__cfgmgr_env != NULL);
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
/*
* 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_ctx.cursor, &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_result_t result = ISC_R_SUCCESS;
MDB_val dbkey;
MDB_val dbval;
const int opt = name == NULL ? MDB_NEXT_DUP : MDB_SET;
REQUIRE(isc__cfgmgr_env != NULL);
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
REQUIRE(isc__cfgmgr_ctx.cursor != 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_ctx.cursor, &dbkey, &dbval, opt) != 0) {
result = opt == MDB_NEXT_DUP ? ISC_R_NOMORE : ISC_R_NOTFOUND;
goto out;
}
memcpy(value, dbval.mv_data, sizeof(*value));
if (value->type == ISC_CFGMGR_STRING) {
value->string = ((char *)dbval.mv_data) + sizeof(value->type);
}
INSIST(value->type != ISC_CFGMGR_UNDEFINED);
out:
return result;
}
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_result_t result = ISC_R_SUCCESS;
MDB_val dbkey;
MDB_val dbval;
REQUIRE(isc__cfgmgr_env != NULL);
REQUIRE(ISC_LIST_EMPTY(isc__cfgmgr_ctx.openedclauses) == false);
REQUIRE(isc__cfgmgr_ctx.prefix != NULL);
REQUIRE(isc__cfgmgr_ctx.buffer != NULL);
REQUIRE(isc__cfgmgr_ctx.txn != NULL);
REQUIRE(isc__cfgmgr_ctx.cursor != NULL);
REQUIRE(isc__cfgmgr_ctx.readonly == false);
REQUIRE(name != NULL);
REQUIRE((value != NULL && value->type != ISC_CFGMGR_UNDEFINED) ||
(value == NULL && list == false));
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_ctx.cursor, &dbkey, NULL,
MDB_SET) == MDB_NOTFOUND)
{
result = ISC_R_NOTFOUND;
goto out;
}
REQUIRE(mdb_cursor_del(isc__cfgmgr_ctx.cursor, MDB_NODUPDATA) ==
0);
goto out;
}
if (value->type == ISC_CFGMGR_STRING) {
dbval.mv_size = sizeof(*value) + strlen(value->string) + 1;
dbval.mv_data = isc_mem_allocate(isc__cfgmgr_mctx,
dbval.mv_size);
memcpy(dbval.mv_data, value, sizeof(value->type));
strcpy(((char *)dbval.mv_data) + sizeof(value->type),
value->string);
} else {
dbval = (MDB_val){ .mv_size = sizeof(*value),
/*
* LMDB won't modify the mv_data buffer but
* its API is designed w/o the const buffer.
*/
.mv_data = (void *)value };
}
if (list == false) {
/*
* Can't use MDB_NOOVERWRITE as it would override the
* data if the key/value already exists. Making a
* value copy ahead just in case is likely more
* expensive than an extra lookup
*/
if (mdb_cursor_get(isc__cfgmgr_ctx.cursor, &dbkey, NULL,
MDB_SET) != MDB_NOTFOUND)
{
REQUIRE(mdb_cursor_del(isc__cfgmgr_ctx.cursor, 0) == 0);
}
}
REQUIRE(mdb_cursor_put(isc__cfgmgr_ctx.cursor, &dbkey, &dbval, 0) == 0);
if (value->type == ISC_CFGMGR_STRING) {
isc_mem_free(isc__cfgmgr_mctx, dbval.mv_data);
}
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);
}
isc_result_t
isc_cfgmgr_setnextlistval(const char *name, const isc_cfgmgr_val_t *value) {
return isc__cfgmgr_setval(name, value, true);
}
+181
View File
@@ -0,0 +1,181 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#pragma once
#include <isc/sockaddr.h>
#include <isc/types.h>
/*
* Supported data types for read/write operations from/to cfgmgr.
*/
typedef enum isc_cfgmgr_type {
ISC_CFGMGR_UNDEFINED = 0,
ISC_CFGMGR_STRING,
ISC_CFGMGR_BOOLEAN,
ISC_CFGMGR_NONE,
ISC_CFGMGR_SOCKADDR,
ISC_CFGMGR_UINT32
} isc_cfgmgr_type_t;
/*
* Generic value holding the actual value and type value for
* read/write from/to cfgmgr.
*
* cfgmgr_type_t::NONE doesn't have associated value,
*/
typedef struct isc_cfgmgr_val {
isc_cfgmgr_type_t type;
union {
const char *string;
bool boolean;
isc_sockaddr_t sockaddr;
uint32_t uint32;
};
} isc_cfgmgr_val_t;
/*
* Get the property "name" in the opened clause into the caller
* allocated "value" and returns ISC_R_SUCCESS. Returns ISC_R_NOTFOUND
* and "*value" is not mutated if "name" is not found. Changes being
* made by other threads aren't visible until the clause (and its
* parent, if nested) is closed. If "name" is a list property, get its
* head.
*/
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
* 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. Changes
* being made can be visible only by the current thread until the
* clause (and its parent, if nested) is closed.
*/
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);
/*
* If used at top-level, create and open as read-write a new clause
* "name". If used inside an opened parent clause, then the parent (or
* parent or the parent, recursively) clause must have been opened
* read-write (so using isc_cfgmgr_openrw or isc_cfgmgr_newclause).
*
* Returns ISC_R_SUCCESS or ISC_R_FAILURE if there is no transaction
* and it fails creating one. Note that in order to have the new
* clause actually written in cfgmgr, at least one property needs to
* be set to that clause.
*/
isc_result_t
isc_cfgmgr_newclause(const char *name);
/*
* 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. Returns ISC_R_SUCCESS.
*/
isc_result_t
isc_cfgmgr_delclause(void);
/*
* 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. If top-level clause was opened with
* isc_cfgmgr_openrw or isc_cfgmgr_newclause, closing the top-level
* clause will applies all modifications done inside the clause (and
* inside the nested clauses). If something is going wrong while
* writing the modifications ISC_R_FAILURE is returned and all
* modification made are discarded.
*/
isc_result_t
isc_cfgmgr_close(void);
/*
* Open the top-level clause "name" for reading and writing and
* returns ISC_R_SUCCESS. If the clause "name" is not found, returns
* ISC_R_NOTFOUND. If there is an issue creating a transaction, it
* returns ISC_R_FAILURE.
*
* This call will block if another thread has already a clause opened
* for reading and writting. Use isc_cfgmgr_openro for reading only.
*/
isc_result_t
isc_cfgmgr_openrw(const char *name);
/*
* Open the clause "name" and returns ISC_R_SUCCES or ISC_R_NOTFOUND
* is the clause is not found. Two possible cases:
*
* - if called at top-level, it open the top-level clause as read
* only. Returns ISC_R_FAILURE if there is an issue creating the
* transaction.
*
* - if called form within an opened clause, it open it with the same
* access than the already opened clause.
*/
isc_result_t
isc_cfgmgr_open(const char *name);
/*
* Initialize cfgmgr. Must be called before any other function. It is
* possible to re-initialize cfgmgr only after calling
* 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.
*/
isc_result_t
isc_cfgmgr_init(isc_mem_t *mctx, const char *dbpath);
/*
* Destroy all cfgmgr data and free memory. Must be called only after
* isc_cfgmgr_init and no function must be called after that one
* (except isc_cfgmgr_init to re-initialize cfgmgr again).
*/
void
isc_cfgmgr_deinit(void);
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#pragma once
#include <lmdb.h>
#define DNS_LMDB_COMMON_FLAGS (MDB_NOSUBDIR | MDB_NOLOCK)
#ifndef __OpenBSD__
#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS)
#else /* __OpenBSD__ */
/*
* OpenBSD does not have a unified buffer cache, which requires both reads and
* writes to be performed using mmap().
*/
#define DNS_LMDB_FLAGS (DNS_LMDB_COMMON_FLAGS | MDB_WRITEMAP)
#endif /* __OpenBSD__ */
+1 -5
View File
@@ -2061,11 +2061,7 @@ static cfg_clausedef_t view_clauses[] = {
{ "ipv4only-server", &cfg_type_astring, 0 },
{ "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
{ "lame-ttl", &cfg_type_duration, 0 },
#ifdef HAVE_LMDB
{ "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_OPTIONAL },
#else /* ifdef HAVE_LMDB */
{ "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_NOTCONFIGURED },
#endif /* ifdef HAVE_LMDB */
{ "lmdb-mapsize", &cfg_type_sizeval, 0 },
{ "max-acache-size", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "max-cache-size", &cfg_type_sizeorpercent, 0 },
{ "max-cache-ttl", &cfg_type_duration, 0 },
+1
View File
@@ -15,6 +15,7 @@ check_PROGRAMS = \
ascii_test \
async_test \
buffer_test \
cfgmgr_test \
counter_test \
dnsstream_utils_test \
errno_test \
File diff suppressed because it is too large Load Diff