engine.c 22.5 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
#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
		"map(expr)\n    run expression on all workers\n"
73 74 75 76
		"net\n    network configuration\n"
		"cache\n    network configuration\n"
		"modules\n    modules configuration\n"
		"kres\n    resolver services\n"
77
		"trust_anchors\n    configure trust anchors\n"
78
		;
79 80
	lua_pushstring(L, help_str);
	return 1;
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 131
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;
}

132 133 134 135 136 137 138
/** Quit current executable. */
static int l_quit(lua_State *L)
{
	engine_stop(engine_luaget(L));
	return 0;
}

139 140 141 142
/** Toggle verbose mode. */
static int l_verbose(lua_State *L)
{
	if (lua_isboolean(L, 1) || lua_isnumber(L, 1)) {
143
		kr_debug_set(lua_toboolean(L, 1));
144
	}
145
	lua_pushboolean(L, kr_debug_status());
146 147 148
	return 1;
}

149 150 151 152 153 154 155 156 157
/** 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;
}

158 159 160 161 162 163 164 165
/** 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);
166
		for (const knot_lookup_t *it = kr_query_flag_names(); it->name; ++it) {
167 168 169 170 171 172 173 174 175 176 177
			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 */
178
	if (lua_isboolean(L, 2) || lua_isnumber(L, 2)) {
179 180 181 182 183 184 185 186 187 188
		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;
}

189 190 191 192 193 194
/** 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;
195
	if (!anchor || strlen(anchor) == 0) {
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
		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;
}
234 235 236
/** Unpack JSON object to table */
static void l_unpack_json(lua_State *L, JsonNode *table)
{
237 238 239 240 241 242 243 244
	/* Unpack POD */
	switch(table->tag) {
		case JSON_STRING: lua_pushstring(L, table->string_); return;
		case JSON_NUMBER: lua_pushnumber(L, table->number_); return;
		case JSON_BOOL:   lua_pushboolean(L, table->bool_); return;
		default: break;
	}
	/* Unpack object or array into table */
245 246 247 248 249 250 251 252 253
	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;
254
		case JSON_BOOL:   lua_pushboolean(L, node->bool_); break;
255 256 257 258 259 260 261 262 263 264 265
		default: continue;
		}
		/* Set table key */
		if (node->key) {
			lua_setfield(L, -2, node->key);
		} else {
			lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
		}
	}
}

266
/** @internal Recursive Lua/JSON serialization. */
267 268
static JsonNode *l_pack_elem(lua_State *L, int top)
{
269 270 271 272 273 274
	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();
275
	}
276 277 278 279
	/* Use absolute indexes here, as the table may be nested. */
	JsonNode *node = NULL;
	lua_pushnil(L);
	while(lua_next(L, top) != 0) {
280
		bool is_array = false;
281
		if (!node) {
282
			is_array = (lua_type(L, top + 1) == LUA_TNUMBER);
283
			node = is_array ? json_mkarray() : json_mkobject();
284 285 286
			if (!node) {
				return NULL;
			}
287 288
		} else {
			is_array = node->tag == JSON_ARRAY;
289
		}
290

291 292 293
		/* Insert to array/table. */
		JsonNode *val = l_pack_elem(L, top + 2);
		if (is_array) {
294 295
			json_append_element(node, val);
		} else {
296 297
			const char *key = lua_tostring(L, top + 1);
			json_append_member(node, key, val);
298 299
		}
		lua_pop(L, 1);
300
	}
301 302
	/* Return empty object for empty tables. */
	return node ? node : json_mkobject();
303 304
}

305
/** @internal Serialize to string */
306 307
static char *l_pack_json(lua_State *L, int top)
{
308
	JsonNode *root = l_pack_elem(L, top);
309 310 311 312 313 314 315 316
	if (!root) {
		return NULL;
	}
	char *result = json_encode(root);
	json_delete(root);
	return result;
}

317 318
static int l_tojson(lua_State *L)
{
319
	auto_free char *json_str = l_pack_json(L, lua_gettop(L));
320 321 322 323 324 325 326
	if (!json_str) {
		return 0;
	}
	lua_pushstring(L, json_str);
	return 1;
}

327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
/** @internal Throw Lua error if expr is false */
#define expr_checked(expr) \
	if (!(expr)) { lua_pushboolean(L, false); lua_rawseti(L, -2, lua_rawlen(L, -2) + 1); continue; }

static int l_map(lua_State *L)
{
	struct engine *engine = engine_luaget(L);
	const char *cmd = lua_tostring(L, 1);
	uint32_t len = strlen(cmd);
	lua_newtable(L);

	/* Execute on leader instance */
	int ntop = lua_gettop(L);
	engine_cmd(L, cmd, true);
	lua_settop(L, ntop + 1); /* Push only one return value to table */
	lua_rawseti(L, -2, 1);

	for (size_t i = 0; i < engine->ipc_set.len; ++i) {
		int fd = engine->ipc_set.at[i];
		/* Send command */
		expr_checked(write(fd, &len, sizeof(len)) == sizeof(len));
		expr_checked(write(fd, cmd, len) == len);
		/* Read response */
		uint32_t rlen = 0;
		if (read(fd, &rlen, sizeof(rlen)) == sizeof(rlen)) {
			auto_free char *rbuf = malloc(rlen + 1);
			expr_checked(rbuf != NULL);
			expr_checked(read(fd, rbuf, rlen) == rlen);
			rbuf[rlen] = '\0';
			/* Unpack from JSON */
			JsonNode *root_node = json_decode(rbuf);
			if (root_node) {
				l_unpack_json(L, root_node);
			} else {
				lua_pushlstring(L, rbuf, rlen);
			}
			json_delete(root_node);
			lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
			continue;
		}
		/* Didn't respond */
		lua_pushboolean(L, false);
		lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
	}
	return 1;
}

#undef expr_checked


377
/** Trampoline function for module properties. */
378 379
static int l_trampoline(lua_State *L)
{
380 381
	struct kr_module *module = lua_touserdata(L, lua_upvalueindex(1));
	void* callback = lua_touserdata(L, lua_upvalueindex(2));
382
	struct engine *engine = engine_luaget(L);
383 384 385 386
	if (!module) {
		lua_pushstring(L, "module closure missing upvalue");
		lua_error(L);
	}
387

388 389 390
	/* Now we only have property callback or config,
	 * if we expand the callables, we might need a callback_type.
	 */
391
	const char *args = NULL;
392
	auto_free char *cleanup_args = NULL;
393
	if (lua_gettop(L) > 0) {
394 395 396 397 398 399
		if (lua_istable(L, 1)) {
			cleanup_args = l_pack_json(L, 1);
			args = cleanup_args;
		} else {
			args = lua_tostring(L, 1);
		}
400
	}
401
	if (callback == module->config) {
402
		module->config(module, args);
403 404
	} else {
		kr_prop_cb *prop = (kr_prop_cb *)callback;
405 406 407 408 409
		auto_free char *ret = prop(engine, module, args);
		if (!ret) { /* No results */
			return 0;
		}
		JsonNode *root_node = json_decode(ret);
410
		if (root_node) {
411 412 413 414 415
			l_unpack_json(L, root_node);
		} else {
			lua_pushstring(L, ret);
		}
		json_delete(root_node);
416
		return 1;
417
	}
418

419 420 421 422 423 424 425 426 427 428 429
	/* No results */
	return 0;
}

/*
 * Engine API.
 */

static int init_resolver(struct engine *engine)
{
	/* Open resolution context */
430
	engine->resolver.trust_anchors = map_make();
431
	engine->resolver.negative_anchors = map_make();
432
	engine->resolver.pool = engine->pool;
433
	engine->resolver.modules = &engine->modules;
434 435 436 437 438 439
	/* 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);
440 441 442
	/* 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);
443
	/* Open NS rtt + reputation cache */
444
	engine->resolver.cache_rtt = mm_alloc(engine->pool, lru_size(kr_nsrep_lru_t, LRU_RTT_SIZE));
445 446 447
	if (engine->resolver.cache_rtt) {
		lru_init(engine->resolver.cache_rtt, LRU_RTT_SIZE);
	}
448
	engine->resolver.cache_rep = mm_alloc(engine->pool, lru_size(kr_nsrep_lru_t, LRU_REP_SIZE));
449 450
	if (engine->resolver.cache_rep) {
		lru_init(engine->resolver.cache_rep, LRU_REP_SIZE);
451
	}
452 453 454 455
	engine->resolver.cache_cookie = mm_alloc(engine->pool, lru_size(kr_cookie_lru_t, LRU_COOKIES_SIZE));
	if (engine->resolver.cache_cookie) {
		lru_init(engine->resolver.cache_cookie, LRU_COOKIES_SIZE);
	}
456 457

	/* Load basic modules */
458 459 460 461
	engine_register(engine, "iterate", NULL, NULL);
	engine_register(engine, "validate", NULL, NULL);
	engine_register(engine, "rrcache", NULL, NULL);
	engine_register(engine, "pktcache", NULL, NULL);
462

463
	return array_push(engine->backends, kr_cdb_lmdb());
464 465 466 467 468 469 470 471 472 473
}

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. */
474
	lua_gc(engine->L, LUA_GCSTOP, 0);
475 476 477 478 479 480
	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");
481 482
	lua_pushcfunction(engine->L, l_hostname);
	lua_setglobal(engine->L, "hostname");
483 484 485 486
	lua_pushcfunction(engine->L, l_verbose);
	lua_setglobal(engine->L, "verbose");
	lua_pushcfunction(engine->L, l_option);
	lua_setglobal(engine->L, "option");
487 488
	lua_pushcfunction(engine->L, l_setuser);
	lua_setglobal(engine->L, "user");
489 490
	lua_pushcfunction(engine->L, l_trustanchor);
	lua_setglobal(engine->L, "trustanchor");
491 492 493 494
	lua_pushliteral(engine->L, libknot_SONAME);
	lua_setglobal(engine->L, "libknot_SONAME");
	lua_pushliteral(engine->L, libzscanner_SONAME);
	lua_setglobal(engine->L, "libzscanner_SONAME");
495 496
	lua_pushcfunction(engine->L, l_tojson);
	lua_setglobal(engine->L, "tojson");
497 498
	lua_pushcfunction(engine->L, l_map);
	lua_setglobal(engine->L, "map");
499
	lua_pushliteral(engine->L, MODULEDIR);
500
	lua_setglobal(engine->L, "moduledir");
501
	lua_pushliteral(engine->L, ETCDIR);
502
	lua_setglobal(engine->L, "etcdir");
503 504 505 506 507
	lua_pushlightuserdata(engine->L, engine);
	lua_setglobal(engine->L, "__engine");
	return kr_ok();
}

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
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);
		}
	}
}

524
int engine_init(struct engine *engine, knot_mm_t *pool)
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
{
	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) {
541
		engine_deinit(engine);
542 543
		return ret;
	}
544 545
	/* Initialize network */
	network_init(&engine->net, uv_default_loop());
546 547 548 549

	return ret;
}

550 551 552 553 554 555 556 557 558 559 560 561 562
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);
}

563 564 565 566 567 568
void engine_deinit(struct engine *engine)
{
	if (engine == NULL) {
		return;
	}

569 570
	/* Only close sockets and services,
	 * no need to clean up mempool. */
571
	network_deinit(&engine->net);
572
	kr_zonecut_deinit(&engine->resolver.root_hints);
573
	kr_cache_close(&engine->resolver.cache);
574 575
	lru_deinit(engine->resolver.cache_rtt);
	lru_deinit(engine->resolver.cache_rep);
576
	lru_deinit(engine->resolver.cache_cookie);
577

578 579 580 581 582
	/* Clear IPC pipes */
	for (size_t i = 0; i < engine->ipc_set.len; ++i) {
		close(engine->ipc_set.at[i]);
	}

583
	/* Unload modules and engine. */
584
	for (size_t i = 0; i < engine->modules.len; ++i) {
585
		engine_unload(engine, engine->modules.at[i]);
586 587 588 589 590
	}
	if (engine->L) {
		lua_close(engine->L);
	}

591 592
	/* Free data structures */
	array_clear(engine->modules);
593
	array_clear(engine->backends);
594
	array_clear(engine->ipc_set);
595 596
	kr_ta_clear(&engine->resolver.trust_anchors);
	kr_ta_clear(&engine->resolver.negative_anchors);
597 598
}

599
int engine_pcall(lua_State *L, int argc)
600 601 602
{
#if LUA_VERSION_NUM >= 502
	lua_getglobal(L, "_SANDBOX");
603
	lua_setupvalue(L, -(2 + argc), 1);
604
#endif
605
	return lua_pcall(L, argc, LUA_MULTRET, 0);
606 607
}

608
int engine_cmd(lua_State *L, const char *str, bool raw)
609
{
610
	if (L == NULL) {
611 612 613 614
		return kr_error(ENOEXEC);
	}

	/* Evaluate results */
615 616 617
	lua_getglobal(L, "eval_cmd");
	lua_pushstring(L, str);
	lua_pushboolean(L, raw);
618 619

	/* Check result. */
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
	return engine_pcall(L, 2);
}

int engine_ipc(struct engine *engine, const char *expr)
{
	if (engine == NULL || engine->L == NULL) {
		return kr_error(ENOEXEC);
	}

	/* Run expression and serialize response. */
	engine_cmd(engine->L, expr, true);
	if (lua_gettop(engine->L) > 0) {
		l_tojson(engine->L);
		return 1;
	} else {
		return 0;
	}
637 638
}

639 640 641
/* Execute byte code */
#define l_dobytecode(L, arr, len, name) \
	(luaL_loadbuffer((L), (arr), (len), (name)) || lua_pcall((L), 0, LUA_MULTRET, 0))
642 643
/** Load file in a sandbox environment. */
#define l_dosandboxfile(L, filename) \
644
	(luaL_loadfile((L), (filename)) || engine_pcall((L), 0))
645

646
static int engine_loadconf(struct engine *engine, const char *config_path)
647
{
648
	/* Use module path for including Lua scripts */
649
	static const char l_paths[] = "package.path = '" MODULEDIR "/?.lua;'..package.path";
650 651 652 653
	int ret = l_dobytecode(engine->L, l_paths, sizeof(l_paths) - 1, "");
	if (ret != 0) {
		lua_pop(engine->L, 1);
	}
654
	/* Init environment */
655 656
	static const char sandbox_bytecode[] = {
		#include "daemon/lua/sandbox.inc"
657
	};
658
	if (l_dobytecode(engine->L, sandbox_bytecode, sizeof(sandbox_bytecode), "init") != 0) {
659 660
		fprintf(stderr, "[system] error %s\n", lua_tostring(engine->L, -1));
		lua_pop(engine->L, 1);
661 662
		return kr_error(ENOEXEC);
	}
663
	/* Load config file */
664 665 666
	if (strcmp(config_path, "-") == 0) {
		return ret; /* No config, no defaults. */
	}
667 668
	if(access(config_path, F_OK ) != -1 ) {
		ret = l_dosandboxfile(engine->L, config_path);
669 670
	}
	if (ret == 0) {
671
		/* Load defaults */
672
		static const char config_bytecode[] = {
673
			#include "daemon/lua/config.inc"
674
		};
675
		ret = l_dobytecode(engine->L, config_bytecode, sizeof(config_bytecode), "config");
676 677 678 679
	}

	/* Evaluate */
	if (ret != 0) {
680
		fprintf(stderr, "%s\n", lua_tostring(engine->L, -1));
681 682
		lua_pop(engine->L, 1);
	}
683
	return ret;
684 685
}

686
int engine_start(struct engine *engine, const char *config_path)
687 688
{
	/* Load configuration. */
689
	int ret = engine_loadconf(engine, config_path);
690 691 692 693
	if (ret != 0) {
		return ret;
	}

694 695 696
	/* Clean up stack and restart GC */
	lua_settop(engine->L, 0);
	lua_gc(engine->L, LUA_GCCOLLECT, 0);
697
	lua_gc(engine->L, LUA_GCSETSTEPMUL, 50);
698 699
	lua_gc(engine->L, LUA_GCSETPAUSE, 400);
	lua_gc(engine->L, LUA_GCRESTART, 0);
700 701 702 703 704 705 706 707 708 709

	/* 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);
	}

710
	return kr_ok();
711 712 713 714
}

void engine_stop(struct engine *engine)
{
715 716 717 718 719 720 721
	if (!engine) {
		return;
	}
	if (engine->updater) {
		uv_timer_stop(engine->updater);
		uv_close((uv_handle_t *)engine->updater, (uv_close_cb) free);
	}
722 723 724
	uv_stop(uv_default_loop());
}

725 726 727 728 729 730 731
/** 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");
	}
732
	for (struct kr_prop *p = module->props; p && p->name; ++p) {
733 734 735 736 737 738 739 740 741
		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);
742
	if (engine_pcall(engine->L, 1) != 0) {
743 744 745 746 747 748
		lua_pop(engine->L, 1);
	}

	return kr_ok();
}

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
/** @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)
764 765 766 767
{
	if (engine == NULL || name == NULL) {
		return kr_error(EINVAL);
	}
768 769
	/* Make sure module is unloaded */
	(void) engine_unregister(engine, name);
770 771 772 773 774 775 776 777 778
	/* 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);
		}
	}
779
	/* Attempt to load binary module */
780 781 782 783
	struct kr_module *module = malloc(sizeof(*module));
	if (!module) {
		return kr_error(ENOMEM);
	}
784
	module->data = engine;
785
	int ret = kr_module_load(module, name, NULL);
786 787
	/* Load Lua module if not a binary */
	if (ret == kr_error(ENOENT)) {
788
		ret = ffimodule_register_lua(engine, module, name);
789
	}
790
	if (ret != 0) {
791
		free(module);
792
		return ret;
793 794 795 796
	}
	if (array_push(engine->modules, module) < 0) {
		engine_unload(engine, module);
		return kr_error(ENOMEM);
797
	}
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
	/* 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;
		}
814
	}
815 816

	/* Register properties */
817
	if (module->props || module->config) {
818
		return register_properties(engine, module);
819 820 821 822 823 824 825
	}

	return kr_ok();
}

int engine_unregister(struct engine *engine, const char *name)
{
826
	module_array_t *mod_list = &engine->modules;
827
	size_t found = module_find(mod_list, name);
828
	if (found < mod_list->len) {
829
		engine_unload(engine, mod_list->at[found]);
830 831 832 833 834 835 836 837 838 839
		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) {
840
#if LUA_VERSION_NUM >= 502
841 842
		luaL_requiref(engine->L, name, lib_cb, 1);
		lua_pop(engine->L, 1);
843 844
#else
		lib_cb(engine->L);
845 846 847 848 849 850
#endif
	}
}

struct engine *engine_luaget(lua_State *L)
{
851
	lua_getglobal(L, "__engine");
852
	struct engine *engine = lua_touserdata(L, -1);
853
	lua_pop(L, 1);
854
	return engine;
855
}