From: Vladimír Čunát Date: Fri, 15 Mar 2019 16:14:58 +0000 (+0100) Subject: modules impl.: avoid using custom trampolines X-Git-Tag: v4.1.0~21^2~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2cf2ee778331efa5778ddaa5dd0c8512355ff26f;p=thirdparty%2Fknot-resolver.git modules impl.: avoid using custom trampolines ...when making calls from lua to C modules; use luaJIT FFI instead. This eliminates some cases of lua_pushlightuserdata(). --- diff --git a/.luacheckrc b/.luacheckrc index 885fa8220..13d8989da 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -12,6 +12,7 @@ new_read_globals = { 'quit', 'resolve', 'ta_update', + 'fromjson', 'todname', 'tojson', 'user', diff --git a/daemon/engine.c b/daemon/engine.c index 99212e28a..d3e6ac23d 100644 --- a/daemon/engine.c +++ b/daemon/engine.c @@ -78,13 +78,6 @@ const size_t CLEANUP_TIMER = 5*60*1000; * Global bindings. */ -/** Register module callback into Lua world. */ -#define REGISTER_MODULE_CALL(L, module, cb, name) do { \ - lua_pushlightuserdata((L), (module)); \ - lua_pushlightuserdata((L), (cb)); \ - lua_pushcclosure((L), l_trampoline, 2); \ - lua_setfield((L), -2, (name)); \ - } while (0) /** Print help and available commands. */ static int l_help(lua_State *L) @@ -449,53 +442,6 @@ static int l_map(lua_State *L) #undef expr_checked -/** Trampoline function for module properties. */ -static int l_trampoline(lua_State *L) -{ - struct kr_module *module = lua_touserdata(L, lua_upvalueindex(1)); - void* callback = lua_touserdata(L, lua_upvalueindex(2)); - struct engine *engine = engine_luaget(L); - if (!module) - lua_error_p(L, "module closure missing upvalue"); - - /* Now we only have property callback or config, - * if we expand the callables, we might need a callback_type. - */ - const char *args = NULL; - auto_free char *cleanup_args = NULL; - if (lua_gettop(L) > 0) { - if (lua_istable(L, 1) || lua_isboolean(L, 1)) { - cleanup_args = l_pack_json(L, 1); - args = cleanup_args; - } else { - args = lua_tostring(L, 1); - } - } - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* void* vs. function pointer */ - if (callback == module->config) { - module->config(module, args); - } else { - kr_prop_cb *prop = (kr_prop_cb *)callback; - auto_free char *ret = prop(engine, module, args); - if (!ret) { /* No results */ - return 0; - } - JsonNode *root_node = json_decode(ret); - if (root_node) { - l_unpack_json(L, root_node); - } else { - lua_pushstring(L, ret); - } - json_delete(root_node); - return 1; - } - #pragma GCC diagnostic pop - - /* No results */ - return 0; -} - /* * Engine API. */ @@ -807,37 +753,6 @@ void engine_stop(struct engine *engine) uv_stop(uv_default_loop()); } -/** Register module properties in Lua environment, if any. */ -static int register_properties(struct engine *engine, struct kr_module *module) -{ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" /* casts in lua_pushlightuserdata() */ - if (!module->config && !module->props) { - return kr_ok(); - } - lua_newtable(engine->L); - if (module->config != NULL) { - REGISTER_MODULE_CALL(engine->L, module, module->config, "config"); - } - - for (const struct kr_prop *p = module->props; p && p->name; ++p) { - if (p->cb != NULL) { - REGISTER_MODULE_CALL(engine->L, module, p->cb, p->name); - } - } - lua_setglobal(engine->L, module->name); - #pragma GCC diagnostic pop - - /* Register module in Lua env */ - lua_getglobal(engine->L, "modules_register"); - lua_getglobal(engine->L, module->name); - if (engine_pcall(engine->L, 1) != 0) { - lua_pop(engine->L, 1); - } - - return kr_ok(); -} - /** @internal Find matching module */ static size_t module_find(module_array_t *mod_list, const char *name) { @@ -874,7 +789,15 @@ int engine_register(struct engine *engine, const char *name, const char *precede return kr_error(ENOMEM); } module->data = engine; + /* TODO: tidy and comment this section. */ int ret = kr_module_load(module, name, LIBDIR "/kres_modules"); + if (ret == kr_ok()) { + lua_getglobal(engine->L, "modules_create_table_for_c"); + lua_pushpointer(engine->L, module); + if (engine_pcall(engine->L, 1) != 0) { + lua_pop(engine->L, 1); + } + } /* Load Lua module if not a binary */ if (ret == kr_error(ENOENT)) { ret = ffimodule_register_lua(engine, module, name); @@ -886,6 +809,7 @@ int engine_register(struct engine *engine, const char *name, const char *precede free(module); return ret; } + if (array_push(engine->modules, module) < 0) { engine_unload(engine, module); return kr_error(ENOMEM); @@ -908,7 +832,7 @@ int engine_register(struct engine *engine, const char *name, const char *precede } } - return register_properties(engine, module); + return kr_ok(); } int engine_unregister(struct engine *engine, const char *name) diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index c63c97163..ade74e636 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -38,6 +38,10 @@ typedef struct { knot_rdataset_t rrs; void *additional; } knot_rrset_t; + +struct kr_module; +typedef char *(kr_prop_cb)(void *, struct kr_module *, const char *); +struct kr_layer; typedef struct knot_pkt knot_pkt_t; typedef struct { uint8_t *ptr[15]; @@ -84,6 +88,7 @@ typedef struct { void *root; struct knot_mm *pool; } map_t; +typedef struct trie trie_t; struct kr_qflags { _Bool NO_MINIMIZE : 1; _Bool NO_THROTTLE : 1; @@ -142,7 +147,6 @@ typedef struct { size_t len; size_t cap; } ranked_rr_array_t; -typedef struct trie trie_t; struct kr_zonecut { knot_dname_t *name; knot_rrset_t *key; @@ -225,6 +229,34 @@ struct kr_cache { struct timeval checkpoint_walltime; uint64_t checkpoint_monotime; }; +typedef struct kr_layer kr_layer_t; +struct kr_layer_api { + int (*begin)(kr_layer_t *); + int (*reset)(kr_layer_t *); + int (*finish)(kr_layer_t *); + int (*consume)(kr_layer_t *, knot_pkt_t *); + int (*produce)(kr_layer_t *, knot_pkt_t *); + int (*checkout)(kr_layer_t *, knot_pkt_t *, struct sockaddr *, int); + int (*answer_finalize)(kr_layer_t *); + void *data; + int cb_slots[]; +}; +typedef struct kr_layer_api kr_layer_api_t; +struct kr_prop { + kr_prop_cb *cb; + const char *name; + const char *info; +}; +struct kr_module { + char *name; + int (*init)(struct kr_module *); + int (*deinit)(struct kr_module *); + int (*config)(struct kr_module *, const char *); + const kr_layer_api_t *layer; + const struct kr_prop *props; + void *lib; + void *data; +}; typedef int32_t (*kr_stale_cb)(int32_t ttl, const knot_dname_t *owner, uint16_t type, const struct kr_query *qry); diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index a5702c5d1..5b8c55d29 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -28,7 +28,7 @@ trap restore ERR INT TERM # - you need to have debugging symbols for knot-dns and knot-resolver; # you get those by compiling with -g; for knot-dns it might be enough # to just install it with debugging symbols included (in your distro way) -# - remove file ./kres-gen.lua and run make as usual +# - run ninja kres-gen # - the knot-dns libraries are found via pkg-config # - you also need gdb on $PATH @@ -73,6 +73,12 @@ genResType() { # No simple way to fixup this rename in ./kres.lua AFAIK. genResType "knot_rrset_t" | sed 's/\/_owner/; s/\/_ttl/' +printf " +struct kr_module; +typedef char *(kr_prop_cb)(void *, struct kr_module *, const char *); +struct kr_layer; +" + ${CDEFS} ${LIBKRES} types <<-EOF knot_pkt_t knot_edns_options_t @@ -80,15 +86,15 @@ ${CDEFS} ${LIBKRES} types <<-EOF struct knot_compr knot_compr_t struct knot_pkt - # generics + # lib/generic/ map_t + trie_t # libkres struct kr_qflags rr_array_t struct ranked_rr_array_entry ranked_rr_array_entry_t ranked_rr_array_t - trie_t struct kr_zonecut kr_qarray_t struct kr_rplan @@ -97,6 +103,13 @@ ${CDEFS} ${LIBKRES} types <<-EOF enum kr_rank struct kr_cdb_stats struct kr_cache + # lib/layer.h + kr_layer_t + struct kr_layer_api + kr_layer_api_t + # lib/module.h + struct kr_prop + struct kr_module EOF printf " diff --git a/daemon/lua/sandbox.lua.in b/daemon/lua/sandbox.lua.in index b2b26c431..6d96b74a2 100644 --- a/daemon/lua/sandbox.lua.in +++ b/daemon/lua/sandbox.lua.in @@ -171,6 +171,79 @@ setmetatable(modules, { end }) +-- Set up lua table for a C module. (Internal function.) +function modules_create_table_for_c(kr_module_ud) + local kr_module = ffi.cast('struct kr_module **', kr_module_ud)[0] + --- Set up the global table named according to the module. + if kr_module.config == nil and kr_module.props == nil then + return + end + local module = {} + _G[ffi.string(kr_module.name)] = module + + --- Construct lua functions for properties. + if kr_module.props ~= nil then + local i = 0 + while true do + local prop = kr_module.props[i] + local cb = prop.cb + if cb == nil then break; end + module[ffi.string(prop.name)] = + function (arg) -- lua wrapper around kr_prop_cb function typedef + local arg_conv + if type(arg) == 'table' or type(arg) == 'boolean' then + arg_conv = tojson(arg) + elseif arg ~= nil then + arg_conv = tostring(arg) + end + local ret_cstr = cb(__engine, kr_module, arg_conv) + if ret_cstr == nil then + return nil + end + -- LATER(optim.): superfluous copying + local ret_str = ffi.string(ret_cstr) + -- This is a bit ugly, but the API is that invalid JSON + -- should be just returned as string :-( + local status, ret = pcall(fromjson, ret_str) + if not status then ret = ret_str end + ffi.C.free(ret_cstr) + return ret + end + i = i + 1 + end + end + + --- Construct lua function for config(). + if kr_module.config ~= nil then + module.config = + function (arg) + local arg_conv + if type(arg) == 'table' or type(arg) == 'boolean' then + arg_conv = tojson(arg) + elseif arg ~= nil then + arg_conv = tostring(arg) + end + return kr_module.config(kr_module, arg_conv) + end + end + + --- Add syntactic sugar for get() and set() properties. + --- That also "catches" any commands like `moduleName.foo = bar`. + setmetatable(module, { + __index = function (t, k) + local v = rawget(t, k) + if v then return v + elseif rawget(t, 'get') then return t.get(k) + end + end, + __newindex = function (t, k, v) + local old_v = rawget(t, k) + if not old_v and rawget(t, 'set') then + t.set(k..' '..v) + end + end + }) +end cache.clear = function (name, exact_name, rr_type, chunk_size, callback, prev_state) if name == nil or (name == '.' and not exact_name) then @@ -277,25 +350,6 @@ setmetatable(cache, { end }) --- Register module in Lua environment -function modules_register(module) - -- Syntactic sugar for get() and set() properties - setmetatable(module, { - __index = function (t, k) - local v = rawget(t, k) - if v then return v - elseif rawget(t, 'get') then return t.get(k) - end - end, - __newindex = function (t, k, v) - local old_v = rawget(t, k) - if not old_v and rawget(t, 'set') then - t.set(k..' '..v) - end - end - }) -end - -- Make sandboxed environment local function make_sandbox(defined) local __protected = { worker = true, env = true, modules = true, cache = true, net = true, trust_anchors = true }