Commit 65f6161a authored by Daniel Salzman's avatar Daniel Salzman

module: create new API with dynamic loading support

parent c5a193a0
......@@ -177,6 +177,8 @@ src/knot/conf/confio.c
src/knot/conf/confio.h
src/knot/conf/migration.c
src/knot/conf/migration.h
src/knot/conf/module.c
src/knot/conf/module.h
src/knot/conf/scheme.c
src/knot/conf/scheme.h
src/knot/conf/tools.c
......
......@@ -241,6 +241,8 @@ libknotd_la_SOURCES = \
knot/conf/confio.h \
knot/conf/migration.c \
knot/conf/migration.h \
knot/conf/module.h \
knot/conf/module.c \
knot/conf/scheme.c \
knot/conf/scheme.h \
knot/conf/tools.c \
......@@ -424,6 +426,10 @@ if HAVE_DAEMON
sbin_PROGRAMS = knotc knotd keymgr
noinst_LTLIBRARIES += libknotd.la libknotus.la
knotddir = $(includedir)/knot
knotd_HEADERS = \
knot/include/module.h
knotc_SOURCES = \
utils/knotc/commands.c \
utils/knotc/commands.h \
......
......@@ -79,7 +79,7 @@ static void dump_counters(FILE *fd, int level, mod_ctr_t *ctr)
static void dump_modules(dump_ctx_t *ctx)
{
int level = 0;
struct query_module *mod = NULL;
knotd_mod_t *mod = NULL;
WALK_LIST(mod, *ctx->query_modules) {
// Skip modules without statistics.
if (mod->stats_count == 0) {
......
......@@ -18,6 +18,7 @@
#include "knot/conf/base.h"
#include "knot/conf/confdb.h"
#include "knot/conf/module.h"
#include "knot/conf/tools.h"
#include "knot/common/log.h"
#include "knot/nameserver/query_module.h"
......@@ -232,6 +233,27 @@ int conf_new(
// Initialize cached values.
init_cache(out);
// Load module schemas.
if (flags & (CONF_FREQMODULES | CONF_FOPTMODULES)) {
ret = conf_mod_load_common(out);
if (ret != KNOT_EOK && (flags & CONF_FREQMODULES)) {
goto new_error;
}
for (conf_iter_t iter = conf_iter(out, C_MODULE);
iter.code == KNOT_EOK; conf_iter_next(out, &iter)) {
conf_val_t id = conf_iter_id(out, &iter);
conf_val_t file = conf_id_get(out, C_MODULE, C_FILE, &id);
ret = conf_mod_load_extra(out, conf_str(&id), conf_str(&file), false);
if (ret != KNOT_EOK && (flags & CONF_FREQMODULES)) {
conf_iter_finish(out, &iter);
goto new_error;
}
}
conf_mod_load_purge(out, false);
}
*conf = out;
return KNOT_EOK;
......@@ -297,7 +319,7 @@ int conf_clone(
return KNOT_EOK;
}
void conf_update(
conf_t *conf_update(
conf_t *conf,
conf_update_flag_t flags)
{
......@@ -311,7 +333,7 @@ void conf_update(
}
if ((flags & CONF_UPD_FMODULES) && s_conf != NULL) {
list_dup(&conf->query_modules, &s_conf->query_modules,
sizeof(struct query_module));
sizeof(knotd_mod_t));
conf->query_plan = s_conf->query_plan;
}
}
......@@ -332,9 +354,13 @@ void conf_update(
WALK_LIST_FREE(old_conf->query_modules);
old_conf->query_plan = NULL;
}
conf_free(old_conf);
if (!(flags & CONF_UPD_FNOFREE)) {
conf_free(old_conf);
old_conf = NULL;
}
}
return old_conf;
}
void conf_free(
......@@ -359,7 +385,9 @@ void conf_free(
mm_free(conf->mm, conf->io.zones);
}
conf_mod_load_purge(conf, false);
conf_deactivate_modules(&conf->query_modules, &conf->query_plan);
conf_mod_unload_shared(conf);
if (!conf->is_clone) {
if (conf->api != NULL) {
......@@ -374,126 +402,6 @@ void conf_free(
free(conf);
}
#define LOG_ARGS(mod_id, msg, ...) "module '%s%s%.*s', " msg, \
mod_id->name + 1, (mod_id->len > 0) ? "/" : "", (int)mod_id->len, \
mod_id->data
void conf_activate_modules(
conf_t *conf,
const knot_dname_t *zone_name,
list_t *query_modules,
struct query_plan **query_plan)
{
int ret = KNOT_EOK;
if (conf == NULL || query_modules == NULL || query_plan == NULL) {
ret = KNOT_EINVAL;
goto activate_error;
}
conf_val_t val;
// Get list of associated modules.
if (zone_name != NULL) {
val = conf_zone_get(conf, C_MODULE, zone_name);
} else {
val = conf_default_get(conf, C_GLOBAL_MODULE);
}
switch (val.code) {
case KNOT_EOK:
break;
case KNOT_ENOENT: // Check if a module is configured at all.
case KNOT_YP_EINVAL_ID:
return;
default:
ret = val.code;
goto activate_error;
}
// Create query plan.
*query_plan = query_plan_create(conf->mm);
if (*query_plan == NULL) {
ret = KNOT_ENOMEM;
goto activate_error;
}
// Initialize query modules list.
init_list(query_modules);
// Open the modules.
while (val.code == KNOT_EOK) {
conf_mod_id_t *mod_id = conf_mod_id(&val);
if (mod_id == NULL) {
ret = KNOT_ENOMEM;
goto activate_error;
}
// Open the module.
struct query_module *mod = query_module_open(conf, mod_id, *query_plan,
zone_name, conf->mm);
if (mod == NULL) {
ret = KNOT_ENOMEM;
goto activate_error;
}
// Check the module scope.
if ((zone_name == NULL && (mod->scope & MOD_SCOPE_GLOBAL) == 0) ||
(zone_name != NULL && (mod->scope & MOD_SCOPE_ZONE) == 0)) {
if (zone_name != NULL) {
log_zone_warning(zone_name, LOG_ARGS(mod_id, "out of scope"));
} else {
log_warning(LOG_ARGS(mod_id, "out of scope"));
}
query_module_close(mod);
conf_val_next(&val);
continue;
}
// Load the module.
ret = mod->load(mod);
if (ret != KNOT_EOK) {
if (zone_name != NULL) {
log_zone_error(zone_name, LOG_ARGS(mod_id, "failed to load"));
} else {
log_error(LOG_ARGS(mod_id, "failed to load"));
}
query_module_close(mod);
conf_val_next(&val);
continue;
}
add_tail(query_modules, &mod->node);
conf_val_next(&val);
}
return;
activate_error:
CONF_LOG(LOG_ERR, "failed to activate modules (%s)", knot_strerror(ret));
}
void conf_deactivate_modules(
list_t *query_modules,
struct query_plan **query_plan)
{
if (query_modules == NULL || query_plan == NULL) {
return;
}
// Free query plan.
query_plan_free(*query_plan);
*query_plan = NULL;
// Free query modules list.
struct query_module *mod = NULL, *next = NULL;
WALK_LIST_DELSAFE(mod, next, *query_modules) {
mod->unload(mod);
query_module_close(mod);
}
init_list(query_modules);
}
#define CONF_LOG_LINE(file, line, msg, ...) do { \
CONF_LOG(LOG_ERR, "%s%s%sline %zu, " msg, \
(file != NULL ? "file '" : ""), (file != NULL ? file : ""), \
......@@ -513,17 +421,17 @@ static void log_parser_err(
static void log_call_err(
yp_parser_t *parser,
conf_check_t *args,
knotd_conf_check_args_t *args,
int ret)
{
CONF_LOG_LINE(args->file_name, args->line,
CONF_LOG_LINE(args->extra->file_name, args->extra->line,
"item '%s', value '%.*s' (%s)", args->item->name + 1,
(int)parser->data_len, parser->data,
args->err_str != NULL ? args->err_str : knot_strerror(ret));
}
static void log_prev_err(
conf_check_t *args,
knotd_conf_check_args_t *args,
int ret)
{
char buff[512] = { 0 };
......@@ -537,7 +445,7 @@ static void log_prev_err(
}
}
CONF_LOG_LINE(args->file_name, args->line,
CONF_LOG_LINE(args->extra->file_name, args->extra->line,
"%s '%s' (%s)", args->item->name + 1, buff,
args->err_str != NULL ? args->err_str : knot_strerror(ret));
}
......@@ -556,14 +464,17 @@ static int finalize_previous_section(
return KNOT_EOK;
}
conf_check_t args = {
knotd_conf_check_extra_t extra = {
.conf = conf,
.txn = txn,
.file_name = parser->file.name,
.line = parser->line_count
};
knotd_conf_check_args_t args = {
.item = node->item,
.id = node->id,
.id_len = node->id_len,
.file_name = parser->file.name,
.line = parser->line_count
.extra = &extra
};
int ret = conf_exec_callbacks(&args);
......@@ -587,16 +498,19 @@ static int finalize_item(
return KNOT_EOK;
}
conf_check_t args = {
knotd_conf_check_extra_t extra = {
.conf = conf,
.txn = txn,
.file_name = parser->file.name,
.line = parser->line_count
};
knotd_conf_check_args_t args = {
.item = (parser->event == YP_EID) ? node->item->var.g.id : node->item,
.id = node->id,
.id_len = node->id_len,
.data = node->data,
.data_len = node->data_len,
.file_name = parser->file.name,
.line = parser->line_count
.extra = &extra
};
int ret = conf_exec_callbacks(&args);
......@@ -693,6 +607,7 @@ int conf_parse(
ret = check_ret;
}
conf_mod_load_purge(conf, false);
yp_scheme_check_deinit(ctx);
parse_error:
yp_deinit(parser);
......
......@@ -29,6 +29,8 @@
#include "libknot/yparser/ypscheme.h"
#include "contrib/qp-trie/trie.h"
#include "contrib/ucw/lists.h"
#include "contrib/dynarray.h"
#include "knot/include/module.h"
/*! Default template identifier. */
#define CONF_DEFAULT_ID ((uint8_t *)"\x08""default\0")
......@@ -67,6 +69,19 @@ typedef struct {
int code;
} conf_val_t;
/*! Query module context. */
typedef struct {
/*! Module interface. */
const knotd_mod_api_t *api;
/*! Shared library dlopen handler. */
void *lib_handle;
/*! Indication of a temporary module created during confio check. */
bool temporary;
} module_t;
dynarray_declare(mod, module_t *, DYNARRAY_VISIBILITY_PUBLIC, 16)
dynarray_declare(old_schema, yp_item_t *, DYNARRAY_VISIBILITY_PUBLIC, 16)
/*! Configuration context. */
typedef struct {
/*! Cloned configuration indicator. */
......@@ -112,6 +127,10 @@ typedef struct {
conf_val_t srv_nsid;
} cache;
/*! List of dynamically loaded modules. */
mod_dynarray_t modules;
/*! List of old schemas (lazy freed). */
old_schema_dynarray_t old_schemas;
/*! List of active query modules. */
list_t query_modules;
/*! Default query modules plan. */
......@@ -122,10 +141,12 @@ typedef struct {
* 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_FNOHOSTNAME = 1 << 2, /*!< Don't set the hostname. */
CONF_FNONE = 0, /*!< Empty flag. */
CONF_FREADONLY = 1 << 0, /*!< Read only access. */
CONF_FNOCHECK = 1 << 1, /*!< Disabled confdb check. */
CONF_FNOHOSTNAME = 1 << 2, /*!< Don't set the hostname. */
CONF_FREQMODULES = 1 << 3, /*!< Load module schemas (must succeed). */
CONF_FOPTMODULES = 1 << 4, /*!< Load module schemas (may fail). */
} conf_flag_t;
/*!
......@@ -133,8 +154,9 @@ typedef enum {
*/
typedef enum {
CONF_UPD_FNONE = 0, /*!< Empty flag. */
CONF_UPD_FMODULES = 1 << 0, /*!< Reuse previous global modules. */
CONF_UPD_FCONFIO = 1 << 1, /*!< Reuse previous cofio reload context. */
CONF_UPD_FNOFREE = 1 << 0, /*!< Disable auto-free of previous config. */
CONF_UPD_FMODULES = 1 << 1, /*!< Reuse previous global modules. */
CONF_UPD_FCONFIO = 1 << 2, /*!< Reuse previous cofio reload context. */
} conf_update_flag_t;
/*!
......@@ -197,8 +219,10 @@ int conf_clone(
*
* \param[in] conf New configuration.
* \param[in] flags Update flags.
*
* \return Previous config if CONF_UPD_FNOFREE, else NULL.
*/
void conf_update(
conf_t *conf_update(
conf_t *conf,
conf_update_flag_t flags
);
......@@ -212,32 +236,6 @@ void conf_free(
conf_t *conf
);
/*!
* Activates configured query modules for the specified zone or for all zones.
*
* \param[in] conf Configuration.
* \param[in] zone_name Zone name, NULL for all zones.
* \param[in] query_modules Destination query modules list.
* \param[in] query_plan Destination query plan.
*/
void conf_activate_modules(
conf_t *conf,
const knot_dname_t *zone_name,
list_t *query_modules,
struct query_plan **query_plan
);
/*!
* Deactivates query modules list.
*
* \param[in] query_modules Destination query modules list.
* \param[in] query_plan Destination query plan.
*/
void conf_deactivate_modules(
list_t *query_modules,
struct query_plan **query_plan
);
/*!
* Parses textual configuration from the string or from the file.
*
......
......@@ -16,8 +16,10 @@
#include <assert.h>
#include "knot/common/log.h"
#include "knot/conf/confdb.h"
#include "knot/conf/confio.h"
#include "knot/conf/module.h"
#include "knot/conf/tools.h"
#define FCN(io) (io->fcn != NULL) ? io->fcn(io) : KNOT_EOK;
......@@ -878,20 +880,32 @@ static int set_item(
}
// Postpone group callbacks to config check.
if (io->key0->type == YP_TGRP) {
if (io->key0->type == YP_TGRP && io->id_len == 0) {
return KNOT_EOK;
}
conf_check_t args = {
knotd_conf_check_extra_t extra = {
.conf = conf(),
.txn = conf()->io.txn,
.item = (io->key1 != NULL) ? io->key1 : io->key0,
.txn = conf()->io.txn
};
knotd_conf_check_args_t args = {
.item = (io->key1 != NULL) ? io->key1 :
((io->id_len == 0) ? io->key0 : io->key0->var.g.id),
.id = io->id,
.id_len = io->id_len,
.data = io->data.bin,
.data_len = io->data.bin_len
.data_len = io->data.bin_len,
.extra = &extra
};
// Call the item callbacks (include).
return conf_exec_callbacks(&args);
// Call the item callbacks (include, item check, mod-id check).
ret = conf_exec_callbacks(&args);
if (ret != KNOT_EOK) {
CONF_LOG(LOG_DEBUG, "item '%s' (%s)", args.item->name + 1,
args.err_str != NULL ? args.err_str : knot_strerror(ret));
}
return ret;
}
int conf_io_set(
......@@ -1276,11 +1290,15 @@ static int check_section(
size_t id_len,
conf_io_t *io)
{
conf_check_t args = {
knotd_conf_check_extra_t extra = {
.conf = conf(),
.txn = conf()->io.txn,
.check = true
};
knotd_conf_check_args_t args = {
.id = id,
.id_len = id_len
.id_len = id_len,
.extra = &extra
};
bool non_empty = false;
......@@ -1482,6 +1500,7 @@ int conf_io_check(
ret = KNOT_EOK;
check_error:
conf_mod_load_purge(conf(), true);
return ret;
}
This diff is collapsed.
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "knot/conf/base.h"
/*!
* Finds specific module in static or dynamic modules.
*
* \param[in] conf Configuration.
* \param[in] name Module name.
* \param[in] len Module name length.
* \param[in] temporary Find only a temporary module indication.
*
* \return Module, NULL if not found.
*/
module_t *conf_mod_find(
conf_t *conf,
const char *name,
size_t len,
bool temporary
);
/*!
* Loads common static and shared modules.
*
* \param[in] conf Configuration.
*
* \return Error code, KNOT_EOK if success.
*/
int conf_mod_load_common(
conf_t *conf
);
/*!
* Loads extra shared module.
*
* \param[in] conf Configuration.
* \param[in] mod_name Module name.
* \param[in] file_name Shared library file name.
* \param[in] temporary Mark module as temporary.
*
* \return Error code, KNOT_EOK if success.
*/
int conf_mod_load_extra(
conf_t *conf,
const char *mod_name,
const char *file_name,
bool temporary
);
/*!
* Purges temporary schemas and modules after all modules loading.
*
* \param[in] conf Configuration.
* \param[in] temporary Purge only temporary modules indication.
*/
void conf_mod_load_purge(
conf_t *conf,
bool temporary
);
/*!
* Unloads all shared modules.
*
* \param[in] conf Configuration.
*/
void conf_mod_unload_shared(
conf_t *conf
);
/*!
* Activates configured query modules for the specified zone or for all zones.
*
* \param[in] conf Configuration.
* \param[in] zone_name Zone name, NULL for all zones.
* \param[in] query_modules Destination query modules list.
* \param[in] query_plan Destination query plan.
*/
void conf_activate_modules(
conf_t *conf,
const knot_dname_t *zone_name,
list_t *query_modules,
struct query_plan **query_plan
);
/*!
* Deactivates query modules list.
*
* \param[in] query_modules Destination query modules list.
* \param[in] query_plan Destination query plan.
*/
void conf_deactivate_modules(
list_t *query_modules,
struct query_plan **query_plan
);
......@@ -23,25 +23,12 @@
#include "knot/conf/confio.h"
#include "knot/conf/tools.h"
#include "knot/common/log.h"
#include "knot/journal/journal.h"
#include "knot/updates/acl.h"
#include "libknot/rrtype/opt.h"
#include "dnssec/lib/dnssec/tsig.h"
#include "dnssec/lib/dnssec/key.h"
#include "knot/modules/rrl/rrl.h"
#include "knot/modules/stats/stats.h"
#include "knot/modules/synth_record/synth_record.h"
#include "knot/modules/dnsproxy/dnsproxy.h"
#include "knot/modules/online_sign/online_sign.h"
#ifdef HAVE_ROSEDB
#include "knot/modules/rosedb/rosedb.h"
#endif
#if USE_DNSTAP
#include "knot/modules/dnstap/dnstap.h"
#endif
#include "knot/modules/whoami/whoami.h"
#include "knot/modules/noudp/noudp.h"
#define HOURS(x) ((x) * 3600)
#define DAYS(x) ((x) * HOURS(24))
......@@ -54,8 +41,6 @@
#define VIRT_MEM_LIMIT(x) (((sizeof(void *) < 8) && ((x) > VIRT_MEM_TOP_32BIT)) \
? VIRT_MEM_TOP_32BIT : (x))
#define FMOD (YP_FMULTI | CONF_IO_FRLD_MOD | CONF_IO_FRLD_ZONES)
static const knot_lookup_t keystore_backends[] = {
{ KEYSTORE_BACKEND_PEM, "pem" },
{ KEYSTORE_BACKEND_PKCS11, "pkcs11" },
......@@ -113,6 +98,13 @@ static const knot_lookup_t journal_modes[] = {
{ 0, NULL }
};
static const yp_item_t desc_module[] = {
{ C_ID, YP_TSTR, YP_VNONE, YP_FNONE, { check_module_id } },
{ C_FILE, YP_TSTR, YP_VNONE },
{ C_COMMENT, YP_TSTR, YP_VNONE },
{ NULL }
};
static const yp_item_t desc_server[] = {
{ C_IDENT, YP_TSTR, YP_VNONE },
{ C_VERSION, YP_TSTR, YP_VNONE },
......@@ -290,6 +282,8 @@ static const yp_item_t desc_zone[] = {
};
const yp_item_t conf_scheme[] = {
{ C_MODULE, YP_TGRP, YP_VGRP = { desc_module }, YP_FMULTI | CONF_IO_FRLD_ALL |
CONF_IO_FCHECK_ZONES, { load_module } },
{ C_SRV, YP_TGRP, YP_VGRP = { desc_server }, CONF_IO_FRLD_SRV, { check_server } },
{ C_CTL, YP_TGRP, YP_VGRP = { desc_control } },
{ C_LOG, YP_TGRP, YP_VGRP = { desc_log }, YP_FMULTI | CONF_IO_FRLD_LOG },
......@@ -299,28 +293,8 @@ const yp_item_t conf_scheme[] = {
{ C_KEY, YP_TGRP, YP_VGRP = { desc_key }, YP_FMULTI, { check_key } },
{ C_ACL, YP_TGRP, YP_VGRP = { desc_acl }, YP_FMULTI, { check_acl } },
{ C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } },
/* MODULES */
{ C_MOD_RRL, YP_TGRP, YP_VGRP = { scheme_mod_rrl }, FMOD,
{ check_mod_rrl } },
{ C_MOD_STATS, YP_TGRP, YP_VGRP = { scheme_mod_stats }, FMOD },
{ C_MOD_SYNTH_RECORD, YP_TGRP, YP_VGRP = { scheme_mod_synth_record }, FMOD,
{ check_mod_synth_record } },
{ C_MOD_DNSPROXY, YP_TGRP, YP_VGRP = { scheme_mod_dnsproxy }, FMOD,
{ check_mod_dnsproxy } },
#if HAVE_ROSEDB
{ C_MOD_ROSEDB, YP_TGRP, YP_VGRP = { scheme_mod_rosedb }, FMOD,
{ check_mod_rosedb } },
#endif
#if USE_DNSTAP
{ C_MOD_DNSTAP, YP_TGRP, YP_VGRP = { scheme_mod_dnstap }, FMOD,
{ check_mod_dnstap } },
#endif
{ C_MOD_ONLINE_SIGN, YP_TGRP, YP_VGRP = { scheme_mod_online_sign }, FMOD },
{ C_MOD_WHOAMI, YP_TGRP, YP_VGRP = { scheme_mod_whoami }, FMOD },
{ C_MOD_NOUDP, YP_TGRP, YP_VGRP = { scheme_mod_noudp }, FMOD },
/***********/
{ C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } },
{ C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI | CONF_IO_FZONE, { check_zone } },
{ C_INCL, YP_TSTR, YP_VNONE, CONF_IO_FDIFF_ZONES | CONF_IO_FRLD_ALL, { include_file } },
{ C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } },
{ C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI | CONF_IO_FZONE, { check_zone } },
{ C_INCL, YP_TSTR, YP_VNONE, CONF_IO_FDIFF_ZONES | CONF_IO_FRLD_ALL, { include_file } },
{ NULL }
};
This diff is collapsed.
......@@ -31,21 +31,16 @@
#include "knot/conf/conf.h"
#include "libknot/yparser/ypscheme.h"
typedef struct {
typedef struct knotd_conf_check_extra {
conf_t *conf;
knot_db_txn_t *txn;
const yp_item_t *item;
const uint8_t *id;
size_t id_len;
const uint8_t *data;
size_t data_len;
const char *file_name;
size_t line;
const char *err_str;
} conf_check_t;
bool check; /*!< Indication of the confio check mode. */
} knotd_conf_check_extra_t;
int conf_exec_callbacks(
conf_check_t *args
knotd_conf_check_args_t *args
);
int mod_id_to_bin(
......@@ -65,51 +60,59 @@ int edns_opt_to_txt(
);