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

    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
/** @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,
39
	SLOT_checkout,
40
	SLOT_answer_finalize,
41
	SLOT_count /* dummy, must be the last */
42 43 44
};
#define SLOT_size sizeof(int)

45 46 47
/** @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;
48
	lua_getglobal(L, module->name);
49
	lua_getfield(L, -1, call);
50
	lua_remove(L, -2);
51
	if (lua_isnil(L, -1)) {
52
		lua_pop(L, 1);
53 54
		return NULL;
	}
55 56 57 58 59 60 61 62
	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;
63 64
	int status = l_resume(L, 0);
	if (status != LUA_YIELD) {
65 66 67
		uv_idle_stop(check); /* Stop coroutine */
		uv_close((uv_handle_t *)check, (uv_close_cb)free);
	}
68
	lua_pop(L, lua_gettop(L));
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
}

/** @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)
{
86
	int status = lua_pcall(L, argc, 1, 0);
87
	if (status != 0) {
88
		fprintf(stderr, "error: %s\n", lua_tostring(L, -1));
89 90 91
		lua_pop(L, 1);
		return kr_error(EIO);
	}
92
	if (lua_isnumber(L, -1)) { /* Return code */
93
		status = lua_tonumber(L, -1);
94 95
	} else if (lua_isthread(L, -1)) { /* Continuations */
		status = l_ffi_defer(lua_tothread(L, -1));
96
	}
97
	lua_pop(L, 1);
98 99 100 101 102 103
	return status;
}

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

static int l_ffi_deinit(struct kr_module *module)
{
112 113 114 115 116
	/* 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);
117
	}
118
	module->lib = NULL;
119
	/* Free the layer API wrapper (unconst it) */
120
	kr_layer_api_t* api = module->data;
121 122
	if (!api) {
		return ret;
123
	}
124 125 126 127 128 129 130
	/* Unregister layer callback references from registry. */
	for (int si = 0; si < SLOT_count; ++si) {
		if (api->cb_slots[si] > 0) {
			luaL_unref(L, LUA_REGISTRYINDEX, api->cb_slots[si]);
		}
	}
	free(api);
131 132 133 134
	return ret;
}

/** @internal Helper for retrieving layer Lua function by name. */
135 136 137
#define LAYER_FFI_CALL(ctx, slot_name) \
	const int *cb_slot = (ctx)->api->cb_slots + SLOT_ ## slot_name; \
	if (*cb_slot <= 0) { \
138 139
		return ctx->state; \
	} \
140 141
	struct kr_module *module = (ctx)->api->data; \
	lua_State *L = module->lib; \
142
	lua_rawgeti(L, LUA_REGISTRYINDEX, *cb_slot); \
143 144
	lua_pushnumber(L, ctx->state)

145
static int l_ffi_layer_begin(kr_layer_t *ctx)
146
{
147
	LAYER_FFI_CALL(ctx, begin);
148
	lua_pushlightuserdata(L, ctx->req);
149
	return l_ffi_call(L, 2);
150 151
}

152
static int l_ffi_layer_reset(kr_layer_t *ctx)
153
{
154
	LAYER_FFI_CALL(ctx, reset);
155
	lua_pushlightuserdata(L, ctx->req);
156
	return l_ffi_call(L, 2);
157 158
}

159
static int l_ffi_layer_finish(kr_layer_t *ctx)
160
{
161
	struct kr_request *req = ctx->req;
162
	LAYER_FFI_CALL(ctx, finish);
163 164 165
	lua_pushlightuserdata(L, req);
	lua_pushlightuserdata(L, req->answer);
	return l_ffi_call(L, 3);
166 167
}

168
static int l_ffi_layer_consume(kr_layer_t *ctx, knot_pkt_t *pkt)
169
{
170
	if (ctx->state & KR_STATE_FAIL) {
171 172
		return ctx->state; /* Already failed, skip */
	}
173
	LAYER_FFI_CALL(ctx, consume);
174
	lua_pushlightuserdata(L, ctx->req);
175
	lua_pushlightuserdata(L, pkt);
176
	return l_ffi_call(L, 3);
177 178
}

179
static int l_ffi_layer_produce(kr_layer_t *ctx, knot_pkt_t *pkt)
180
{
181
	if (ctx->state & (KR_STATE_FAIL)) {
182
		return ctx->state; /* Already failed or done, skip */
183
	}
184
	LAYER_FFI_CALL(ctx, produce);
185
	lua_pushlightuserdata(L, ctx->req);
186
	lua_pushlightuserdata(L, pkt);
187
	return l_ffi_call(L, 3);
188
}
189 190 191 192 193 194 195 196 197 198 199 200 201

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 */
	}
	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(L, 5);
}
202 203 204 205 206 207 208

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(L, 2);
}
209
#undef LAYER_FFI_CALL
210

211 212
/** @internal Conditionally register layer trampoline
  * @warning Expects 'module.layer' to be on top of Lua stack. */
213
#define LAYER_REGISTER(L, api, name) do { \
214
	int *cb_slot = (api)->cb_slots + SLOT_ ## name; \
215
	lua_getfield((L), -1, #name); \
216 217
	if (!lua_isnil((L), -1)) { \
		(api)->name = l_ffi_layer_ ## name; \
218
		*cb_slot = luaL_ref((L), LUA_REGISTRYINDEX); \
219 220 221
	} else { \
		lua_pop((L), 1); \
	} \
222 223
} while(0)

224
/** @internal Create C layer api wrapper. */
225
static kr_layer_api_t *l_ffi_layer_create(lua_State *L, struct kr_module *module)
226
{
227 228
	/* Fabricate layer API wrapping the Lua functions
	 * reserve slots after it for references to Lua callbacks. */
229 230
	const size_t api_length = offsetof(kr_layer_api_t, cb_slots)
				+ (SLOT_count * SLOT_size);
231
	kr_layer_api_t *api = malloc(api_length);
232 233 234 235 236 237
	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);
238
		LAYER_REGISTER(L, api, checkout);
239
		LAYER_REGISTER(L, api, answer_finalize);
240 241 242 243
		LAYER_REGISTER(L, api, reset);
		/* Begin is always set, as it initializes layer baton. */
		api->begin = l_ffi_layer_begin;
		api->data = module;
244
	}
245
	return api;
246 247
}

248
/** @internal Retrieve C layer api wrapper. */
249
static const kr_layer_api_t *l_ffi_layer(struct kr_module *module)
250 251
{
	if (module) {
252
		return (const kr_layer_api_t *)module->data;
253 254 255 256
	}
	return NULL;
}
#undef LAYER_REGISTER
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274

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);
275 276
	module->init = &l_ffi_init;
	module->deinit = &l_ffi_deinit;
277 278 279 280 281 282
	/* 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);
	}
283
	module->lib = L;
284
	lua_pop(L, 2); /* Clear the layer + module global */
285 286 287 288
	if (module->init) {
		return module->init(module);
	}
	return kr_ok();
289
}