engine.c 20.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*  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
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

17
#include <contrib/cleanup.h>
18
#include <ccan/json/json.h>
19
#include <ccan/asprintf/asprintf.h>
20 21
#include <uv.h>
#include <unistd.h>
22 23
#include <grp.h>
#include <pwd.h>
24
#include <zscanner/scanner.h>
25 26

#include "daemon/engine.h"
27
#include "daemon/bindings.h"
28
#include "daemon/ffimodule.h"
29
#include "lib/nsrep.h"
30 31
#include "lib/cache.h"
#include "lib/defines.h"
32
#include "lib/cdb_lmdb.h"
33
#include "lib/dnssec/ta.h"
34

35 36 37 38 39
/** @internal Compatibility wrapper for Lua < 5.2 */
#if LUA_VERSION_NUM < 502
#define lua_rawlen(L, obj) lua_objlen((L), (obj))
#endif

40 41 42
/** @internal Annotate for static checkers. */
KR_NORETURN int lua_error (lua_State *L);

43 44 45
/* Cleanup engine state every 5 minutes */
const size_t CLEANUP_TIMER = 5*60*1000;

46 47 48 49
/*
 * Global bindings.
 */

50
/** Register module callback into Lua world. */
51
#define REGISTER_MODULE_CALL(L, module, cb, name) do { \
52 53 54
	lua_pushlightuserdata((L), (module)); \
	lua_pushlightuserdata((L), (cb)); \
	lua_pushcclosure((L), l_trampoline, 2); \
55 56
	lua_setfield((L), -2, (name)); \
	} while (0)
57

58 59 60
/** Print help and available commands. */
static int l_help(lua_State *L)
{
61
	static const char *help_str =
62 63
		"help()\n    show this help\n"
		"quit()\n    quit\n"
64
		"hostname()\n    hostname\n"
65
		"user(name[, group])\n    change process user (and group)\n"
66
		"verbose(true|false)\n    toggle verbose mode\n"
67
		"option(opt[, new_val])\n    get/set server option\n"
68
		"mode(strict|normal|permissive)\n    set resolver strictness level\n"
69 70
		"resolve(name, type[, class, flags, callback])\n    resolve query, callback when it's finished\n"
		"todname(name)\n    convert name to wire format\n"
71
		"tojson(val)\n    convert value to JSON\n"
72 73 74 75
		"net\n    network configuration\n"
		"cache\n    network configuration\n"
		"modules\n    modules configuration\n"
		"kres\n    resolver services\n"
76
		"trust_anchors\n    configure trust anchors\n"
77
		;
78 79
	lua_pushstring(L, help_str);
	return 1;
80 81
}

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
static bool update_privileges(int uid, int gid)
{
	if ((gid_t)gid != getgid()) {
		if (setregid(gid, gid) < 0) {
			return false;
		}
	}
	if ((uid_t)uid != getuid()) {
		if (setreuid(uid, uid) < 0) {
			return false;
		}
	}
	return true;
}

/** Set process user/group. */
static int l_setuser(lua_State *L)
{
	int n = lua_gettop(L);
	if (n < 1 || !lua_isstring(L, 1)) {
		lua_pushliteral(L, "user(user[, group)");
		lua_error(L);
	}
	/* Fetch UID/GID based on string identifiers. */
	struct passwd *user_pw = getpwnam(lua_tostring(L, 1));
	if (!user_pw) {
		lua_pushliteral(L, "invalid user name");
		lua_error(L);
	}
	int uid = user_pw->pw_uid;
	int gid = getgid();
	if (n > 1 && lua_isstring(L, 2)) {
		struct group *group_pw = getgrnam(lua_tostring(L, 2));
		if (!group_pw) {
			lua_pushliteral(L, "invalid group name");
			lua_error(L);
		}
		gid = group_pw->gr_gid;
	}
	/* Drop privileges */
	bool ret = update_privileges(uid, gid);
	if (!ret) {
		lua_pushstring(L, strerror(errno));
		lua_error(L);
	}
	lua_pushboolean(L, ret);
	return 1;
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
/** Return platform-specific versioned library name. */
static int l_libpath(lua_State *L)
{
	int n = lua_gettop(L);
	if (n < 2)
		return 0;
	auto_free char *lib_path = NULL;
	const char *lib_name = lua_tostring(L, 1);
	const char *lib_version = lua_tostring(L, 2);
#if defined(__APPLE__)
	lib_path = afmt("%s.%s.dylib", lib_name, lib_version);
#elif _WIN32
	lib_path = afmt("%s.dll", lib_name); /* Versioned in RC files */
#else
	lib_path = afmt("%s.so.%s", lib_name, lib_version);
#endif
	lua_pushstring(L, lib_path);
	return 1;
}

151 152 153 154 155 156 157
/** Quit current executable. */
static int l_quit(lua_State *L)
{
	engine_stop(engine_luaget(L));
	return 0;
}

158 159 160 161
/** Toggle verbose mode. */
static int l_verbose(lua_State *L)
{
	if (lua_isboolean(L, 1) || lua_isnumber(L, 1)) {
162
		kr_debug_set(lua_toboolean(L, 1));
163
	}
164
	lua_pushboolean(L, kr_debug_status());
165 166 167
	return 1;
}

168 169 170 171 172 173 174 175 176
/** Return hostname. */
static int l_hostname(lua_State *L)
{
	char host_str[KNOT_DNAME_MAXLEN];
	gethostname(host_str, sizeof(host_str));
	lua_pushstring(L, host_str);
	return 1;
}

177 178 179 180 181 182 183 184
/** Get/set context option. */
static int l_option(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
	/* Look up option name */
	unsigned opt_code = 0;
	if (lua_isstring(L, 1)) {
		const char *opt = lua_tostring(L, 1);
185
		for (const knot_lookup_t *it = kr_query_flag_names(); it->name; ++it) {
186 187 188 189 190 191 192 193 194 195 196
			if (strcmp(it->name, opt) == 0) {
				opt_code = it->id;
				break;
			}
		}
		if (!opt_code) {
			lua_pushstring(L, "invalid option name");
			lua_error(L);
		}
	}
	/* Get or set */
197
	if (lua_isboolean(L, 2) || lua_isnumber(L, 2)) {
198 199 200 201 202 203 204 205 206 207
		if (lua_toboolean(L, 2)) {
			engine->resolver.options |= opt_code;
		} else {
			engine->resolver.options &= ~opt_code; 
		}
	}
	lua_pushboolean(L, engine->resolver.options & opt_code);
	return 1;
}

208 209 210 211 212 213
/** Enable/disable trust anchor. */
static int l_trustanchor(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
	const char *anchor = lua_tostring(L, 1);
	bool enable = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : true;
214
	if (!anchor || strlen(anchor) == 0) {
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
		return 0;
	}
	/* If disabling, parse the owner string only. */
	if (!enable) {
		knot_dname_t *owner = knot_dname_from_str(NULL, anchor, KNOT_DNAME_MAXLEN);
		if (!owner) {
			lua_pushstring(L, "invalid trust anchor owner");
			lua_error(L);
		}
		lua_pushboolean(L, kr_ta_del(&engine->resolver.trust_anchors, owner) == 0);
		free(owner);
		return 1;
	}

	/* Parse the record */
	zs_scanner_t *zs = malloc(sizeof(*zs));
	if (!zs || zs_init(zs, ".", 1, 0) != 0) {
		free(zs);
		lua_pushstring(L, "not enough memory");
		lua_error(L);
	}
	int ok = zs_set_input_string(zs, anchor, strlen(anchor)) == 0 &&
	         zs_parse_all(zs) == 0;
	/* Add it to TA set and cleanup */
	if (ok) {
		ok = kr_ta_add(&engine->resolver.trust_anchors,
		               zs->r_owner, zs->r_type, zs->r_ttl, zs->r_data, zs->r_data_length) == 0;
	}
	zs_deinit(zs);
	free(zs);
	/* Report errors */
	if (!ok) {
		lua_pushstring(L, "failed to process trust anchor RR");
		lua_error(L);
	}
	lua_pushboolean(L, true);
	return 1;
}

254 255 256 257 258 259 260 261 262 263 264 265
/** Unpack JSON object to table */
static void l_unpack_json(lua_State *L, JsonNode *table)
{
	lua_newtable(L);
	JsonNode *node = NULL;
	json_foreach(node, table) {
		/* Push node value */
		switch(node->tag) {
		case JSON_OBJECT: /* as array */
		case JSON_ARRAY:  l_unpack_json(L, node); break;
		case JSON_STRING: lua_pushstring(L, node->string_); break;
		case JSON_NUMBER: lua_pushnumber(L, node->number_); break;
266
		case JSON_BOOL:   lua_pushboolean(L, node->bool_); break;
267 268 269 270 271 272 273 274 275 276 277
		default: continue;
		}
		/* Set table key */
		if (node->key) {
			lua_setfield(L, -2, node->key);
		} else {
			lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
		}
	}
}

278
/** @internal Recursive Lua/JSON serialization. */
279 280
static JsonNode *l_pack_elem(lua_State *L, int top)
{
281 282 283 284 285 286
	switch(lua_type(L, top)) {
	case LUA_TSTRING:  return json_mkstring(lua_tostring(L, top));
	case LUA_TNUMBER:  return json_mknumber(lua_tonumber(L, top));
	case LUA_TBOOLEAN: return json_mkbool(lua_toboolean(L, top));
	case LUA_TTABLE:   break; /* Table, iterate it. */
	default:           return json_mknull();
287
	}
288 289 290 291
	/* Use absolute indexes here, as the table may be nested. */
	JsonNode *node = NULL;
	lua_pushnil(L);
	while(lua_next(L, top) != 0) {
292
		bool is_array = false;
293
		if (!node) {
294
			is_array = (lua_type(L, top + 1) == LUA_TNUMBER);
295
			node = is_array ? json_mkarray() : json_mkobject();
296 297 298
			if (!node) {
				return NULL;
			}
299 300
		} else {
			is_array = node->tag == JSON_ARRAY;
301
		}
302

303 304 305
		/* Insert to array/table. */
		JsonNode *val = l_pack_elem(L, top + 2);
		if (is_array) {
306 307
			json_append_element(node, val);
		} else {
308 309
			const char *key = lua_tostring(L, top + 1);
			json_append_member(node, key, val);
310 311
		}
		lua_pop(L, 1);
312
	}
313 314
	/* Return empty object for empty tables. */
	return node ? node : json_mkobject();
315 316
}

317
/** @internal Serialize to string */
318 319
static char *l_pack_json(lua_State *L, int top)
{
320
	JsonNode *root = l_pack_elem(L, top);
321 322 323 324 325 326 327 328
	if (!root) {
		return NULL;
	}
	char *result = json_encode(root);
	json_delete(root);
	return result;
}

329 330
static int l_tojson(lua_State *L)
{
331
	auto_free char *json_str = l_pack_json(L, lua_gettop(L));
332 333 334 335 336 337 338
	if (!json_str) {
		return 0;
	}
	lua_pushstring(L, json_str);
	return 1;
}

339
/** Trampoline function for module properties. */
340 341
static int l_trampoline(lua_State *L)
{
342 343
	struct kr_module *module = lua_touserdata(L, lua_upvalueindex(1));
	void* callback = lua_touserdata(L, lua_upvalueindex(2));
344
	struct engine *engine = engine_luaget(L);
345 346 347 348
	if (!module) {
		lua_pushstring(L, "module closure missing upvalue");
		lua_error(L);
	}
349

350 351 352
	/* Now we only have property callback or config,
	 * if we expand the callables, we might need a callback_type.
	 */
353
	const char *args = NULL;
354
	auto_free char *cleanup_args = NULL;
355
	if (lua_gettop(L) > 0) {
356 357 358 359 360 361
		if (lua_istable(L, 1)) {
			cleanup_args = l_pack_json(L, 1);
			args = cleanup_args;
		} else {
			args = lua_tostring(L, 1);
		}
362
	}
363
	if (callback == module->config) {
364
		module->config(module, args);
365 366
	} else {
		kr_prop_cb *prop = (kr_prop_cb *)callback;
367 368 369 370 371
		auto_free char *ret = prop(engine, module, args);
		if (!ret) { /* No results */
			return 0;
		}
		JsonNode *root_node = json_decode(ret);
Marek Vavruša's avatar
Marek Vavruša committed
372
		if (root_node && (root_node->tag == JSON_OBJECT || root_node->tag == JSON_ARRAY)) {
373 374 375 376 377
			l_unpack_json(L, root_node);
		} else {
			lua_pushstring(L, ret);
		}
		json_delete(root_node);
378
		return 1;
379
	}
380

381 382 383 384 385 386 387 388 389 390 391
	/* No results */
	return 0;
}

/*
 * Engine API.
 */

static int init_resolver(struct engine *engine)
{
	/* Open resolution context */
392
	engine->resolver.trust_anchors = map_make();
393
	engine->resolver.negative_anchors = map_make();
394
	engine->resolver.pool = engine->pool;
395
	engine->resolver.modules = &engine->modules;
396 397 398 399 400 401
	/* Create OPT RR */
	engine->resolver.opt_rr = mm_alloc(engine->pool, sizeof(knot_rrset_t));
	if (!engine->resolver.opt_rr) {
		return kr_error(ENOMEM);
	}
	knot_edns_init(engine->resolver.opt_rr, KR_EDNS_PAYLOAD, 0, KR_EDNS_VERSION, engine->pool);
402 403 404
	/* Set default root hints */
	kr_zonecut_init(&engine->resolver.root_hints, (const uint8_t *)"", engine->pool);
	kr_zonecut_set_sbelt(&engine->resolver, &engine->resolver.root_hints);
405
	/* Open NS rtt + reputation cache */
406
	engine->resolver.cache_rtt = mm_alloc(engine->pool, lru_size(kr_nsrep_lru_t, LRU_RTT_SIZE));
407 408 409
	if (engine->resolver.cache_rtt) {
		lru_init(engine->resolver.cache_rtt, LRU_RTT_SIZE);
	}
410
	engine->resolver.cache_rep = mm_alloc(engine->pool, lru_size(kr_nsrep_lru_t, LRU_REP_SIZE));
411 412
	if (engine->resolver.cache_rep) {
		lru_init(engine->resolver.cache_rep, LRU_REP_SIZE);
413
	}
414 415

	/* Load basic modules */
416 417 418 419
	engine_register(engine, "iterate", NULL, NULL);
	engine_register(engine, "validate", NULL, NULL);
	engine_register(engine, "rrcache", NULL, NULL);
	engine_register(engine, "pktcache", NULL, NULL);
420

421
	return array_push(engine->backends, kr_cdb_lmdb());
422 423 424 425 426 427 428 429 430 431
}

static int init_state(struct engine *engine)
{
	/* Initialize Lua state */
	engine->L = luaL_newstate();
	if (engine->L == NULL) {
		return kr_error(ENOMEM);
	}
	/* Initialize used libraries. */
432
	lua_gc(engine->L, LUA_GCSTOP, 0);
433 434 435 436 437 438
	luaL_openlibs(engine->L);
	/* Global functions */
	lua_pushcfunction(engine->L, l_help);
	lua_setglobal(engine->L, "help");
	lua_pushcfunction(engine->L, l_quit);
	lua_setglobal(engine->L, "quit");
439 440
	lua_pushcfunction(engine->L, l_hostname);
	lua_setglobal(engine->L, "hostname");
441 442 443 444
	lua_pushcfunction(engine->L, l_verbose);
	lua_setglobal(engine->L, "verbose");
	lua_pushcfunction(engine->L, l_option);
	lua_setglobal(engine->L, "option");
445 446
	lua_pushcfunction(engine->L, l_setuser);
	lua_setglobal(engine->L, "user");
447 448
	lua_pushcfunction(engine->L, l_trustanchor);
	lua_setglobal(engine->L, "trustanchor");
449 450
	lua_pushcfunction(engine->L, l_libpath);
	lua_setglobal(engine->L, "libpath");
451 452
	lua_pushcfunction(engine->L, l_tojson);
	lua_setglobal(engine->L, "tojson");
453
	lua_pushliteral(engine->L, MODULEDIR);
454
	lua_setglobal(engine->L, "moduledir");
455
	lua_pushliteral(engine->L, ETCDIR);
456
	lua_setglobal(engine->L, "etcdir");
457 458 459 460 461
	lua_pushlightuserdata(engine->L, engine);
	lua_setglobal(engine->L, "__engine");
	return kr_ok();
}

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
static void update_state(uv_timer_t *handle)
{
	struct engine *engine = handle->data;

	/* Walk RTT table, clearing all entries with bad score
	 * to compensate for intermittent network issues or temporary bad behaviour. */
	kr_nsrep_lru_t *table = engine->resolver.cache_rtt;
	for (size_t i = 0; i < table->size; ++i) {
		if (!table->slots[i].key)
			continue;
		if (table->slots[i].data > KR_NS_LONG) {
			lru_evict(table, i);
		}
	}
}

478
int engine_init(struct engine *engine, knot_mm_t *pool)
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
{
	if (engine == NULL) {
		return kr_error(EINVAL);
	}

	memset(engine, 0, sizeof(*engine));
	engine->pool = pool;

	/* Initialize state */
	int ret = init_state(engine);
	if (ret != 0) {
		engine_deinit(engine);
	}
	/* Initialize resolver */
	ret = init_resolver(engine);
	if (ret != 0) {
495
		engine_deinit(engine);
496 497
		return ret;
	}
498 499
	/* Initialize network */
	network_init(&engine->net, uv_default_loop());
500 501 502 503

	return ret;
}

504 505 506 507 508 509 510 511 512 513 514 515 516
static void engine_unload(struct engine *engine, struct kr_module *module)
{
	/* Unregister module */
	auto_free char *name = strdup(module->name);
	kr_module_unload(module);
	/* Clear in Lua world */
	if (name) {
		lua_pushnil(engine->L);
		lua_setglobal(engine->L, name);
	}
	free(module);
}

517 518 519 520 521 522
void engine_deinit(struct engine *engine)
{
	if (engine == NULL) {
		return;
	}

523 524
	/* Only close sockets and services,
	 * no need to clean up mempool. */
525
	network_deinit(&engine->net);
526
	kr_zonecut_deinit(&engine->resolver.root_hints);
527
	kr_cache_close(&engine->resolver.cache);
528 529
	lru_deinit(engine->resolver.cache_rtt);
	lru_deinit(engine->resolver.cache_rep);
530

531
	/* Unload modules and engine. */
532
	for (size_t i = 0; i < engine->modules.len; ++i) {
533
		engine_unload(engine, engine->modules.at[i]);
534 535 536 537 538
	}
	if (engine->L) {
		lua_close(engine->L);
	}

539 540
	/* Free data structures */
	array_clear(engine->modules);
541
	array_clear(engine->backends);
542 543
	kr_ta_clear(&engine->resolver.trust_anchors);
	kr_ta_clear(&engine->resolver.negative_anchors);
544 545
}

546
int engine_pcall(lua_State *L, int argc)
547 548 549
{
#if LUA_VERSION_NUM >= 502
	lua_getglobal(L, "_SANDBOX");
550
	lua_setupvalue(L, -(2 + argc), 1);
551
#endif
552
	return lua_pcall(L, argc, LUA_MULTRET, 0);
553 554
}

555 556 557 558 559 560 561
int engine_cmd(struct engine *engine, const char *str)
{
	if (engine == NULL || engine->L == NULL) {
		return kr_error(ENOEXEC);
	}

	/* Evaluate results */
562 563
	lua_getglobal(engine->L, "eval_cmd");
	lua_pushstring(engine->L, str);
564 565

	/* Check result. */
566
	return engine_pcall(engine->L, 1);
567 568
}

569 570 571
/* Execute byte code */
#define l_dobytecode(L, arr, len, name) \
	(luaL_loadbuffer((L), (arr), (len), (name)) || lua_pcall((L), 0, LUA_MULTRET, 0))
572 573
/** Load file in a sandbox environment. */
#define l_dosandboxfile(L, filename) \
574
	(luaL_loadfile((L), (filename)) || engine_pcall((L), 0))
575

576
static int engine_loadconf(struct engine *engine, const char *config_path)
577
{
578
	/* Use module path for including Lua scripts */
579
	static const char l_paths[] = "package.path = '" MODULEDIR "/?.lua;'..package.path";
580 581 582 583
	int ret = l_dobytecode(engine->L, l_paths, sizeof(l_paths) - 1, "");
	if (ret != 0) {
		lua_pop(engine->L, 1);
	}
584
	/* Init environment */
585 586
	static const char sandbox_bytecode[] = {
		#include "daemon/lua/sandbox.inc"
587
	};
588
	if (l_dobytecode(engine->L, sandbox_bytecode, sizeof(sandbox_bytecode), "init") != 0) {
589 590
		fprintf(stderr, "[system] error %s\n", lua_tostring(engine->L, -1));
		lua_pop(engine->L, 1);
591 592
		return kr_error(ENOEXEC);
	}
593
	/* Load config file */
594 595 596
	if (strcmp(config_path, "-") == 0) {
		return ret; /* No config, no defaults. */
	}
597 598
	if(access(config_path, F_OK ) != -1 ) {
		ret = l_dosandboxfile(engine->L, config_path);
599 600
	}
	if (ret == 0) {
601
		/* Load defaults */
602
		static const char config_bytecode[] = {
603
			#include "daemon/lua/config.inc"
604
		};
605
		ret = l_dobytecode(engine->L, config_bytecode, sizeof(config_bytecode), "config");
606 607 608 609
	}

	/* Evaluate */
	if (ret != 0) {
610
		fprintf(stderr, "%s\n", lua_tostring(engine->L, -1));
611 612
		lua_pop(engine->L, 1);
	}
613
	return ret;
614 615
}

616
int engine_start(struct engine *engine, const char *config_path)
617 618
{
	/* Load configuration. */
619
	int ret = engine_loadconf(engine, config_path);
620 621 622 623
	if (ret != 0) {
		return ret;
	}

624 625 626
	/* Clean up stack and restart GC */
	lua_settop(engine->L, 0);
	lua_gc(engine->L, LUA_GCCOLLECT, 0);
627
	lua_gc(engine->L, LUA_GCSETSTEPMUL, 50);
628 629
	lua_gc(engine->L, LUA_GCSETPAUSE, 400);
	lua_gc(engine->L, LUA_GCRESTART, 0);
630 631 632 633 634 635 636 637 638 639

	/* Set up periodic update function */
	uv_timer_t *timer = malloc(sizeof(*timer));
	if (timer) {
		uv_timer_init(uv_default_loop(), timer);
		timer->data = engine;
		engine->updater = timer;
		uv_timer_start(timer, update_state, CLEANUP_TIMER, CLEANUP_TIMER);
	}

640
	return kr_ok();
641 642 643 644
}

void engine_stop(struct engine *engine)
{
645 646
	uv_timer_stop(engine->updater);
	uv_close((uv_handle_t *)engine->updater, (uv_close_cb) free);
647 648 649
	uv_stop(uv_default_loop());
}

650 651 652 653 654 655 656
/** Register module properties in Lua environment */
static int register_properties(struct engine *engine, struct kr_module *module)
{
	lua_newtable(engine->L);
	if (module->config != NULL) {
		REGISTER_MODULE_CALL(engine->L, module, module->config, "config");
	}
657
	for (struct kr_prop *p = module->props; p && p->name; ++p) {
658 659 660 661 662 663 664 665 666
		if (p->cb != NULL && p->name != NULL) {
			REGISTER_MODULE_CALL(engine->L, module, p->cb, p->name);
		}
	}
	lua_setglobal(engine->L, module->name);

	/* Register module in Lua env */
	lua_getglobal(engine->L, "modules_register");
	lua_getglobal(engine->L, module->name);
667
	if (engine_pcall(engine->L, 1) != 0) {
668 669 670 671 672 673
		lua_pop(engine->L, 1);
	}

	return kr_ok();
}

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
/** @internal Find matching module */
static size_t module_find(module_array_t *mod_list, const char *name)
{
	size_t found = mod_list->len;
	for (size_t i = 0; i < mod_list->len; ++i) {
		struct kr_module *mod = mod_list->at[i];
		if (strcmp(mod->name, name) == 0) {
			found = i;
			break;
		}
	}
	return found;
}

int engine_register(struct engine *engine, const char *name, const char *precedence, const char* ref)
689 690 691 692
{
	if (engine == NULL || name == NULL) {
		return kr_error(EINVAL);
	}
693 694
	/* Make sure module is unloaded */
	(void) engine_unregister(engine, name);
695 696 697 698 699 700 701 702 703
	/* Find the index of referenced module. */
	module_array_t *mod_list = &engine->modules;
	size_t ref_pos = mod_list->len;
	if (precedence && ref) {
		ref_pos = module_find(mod_list, ref);
		if (ref_pos >= mod_list->len) {
			return kr_error(EIDRM);
		}
	}
704
	/* Attempt to load binary module */
705 706 707 708
	struct kr_module *module = malloc(sizeof(*module));
	if (!module) {
		return kr_error(ENOMEM);
	}
709
	module->data = engine;
710
	int ret = kr_module_load(module, name, NULL);
711 712
	/* Load Lua module if not a binary */
	if (ret == kr_error(ENOENT)) {
713
		ret = ffimodule_register_lua(engine, module, name);
714
	}
715
	if (ret != 0) {
716
		free(module);
717
		return ret;
718 719 720 721
	}
	if (array_push(engine->modules, module) < 0) {
		engine_unload(engine, module);
		return kr_error(ENOMEM);
722
	}
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
	/* Evaluate precedence operator */
	if (precedence) {
		struct kr_module **arr = mod_list->at;
		size_t emplacement = mod_list->len;
		if (strcasecmp(precedence, ">") == 0) {
			if (ref_pos + 1 < mod_list->len)
				emplacement = ref_pos + 1; /* Insert after target */
		}
		if (strcasecmp(precedence, "<") == 0) {
			emplacement = ref_pos; /* Insert at target */
		}
		/* Move the tail if it has some elements. */
		if (emplacement + 1 < mod_list->len) {
			memmove(&arr[emplacement + 1], &arr[emplacement], sizeof(*arr) * (mod_list->len - (emplacement + 1)));
			arr[emplacement] = module;
		}
739
	}
740 741

	/* Register properties */
742
	if (module->props || module->config) {
743
		return register_properties(engine, module);
744 745 746 747 748 749 750
	}

	return kr_ok();
}

int engine_unregister(struct engine *engine, const char *name)
{
751
	module_array_t *mod_list = &engine->modules;
752
	size_t found = module_find(mod_list, name);
753
	if (found < mod_list->len) {
754
		engine_unload(engine, mod_list->at[found]);
755 756 757 758 759 760 761 762 763 764
		array_del(*mod_list, found);
		return kr_ok();
	}

	return kr_error(ENOENT);
}

void engine_lualib(struct engine *engine, const char *name, lua_CFunction lib_cb)
{
	if (engine != NULL) {
765
#if LUA_VERSION_NUM >= 502
766 767
		luaL_requiref(engine->L, name, lib_cb, 1);
		lua_pop(engine->L, 1);
768 769
#else
		lib_cb(engine->L);
770 771 772 773 774 775
#endif
	}
}

struct engine *engine_luaget(lua_State *L)
{
776
	lua_getglobal(L, "__engine");
777
	struct engine *engine = lua_touserdata(L, -1);
778
	lua_pop(L, 1);
779
	return engine;
780
}