From: Vladimír Čunát Date: Thu, 4 Apr 2019 10:42:34 +0000 (+0200) Subject: lua module layers: passing from C to lua X-Git-Tag: v4.1.0~21^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ca31947f70cd95c84976363159d526b680ba9326;p=thirdparty%2Fknot-resolver.git lua module layers: passing from C to lua ... without light userdata and yet it should be efficient. Also with checkout layer, but I have no nice way of testing that one. --- diff --git a/daemon/engine.c b/daemon/engine.c index d3e6ac23d..036b2fe51 100644 --- a/daemon/engine.c +++ b/daemon/engine.c @@ -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) diff --git a/daemon/ffimodule.c b/daemon/ffimodule.c index 79ebf8c57..8c9ce72fd 100644 --- a/daemon/ffimodule.c +++ b/daemon/ffimodule.c @@ -18,8 +18,10 @@ #include #include +#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; diff --git a/daemon/ffimodule.h b/daemon/ffimodule.h index 92298f539..564fa991b 100644 --- a/daemon/ffimodule.h +++ b/daemon/ffimodule.h @@ -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); + diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index ade74e636..8cd5d974c 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -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 *); diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index 5b8c55d29..09bb36349 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -76,7 +76,6 @@ 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 @@ -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 diff --git a/daemon/lua/sandbox.lua.in b/daemon/lua/sandbox.lua.in index 0b1eebd93..6d9d87826 100644 --- a/daemon/lua/sandbox.lua.in +++ b/daemon/lua/sandbox.lua.in @@ -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 diff --git a/lib/layer.h b/lib/layer.h index 1292359b2..56f6bd15e 100644 --- a/lib/layer.h +++ b/lib/layer.h @@ -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. */