Commit 41799683 authored by Daniel Salzman's avatar Daniel Salzman

conf: extend config access flags

parent 71d7f795
......@@ -88,11 +88,52 @@ static void rm_dir(const char *path)
}
}
static int init_and_check(
conf_t *conf,
conf_flag_t flags)
{
if (conf == NULL) {
return KNOT_EINVAL;
}
knot_db_txn_t txn;
unsigned txn_flags = (flags & CONF_FREADONLY) ? KNOT_DB_RDONLY : 0;
int ret = conf->api->txn_begin(conf->db, &txn, txn_flags);
if (ret != KNOT_EOK) {
return ret;
}
// Initialize the database.
if (!(flags & CONF_FREADONLY)) {
ret = conf_db_init(conf, &txn, false);
if (ret != KNOT_EOK) {
conf->api->txn_abort(&txn);
return ret;
}
}
// Check the database.
if (!(flags & CONF_FNOCHECK)) {
ret = conf_db_check(conf, &txn);
if (ret < KNOT_EOK) {
conf->api->txn_abort(&txn);
return ret;
}
}
ret = conf->api->txn_commit(&txn);
if (ret != KNOT_EOK) {
return ret;
}
return KNOT_EOK;
}
int conf_new(
conf_t **conf,
const yp_item_t *scheme,
const char *db_dir,
bool read_only)
conf_flag_t flags)
{
if (conf == NULL) {
return KNOT_EINVAL;
......@@ -111,20 +152,18 @@ int conf_new(
return ret;
}
// Prepare namedb api.
// Initialize a config mempool.
out->mm = malloc(sizeof(knot_mm_t));
mm_ctx_mempool(out->mm, MM_DEFAULT_BLKSIZE);
// Set the DB api.
out->api = knot_db_lmdb_api();
struct knot_db_lmdb_opts lmdb_opts = KNOT_DB_LMDB_OPTS_INITIALIZER;
lmdb_opts.mapsize = 500 * 1024 * 1024;
lmdb_opts.flags.env = KNOT_DB_LMDB_NOTLS;
// Set read-only mode.
if (read_only) {
lmdb_opts.flags.env |= KNOT_DB_LMDB_RDONLY;
}
// Open database.
if (db_dir == NULL) {
// Prepare a temporary database.
char tpl[] = "/tmp/knot-confdb.XXXXXX";
lmdb_opts.path = mkdtemp(tpl);
if (lmdb_opts.path == NULL) {
......@@ -132,39 +171,29 @@ int conf_new(
ret = KNOT_ENOMEM;
goto new_error;
}
out->api = knot_db_lmdb_api();
ret = out->api->init(&out->db, out->mm, &lmdb_opts);
// Remove opened database to ensure it is temporary.
rm_dir(tpl);
} else {
// Set the specified database.
lmdb_opts.path = db_dir;
out->api = knot_db_lmdb_api();
ret = out->api->init(&out->db, out->mm, &lmdb_opts);
}
// Check database opening.
if (ret != KNOT_EOK) {
goto new_error;
// Set the read-only mode.
if (flags & CONF_FREADONLY) {
lmdb_opts.flags.env |= KNOT_DB_LMDB_RDONLY;
}
}
// Initialize and check the database.
knot_db_txn_t txn;
unsigned flags = read_only ? KNOT_DB_RDONLY : 0;
ret = out->api->txn_begin(out->db, &txn, flags);
// Open the database.
ret = out->api->init(&out->db, out->mm, &lmdb_opts);
if (ret != KNOT_EOK) {
out->api->deinit(out->db);
goto new_error;
}
ret = read_only ? conf_db_check(out, &txn) : conf_db_init(out, &txn);
if (ret != KNOT_EOK) {
out->api->txn_abort(&txn);
out->api->deinit(out->db);
goto new_error;
// Remove opened database to ensure it is temporary.
if (db_dir == NULL) {
rm_dir(lmdb_opts.path);
}
ret = out->api->txn_commit(&txn);
// Initialize and check the database.
ret = init_and_check(out, flags);
if (ret != KNOT_EOK) {
out->api->deinit(out->db);
goto new_error;
......@@ -216,7 +245,6 @@ int conf_clone(
out->api = s_conf->api;
out->mm = s_conf->mm;
out->db = s_conf->db;
out->filename = s_conf->filename;
// Open common read-only transaction.
ret = out->api->txn_begin(out->db, &out->read_txn, KNOT_DB_RDONLY);
......@@ -226,9 +254,16 @@ int conf_clone(
return ret;
}
// Copy the filename.
if (s_conf->filename != NULL) {
out->filename = strdup(s_conf->filename);
}
// Initialize query modules list.
init_list(&out->query_modules);
out->is_clone = true;
*conf = out;
return KNOT_EOK;
......@@ -262,19 +297,21 @@ void conf_update(
return;
}
conf->is_clone = false;
conf_t **current_conf = &s_conf;
conf_t *old_conf = rcu_xchg_pointer(current_conf, conf);
synchronize_rcu();
if (old_conf) {
conf_free(old_conf, true);
old_conf->is_clone = true;
conf_free(old_conf);
}
}
void conf_free(
conf_t *conf,
bool is_clone)
conf_t *conf)
{
if (conf == NULL) {
return;
......@@ -282,6 +319,7 @@ void conf_free(
yp_scheme_free(conf->scheme);
conf->api->txn_abort(&conf->read_txn);
free(conf->filename);
free(conf->hostname);
if (conf->io.txn != NULL) {
......@@ -290,11 +328,10 @@ void conf_free(
conf_deactivate_modules(conf, &conf->query_modules, &conf->query_plan);
if (!is_clone) {
if (!conf->is_clone) {
conf->api->deinit(conf->db);
mp_delete(conf->mm->ctx);
free(conf->mm);
free(conf->filename);
}
free(conf);
......@@ -670,15 +707,8 @@ int conf_import(
goto import_error;
}
// Drop the current DB content.
ret = conf->api->clear(&txn);
if (ret != KNOT_EOK) {
conf->api->txn_abort(&txn);
goto import_error;
}
// Initialize new DB.
ret = conf_db_init(conf, &txn);
// Initialize the DB.
ret = conf_db_init(conf, &txn, true);
if (ret != KNOT_EOK) {
conf->api->txn_abort(&txn);
goto import_error;
......@@ -706,6 +736,13 @@ int conf_import(
goto import_error;
}
// Reset the filename.
free(conf->filename);
conf->filename = NULL;
if (is_file) {
conf->filename = strdup(input);
}
ret = KNOT_EOK;
import_error:
......
......@@ -62,6 +62,8 @@ typedef struct {
/*! Configuration context. */
typedef struct {
/*! Cloned configuration indicator. */
bool is_clone;
/*! Currently used namedb api. */
const struct knot_db_api *api;
/*! Configuration scheme. */
......@@ -81,11 +83,12 @@ typedef struct {
knot_db_txn_t txn_stack[CONF_MAX_TXN_DEPTH];
} io;
/*! Prearranged hostname string (for automatic NSID or CH ident value). */
char *hostname;
/*! Current config file (for reload if started with config file). */
char *filename;
/*! Prearranged hostname string (for automatic NSID or CH ident value). */
char *hostname;
/*! Cached critical confdb items. */
struct {
conf_val_t srv_nsid;
......@@ -102,6 +105,15 @@ typedef struct {
struct query_plan *query_plan;
} conf_t;
/*!
* Configuration access flags.
*/
typedef enum {
CONF_FNONE = 0, /*!< Empty flag. */
CONF_FREADONLY = 1 << 0, /*!< Read only access. */
CONF_FNOCHECK = 1 << 1 /*!< Disabled confdb check. */
} conf_flag_t;
/*!
* Returns the active configuration.
*/
......@@ -110,10 +122,10 @@ conf_t* conf(void);
/*!
* Creates new or opens old configuration database.
*
* \param[out] conf Configuration.
* \param[in] scheme Configuration scheme.
* \param[in] db_dir Database path or NULL.
* \param[in] read_only Set to read-only access.
* \param[out] conf Configuration.
* \param[in] scheme Configuration scheme.
* \param[in] db_dir Database path or NULL.
* \param[in] flags Access flags.
*
* \return Error code, KNOT_EOK if success.
*/
......@@ -121,7 +133,7 @@ int conf_new(
conf_t **conf,
const yp_item_t *scheme,
const char *db_dir,
bool read_only
conf_flag_t flags
);
/*!
......@@ -160,12 +172,10 @@ void conf_update(
/*!
* Removes the specified configuration.
*
* \param[in] conf Configuration.
* \param[in] is_clone Specifies if the configuration is a clone.
* \param[in] conf Configuration.
*/
void conf_free(
conf_t *conf,
bool is_clone
conf_t *conf
);
/*!
......
......@@ -110,7 +110,8 @@ static int db_check_version(
int conf_db_init(
conf_t *conf,
knot_db_txn_t *txn)
knot_db_txn_t *txn,
bool purge)
{
if (conf == NULL || txn == NULL) {
return KNOT_EINVAL;
......@@ -124,8 +125,16 @@ int conf_db_init(
uint8_t d[1] = { CONF_DB_VERSION };
knot_db_val_t data = { d, sizeof(d) };
return conf->api->insert(txn, &key, &data, 0);
} else if (ret > 0) { // Check existing DB.
return conf_db_check(conf, txn);
} else if (ret > 0) { // Non-empty DB.
if (purge) {
// Purge the DB.
ret = conf->api->clear(txn);
if (ret != KNOT_EOK) {
return ret;
}
return conf_db_init(conf, txn, false);
}
return KNOT_EOK;
} else { // DB error.
return ret;
}
......@@ -136,10 +145,19 @@ int conf_db_check(
knot_db_txn_t *txn)
{
int ret = conf->api->count(txn);
if (ret == 0) { // Empty DB.
return KNOT_CONF_EMPTY;
} else if (ret > 0) { // Check existing DB.
return db_check_version(conf, txn);
if (ret == 0) { // Not initialized DB.
return KNOT_CONF_ENOTINIT;
} else if (ret > 0) { // Check the DB.
int count = ret;
ret = db_check_version(conf, txn);
if (ret != KNOT_EOK) {
return ret;
} else if (count == 1) {
return KNOT_EOK; // Empty but initialized DB.
} else {
return count - 1; // Non-empty DB.
}
} else { // DB error.
return ret;
}
......
......@@ -42,25 +42,27 @@
#define CONF_MAX_DATA_LEN 65536
/*!
* Opens and checks or initializes the configuration DB.
* Initializes the configuration DB if empty.
*
* \param[in] conf Configuration.
* \param[in] txn Configuration DB transaction.
* \param[in] conf Configuration.
* \param[in] txn Configuration DB transaction.
* \param[in] purge Purge the DB indicator.
*
* \return Error code, KNOT_EOK if success.
*/
int conf_db_init(
conf_t *conf,
knot_db_txn_t *txn
knot_db_txn_t *txn,
bool purge
);
/*!
* Checks the configuration DB.
* Checks the configuration DB and returns the number of items.
*
* \param[in] conf Configuration.
* \param[in] txn Configuration DB transaction.
*
* \return Error code, KNOT_EOK if success.
* \return Error code, KNOT_EOK if ok and empty, > 0 number of records.
*/
int conf_db_check(
conf_t *conf,
......
......@@ -122,14 +122,14 @@ int conf_io_commit(
ret = new_conf->api->txn_begin(new_conf->db, &new_conf->read_txn,
KNOT_DB_RDONLY);
if (ret != KNOT_EOK) {
conf_free(new_conf, true);
conf_free(new_conf);
return ret;
}
// Run post-open config operations.
ret = conf_post_open(new_conf);
if (ret != KNOT_EOK) {
conf_free(new_conf, true);
conf_free(new_conf);
return ret;
}
......
......@@ -239,10 +239,11 @@ static void print_help(void)
" -c, --config <file> Use a textual configuration file.\n"
" (default %s)\n"
" -C, --confdb <dir> Use a binary configuration database directory.\n"
" (default %s)\n"
" -d, --daemonize=[dir] Run the server as a daemon (with new root directory).\n"
" -h, --help Print the program help.\n"
" -V, --version Print the program version.\n",
PROGRAM_NAME, CONF_DEFAULT_FILE);
PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR);
}
static void print_version(void)
......@@ -250,11 +251,71 @@ static void print_version(void)
printf("%s (Knot DNS), version %s\n", PROGRAM_NAME, PACKAGE_VERSION);
}
static int set_config(const char *confdb, const char *config)
{
if (config != NULL && confdb != NULL) {
log_fatal("ambiguous configuration source");
return KNOT_EINVAL;
}
/* Choose the optimal config source. */
struct stat st;
bool import = false;
if (confdb != NULL) {
import = false;
} else if (config != NULL){
import = true;
} else if (stat(CONF_DEFAULT_DBDIR, &st) == 0) {
import = false;
confdb = CONF_DEFAULT_DBDIR;
} else {
import = true;
config = CONF_DEFAULT_FILE;
}
const char *src = import ? config : confdb;
log_debug("%s '%s'", import ? "config" : "confdb",
(src != NULL) ? src : "empty");
/* Open confdb. */
conf_t *new_conf = NULL;
int ret = conf_new(&new_conf, conf_scheme, confdb, CONF_FNONE);
if (ret != KNOT_EOK) {
log_fatal("failed to open configuration database '%s' (%s)",
(confdb != NULL) ? confdb : "", knot_strerror(ret));
return ret;
}
/* Import the config file. */
if (import) {
ret = conf_import(new_conf, config, true);
if (ret != KNOT_EOK) {
log_fatal("failed to load configuration file '%s' (%s)",
config, knot_strerror(ret));
conf_free(new_conf);
return ret;
}
}
/* Run post-open config operations. */
ret = conf_post_open(new_conf);
if (ret != KNOT_EOK) {
log_fatal("failed to use configuration (%s)", knot_strerror(ret));
conf_free(new_conf);
return ret;
}
/* Update to the new config. */
conf_update(new_conf);
return KNOT_EOK;
}
int main(int argc, char **argv)
{
bool daemonize = false;
const char *config_fn = CONF_DEFAULT_FILE;
const char *config_db = NULL;
const char *config = NULL;
const char *confdb = NULL;
const char *daemon_root = "/";
/* Long options. */
......@@ -272,10 +333,10 @@ int main(int argc, char **argv)
while ((opt = getopt_long(argc, argv, "c:C:dhV", opts, &li)) != -1) {
switch (opt) {
case 'c':
config_fn = optarg;
config = optarg;
break;
case 'C':
config_db = optarg;
confdb = optarg;
break;
case 'd':
daemonize = true;
......@@ -325,55 +386,17 @@ int main(int argc, char **argv)
/* POSIX 1003.1e capabilities. */
setup_capabilities();
/* Default logging to std out/err. */
/* Initialize logging subsystem. */
log_init();
/* Open configuration. */
conf_t *new_conf = NULL;
if (config_db == NULL) {
int ret = conf_new(&new_conf, conf_scheme, NULL, false);
if (ret != KNOT_EOK) {
log_fatal("failed to initialize configuration database "
"(%s)", knot_strerror(ret));
log_close();
return EXIT_FAILURE;
}
/* Import the configuration file. */
ret = conf_import(new_conf, config_fn, true);
if (ret != KNOT_EOK) {
log_fatal("failed to load configuration file (%s)",
knot_strerror(ret));
conf_free(new_conf, false);
log_close();
return EXIT_FAILURE;
}
new_conf->filename = strdup(config_fn);
} else {
/* Open configuration database. */
int ret = conf_new(&new_conf, conf_scheme, config_db, false);
if (ret != KNOT_EOK) {
log_fatal("failed to open configuration database '%s' "
"(%s)", config_db, knot_strerror(ret));
log_close();
return EXIT_FAILURE;
}
}
/* Run post-open config operations. */
int ret = conf_post_open(new_conf);
/* Set up the configuration */
int ret = set_config(confdb, config);
if (ret != KNOT_EOK) {
log_fatal("failed to use configuration (%s)", knot_strerror(ret));
conf_free(new_conf, false);
log_close();
return EXIT_FAILURE;
}
/* Update to the new config. */
conf_update(new_conf);
/* Initialize logging subsystem. */
/* Reconfigure logging. */
log_reconfigure(conf(), NULL);
/* Initialize server. */
......@@ -381,7 +404,7 @@ int main(int argc, char **argv)
ret = server_init(&server, conf_bg_threads(conf()));
if (ret != KNOT_EOK) {
log_fatal("failed to initialize server (%s)", knot_strerror(ret));
conf_free(conf(), false);
conf_free(conf());
log_close();
return EXIT_FAILURE;
}
......@@ -399,7 +422,7 @@ int main(int argc, char **argv)
log_fatal("failed to drop privileges");
server_wait(&server);
server_deinit(&server);
conf_free(conf(), false);
conf_free(conf());
log_close();
return EXIT_FAILURE;
}
......@@ -411,7 +434,7 @@ int main(int argc, char **argv)
if (pidfile == NULL) {
server_wait(&server);
server_deinit(&server);
conf_free(conf(), false);
conf_free(conf());
log_close();
return EXIT_FAILURE;
}
......@@ -449,7 +472,7 @@ int main(int argc, char **argv)
rcu_unregister_thread();
pid_cleanup();
log_close();
conf_free(conf(), false);
conf_free(conf());
return EXIT_FAILURE;
}
......@@ -475,7 +498,7 @@ int main(int argc, char **argv)
/* Free server and configuration. */
server_deinit(&server);
conf_free(conf(), false);
conf_free(conf());
/* Unhook from RCU. */
rcu_unregister_thread();
......
......@@ -502,7 +502,7 @@ int server_reload(server_t *server, const char *cf)
if (ret != KNOT_EOK) {
log_fatal("failed to load configuration file (%s)",
knot_strerror(ret));
conf_free(new_conf, true);
conf_free(new_conf);
return ret;
}
......@@ -511,7 +511,7 @@ int server_reload(server_t *server, const char *cf)
if (ret != KNOT_EOK) {
log_fatal("failed to use configuration (%s)",
knot_strerror(ret));
conf_free(new_conf, true);
conf_free(new_conf);
return ret;
}
......
......@@ -134,7 +134,7 @@ enum knot_error {
KNOT_YP_ENOID,
/* Configuration errors. */
KNOT_CONF_EMPTY,
KNOT_CONF_ENOTINIT,
KNOT_CONF_EVERSION,
KNOT_CONF_EREDEFINE,
KNOT_CONF_ETXN,
......
......@@ -133,12 +133,12 @@ static const struct error errors[] = {
{ KNOT_YP_ENOID, "missing identifier" },
/* Configuration errors. */
{ KNOT_CONF_EMPTY, "empty configuration database" },
{ KNOT_CONF_EVERSION, "invalid configuration database version" },
{ KNOT_CONF_EREDEFINE, "duplicate identifier" },
{ KNOT_CONF_ETXN, "failed to open another config DB transaction" },
{ KNOT_CONF_ENOTXN, "no active config DB transaction" },
{ KNOT_CONF_EMANYTXN, "too many nested config DB transactions" },
{ KNOT_CONF_ENOTINIT, "config DB not initialized" },
{ KNOT_CONF_EVERSION, "invalid config DB version" },
{ KNOT_CONF_EREDEFINE, "duplicate identifier" },
{ KNOT_CONF_ETXN, "failed to open another config DB transaction" },
{ KNOT_CONF_ENOTXN, "no active config DB transaction" },
{ KNOT_CONF_EMANYTXN, "too many nested config DB transactions" },
/* Processing errors. */
{ KNOT_LAYER_ERROR, "processing layer error" },
......
......@@ -342,7 +342,7 @@ static void test_acl_allowed(void)
ret = acl_allowed(&acl, ACL_ACTION_TRANSFER, &addr, &key0);
ok(ret == true, "IPv6 address from range, no key, action match");
conf_free(conf(), false);
conf_free(conf());
knot_dname_free(&zone_name, NULL);
knot_dname_free(&key1_name, NULL);
knot_dname_free(&key2_name, NULL);
......
......@@ -161,7 +161,7 @@ static void test_conf_zonefile(void)
free(file);
}
conf_free(conf(), false);
conf_free(conf());
knot_dname_free(&zone1, NULL);
knot_dname_free(&zone2, NULL);
knot_dname_free(&zone3, NULL);
......
......@@ -480,7 +480,7 @@ int main(int argc, char *argv[])
conf()->api->txn_abort(&txn);
conf_free(conf(), false);
conf_free(conf());
return 0;
}
......@@ -838,7 +838,7 @@ int main(int argc, char *argv[])
diag("conf_io_desc");
test_conf_io_desc();
conf_free(conf(), false);
conf_free(conf());
return 0;
}
......@@ -158,7 +158,7 @@ int main(int argc, char *argv[])
/* Cleanup. */
mp_delete(mm.ctx);
server_deinit(&server);
conf_free(conf(), false);
conf_free(conf());
return 0;
}
......
......@@ -185,7 +185,7 @@ int main(int argc, char *argv[])
/* Cleanup. */
mp_delete((struct mempool *)mm.ctx);
server_deinit(&server);
conf_free(conf(), false);
conf_free(conf());
return 0;
}
......
......@@ -178,7 +178,7 @@ int main(int argc, char *argv[])
/* Cleanup. */
mp_delete((struct mempool *)mm.ctx);
conf_free(conf(), false);
conf_free(conf());
return 0;
}
......@@ -28,20 +28,20 @@ static inline int test_conf(const char *conf_str, const yp_item_t *scheme)
}
conf_t *conf;
int ret = conf_new(&conf, scheme, NULL, false);
int ret = conf_new(&conf, scheme, NULL, CONF_FNONE);
if (ret != KNOT_EOK) {
return ret;
}
ret = conf_import(conf, conf_str, false);
if (ret != KNOT_EOK) {
conf_free(conf, false);
conf_free(conf);
return ret;
}
ret = conf_post_open(conf);
if (ret != KNOT_EOK) {
conf_free(conf, false);
conf_free(conf);
return ret;
}
......