Commit be27376b authored by Marek Vavruša's avatar Marek Vavruša

Merge branch 'dynamic_modules'

parents d7fece8f cfd3e895
......@@ -30,9 +30,3 @@
/aclocal.m4
/ltmain.sh
/ylwrap
# CMake files
/CMakeCache.txt
/CMakeFiles
/Makefile
/cmake_install.cmake
......@@ -2,10 +2,10 @@ include config.mk
include platform.mk
# Targets
all: info libkresolve kresolved
install: libkresolve-install kresolved-install
check: all tests-check
clean: libkresolve-clean kresolved-clean tests-clean
all: info libkresolve modules kresolved
install: libkresolve-install modules-install kresolved-install
check: all tests
clean: libkresolve-clean modules-clean kresolved-clean tests-clean
.PHONY: all install check clean
# Options
......@@ -25,4 +25,5 @@ CFLAGS += $(libknot_CFLAGS) $(libuv_CFLAGS) $(cmocka_CFLAGS) $(python_CFLAGS)
include help.mk
include lib/libkresolve.mk
include daemon/kresolved.mk
include modules/modules.mk
include tests/tests.mk
......@@ -6,13 +6,15 @@ MINOR := 04
PREFIX := /usr/local
BINDIR := /bin
LIBDIR := /lib
INCLUDEDIR = /include
INCLUDEDIR := /include
MODULEDIR := $(LIBDIR)/kdns_modules
# Tools
ifndef CC
CC := cc
endif
CFLAGS += -std=c99 -D_GNU_SOURCE -Wall -fPIC -I$(abspath .) -DPACKAGE_VERSION="\"$(MAJOR).$(MINOR)\""
CFLAGS += -std=c99 -D_GNU_SOURCE -Wall -fPIC -I$(abspath .)
CFLAGS += -DPACKAGE_VERSION="\"$(MAJOR).$(MINOR)\"" -DPREFIX="\"$(PREFIX)\"" -DMODULEDIR="\"$(MODULEDIR)\""
RM := rm -f
LN := ln -s
INSTALL := install
......
......@@ -44,11 +44,17 @@ int worker_init(struct worker_ctx *worker, mm_ctx_t *mm)
/* Open resolution context cache */
worker->resolve.cache = kr_cache_open("/tmp/kresolved", mm, CACHE_DEFAULT_SIZE);
if (worker->resolve.cache == NULL) {
fprintf(stderr, "Cache directory '/tmp/kresolved' not exists, exitting.\n");
fprintf(stderr, "Cache directory '/tmp/kresolved' not exists, exiting.\n");
kr_context_deinit(&worker->resolve);
return KNOT_ERROR;
}
/* Load basic modules */
kr_context_register(&worker->resolve, "iterate");
kr_context_register(&worker->resolve, "itercache");
kr_context_register(&worker->resolve, "hints");
kr_context_register(&worker->resolve, "gostats");
return KNOT_EOK;
}
......
......@@ -7,6 +7,7 @@ info:
$(info BINDIR: $(BINDIR))
$(info LIBDIR: $(LIBDIR))
$(info INCLUDEDIR: $(INCLUDEDIR))
$(info MODULEDIR: $(MODULEDIR))
$(info )
$(info Features)
$(info --------)
......@@ -15,4 +16,5 @@ info:
$(info [$(HAS_libuv)] daemon (libuv))
$(info [$(HAS_cmocka)] unit tests (libcmocka))
$(info [$(HAS_python)] integration tests (libpython))
$(info [$(HAS_gccgo)] golang modules (gccgo))
$(info )
......@@ -32,6 +32,10 @@
namedb_t *kr_cache_open(const char *handle, mm_ctx_t *mm, size_t maxsize)
{
if (handle == NULL || maxsize == 0) {
return NULL;
}
struct namedb_lmdb_opts opts = NAMEDB_LMDB_OPTS_INITIALIZER;
opts.mapsize = maxsize;
opts.path = handle;
......@@ -47,16 +51,26 @@ namedb_t *kr_cache_open(const char *handle, mm_ctx_t *mm, size_t maxsize)
void kr_cache_close(namedb_t *cache)
{
db_api->deinit(cache);
if (cache != NULL) {
db_api->deinit(cache);
}
}
int kr_cache_txn_begin(namedb_t *cache, namedb_txn_t *txn, unsigned flags)
{
if (cache == NULL || txn == NULL) {
return KNOT_EINVAL;
}
return db_api->txn_begin(cache, txn, flags);
}
int kr_cache_txn_commit(namedb_txn_t *txn)
{
if (txn == NULL) {
return KNOT_EINVAL;
}
int ret = db_api->txn_commit(txn);
if (ret != KNOT_EOK) {
kr_cache_txn_abort(txn);
......@@ -66,7 +80,9 @@ int kr_cache_txn_commit(namedb_txn_t *txn)
void kr_cache_txn_abort(namedb_txn_t *txn)
{
return db_api->txn_abort(txn);
if (txn != NULL) {
db_api->txn_abort(txn);
}
}
static size_t cache_key(uint8_t *buf, const knot_dname_t *name, uint16_t type)
......@@ -94,6 +110,10 @@ static struct kr_cache_rrset *cache_rr(namedb_txn_t *txn, const knot_dname_t *na
int kr_cache_peek(namedb_txn_t *txn, knot_rrset_t *rr, uint32_t *timestamp)
{
if (txn == NULL || rr == NULL) {
return KNOT_EINVAL;
}
/* Check if the RRSet is in the cache. */
struct kr_cache_rrset *found_rr = cache_rr(txn, rr->owner, rr->type);
if (found_rr != NULL) {
......@@ -132,6 +152,8 @@ int kr_cache_peek(namedb_txn_t *txn, knot_rrset_t *rr, uint32_t *timestamp)
knot_rrset_t kr_cache_materialize(const knot_rrset_t *src, uint32_t drift, mm_ctx_t *mm)
{
assert(src);
/* Make RRSet copy. */
knot_rrset_t copy;
knot_rrset_init(&copy, NULL, src->type, src->rclass);
......@@ -161,6 +183,10 @@ knot_rrset_t kr_cache_materialize(const knot_rrset_t *src, uint32_t drift, mm_ct
int kr_cache_insert(namedb_txn_t *txn, const knot_rrset_t *rr, uint32_t timestamp)
{
if (txn == NULL || rr == NULL) {
return KNOT_EINVAL;
}
/* Ignore empty records. */
if (knot_rrset_empty(rr)) {
return KNOT_EOK;
......@@ -187,6 +213,10 @@ int kr_cache_insert(namedb_txn_t *txn, const knot_rrset_t *rr, uint32_t timestam
int kr_cache_remove(namedb_txn_t *txn, const knot_rrset_t *rr)
{
if (txn == NULL || rr == NULL) {
return KNOT_EINVAL;
}
uint8_t keybuf[KNOT_DNAME_MAXLEN + sizeof(uint16_t)];
size_t key_len = cache_key(keybuf, rr->owner, rr->type);
namedb_val_t key = { keybuf, key_len };
......@@ -196,6 +226,10 @@ int kr_cache_remove(namedb_txn_t *txn, const knot_rrset_t *rr)
int kr_cache_clear(namedb_txn_t *txn)
{
if (txn == NULL) {
return KNOT_EINVAL;
}
return db_api->clear(txn);
}
......
......@@ -15,10 +15,14 @@
*/
#include <string.h>
#include <limits.h>
#include <libknot/errcode.h>
#include <libknot/internal/sockaddr.h>
#include <libknot/internal/mem.h>
#include "lib/context.h"
#include "lib/defines.h"
#include "lib/rplan.h"
int kr_context_init(struct kr_context *ctx, mm_ctx_t *mm)
......@@ -43,5 +47,28 @@ int kr_context_deinit(struct kr_context *ctx)
kr_cache_close(ctx->cache);
}
for (size_t i = 0; i < ctx->mod_loaded; ++i) {
kr_module_unload(&ctx->modules[i]);
}
return KNOT_EOK;
}
int kr_context_register(struct kr_context *ctx, const char *module_name)
{
size_t last = ctx->mod_loaded;
int ret = mreserve((char **) &ctx->modules, sizeof(struct kr_module),
last + 1, 0, &ctx->mod_reserved);
if (ret < 0) {
return kr_error(ENOMEM);
}
struct kr_module *mod = &ctx->modules[last];
ret = kr_module_load(mod, module_name, NULL);
if (ret != 0) {
return ret;
}
ctx->mod_loaded += 1;
return kr_ok();
}
......@@ -19,6 +19,7 @@
#include <libknot/internal/mempattern.h>
#include <libknot/internal/lists.h>
#include "lib/module.h"
#include "lib/cache.h"
/*!
......@@ -31,10 +32,12 @@
*/
struct kr_context
{
struct kr_cache *cache;
list_t layers;
unsigned options;
mm_ctx_t *pool;
mm_ctx_t *pool;
struct kr_cache *cache;
struct kr_module *modules;
size_t mod_loaded;
size_t mod_reserved;
uint32_t options;
};
/*!
......@@ -51,3 +54,11 @@ int kr_context_init(struct kr_context *ctx, mm_ctx_t *mm);
* \return KNOT_E*
*/
int kr_context_deinit(struct kr_context *ctx);
/*!
* \brief Register module to context.
* \param ctx context
* \param module_name
* \return KNOT_E*
*/
int kr_context_register(struct kr_context *ctx, const char *module_name);
......@@ -16,10 +16,18 @@
#pragma once
#include <errno.h>
#include <libknot/errcode.h>
#include <libknot/dname.h>
#include <libknot/rrset.h>
/*
* Error codes.
*/
#define kr_ok() 0
#define kr_error(x) -abs(x)
#define kr_strerror(x) strerror(abs(x))
/*
* Connection limits.
*/
......@@ -36,4 +44,4 @@
#define KR_DNS_PORT 53
#define KR_DNAME_ROOT ((const knot_dname_t*)"")
#define KR_EDNS_VERSION 0
#define KR_EDNS_PAYLOAD 4096
#define KR_EDNS_PAYLOAD 4096
\ No newline at end of file
......@@ -26,6 +26,7 @@
#include "lib/rplan.h"
#include "lib/defines.h"
#include "lib/nsrep.h"
#include "lib/module.h"
#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(param->rplan), "iter", fmt)
......@@ -463,16 +464,16 @@ static int resolve(knot_layer_t *ctx, knot_pkt_t *pkt)
}
/*! \brief Module implementation. */
static const knot_layer_api_t LAYER_ITERATE_MODULE = {
&begin,
&reset,
&finish,
&resolve,
&prepare_query,
NULL
};
const knot_layer_api_t *layer_iterate_module(void)
const knot_layer_api_t *iterate_layer(void)
{
return &LAYER_ITERATE_MODULE;
static const knot_layer_api_t _layer = {
.begin = &begin,
.reset = &reset,
.finish = &finish,
.in = &resolve,
.out = &prepare_query
};
return &_layer;
}
KR_MODULE_EXPORT(iterate);
......@@ -19,8 +19,7 @@
#include "lib/layer.h"
/* Processing module implementation. */
const knot_layer_api_t *layer_iterate_module(void);
#define LAYER_ITERATE layer_iterate_module()
extern const knot_layer_api_t *iterate_layer(void);
/*!
* \brief Result updates the query parent.
......
......@@ -20,8 +20,8 @@
#include <libknot/internal/mempool.h>
#include <libknot/rrtype/rdname.h>
#include "lib/layer/static.h"
#include "lib/layer/iterate.h"
#include "lib/module.h"
#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(param->rplan), " cc ", fmt)
......@@ -95,7 +95,7 @@ static int read_cache(knot_layer_t *ctx, knot_pkt_t *pkt)
int state = read_cache_rr(txn, &cache_rr, timestamp, callback, param);
if (state == KNOT_NS_PROC_DONE) {
DEBUG_MSG("=> satisfied from cache\n");
kr_rplan_pop(param->rplan, cur);
cur->resolved = true;
return state;
}
......@@ -110,7 +110,7 @@ static int read_cache(knot_layer_t *ctx, knot_pkt_t *pkt)
}
}
kr_rplan_pop(param->rplan, cur);
cur->resolved = true;
return KNOT_NS_PROC_DONE;
}
......@@ -267,16 +267,15 @@ static int write_cache(knot_layer_t *ctx, knot_pkt_t *pkt)
}
/*! \brief Module implementation. */
static const knot_layer_api_t LAYER_ITERCACHE_MODULE = {
&begin,
NULL,
NULL,
&write_cache,
&read_cache,
NULL
};
const knot_layer_api_t *layer_itercache_module(void)
const knot_layer_api_t *itercache_layer(void)
{
return &LAYER_ITERCACHE_MODULE;
static const knot_layer_api_t _layer = {
.begin = &begin,
.in = &write_cache,
.out = &read_cache
};
return &_layer;
}
KR_MODULE_EXPORT(itercache);
......@@ -19,5 +19,4 @@
#include "lib/layer.h"
/* Processing module implementation. */
const knot_layer_api_t *layer_itercache_module(void);
#define LAYER_ITERCACHE layer_itercache_module()
extern const knot_layer_api_t *itercache_layer(void);
libkresolve_SOURCES := \
lib/layer/iterate.c \
lib/layer/itercache.c \
lib/layer/static.c \
lib/layer/stats.c \
lib/utils.c \
lib/nsrep.c \
lib/module.c \
lib/context.c \
lib/resolve.c \
lib/zonecut.c \
......@@ -11,12 +11,10 @@ libkresolve_SOURCES := \
lib/cache.c
libkresolve_HEADERS := \
lib/layer/iterate.h \
lib/layer/itercache.h \
lib/layer/static.h \
lib/layer/stats.h \
lib/layer.h \
lib/utils.h \
lib/nsrep.h \
lib/module.h \
lib/context.h \
lib/resolve.h \
lib/zonecut.h \
......
#include <stdlib.h>
#include <dlfcn.h>
#include <pthread.h>
#include <unistd.h>
#include "lib/defines.h"
#include "lib/utils.h"
#include "lib/module.h"
/*! \brief Library extension. */
static inline const char *library_ext(void)
{
#if defined(__APPLE__)
return ".dylib";
#elif _WIN32
return ".lib";
#else
return ".so";
#endif
}
static void *load_symbol(void *lib, const char *prefix, const char *name)
{
auto_free char *symbol = kr_strcatdup(2, prefix, name);
return dlsym(lib, symbol);
}
static int load_library(struct kr_module *module, const char *name, const char *path)
{
const char *ext = library_ext();
auto_free char *lib_path = NULL;
if (path != NULL) {
lib_path = kr_strcatdup(4, path, "/", name, ext);
} else {
lib_path = kr_strcatdup(2, name, ext);
}
if (lib_path == NULL) {
return kr_error(ENOMEM);
}
/* Workaround for buggy _fini/__attribute__((destructor)) and dlclose(),
* this keeps the library mapped until the program finishes though. */
module->lib = dlopen(lib_path, RTLD_NOW | RTLD_NODELETE);
if (module->lib) {
return kr_ok();
}
return kr_error(ENOENT);
}
static int bootstrap_libgo(struct kr_module *module)
{
/* Check if linked against compatible libgo */
void (*go_check)(void) = dlsym(module->lib, "runtime_check");
void (*go_args)(int, void*) = dlsym(module->lib, "runtime_args");
void (*go_init_os)(void) = dlsym(module->lib, "runtime_osinit");
void (*go_init_sched)(void) = dlsym(module->lib, "runtime_schedinit");
void (*go_init_main)(void) = dlsym(module->lib, "__go_init_main");
if ((go_check && go_args && go_init_os && go_init_sched && go_init_main) == false) {
return kr_error(EINVAL);
}
/*
* Bootstrap runtime - this is minimal runtime, we would need a running scheduler
* and gc for coroutines and memory allocation. That would require a custom "world loop",
* message passing, and either runtime sharing or module isolation.
* https://github.com/gcc-mirror/gcc/blob/gcc-4_9_2-release/libgo/runtime/proc.c#L457
*/
char *fake_argv[2] = {
getenv("_"),
NULL
};
go_check();
go_args(1, fake_argv);
go_init_os();
go_init_sched();
go_init_main();
return kr_ok();
}
static int load_libgo(struct kr_module *module, module_api_cb **module_api)
{
/* Bootstrap libgo */
int ret = bootstrap_libgo(module);
if (ret != 0) {
return ret;
}
/* Enforced prefix for now. */
const char *module_prefix = "main.";
*(void **) (module_api) = load_symbol(module->lib, module_prefix, "Api");
*(void **) (&module->init) = load_symbol(module->lib, module_prefix, "Init");
*(void **) (&module->deinit) = load_symbol(module->lib, module_prefix, "Deinit");
*(void **) (&module->config) = load_symbol(module->lib, module_prefix, "Config");
*(void **) (&module->layer) = load_symbol(module->lib, module_prefix, "Layer");
return kr_ok();
}
int kr_module_load(struct kr_module *module, const char *name, const char *path)
{
if (module == NULL || name == NULL) {
return kr_error(EINVAL);
}
/* Search for module library. */
memset(module, 0, sizeof(struct kr_module));
if (load_library(module, name, path) != 0) {
/* Expand HOME env variable, as the linker may not expand it. */
auto_free char *local_path = kr_strcatdup(2, getenv("HOME"), "/.local" MODULEDIR);
if (load_library(module, name, local_path) != 0) {
if (load_library(module, name, PREFIX MODULEDIR) != 0) {
}
}
}
/* It's okay if it fails, then current exec space is searched. */
if (module->lib == NULL) {
module->lib = RTLD_DEFAULT;
}
/* Load all symbols. */
auto_free char *module_prefix = kr_strcatdup(2, name, "_");
*(void **) (&module->init) = load_symbol(module->lib, module_prefix, "init");
*(void **) (&module->deinit) = load_symbol(module->lib, module_prefix, "deinit");
*(void **) (&module->config) = load_symbol(module->lib, module_prefix, "config");
*(void **) (&module->layer) = load_symbol(module->lib, module_prefix, "layer");
module_api_cb *module_api = NULL;
*(void **) (&module_api) = load_symbol(module->lib, module_prefix, "api");
/* No API version, try loading it as Go module. */
if (module->lib != RTLD_DEFAULT && module_api == NULL) {
(void) load_libgo(module, &module_api);
}
/* Check module API version (if declared). */
if (module_api == NULL) {
kr_module_unload(module);
return kr_error(KNOT_ENOENT);
} else if (module_api() != KR_MODULE_API) {
kr_module_unload(module);
return kr_error(ENOTSUP);
}
/* Initialize module */
if (module->init) {
module->init(module);
}
return kr_ok();
}
void kr_module_unload(struct kr_module *module)
{
if (module == NULL) {
return;
}
if (module->deinit) {
module->deinit(module);
}
if (module->lib && module->lib != RTLD_DEFAULT) {
dlclose(module->lib);
}
memset(module, 0, sizeof(struct kr_module));
}
/* Copyright (C) 2015 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 <libknot/processing/layer.h>
#include "lib/defines.h"
#include "lib/utils.h"
/*
* Forward decls
*/
struct kr_module;
/*
* API definition.
*/
#define KR_MODULE_API 0x20150401 /*!< API version */
typedef uint32_t (module_api_cb)(void);
typedef int (module_init_cb)(struct kr_module *);
typedef int (module_deinit_cb)(struct kr_module *);
typedef int (module_config_cb)(struct kr_module *, void *);
typedef const knot_layer_api_t* (module_layer_cb)(void);
/*! Loaded module representation. */
struct kr_module {
module_init_cb *init; /*!< Constructor */
module_deinit_cb *deinit; /*!< Destructor */
module_config_cb *config; /*!< Configuration */
module_layer_cb *layer; /*!< Layer getter */
void *lib; /*!< Shared library handle or RTLD_DEFAULT */
void *data; /*!< Custom data context. */
};
/*! Load module instance into memory.
* @param module module structure
* @param name module name
* @param path module search path
* @return 0 or an error
*/
int kr_module_load(struct kr_module *module, const char *name, const char *path);
/*! Unload module instance.
* @param module module structure
*/
void kr_module_unload(struct kr_module *module);
/*! Export module API version (place this at the end of your module).
* @param module module name (f.e. hints)
*/
#define KR_MODULE_EXPORT(module) \
uint32_t module ## _api() { return KR_MODULE_API; }
......@@ -26,8 +26,6 @@
#include "lib/defines.h"
#include "lib/layer/itercache.h"
#include "lib/layer/iterate.h"
#include "lib/layer/static.h"
#include "lib/layer/stats.h"
#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(param->rplan), "resl", fmt)
......@@ -147,15 +145,23 @@ static int iterate(struct knot_requestor *requestor, struct kr_layer_param *para
return ret;
}
static void prepare_layers(struct knot_requestor *req, struct kr_layer_param *param)
{
struct kr_context *ctx = param->ctx;
for (size_t i = 0; i < ctx->mod_loaded; ++i) {
struct kr_module *mod = &ctx->modules[i];
if (mod->layer) {
knot_requestor_overlay(req, mod->layer(), param);
}
}
}
static int resolve_iterative(struct kr_layer_param *param, mm_ctx_t *pool)
{
/* Initialize requestor and overlay. */
/* Initialize requestor. */
struct knot_requestor requestor;
knot_requestor_init(&requestor, pool);
knot_requestor_overlay(&requestor, LAYER_STATIC, param);
knot_requestor_overlay(&requestor, LAYER_ITERCACHE, param);
knot_requestor_overlay(&requestor, LAYER_ITERATE, param);
knot_requestor_overlay(&requestor, LAYER_STATS, param);
prepare_layers(&requestor, param);
/* Iteratively solve the query. */
int ret = KNOT_EOK;
......
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "lib/defines.h"
#include "lib/utils.h"
/*
* Macros.
*/
#define strlen_safe(x) ((x) ? strlen(x) : 0)
/*
* Cleanup callbacks.
*/
void _cleanup_free(char **p)
{
free(*p);
}
void _cleanup_close(int *p)
{
if (*p > 0) close(*p);
}
void _cleanup_fclose(FILE **p)
{
if (*p) fclose(*p);
}
char* kr_strcatdup(unsigned n, ...)
{
/* Calculate total length */
size_t total_len = 0;
va_list vl;