diff --git a/doc/Doxyfile b/doc/Doxyfile index 6bd9938f27da7e1e63062410c5ff58ce71152c9c..63a8dab7eb130e13527775262d54c721e92f1063 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -8,9 +8,12 @@ CASE_SENSE_NAMES = NO INPUT = ../lib FILE_PATTERNS = *.h QUIET = YES +RECURSIVE = YES JAVADOC_AUTOBRIEF = YES AUTOLINK_SUPPORT = YES XML_OUTPUT = doxyxml HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_CLASSES = YES OPTIMIZE_OUTPUT_FOR_C = YES +ENABLE_PREPROCESSING = YES +PREDEFINED = NDEBUG diff --git a/doc/conf.py b/doc/conf.py index 4efd14e5b9ec1a700154ce3ceded62604aee901f..7fdba137532ad5938e85cafbdc9af0f1a2952bf2 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -8,7 +8,7 @@ if os.environ.get('READTHEDOCS', None) == 'True': subprocess.call('doxygen') # Add any Sphinx extension module names here, as strings. -extensions = ['sphinx.ext.todo', 'breathe'] +extensions = ['sphinx.ext.todo', 'sphinx.ext.viewcode', 'breathe'] # Breathe configuration breathe_projects = { "libkresolve": "doxyxml" } diff --git a/doc/index.rst b/doc/index.rst index d5490698002522f498d499e6012291aab5f75fae..0d5221e2706b232b1ab2c434e7056acf50342e91 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -11,6 +11,7 @@ Modular architecture of the library keeps the core tiny and efficient, and provi build daemon lib + modules Indices and tables diff --git a/doc/lib.rst b/doc/lib.rst index 7b8687ace1c7247ae2e7057d88920368579aebda..80d38e39c0e9e7cba55e9e78e6d429e12c52f486 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -1,3 +1,5 @@ +.. _lib_index: + .. include:: ../lib/README.rst Library layout @@ -10,27 +12,39 @@ you an idea about the API and the library layout. Name resolution ~~~~~~~~~~~~~~~ +.. _lib_rplan: + Resolution plan ~~~~~~~~~~~~~~~ +.. _lib_cache: + Cache ~~~~~ +.. _lib_nameservers: + Nameservers ~~~~~~~~~~~ +.. _lib_modules: + Modules ~~~~~~~ Utilities ~~~~~~~~~ +.. _lib_api: + API reference ------------- .. doxygengroup:: resolution :project: libkresolve +.. _lib_api_rplan: + .. doxygengroup:: rplan :project: libkresolve diff --git a/doc/modules.rst b/doc/modules.rst new file mode 100644 index 0000000000000000000000000000000000000000..428e09530bf086240a421985f88bb2e87707ec5c --- /dev/null +++ b/doc/modules.rst @@ -0,0 +1,7 @@ +.. include:: ../modules/README.rst + +Implemented modules +------------------- + +.. include:: ../modules/hints/README.rst +.. include:: ../modules/cachectl/README.rst diff --git a/lib/layer.h b/lib/layer.h index 6bbcea30818238d43ddf8b0013f98e2057c4a789..c830401910bfab8bc5b99893db527461dd6c131f 100644 --- a/lib/layer.h +++ b/lib/layer.h @@ -16,7 +16,7 @@ #pragma once -/** \addtogroup modules +/** \addtogroup rplan * @{ */ @@ -37,8 +37,8 @@ struct kr_layer_param { knot_pkt_t *answer; }; -/** \internal Print a debug message related to resolution. */ #ifndef NDEBUG +/** @internal Print a debug message related to resolution. */ #define QRDEBUG(query, cls, fmt, ...) do { \ unsigned _ind = 0; \ for (struct kr_query *q = (query); q; q = q->parent, _ind += 2); \ diff --git a/lib/layer/iterate.h b/lib/layer/iterate.h index 67decc0174c92ca251b4211040e688fc513eed1b..489e15a16d684b310338fa754079e5982171e5e0 100644 --- a/lib/layer/iterate.h +++ b/lib/layer/iterate.h @@ -32,3 +32,6 @@ int rr_update_parent(const knot_rrset_t *rr, unsigned hint, struct kr_layer_para * @note When \a hint is KNOT_PF_FREE, RR is treated as a copy and answer takes its ownership. */ int rr_update_answer(const knot_rrset_t *rr, unsigned hint, struct kr_layer_param *param); + +/* Processing module implementation. */ +const knot_layer_api_t *iterate_layer(void); \ No newline at end of file diff --git a/modules/README.md b/modules/README.md deleted file mode 100644 index 8be5e8dae707b6b4a541d5317e1aef0a7c654907..0000000000000000000000000000000000000000 --- a/modules/README.md +++ /dev/null @@ -1,295 +0,0 @@ -# Knot DNS Resolver extensions - -The resolver [library][lib] leverages the [processing API][processing] from the libknot to separate packet processing code -into layers. In order to keep the core library sane and coverable, there are only two built-in layers: -the [iterator](lib/layer/iterate.c), and the [cache](lib/layer/itercache.c). The resolver context however can -load shared libraries on runtime, which allows us to build and register external modules as well. - -## Supported languages - -Currently modules written in C are supported. -There is also a rudimentary support for writing modules in Go — â‘´ the library has no native Go bindings, library is accessible using [CGO][cgo], ⑵ gc doesn't support building shared libraries, [GCCGO][gccgo] is required, ⑶ no coroutines and no garbage collecting thread, as the Go code is called from C threads. - -There is a plan for Lua scriptables, but it's not implemented yet. - -## Available services - -*Note* — This is only crash-course in the library internals, see the resolver [library][lib] documentation for the complete overview of the services. - -<a name="services"></a> - -The library offers following services: - -- [cache](lib/cache.h) - MVCC cache interface for retrieving/storing resource records. -- [rplan](lib/rplan.h) - Query resolution plan, a list of partial queries (with hierarchy) sent in order to satisfy original query. - This contains information about the queries, nameserver choice, timing information, answer and its class. -- [nsrep](lib/nsrep.h) - Reputation database of nameservers, this serves as an aid for nameserver choice. - -If you're going to publish a layer in your module, it's going to be called by the query resolution driver for each query, -so you're going to work with [`struct kr_layer_param`](lib/layer.h) as your per-query context. This structure contains pointers to -resolution context, resolution plan and also the final answer. You're likely to retrieve currently solved query from the query plan: - -```c -int consume(knot_layer_t *ctx, knot_pkt_t *pkt) -{ - struct kr_layer_param *param = ctx->data; - struct kr_query *query = kr_rplan_current(param->rplan); -} -``` - -This is only passive processing of the incoming answer. If you want to change the course of resolution, say satisfy a query from a local cache before the library issues a query to the nameserver, you can use states (see the [modules/hints](lib/layer/itercache.c) for example). - -```c -int produce(knot_layer_t *ctx, knot_pkt_t *pkt) -{ - struct kr_layer_param *param = ctx->data; - struct kr_query *cur = kr_rplan_current(param->rplan); - - /* Query can be satisfied locally. */ - if (can_satisfy(cur)) { - /* This flag makes the resolver move the query - * to the "resolved" list. */ - query->resolved = true; - return KNOT_STATE_DONE; - } - - /* Pass-through. */ - return ctx->state; -} -``` - -It is possible to not only act during the query resolution, but also to view the complete resolution plan afterwards. -This is useful for analysis-type tasks, or *"on-resolution"* hooks. - -```c -int finish(knot_layer_t *ctx) -{ - struct kr_layer_param *param = ctx->data; - struct kr_rplan *rplan = param->rplan; - - /* Print the query sequence with start time. */ - char qname_str[KNOT_DNAME_MAXLEN]; - struct kr_query *qry = NULL - WALK_LIST(qry, rplan->resolved) { - knot_dname_to_str(qname_str, qry->sname, sizeof(qname_str)); - printf("%s at %u\n", qname_str, qry->timestamp); - } - - return ctx->state; -} -``` - -## The anatomy of an extension - -A module is a shared library defining specific functions, here's an overview of the functions. - -*Note* — the [`lib/module.h`](lib/module.h) header documents the module loading and API. - -| C | Go | Returns | Params | Mandatory? | Version | Comment | -|-------------------|------------|---------------------|---------------|------------|---------|------------------------| -| `module_api()` | `Api()` | `uint32_t` | | âś“ | 0 | Implemented API | -| `module_init()` | `Init()` | `int` | `module` | âś• | 0 | Constructor | -| `module_deinit()` | `Deinit()` | `int` | `module` | âś• | 0 | Destructor | -| `module_config()` | `Config()` | `int` | `module, key` | âś• | 0 | Configuration callback | -| `module_layer()` | `Layer()` | `knot_layer_api_t*` | | âś• | 0 | Returns module layer | -| `module_props()` | `Props()` | `struct kr_prop*` | | âś• | 0 | Return NULL-terminated list of properties. | - -The `module_` corresponds to the module name, if the module name is `hints`, then the prefix for constructor would be `hints_init()`. -This doesn't apply for Go, as it for now always implements `main` and requires capitalized first letter in order to export its symbol. - -### How does the module get loaded - -The [resolution context](lib/context.h) holds loaded modules for current context. A module can be registered with `kr_context_register()`, which triggers module constructor *immediately* after the load. Module destructor is automatically called when the resolution context closes. - -If the module exports a layer implementation, it is automatically discovered by [resolver](lib/resolve.h) on resolution init and plugged in. The order in which the modules are registered corresponds to the call order of layers. - -### Writing a module in C - -As almost all the functions are optional, the minimal module looks like this: - -```c -#include "lib/module.h" - -/* Convenience macro to declare module API. */ -KR_MODULE_EXPORT(mymodule); -``` - -Let's define an observer thread for the module as well. It's going to be stub for the sake of brevity, -but you can for example create a condition, and notify the thread from query processing by declaring -module layer (see the [Available services](#services)). - -```c -static void* observe(void *arg) -{ - /* ... do some observing ... */ -} - -int mymodule_init(struct kr_module *module) -{ - /* Create a thread and start it in the background. */ - pthread_t thr_id; - int ret = pthread_create(&thr_id, NULL, &observe, NULL); - if (ret != 0) { - return kr_error(errno); - } - - /* Keep it in the thread */ - module->data = thr_id; - return kr_ok(); -} - -int mymodule_deinit(struct kr_module *module) -{ - /* ... signalize cancellation ... */ - void *res = NULL; - pthread_t thr_id = (pthread_t) module->data; - int ret = pthread_join(thr_id, res); - if (ret != 0) { - return kr_error(errno); - } - - return kr_ok(); -} -``` - -This example shows how a module can run in the background, this enables you to, for example, observe -and publish data about query resolution. - -### Writing a module in Go - -*Note* — At the moment only a limited subset of Go is supported. The reason is that the Go functions must run inside the goroutines, and *presume* the garbage collector and scheduler are running in the background. -[GCCGO][gccgo] compiler can build dynamic libraries, and also allow us to bootstrap basic Go runtime, including a trampoline to call Go functions. -The problem with the `layer()` and callbacks is that they're called from C threads, that Go runtime has no knowledge of. -Thus neither garbage collection or spawning routines can work. The solution could be to register C threads to Go runtime, -or have each module to run inside its world loop and use IPC instead of callbacks — alas neither is implemented at the moment, but may be in the future. - -The Go modules also use CGO to interface C resolver library, and to declare layers with function pointers, which are [not present in Go][golang-syntax]. Each module must be the `main` package, here's a minimal example: - -```go -package main - -/* -#include "lib/module.h" -*/ -import "C" -import "unsafe" - -func Api() C.uint32_t { - return C.KR_MODULE_API -} -``` - -In order to integrate with query processing, you have to declare a helper function with function pointers to the -the layer implementation. Since the code prefacing `import "C"` is expanded in headers, you need the `static inline` trick -to avoid multiple declarations. Here's how the preface looks like: - -```go -/* -#include "lib/module.h" -#include "lib/layer.h" - -//! Trampoline for Go callbacks, note that this is going to work -//! with ELF only, this is hopefully going to change in the future -extern int Begin(knot_layer_t *, void *) __asm__ ("main.Begin"); -extern int Finish(knot_layer_t *) __asm__ ("main.Finish"); -static inline const knot_layer_api_t *_gostats_layer(void) -{ - static const knot_layer_api_t api = { - .begin = &Begin, - .finish = &Finish - }; - return &api; -} -*/ -import "C" -import "unsafe" -import "fmt" -``` - -Now we can add the implementations for the `Begin` and `Finish` functions, and finalize the module: - -```go -func Begin(ctx *C.knot_layer_t, param unsafe.Pointer) C.int { - // Save the context - ctx.data = param - return 0 -} - -func Finish(ctx *C.knot_layer_t) C.int { - // Since the context is unsafe.Pointer, we need to cast it - var param *C.struct_kr_layer_param = (*C.struct_kr_layer_param)(ctx.data) - // Now we can use the C API as well - fmt.Printf("[go] resolved %d queries", C.list_size(¶m.rplan.resolved)) - return 0 -} - -func Layer() *C.knot_layer_api_t { - // Wrapping the inline trampoline function - return C._layer() -} -``` - -See the [CGO][cgo] for more information about type conversions and interoperability between the C/Go. - -### Configuring modules - -There is a callback `module_config()` but it's NOOP for now, as the configuration is not yet implemented. - -### Exposing module properties - -A module can offer NULL-terminated list of *properties*, each property is essentially a callable with free-form JSON input/output. -JSON was chosen as an interchangeable format that doesn't require any schema beforehand, so you can do two things - query the module properties -from external applications or between modules (i.e. `statistics` module can query `cache` module for memory usage). -JSON was chosen not because it's the most efficient protocol, but because it's easy to read and write and interface to outside world. -Here's an example how a module can expose its property: - -```c -static char* cached_size(struct kr_context *ctx, struct kr_module *module, const char *args) -{ - /* Parameters are ignored. */ - char *result = NULL; - namedb_txn_t txn; - int ret = kr_cache_txn_begin(ctx->cache, &txn, NAMEDB_RDONLY); - if (ret != 0) { - return NULL; - } - - /* For the sake of brevity... */ - asprintf(&result, "{ \"size\": %d }\n", kr_cache_count(&txn)); - - kr_cache_txn_abort(&txn); - return result; -} - -struct kr_prop *cached_props(void) -{ - static struct kr_prop prop_list[] = { - /* Callback, Name, Description */ - { &cached_size, "size", "Return number of cached records.", }, - { NULL, NULL, NULL } - }; - return prop_list; -} - -KR_MODULE_EXPORT(cached) - -``` - -Once you load the module, you can call the module property from the interactive console: - -```sh -$ kresolved -... -[system] started in interactive mode, type 'help' -> load cached -> cached.cached_size -{ "size": 53 } -``` - -*Note* — this relies on function pointers, so the same `static inline` trick as for the `Layer()` is required for C/Go. - -[lib]: lib/README.md -[processing]: https://gitlab.labs.nic.cz/labs/knot/tree/master/src/libknot/processing -[golang-syntax]: http://blog.golang.org/gos-declaration-syntax -[cgo]: http://golang.org/cmd/cgo/ -[gccgo]: https://golang.org/doc/install/gccgo diff --git a/modules/README.rst b/modules/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..e655fa8094eb52d5c6de8996326ea1cedb1117e1 --- /dev/null +++ b/modules/README.rst @@ -0,0 +1,307 @@ +Knot DNS Resolver extensions +============================ + +The resolver :ref:`library <lib_index>` leverages the `processing API`_ from the libknot to separate packet processing code +into layers. In order to keep the core library sane and coverable, there are only two built-in layers: +the :c:func:`iterate_layer`, and the :c:func:`itercache_layer`. The resolver context however can +load shared libraries on runtime, which allows us to build and register external modules as well. + +Supported languages +------------------- + +Currently modules written in C are supported. +There is also a rudimentary support for writing modules in Go |---| â‘´ the library has no native Go bindings, library is accessible using CGO_, ⑵ gc doesn't support building shared libraries, GCCGO_ is required, ⑶ no coroutines and no garbage collecting thread, as the Go code is called from C threads. + +There is a plan for Lua scriptables, but it's not implemented yet. + +Available services +------------------ + +*Note* |---| This is only crash-course in the library internals, see the resolver :ref:`library <lib_index>` documentation for the complete overview of the services. + +The library offers following services: + +- :ref:`Cache <lib_cache>` - MVCC cache interface for retrieving/storing resource records. +- :ref:`Resolution plan <lib_rplan>` - Query resolution plan, a list of partial queries (with hierarchy) sent in order to satisfy original query. This contains information about the queries, nameserver choice, timing information, answer and its class. +- :ref:`Nameservers <lib_nameservers>` - Reputation database of nameservers, this serves as an aid for nameserver choice. + +If you're going to publish a layer in your module, it's going to be called by the query resolution driver for each query, +so you're going to work with :ref:`struct kr_layer_param <lib_api_rplan>` as your per-query context. This structure contains pointers to +resolution context, resolution plan and also the final answer. You're likely to retrieve currently solved query from the query plan: + +.. code-block:: c + + int consume(knot_layer_t *ctx, knot_pkt_t *pkt) + { + struct kr_layer_param *param = ctx->data; + struct kr_query *query = kr_rplan_current(param->rplan); + } + +This is only passive processing of the incoming answer. If you want to change the course of resolution, say satisfy a query from a local cache before the library issues a query to the nameserver, you can use states (see the `Static hints`_ for example). + +.. code-block:: c + + int produce(knot_layer_t *ctx, knot_pkt_t *pkt) + { + struct kr_layer_param *param = ctx->data; + struct kr_query *cur = kr_rplan_current(param->rplan); + + /* Query can be satisfied locally. */ + if (can_satisfy(cur)) { + /* This flag makes the resolver move the query + * to the "resolved" list. */ + query->resolved = true; + return KNOT_STATE_DONE; + } + + /* Pass-through. */ + return ctx->state; + } + +It is possible to not only act during the query resolution, but also to view the complete resolution plan afterwards. +This is useful for analysis-type tasks, or *"on-resolution"* hooks. + +.. code-block:: c + + int finish(knot_layer_t *ctx) + { + struct kr_layer_param *param = ctx->data; + struct kr_rplan *rplan = param->rplan; + + /* Print the query sequence with start time. */ + char qname_str[KNOT_DNAME_MAXLEN]; + struct kr_query *qry = NULL + WALK_LIST(qry, rplan->resolved) { + knot_dname_to_str(qname_str, qry->sname, sizeof(qname_str)); + printf("%s at %u\n", qname_str, qry->timestamp); + } + + return ctx->state; + } + +The anatomy of an extension +--------------------------- + +A module is a shared library defining specific functions, here's an overview of the functions. + +*Note* |---| the :ref:`Modules <lib_modules>` header documents the module loading and API. + +.. csv-table:: + :header: "C", "Go", "Params", "Comment" + + "``X_api()`` [#]_", "``Api()``", "", "Implemented API (``uint32_t``)" + "``X_init()``", "``Init()``", "``module``", "Constructor" + "``X_deinit()``", "``Deinit()``", "``module, key``", "Destructor" + "``X_config()``", "``Config()``", "``module``", "Configuration" + "``X_layer()``", "``Layer()``", "", "Module layer" + "``X_props()``", "``Props()``", "", "NULL-terminated list of properties" + +.. [#] Mandatory symbol. + +The ``X_`` corresponds to the module name, if the module name is ``hints``, then the prefix for constructor would be ``hints_init()``. +This doesn't apply for Go, as it for now always implements `main` and requires capitalized first letter in order to export its symbol. + +How does the module get loaded +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The resolution context :c:type:`struct kr_context` holds loaded modules for current context. A module can be registered with :c:func:`kr_context_register`, which triggers module constructor *immediately* after the load. Module destructor is automatically called when the resolution context closes. + +If the module exports a layer implementation, it is automatically discovered by :c:func:`kr_resolver` on resolution init and plugged in. The order in which the modules are registered corresponds to the call order of layers. + +Writing a module in C +--------------------- + +As almost all the functions are optional, the minimal module looks like this: + +.. code-block:: c + + #include "lib/module.h" + /* Convenience macro to declare module API. */ + KR_MODULE_EXPORT(mymodule); + + +Let's define an observer thread for the module as well. It's going to be stub for the sake of brevity, +but you can for example create a condition, and notify the thread from query processing by declaring +module layer (see the `Available services`_). + +.. code-block:: c + + static void* observe(void *arg) + { + /* ... do some observing ... */ + } + + int mymodule_init(struct kr_module *module) + { + /* Create a thread and start it in the background. */ + pthread_t thr_id; + int ret = pthread_create(&thr_id, NULL, &observe, NULL); + if (ret != 0) { + return kr_error(errno); + } + + /* Keep it in the thread */ + module->data = thr_id; + return kr_ok(); + } + + int mymodule_deinit(struct kr_module *module) + { + /* ... signalize cancellation ... */ + void *res = NULL; + pthread_t thr_id = (pthread_t) module->data; + int ret = pthread_join(thr_id, res); + if (ret != 0) { + return kr_error(errno); + } + + return kr_ok(); + } + +This example shows how a module can run in the background, this enables you to, for example, observe +and publish data about query resolution. + +Writing a module in Go +---------------------- + +*Note* |---| At the moment only a limited subset of Go is supported. The reason is that the Go functions must run inside the goroutines, and *presume* the garbage collector and scheduler are running in the background. +`GCCGO`_ compiler can build dynamic libraries, and also allow us to bootstrap basic Go runtime, including a trampoline to call Go functions. +The problem with the ``layer()`` and callbacks is that they're called from C threads, that Go runtime has no knowledge of. +Thus neither garbage collection or spawning routines can work. The solution could be to register C threads to Go runtime, +or have each module to run inside its world loop and use IPC instead of callbacks |---| alas neither is implemented at the moment, but may be in the future. + +The Go modules also use CGO_ to interface C resolver library, and to declare layers with function pointers, which are `not present in Go`_. Each module must be the ``main`` package, here's a minimal example: + +.. code-block:: go + + package main + + /* + #include "lib/module.h" + */ + import "C" + import "unsafe" + + func Api() C.uint32_t { + return C.KR_MODULE_API + } + +In order to integrate with query processing, you have to declare a helper function with function pointers to the +the layer implementation. Since the code prefacing ``import "C"`` is expanded in headers, you need the `static inline` trick +to avoid multiple declarations. Here's how the preface looks like: + +.. code-block:: go + + /* + #include "lib/module.h" + #include "lib/layer.h" + + //! Trampoline for Go callbacks, note that this is going to work + //! with ELF only, this is hopefully going to change in the future + extern int Begin(knot_layer_t *, void *) __asm__ ("main.Begin"); + extern int Finish(knot_layer_t *) __asm__ ("main.Finish"); + static inline const knot_layer_api_t *_gostats_layer(void) + { + static const knot_layer_api_t api = { + .begin = &Begin, + .finish = &Finish + }; + return &api; + } + */ + import "C" + import "unsafe" + import "fmt" + +Now we can add the implementations for the ``Begin`` and ``Finish`` functions, and finalize the module: + +.. code-block:: go + + func Begin(ctx *C.knot_layer_t, param unsafe.Pointer) C.int { + // Save the context + ctx.data = param + return 0 + } + + func Finish(ctx *C.knot_layer_t) C.int { + // Since the context is unsafe.Pointer, we need to cast it + var param *C.struct_kr_layer_param = (*C.struct_kr_layer_param)(ctx.data) + // Now we can use the C API as well + fmt.Printf("[go] resolved %d queries", C.list_size(¶m.rplan.resolved)) + return 0 + } + + func Layer() *C.knot_layer_api_t { + // Wrapping the inline trampoline function + return C._layer() + } + +See the CGO_ for more information about type conversions and interoperability between the C/Go. + +Configuring modules +------------------- + +There is a callback ``X_config()`` but it's NOOP for now, as the configuration is not yet implemented. + +Exposing module properties +-------------------------- + +A module can offer NULL-terminated list of *properties*, each property is essentially a callable with free-form JSON input/output. +JSON was chosen as an interchangeable format that doesn't require any schema beforehand, so you can do two things - query the module properties +from external applications or between modules (i.e. `statistics` module can query `cache` module for memory usage). +JSON was chosen not because it's the most efficient protocol, but because it's easy to read and write and interface to outside world. +Here's an example how a module can expose its property: + +.. code-block:: c + + char* get_size(struct kr_context *ctx, struct kr_module *m, + const char *args) + { + /* Open read transaction */ + namedb_txn_t txn; + namedb_t *cache = ctx->cache; + int ret = kr_cache_txn_begin(cache, &txn, NAMEDB_RDONLY); + if (ret != 0) { + return NULL; + } + + /* Read item count */ + char *result = NULL; + const namedb_api_t *api = kr_cache_storage(); + asprintf(&result, "{ \"result\": %d }", api->count(&txn)); + kr_cache_txn_abort(&txn); + + return result; + } + + struct kr_prop *cache_props(void) + { + static struct kr_prop prop_list[] = { + /* Callback, Name, Description */ + {&get_size, "size", "Return number of records."}, + {NULL, NULL, NULL} + }; + return prop_list; + } + + KR_MODULE_EXPORT(cache) + +Once you load the module, you can call the module property from the interactive console: + +.. code-block:: bash + + $ kresolved + ... + [system] started in interactive mode, type 'help' + > load cached + > cached.cached_size + { "size": 53 } + +*Note* |---| this relies on function pointers, so the same ``static inline`` trick as for the ``Layer()`` is required for C/Go. + +.. _`processing API`: https://gitlab.labs.nic.cz/labs/knot/tree/master/src/libknot/processing +.. _`not present in Go`: http://blog.golang.org/gos-declaration-syntax +.. _CGO: http://golang.org/cmd/cgo/ +.. _GCCGO: https://golang.org/doc/install/gccgo + +.. |---| unicode:: U+02014 .. em dash diff --git a/modules/cachectl/README.rst b/modules/cachectl/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..ebf86466f7f5b53255143250e7e72fb7f845659c --- /dev/null +++ b/modules/cachectl/README.rst @@ -0,0 +1,23 @@ +Cache control +~~~~~~~~~~~~~ + +Module providing an interface to cache database, for inspection, manipulation and purging. + +Properties +.......... + +``get_size`` + Return number of cached records. + + :Input: N/A + :Output: ``{ size: int }`` +``prune`` + Prune expired/invalid records. + + :Input: N/A + :Output: ``{ pruned: int }`` +``clear`` + Clear all cache records. + + :Input: N/A + :Output: ``{ result: bool }`` diff --git a/modules/cachectl/cachectl.c b/modules/cachectl/cachectl.c index 18105ba8dbc91256e3ce8ab2de1f803e832f6ca2..92e7888f4a903dc060b350d2ad6a2f3a03a4d583 100644 --- a/modules/cachectl/cachectl.c +++ b/modules/cachectl/cachectl.c @@ -39,7 +39,7 @@ * Return number of cached records. * * Input: N/A - * Output: { result: [int] size } + * Output: { size: int } * */ static char* get_size(struct kr_context *ctx, struct kr_module *module, const char *args) @@ -51,7 +51,7 @@ static char* get_size(struct kr_context *ctx, struct kr_module *module, const ch namedb_txn_t txn; int ret = kr_cache_txn_begin(ctx->cache, &txn, NAMEDB_RDONLY); if (ret == 0) { - asprintf(&result, "{ \"result\": %d }", storage->count(&txn)); + asprintf(&result, "{ \"size\": %d }", storage->count(&txn)); kr_cache_txn_abort(&txn); } @@ -80,7 +80,7 @@ static int is_expired(struct kr_cache_rrset *rr, uint32_t drift) * Prune expired/invalid records. * * Input: N/A - * Output: { result: [int] nr_pruned } + * Output: { pruned: int } * */ static char* prune(struct kr_context *ctx, struct kr_module *module, const char *args) @@ -116,9 +116,9 @@ static char* prune(struct kr_context *ctx, struct kr_module *module, const char /* Commit and format result. */ char *result = NULL; if (kr_cache_txn_commit(&txn) != 0) { - asprintf(&result, "{ \"result\": %d, \"error\": \"%s\" }", pruned, knot_strerror(ret)); + asprintf(&result, "{ \"pruned\": %d, \"error\": \"%s\" }", pruned, knot_strerror(ret)); } else { - asprintf(&result, "{ \"result\": %d }", pruned); + asprintf(&result, "{ \"pruned\": %d }", pruned); } return result; @@ -128,7 +128,7 @@ static char* prune(struct kr_context *ctx, struct kr_module *module, const char * Clear all records. * * Input: N/A - * Output: { result: [bool] success } + * Output: { result: bool } * */ static char* clear(struct kr_context *ctx, struct kr_module *module, const char *args) diff --git a/modules/hints/README.rst b/modules/hints/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..6c6e9b966d9ffbfd7f650b0577e4c37427170756 --- /dev/null +++ b/modules/hints/README.rst @@ -0,0 +1,4 @@ +Static hints +~~~~~~~~~~~~ + +This is a module providing static hints from ``/etc/hosts``, document me. \ No newline at end of file