ffimodule.c 7.39 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*  Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
14
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
15 16 17 18
 */

#include <uv.h>

19
#include "daemon/engine.h"
20
#include "daemon/ffimodule.h"
21
#include "daemon/bindings.h"
22 23 24
#include "lib/module.h"
#include "lib/layer.h"

25 26 27 28 29 30
#if LUA_VERSION_NUM >= 502
#define l_resume(L, argc) lua_resume((L), NULL, (argc))
#else
#define l_resume(L, argc) lua_resume((L), (argc))
#endif

31 32 33 34 35 36 37 38 39 40 41 42
/** @internal Slots for layer callbacks.
  * Each slot ID corresponds to Lua reference in module API. */
enum {
	SLOT_begin = 0,
	SLOT_reset,
	SLOT_finish,
	SLOT_consume,
	SLOT_produce,
	SLOT_count
};
#define SLOT_size sizeof(int)

43 44 45
/** @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;
46
	lua_getglobal(L, module->name);
47
	lua_getfield(L, -1, call);
48
	lua_remove(L, -2);
49
	if (lua_isnil(L, -1)) {
50
		lua_pop(L, 1);
51 52
		return NULL;
	}
53 54 55 56 57 58 59 60
	lua_pushlightuserdata(L, module);
	return L;
}

/** @internal Continue with coroutine. */
static void l_ffi_resume_cb(uv_idle_t *check)
{
	lua_State *L = check->data;
61 62
	int status = l_resume(L, 0);
	if (status != LUA_YIELD) {
63 64 65
		uv_idle_stop(check); /* Stop coroutine */
		uv_close((uv_handle_t *)check, (uv_close_cb)free);
	}
66
	lua_pop(L, lua_gettop(L));
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
}

/** @internal Schedule deferred continuation. */
static int l_ffi_defer(lua_State *L)
{
	uv_idle_t *check = malloc(sizeof(*check));
	if (!check) {
		return kr_error(ENOMEM);
	}
	uv_idle_init(uv_default_loop(), check);
	check->data = L;
	return uv_idle_start(check, l_ffi_resume_cb);
}

/** @internal Helper for calling the entrypoint. */
static inline int l_ffi_call(lua_State *L, int argc)
{
84
	int status = lua_pcall(L, argc, 1, 0);
85
	if (status != 0) {
86
		fprintf(stderr, "error: %s\n", lua_tostring(L, -1));
87 88 89
		lua_pop(L, 1);
		return kr_error(EIO);
	}
90
	if (lua_isnumber(L, -1)) { /* Return code */
91
		status = lua_tonumber(L, -1);
92 93
	} else if (lua_isthread(L, -1)) { /* Continuations */
		status = l_ffi_defer(lua_tothread(L, -1));
94
	}
95
	lua_pop(L, 1);
96 97 98 99 100 101
	return status;
}

static int l_ffi_init(struct kr_module *module)
{
	lua_State *L = l_ffi_preface(module, "init");
102 103 104
	if (!L) {
		return 0;
	}
105 106 107
	return l_ffi_call(L, 1);
}

108 109 110 111 112 113 114
/** @internal Unregister layer callback reference from registry. */
#define LAYER_UNREGISTER(L, api, name) do { \
	int *cb_slot = (int *)((char *)api + sizeof(knot_layer_api_t)); \
	if (cb_slot[SLOT_ ## name] > 0) \
		luaL_unref(L, LUA_REGISTRYINDEX, cb_slot[SLOT_ ## name]); \
} while(0)

115 116
static int l_ffi_deinit(struct kr_module *module)
{
117 118 119 120 121
	/* Deinit the module in Lua (if possible) */
	int ret = 0;
	lua_State *L = module->lib;
	if (l_ffi_preface(module, "deinit")) {
		ret = l_ffi_call(L, 1);
122
	}
123 124
	/* Free the layer API wrapper (unconst it) */
	knot_layer_api_t* api = module->data;
125 126 127 128 129 130 131 132
	if (api) {
		LAYER_UNREGISTER(L, api, begin);
		LAYER_UNREGISTER(L, api, finish);
		LAYER_UNREGISTER(L, api, consume);
		LAYER_UNREGISTER(L, api, produce);
		LAYER_UNREGISTER(L, api, reset);
		free(api);
	}
133 134 135
	module->lib = NULL;
	return ret;
}
136
#undef LAYER_UNREGISTER
137 138

/** @internal Helper for retrieving layer Lua function by name. */
139 140
#define LAYER_FFI_CALL(ctx, slot) \
	int *cb_slot = (int *)((char *)(ctx)->api + sizeof(knot_layer_api_t)); \
141 142 143
	if (cb_slot[SLOT_ ## slot] <= 0) { \
		return ctx->state; \
	} \
144 145
	struct kr_module *module = (ctx)->api->data; \
	lua_State *L = module->lib; \
146
	lua_rawgeti(L, LUA_REGISTRYINDEX, cb_slot[SLOT_ ## slot]); \
147 148
	lua_pushnumber(L, ctx->state)

149 150
static int l_ffi_layer_begin(knot_layer_t *ctx, void *module_param)
{
151
	LAYER_FFI_CALL(ctx, begin);
152
	lua_pushlightuserdata(L, ctx->data);
153
	return l_ffi_call(L, 2);
154 155 156 157
}

static int l_ffi_layer_reset(knot_layer_t *ctx)
{
158
	LAYER_FFI_CALL(ctx, reset);
159
	lua_pushlightuserdata(L, ctx->data);
160
	return l_ffi_call(L, 2);
161 162 163 164
}

static int l_ffi_layer_finish(knot_layer_t *ctx)
{
165
	struct kr_request *req = ctx->data;
166
	LAYER_FFI_CALL(ctx, finish);
167 168 169
	lua_pushlightuserdata(L, req);
	lua_pushlightuserdata(L, req->answer);
	return l_ffi_call(L, 3);
170 171 172 173
}

static int l_ffi_layer_consume(knot_layer_t *ctx, knot_pkt_t *pkt)
{
174 175 176
	if (ctx->state & KNOT_STATE_FAIL) {
		return ctx->state; /* Already failed, skip */
	}
177
	LAYER_FFI_CALL(ctx, consume);
178
	lua_pushlightuserdata(L, ctx->data);
179
	lua_pushlightuserdata(L, pkt);
180
	return l_ffi_call(L, 3);
181 182 183 184
}

static int l_ffi_layer_produce(knot_layer_t *ctx, knot_pkt_t *pkt)
{
185
	if (ctx->state & (KNOT_STATE_FAIL)) {
186
		return ctx->state; /* Already failed or done, skip */
187
	}
188
	LAYER_FFI_CALL(ctx, produce);
189
	lua_pushlightuserdata(L, ctx->data);
190
	lua_pushlightuserdata(L, pkt);
191
	return l_ffi_call(L, 3);
192
}
193
#undef LAYER_FFI_CALL
194

195 196
/** @internal Conditionally register layer trampoline
  * @warning Expects 'module.layer' to be on top of Lua stack. */
197
#define LAYER_REGISTER(L, api, name) do { \
198
	int *cb_slot = (int *)((char *)api + sizeof(knot_layer_api_t)); \
199
	lua_getfield((L), -1, #name); \
200 201
	if (!lua_isnil((L), -1)) { \
		(api)->name = l_ffi_layer_ ## name; \
202
		cb_slot[SLOT_ ## name] = luaL_ref((L), LUA_REGISTRYINDEX); \
203 204 205
	} else { \
		lua_pop((L), 1); \
	} \
206 207
} while(0)

208 209
/** @internal Create C layer api wrapper. */
static knot_layer_api_t *l_ffi_layer_create(lua_State *L, struct kr_module *module)
210
{
211 212 213 214 215 216 217 218 219 220 221 222 223 224
	/* Fabricate layer API wrapping the Lua functions
	 * reserve slots after it for references to Lua callbacks. */
	const size_t api_length = sizeof(knot_layer_api_t) + (SLOT_count * SLOT_size);
	knot_layer_api_t *api = malloc(api_length);
	if (api) {
		memset(api, 0, api_length);
		LAYER_REGISTER(L, api, begin);
		LAYER_REGISTER(L, api, finish);
		LAYER_REGISTER(L, api, consume);
		LAYER_REGISTER(L, api, produce);
		LAYER_REGISTER(L, api, reset);
		/* Begin is always set, as it initializes layer baton. */
		api->begin = l_ffi_layer_begin;
		api->data = module;
225
	}
226
	return api;
227 228
}

229 230 231 232 233 234 235 236 237
/** @internal Retrieve C layer api wrapper. */
static const knot_layer_api_t *l_ffi_layer(struct kr_module *module)
{
	if (module) {
		return (const knot_layer_api_t *)module->data;
	}
	return NULL;
}
#undef LAYER_REGISTER
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255

int ffimodule_register_lua(struct engine *engine, struct kr_module *module, const char *name)
{
	/* Register module in Lua */
	lua_State *L = engine->L;
	lua_getglobal(L, "require");
	lua_pushstring(L, name);
	if (lua_pcall(L, 1, LUA_MULTRET, 0) != 0) {
		fprintf(stderr, "error: %s\n", lua_tostring(L, -1));
		lua_pop(L, 1);
		return kr_error(ENOENT);
	}
	lua_setglobal(L, name);
	lua_getglobal(L, name);

	/* Create FFI module with trampolined functions. */
	memset(module, 0, sizeof(*module));
	module->name = strdup(name);
256 257
	module->init = &l_ffi_init;
	module->deinit = &l_ffi_deinit;
258 259 260 261 262 263
	/* Bake layer API if defined in module */
	lua_getfield(L, -1, "layer");
	if (!lua_isnil(L, -1)) {
		module->layer = &l_ffi_layer;
		module->data = l_ffi_layer_create(L, module);
	}
264
	module->lib = L;
265
	lua_pop(L, 2); /* Clear the layer + module global */
266 267 268 269
	if (module->init) {
		return module->init(module);
	}
	return kr_ok();
270
}