Modules API reference¶
Supported languages¶
Currently modules written in C and Lua(JIT) are supported.
The anatomy of an extension¶
A module is a shared object or script defining specific functions/fields; here’s an overview.
| C | Lua | Params | Comment |
|---|---|---|---|
X_api()[1] | API version | ||
X_init() | X.init() | module | Constructor |
X_deinit() | X.deinit() | module | Destructor |
X_config() | X.config() | module,str | Configuration |
X_layer | X.layer | Module layer | |
X_props | List of properties |
| [1] | Mandatory symbol; defined by usingKR_MODULE_EXPORT(). |
TheX corresponds to the module name; if the module name ishints, the prefix for constructor would behints_init().More details are in docs for thekr_module andkr_layer_api structures.
Note
The modules get ordered – by default in the same as the order in which they were loaded. The loading command can specify where in the order the module should be positioned.
Writing a module in Lua¶
The probably most convenient way of writing modules is Lua since you can use already installed modulesfrom system and have first-class access to the scripting engine. You can also tap to all the events, thatthe C API has access to, but keep in mind that transitioning from the C to Lua function is slower thanthe other way round, especially when JIT-compilation is taken into account.
Note
The Lua functions retrieve an additional first parameter compared to the C counterparts - a “state”.Most useful C functions and structures have lua FFI wrappers, sometimes with extra sugar.
The modules follow theLua way, where the module interface is returned in a named table.
--- @module Count incoming querieslocalcounter={}functioncounter.init(module)counter.total=0counter.last=0counter.failed=0endfunctioncounter.deinit(module)print('counted',counter.total,'queries')end-- @function Run the q/s counter with given interval.functioncounter.config(conf)-- We can use the scripting facilities hereifcounter.evthenevent.cancel(counter.ev)event.recurrent(conf.interval,function()print(counter.total-counter.last,'q/s')counter.last=counter.totalend)endreturncounter
The created module can be then loaded just like any other module, except it isn’t very useful since itdoesn’t provide any layer to capture events. The Lua module can however provide a processing layer, justlike its C counterpart.
-- Notice it isn't a function, but a table of functionscounter.layer={begin=function(state,data)counter.total=counter.total+1returnstateend,finish=function(state,req,answer)ifstate==kres.FAILthencounter.failed=counter.failed+1endreturnstateend}
There is currently an additional “feature” in comparison to C layer functions:some functions do not get called at all ifstate==kres.FAIL;see docs for details:kr_layer_api.
Since the modules are like any other Lua modules, you can interact with them through the CLI and and any interface.
Tip
Module discovery:kres_modules. is prepended to the module name and lua search path is used on that.
Writing a module in C¶
As almost all the functions are optional, the minimal module looks like this:
#include"lib/module.h"/* Convenience macro to declare module ABI. */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 declaringmodule layer (see theWriting layers).
staticvoid*observe(void*arg){/* ... do some observing ... */}intmymodule_init(structkr_module*module){/* Create a thread and start it in the background. */pthread_tthr_id;intret=pthread_create(&thr_id,NULL,&observe,NULL);if(ret!=0){returnkr_error(errno);}/* Keep it in the thread */module->data=thr_id;returnkr_ok();}intmymodule_deinit(structkr_module*module){/* ... signalize cancellation ... */void*res=NULL;pthread_tthr_id=(pthread_t)module->data;intret=pthread_join(thr_id,res);if(ret!=0){returnkr_error(errno);}returnkr_ok();}
This example shows how a module can run in the background, this enables you to, for example, observeand publish data about query resolution.
Configuring modules¶
There is a callbackX_config() that you can implement, see hints module.
Exposing C module properties¶
A module can offer NULL-terminated list ofproperties, 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 propertiesfrom external applications or between modules (e.g.statistics module can querycache 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.
Note
Thevoid*env is a generic module interface. Since we’re implementing daemon modules, the pointer can be cast tostructengine*.This is guaranteed by the implemented API version (seeWriting a module in C).
Here’s an example how a module can expose its property:
char*get_size(void*env,structkr_module*m,constchar*args){/* Get cache from engine. */structengine*engine=env;structkr_cache*cache=&engine->resolver.cache;/* Read item count */intcount=(cache->api)->count(cache->db);char*result=NULL;asprintf(&result,"{\"result\": %d }",count);returnresult;}structkr_prop*cache_props(void){staticstructkr_propprop_list[]={/* Callback, Name, Description */{&get_size,"get_size","Return number of records."},{NULL,NULL,NULL}};returnprop_list;}KR_MODULE_EXPORT(cache)
Once you load the module, you can call the module property from the interactive console.Note: the JSON output will be transparently converted to Lua tables.
$ kresd...[system] started in interactive mode,type'help()'> modules.load('cached')> cached.get_size()[size]=>53
Special properties¶
If the module declares propertiesget orset, they can be used in the Lua interpreter asregular tables.