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

lib: loadable versioned modules (api subject to change)

parent d7fece8f
......@@ -2,10 +2,10 @@ include config.mk
include platform.mk
# Targets
all: info libkresolve kresolved
install: libkresolve-install kresolved-install
all: info libkresolve modules kresolved
install: libkresolve-install modules-install kresolved-install
check: all tests-check
clean: libkresolve-clean kresolved-clean tests-clean
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,16 @@ 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");
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,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,14 @@ 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;
}
\ No newline at end of file
......@@ -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)
......@@ -267,16 +267,13 @@ 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;
}
......@@ -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 <dlfcn.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(3, prefix, "_", name);
return dlsym(lib, symbol);
}
static int load_library(struct kr_module *module, const char *name, const char *path)
{
if (path == NULL) {
return kr_error(EINVAL);
}
auto_free char *lib_path = kr_strcatdup(4, path, "/", name, library_ext());
if (lib_path == NULL) {
return kr_error(ENOMEM);
}
module->lib = dlopen(lib_path, RTLD_LAZY);
if (module->lib) {
return kr_ok();
}
return kr_error(ENOENT);
}
int kr_module_load(struct kr_module *module, const char *name, const char *path)
{
if (load_library(module, name, path) != 0) {
if (load_library(module, name, "~/.local/" MODULEDIR) != 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. */
module_api_cb *module_api = NULL;
*(void **) (&module_api) = load_symbol(module->lib, name, "api");
*(void **) (&module->init) = load_symbol(module->lib, name, "init");
*(void **) (&module->deinit) = load_symbol(module->lib, name, "deinit");
*(void **) (&module->config) = load_symbol(module->lib, name, "config");
*(void **) (&module->layer) = load_symbol(module->lib, name, "layer");
/* Check module API version (if declared). */
if (module_api && module_api() > KR_MODULE_API) {
return kr_error(ENOTSUP);
}
/* Initialize module */
if (module->init) {
return module->init(module);
}
return kr_ok();
}
void kr_module_unload(struct kr_module *module)
{
if (module->deinit) {
module->deinit(module);
}
if (module->lib && module->lib != RTLD_DEFAULT) {
dlclose(module->lib);
}
}
\ No newline at end of file
/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* 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
......@@ -14,64 +14,50 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libknot/internal/print.h>
#pragma once
#include "lib/layer/stats.h"
#include "lib/rplan.h"
#include <libknot/processing/layer.h>
#define DEBUG_MSG(fmt...) QRDEBUG(NULL, "stat", fmt)
//static void update_stats(struct kr_ns *ns, double rtt)
//{
// /* Knuth, TAOCP, p.232 (Welford running variance/mean). */
// double d_mean = (rtt - ns->stat.M);
// ns->stat.n += 1;
// ns->stat.M += d_mean / ns->stat.n;
// ns->stat.S += d_mean * (rtt - ns->stat.M);
//}
static int begin(knot_layer_t *ctx, void *param)
{
ctx->data = param;
return ctx->state;
}
static int finish(knot_layer_t *ctx)
{
#ifndef NDEBUG
struct kr_layer_param *param = ctx->data;
struct kr_rplan *rplan = param->rplan;
const knot_pkt_t *answer = param->answer;
/* Calculate total RTT and number of queries. */
double total_rtt = 0.0;
size_t nr_queries = list_size(&rplan->resolved);
if (nr_queries > 0) {
struct kr_query *query_first = HEAD(rplan->resolved);
struct timeval t_end;
gettimeofday(&t_end, NULL);
total_rtt = time_diff(&query_first->timestamp, &t_end);
}
/*
* Forward decls
*/
struct kr_module;
lookup_table_t *rcode = lookup_by_id(knot_rcode_names, knot_wire_get_rcode(answer->wire));
DEBUG_MSG("result => %s [%u records]\n", rcode ? rcode->name : "??", answer->rrset_count);
DEBUG_MSG("rtt => %.02lf [ms]\n", total_rtt);
#endif
/*
* 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. */
};
return ctx->state;
}
/*! 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);
/*! \brief Module implementation. */
static const knot_layer_api_t LAYER_STATS_MODULE = {
&begin,
NULL,
&finish,
NULL,
NULL,
NULL
};
/*! Unload module instance.
* @param module module structure
*/
void kr_module_unload(struct kr_module *module);
const knot_layer_api_t *layer_stats_module(void)
{
return &LAYER_STATS_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; }
\ No newline at end of file
......@@ -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);
}
char* kr_strcatdup(unsigned n, ...)
{
/* Calculate total length */
size_t total_len = 0;
va_list vl;
va_start(vl, n);
for (unsigned i = 0; i < n; ++i) {
char *item = va_arg(vl, char *);
total_len += strlen_safe(item);
}
va_end(vl);
/* Allocate result and fill */
char *result = NULL;
if (total_len > 0) {
result = malloc(total_len + 1);
}
if (result) {
char *stream = result;
va_start(vl, n);
for (unsigned i = 0; i < n; ++i) {
char *item = va_arg(vl, char *);
if (item) {
size_t len = strlen(item);
memcpy(stream, item, len + 1);
stream += len;
}
}
va_end(vl);
}
return result;
}
\ No newline at end of file
......@@ -16,8 +16,18 @@
#pragma once
#include "lib/layer.h"
/*
* General-purpose attributes.
*/
#define auto_free __attribute__((cleanup(_cleanup_free)))
extern void _cleanup_free(char **p);
#define auto_close __attribute__((cleanup(_cleanup_close)))
extern void _cleanup_close(int *p);
/*
* Defines.
*/
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*(x)))
/* Processing module implementation. */
const knot_layer_api_t *layer_static_module(void);
#define LAYER_STATIC layer_static_module()
/*! \brief Concatenate N strings. */
char* kr_strcatdup(unsigned n, ...);
package gostats
/*
#include "lib/layer.h"
#include "lib/module.h"
extern int gostats_begin(knot_layer_t *, void *);
extern int gostats_finish(knot_layer_t *);
static inline const knot_layer_api_t *_layer(void)
{
static const knot_layer_api_t _module = {
.begin = &gostats_begin,
.finish = &gostats_finish
};
return &_module;
}
*/
import "C"
import "unsafe"
import "fmt"
//export gostats_begin
func gostats_begin(ctx *C.knot_layer_t, param unsafe.Pointer) C.int {
fmt.Println("go_begin()")
return 0
}
//export gostats_finish
func gostats_finish(ctx *C.knot_layer_t) C.int {
fmt.Println("go_finish()")
return 0
}
//export gostats_layer
func gostats_layer() *C.knot_layer_api_t {
return C._layer()
}
\ No newline at end of file
hints_SOURCES := modules/gostats/gostats.g
gostats_DEPEND := libkresolve
gostats_LIBS := $(libkresolve_TARGET) $(libkresolve_LIBS)
$(call make_go_module,gostats)
\ No newline at end of file
......@@ -14,36 +14,43 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lib/layer/static.h"
#include "lib/module.h"
#include "lib/layer.h"
#define DEBUG_MSG(fmt...) QRDEBUG(NULL, "hint", fmt)
static int reset(knot_layer_t *ctx)
static int begin(knot_layer_t *ctx, void *module_param)
{
/* TODO: sync, cleanup after resolution */
ctx->data = module_param;
return ctx->state;
}
static int begin(knot_layer_t *ctx, void *param)
static int query(knot_layer_t *ctx, knot_pkt_t *pkt)
{
ctx->data = param;
return ctx->state;
}
/* TODO: read static hosts file */
/*
* Module implementation.
*/
return ctx->state;
const knot_layer_api_t *hints_layer(void)
{
static const knot_layer_api_t _layer = {
.begin = &begin,
.out = &query
};
return &_layer;
}
/*! \brief Module implementation. */
static const knot_layer_api_t LAYER_STATIC_MODULE = {
&begin,
&reset,
NULL,
NULL,
NULL,
NULL
};
const knot_layer_api_t *layer_static_module(void)
int hints_init(struct kr_module *module)
{
return &LAYER_STATIC_MODULE;
return 0;
}
int hints_deinit(struct kr_module *module)
{
return 0;
}
KR_MODULE_EXPORT(hints);
hints_SOURCES := modules/hints/hints.c
hints_DEPEND := libkresolve
hints_LIBS := $(libkresolve_TARGET) $(libkresolve_LIBS)
$(call make_c_module,hints)
\ No newline at end of file
# List of built-in modules
modules_TARGETS := hints
# Make C module
define make_c_module
$(eval $(call make_module,$(1),modules/$(1)))
endef
# Make Go module
define make_go_module
# TODO: compilable only with gccgo -shared
# go tool cgo -- $(CFLAGS) $$($(1)_SOURCES)
endef
# Build rules
modules: $(modules_TARGETS)
modules-clean: $(addsuffix -clean,$(modules_TARGETS))
modules-install: $(addsuffix -install,$(modules_TARGETS))
$(foreach module,$(modules_TARGETS),$(eval include modules/$(module)/$(module).mk))
\ No newline at end of file
......@@ -50,7 +50,8 @@ $(2)/$(1)$(3): $$($(1)_OBJ) $$($(1)_DEPEND)
$(1)-clean:
$(RM) $$($(1)_OBJ) $$($(1)_DEP) $(2)/$(1)$(3)
$(1)-install: $(2)/$(1)$(3)
$(INSTALL) $$^ $(PREFIX)/$(5)
$(INSTALL) -d $(PREFIX)/$(5)
$(INSTALL) $$^ $(PREFIX)/$(5)
ifneq ($$(strip $$($(1)_HEADERS)),)
$(INSTALL) -d $(PREFIX)/$(INCLUDEDIR)/$(1)
$(INSTALL) $$($(1)_HEADERS) $(PREFIX)/$(INCLUDEDIR)/$(1)
......@@ -61,7 +62,8 @@ endef
# Make targets (name,path)
make_bin = $(call make_target,$(1),$(2),$(BINEXT),,$(BINDIR))
make_lib = $(call make_target,$(1),$(2),$(LIBEXT),-$(LIBTYPE),$(LIBDIR))
make_module = $(call make_target,$(1),$(2),$(MODEXT),-$(MODTYPE),$(LIBDIR))
make_module = $(call make_target,$(1),$(2),$(LIBEXT),-$(LIBTYPE),$(MODULEDIR))
make_shared = $(call make_target,$(1),$(2),$(MODEXT),-$(MODTYPE),$(LIBDIR))