]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
modules impl.: avoid using custom trampolines
authorVladimír Čunát <vladimir.cunat@nic.cz>
Fri, 15 Mar 2019 16:14:58 +0000 (17:14 +0100)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Thu, 13 Jun 2019 13:02:44 +0000 (15:02 +0200)
...when making calls from lua to C modules; use luaJIT FFI instead.
This eliminates some cases of lua_pushlightuserdata().

.luacheckrc
daemon/engine.c
daemon/lua/kres-gen.lua
daemon/lua/kres-gen.sh
daemon/lua/sandbox.lua.in

index 885fa82205c52ec64a809f275423d5d325387a32..13d8989dad5756b35d273bad9f032af7430fcc86 100644 (file)
@@ -12,6 +12,7 @@ new_read_globals = {
        'quit',
        'resolve',
        'ta_update',
+       'fromjson',
        'todname',
        'tojson',
        'user',
index 99212e28a8d4aea983b6c834c5d750a5a1aa53c8..d3e6ac23d3bf6ce59a7fc3c3d2046d26b432d3b0 100644 (file)
@@ -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)
index c63c97163c52b1669139969841b9e498d68c464f..ade74e636dd704a2f2b43ebcd466f363d20ba4e8 100644 (file)
@@ -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);
index a5702c5d156634e488d60b52f38adbd6fd1377a7..5b8c55d29706e0fae3b84ceb32e5f5f1416a0f96 100755 (executable)
@@ -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\>/_owner/; s/\<ttl\>/_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 "
index b2b26c4312b755438b9ffc72c0645472bbe996ce..6d96b74a2c1c138665e1c0af791cf2124432865d 100644 (file)
@@ -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 }