]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lua module layers: passing from C to lua
authorVladimír Čunát <vladimir.cunat@nic.cz>
Thu, 4 Apr 2019 10:42:34 +0000 (12:42 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Thu, 13 Jun 2019 13:03:14 +0000 (15:03 +0200)
... without light userdata and yet it should be efficient.
Also with checkout layer, but I have no nice way of testing that one.

daemon/engine.c
daemon/ffimodule.c
daemon/ffimodule.h
daemon/lua/kres-gen.lua
daemon/lua/kres-gen.sh
daemon/lua/sandbox.lua.in
lib/layer.h

index d3e6ac23d3bf6ce59a7fc3c3d2046d26b432d3b0..036b2fe517f9da9881760b7876cc05dbfe7bb66a 100644 (file)
@@ -626,6 +626,10 @@ void engine_deinit(struct engine *engine)
        if (engine == NULL) {
                return;
        }
+       if (!engine->L) {
+               assert(false);
+               return;
+       }
        /* Only close sockets and services; no need to clean up mempool. */
 
        /* Network deinit is split up.  We first need to stop listening,
@@ -648,6 +652,7 @@ void engine_deinit(struct engine *engine)
        lru_free(engine->resolver.cache_cookie);
 
        network_deinit(&engine->net);
+       ffimodule_deinit(engine->L);
        lua_close(engine->L);
 
        /* Free data structures */
@@ -702,13 +707,14 @@ int engine_ipc(struct engine *engine, const char *expr)
 int engine_load_sandbox(struct engine *engine)
 {
        /* Init environment */
-    int ret = l_dosandboxfile(engine->L, LIBDIR "/sandbox.lua");
+       int ret = l_dosandboxfile(engine->L, LIBDIR "/sandbox.lua");
        if (ret != 0) {
                fprintf(stderr, "[system] error %s\n", lua_tostring(engine->L, -1));
                lua_pop(engine->L, 1);
                return kr_error(ENOEXEC);
        }
-       return kr_ok();
+       ret = ffimodule_init(engine->L);
+       return ret;
 }
 
 int engine_loadconf(struct engine *engine, const char *config_path)
index 79ebf8c57f4685b173d00c7ecc4815032ac8870f..8c9ce72fdf4c7dffb2449f9b788d3dea8f4e2ca0 100644 (file)
 #include <lua.h>
 #include <lauxlib.h>
 
+#include "daemon/bindings/impl.h"
 #include "daemon/engine.h"
 #include "daemon/ffimodule.h"
+#include "daemon/worker.h"
 #include "lib/module.h"
 #include "lib/layer.h"
 
@@ -43,6 +45,9 @@ enum {
 };
 #define SLOT_size sizeof(int)
 
+/** Lua registry indices for functions that wrap layer callbacks (shared by all lua modules). */
+static int l_ffi_wrap_slots[SLOT_count] = { 0 };
+
 /** @internal Helper for retrieving the right function entrypoint. */
 static inline lua_State *l_ffi_preface(struct kr_module *module, const char *call) {
        lua_State *L = module->lib;
@@ -142,36 +147,32 @@ static int l_ffi_deinit(struct kr_module *module)
 
 /** @internal Helper for retrieving layer Lua function by name. */
 #define LAYER_FFI_CALL(ctx, slot_name) \
-       const int *cb_slot = (ctx)->api->cb_slots + SLOT_ ## slot_name; \
-       if (*cb_slot <= 0) { \
-               return (ctx)->state; \
-       } \
-       struct kr_module *module = (ctx)->api->data; \
-       lua_State *L = module->lib; \
-       lua_rawgeti(L, LUA_REGISTRYINDEX, *cb_slot); \
-       lua_pushnumber(L, (ctx)->state)
+       (ctx)->pkt = NULL; /* for debugging */ \
+       const int wrap_slot = l_ffi_wrap_slots[SLOT_ ## slot_name]; \
+       const int cb_slot = (ctx)->api->cb_slots[SLOT_ ## slot_name]; \
+       assert(wrap_slot > 0 && cb_slot > 0); \
+       lua_State *L = the_worker->engine->L; \
+       lua_rawgeti(L, LUA_REGISTRYINDEX, wrap_slot); \
+       lua_rawgeti(L, LUA_REGISTRYINDEX, cb_slot); \
+       lua_pushpointer(L, (ctx));
 
 static int l_ffi_layer_begin(kr_layer_t *ctx)
 {
        LAYER_FFI_CALL(ctx, begin);
-       lua_pushlightuserdata(L, ctx->req);
        return l_ffi_call_layer(L, 2);
 }
 
 static int l_ffi_layer_reset(kr_layer_t *ctx)
 {
        LAYER_FFI_CALL(ctx, reset);
-       lua_pushlightuserdata(L, ctx->req);
        return l_ffi_call_layer(L, 2);
 }
 
 static int l_ffi_layer_finish(kr_layer_t *ctx)
 {
-       struct kr_request *req = ctx->req;
        LAYER_FFI_CALL(ctx, finish);
-       lua_pushlightuserdata(L, req);
-       lua_pushlightuserdata(L, req->answer);
-       return l_ffi_call_layer(L, 3);
+       ctx->pkt = ctx->req->answer;
+       return l_ffi_call_layer(L, 2);
 }
 
 static int l_ffi_layer_consume(kr_layer_t *ctx, knot_pkt_t *pkt)
@@ -180,43 +181,80 @@ static int l_ffi_layer_consume(kr_layer_t *ctx, knot_pkt_t *pkt)
                return ctx->state; /* Already failed, skip */
        }
        LAYER_FFI_CALL(ctx, consume);
-       lua_pushlightuserdata(L, ctx->req);
-       lua_pushlightuserdata(L, pkt);
-       return l_ffi_call_layer(L, 3);
+       ctx->pkt = pkt;
+       return l_ffi_call_layer(L, 2);
 }
 
 static int l_ffi_layer_produce(kr_layer_t *ctx, knot_pkt_t *pkt)
 {
-       if (ctx->state & (KR_STATE_FAIL)) {
-               return ctx->state; /* Already failed or done, skip */
+       if (ctx->state & KR_STATE_FAIL) {
+               return ctx->state; /* Already failed, skip */
        }
        LAYER_FFI_CALL(ctx, produce);
-       lua_pushlightuserdata(L, ctx->req);
-       lua_pushlightuserdata(L, pkt);
-       return l_ffi_call_layer(L, 3);
+       ctx->pkt = pkt;
+       return l_ffi_call_layer(L, 2);
 }
 
 static int l_ffi_layer_checkout(kr_layer_t *ctx, knot_pkt_t *pkt, struct sockaddr *dst, int type)
 {
-       if (ctx->state & (KR_STATE_FAIL)) {
-               return ctx->state; /* Already failed or done, skip */
+       if (ctx->state & KR_STATE_FAIL) {
+               return ctx->state; /* Already failed, skip */
        }
        LAYER_FFI_CALL(ctx, checkout);
-       lua_pushlightuserdata(L, ctx->req);
-       lua_pushlightuserdata(L, pkt);
-       lua_pushlightuserdata(L, dst);
-       lua_pushboolean(L, type == SOCK_STREAM);
-       return l_ffi_call_layer(L, 5);
+       ctx->pkt = pkt;
+       ctx->dst = dst;
+       ctx->is_stream = (type == SOCK_STREAM);
+       return l_ffi_call_layer(L, 2);
 }
 
 static int l_ffi_layer_answer_finalize(kr_layer_t *ctx)
 {
        LAYER_FFI_CALL(ctx, answer_finalize);
-       lua_pushlightuserdata(L, ctx->req);
        return l_ffi_call_layer(L, 2);
 }
 #undef LAYER_FFI_CALL
 
+int ffimodule_init(lua_State *L)
+{
+       /* Wrappers defined in ./lua/sandbox.lua */
+       /* for API: (int state, kr_request_t *req) */
+       lua_getglobal(L, "modules_ffi_layer_wrap1");
+       const int wrap1 = luaL_ref(L, LUA_REGISTRYINDEX);
+       /* for API: (int state, kr_request_t *req, knot_pkt_t *) */
+       lua_getglobal(L, "modules_ffi_layer_wrap2");
+       const int wrap2 = luaL_ref(L, LUA_REGISTRYINDEX);
+       lua_getglobal(L, "modules_ffi_layer_wrap_checkout");
+       const int wrap_checkout = luaL_ref(L, LUA_REGISTRYINDEX);
+       if (wrap1 == LUA_REFNIL || wrap2 == LUA_REFNIL || wrap_checkout == LUA_REFNIL) {
+               return kr_error(ENOENT);
+       }
+
+       const int slots[SLOT_count] = {
+               [SLOT_begin]   = wrap1,
+               [SLOT_reset]   = wrap1,
+               [SLOT_finish]  = wrap2,
+               [SLOT_consume] = wrap2,
+               [SLOT_produce] = wrap2,
+               [SLOT_checkout] = wrap_checkout,
+               [SLOT_answer_finalize] = wrap1,
+       };
+       memcpy(l_ffi_wrap_slots, slots, sizeof(l_ffi_wrap_slots));
+       return kr_ok();
+}
+void ffimodule_deinit(lua_State *L)
+{
+       /* Unref each wrapper function from lua.
+        * It's probably useless, as we're about to destroy lua_State, but... */
+       const int wrapsIndices[] = {
+               SLOT_begin,
+               SLOT_consume,
+               SLOT_checkout,
+       };
+       for (int i = 0; i < sizeof(wrapsIndices) / sizeof(wrapsIndices[0]); ++i) {
+               luaL_unref(L, LUA_REGISTRYINDEX, l_ffi_wrap_slots[wrapsIndices[i]]);
+       }
+}
+
 /** @internal Conditionally register layer trampoline
   * @warning Expects 'module.layer' to be on top of Lua stack. */
 #define LAYER_REGISTER(L, api, name) do { \
@@ -247,8 +285,6 @@ static kr_layer_api_t *l_ffi_layer_create(lua_State *L, struct kr_module *module
                LAYER_REGISTER(L, api, checkout);
                LAYER_REGISTER(L, api, answer_finalize);
                LAYER_REGISTER(L, api, reset);
-               /* Begin is always set, as it initializes layer baton. */
-               api->begin = l_ffi_layer_begin;
                api->data = module;
        }
        return api;
index 92298f539c40f0d571d17ea4267bf5714554f6ff..564fa991b890270d384e0ef0217a14e9647d727a 100644 (file)
@@ -31,3 +31,7 @@
  * @return        0 or an error
  */
 int ffimodule_register_lua(struct engine *engine, struct kr_module *module, const char *name);
+
+int ffimodule_init(lua_State *L);
+void ffimodule_deinit(lua_State *L);
+
index ade74e636dd704a2f2b43ebcd466f363d20ba4e8..8cd5d974c46a77396c46f21c6d0f81a833342c84 100644 (file)
@@ -41,7 +41,6 @@ typedef struct {
 
 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];
@@ -229,6 +228,14 @@ struct kr_cache {
        struct timeval checkpoint_walltime;
        uint64_t checkpoint_monotime;
 };
+struct kr_layer {
+       int state;
+       struct kr_request *req;
+       const struct kr_layer_api *api;
+       knot_pkt_t *pkt;
+       struct sockaddr *dst;
+       _Bool is_stream;
+};
 typedef struct kr_layer kr_layer_t;
 struct kr_layer_api {
        int (*begin)(kr_layer_t *);
index 5b8c55d29706e0fae3b84ceb32e5f5f1416a0f96..09bb3634985f90560f96f1a85403d0d7256b1fde 100755 (executable)
@@ -76,7 +76,6 @@ 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
@@ -104,6 +103,7 @@ ${CDEFS} ${LIBKRES} types <<-EOF
        struct kr_cdb_stats
        struct kr_cache
        # lib/layer.h
+       struct kr_layer
        kr_layer_t
        struct kr_layer_api
        kr_layer_api_t
index 0b1eebd93fdbaa15a736fcfb22939c812e521b29..6d9d87826f54fa25708cd0330fe89514a022f70d 100644 (file)
@@ -260,6 +260,20 @@ function modules_create_table_for_c(kr_module_ud)
        })
 end
 
+-- Utilities internal for lua layer glue; see ../ffimodule.c
+modules_ffi_layer_wrap1 = function (layer_cb, ctx_udata)
+       local ctx = ffi.cast('kr_layer_t **', ctx_udata)[0]
+       return layer_cb(ctx.state, ctx.req)
+end
+modules_ffi_layer_wrap2 = function (layer_cb, ctx_udata)
+       local ctx = ffi.cast('kr_layer_t **', ctx_udata)[0]
+       return layer_cb(ctx.state, ctx.req, ctx.pkt)
+end
+modules_ffi_layer_wrap_checkout = function (layer_cb, ctx_udata)
+       local ctx = ffi.cast('kr_layer_t **', ctx_udata)[0]
+       return layer_cb(ctx.state, ctx.req, ctx.pkt, ctx.dst, ctx.is_stream)
+end
+
 cache.clear = function (name, exact_name, rr_type, chunk_size, callback, prev_state)
        if name == nil or (name == '.' and not exact_name) then
                -- keep same output format as for 'standard' clear
index 1292359b26ab9b5b79d8cef750fbecc78a240510..56f6bd15e285cf34edef27588ce64e1d646e66e5 100644 (file)
@@ -62,6 +62,9 @@ typedef struct kr_layer {
        int state; /*!< The current state; bitmap of enum kr_layer_state. */
        struct kr_request *req; /*!< The corresponding request. */
        const struct kr_layer_api *api;
+       knot_pkt_t *pkt; /*!< In glue for lua kr_layer_api it's used to pass the parameter. */
+       struct sockaddr *dst; /*!< In glue for checkout layer it's used to pass the parameter. */
+       bool is_stream;       /*!< In glue for checkout layer it's used to pass the parameter. */
 } kr_layer_t;
 
 /** Packet processing module API.  All functions return the new kr_layer_state. */
@@ -74,22 +77,26 @@ struct kr_layer_api {
        /** Paired to begin, called both on successes and failures. */
        int (*finish)(kr_layer_t *ctx);
 
-       /** Processing an answer from upstream or the answer to the request. */
+       /** Processing an answer from upstream or the answer to the request.
+        * Lua API: call is omitted iff (state & KR_STATE_FAIL). */
        int (*consume)(kr_layer_t *ctx, knot_pkt_t *pkt);
 
-       /** Produce either an answer to the request or a query for upstream (or fail). */
+       /** Produce either an answer to the request or a query for upstream (or fail).
+        * Lua API: call is omitted iff (state & KR_STATE_FAIL). */
        int (*produce)(kr_layer_t *ctx, knot_pkt_t *pkt);
 
        /** Finalises the outbound query packet with the knowledge of the IP addresses.
         * The checkout layer doesn't persist the state, so canceled subrequests
-        * don't affect the resolution or rest of the processing. */
+        * don't affect the resolution or rest of the processing.
+        * Lua API: call is omitted iff (state & KR_STATE_FAIL). */
        int (*checkout)(kr_layer_t *ctx, knot_pkt_t *packet, struct sockaddr *dst, int type);
 
        /** Finalises the answer.
         * Last chance to affect what will get into the answer, including EDNS.*/
        int (*answer_finalize)(kr_layer_t *ctx);
 
-       /** The module can store anything in here. */
+       /** The module can store anything in here.
+        * In lua case we store kr_module pointer. */
        void *data;
 
        /** Internal to ./daemon/ffimodule.c. */