Commit 8819ce16 authored by Marek Vavruša's avatar Marek Vavruša

Merge branch 'lua_config'

parents ec617038 80b63c49
......@@ -12,7 +12,7 @@ MODULEDIR := $(LIBDIR)/kdns_modules
# Tools
CC ?= cc
CFLAGS += -std=c99 -D_GNU_SOURCE -Wall -fPIC -I$(abspath .)
CFLAGS += -std=c99 -D_GNU_SOURCE -Wall -fPIC -I$(abspath .) -I$(abspath lib/generic)
CFLAGS += -DPACKAGE_VERSION="\"$(MAJOR).$(MINOR)\"" -DPREFIX="\"$(PREFIX)\"" -DMODULEDIR="\"$(MODULEDIR)\""
RM := rm -f
LN := ln -s
......
......@@ -14,11 +14,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <uv.h>
#include "lib/cache.h"
#include "daemon/bindings.h"
/** @internal Compatibility wrapper for Lua 5.0 - 5.2 */
#if LUA_VERSION_NUM < 502
#define lua_len(L, obj) \
lua_objlen((L), (obj))
#define register_lib(L, name, lib) \
luaL_openlib((L), (name), (lib), 0)
#else
......@@ -59,8 +63,19 @@ static int mod_load(lua_State *L)
/** Unload module. */
static int mod_unload(lua_State *L)
{
lua_pushstring(L, "not implemented");
lua_error(L);
/* Check parameters */
int n = lua_gettop(L);
if (n != 1 || !lua_isstring(L, 1)) {
lua_pushstring(L, "expected module name");
lua_error(L);
}
/* Unload engine module */
struct engine *engine = engine_luaget(L);
int ret = engine_unregister(engine, lua_tostring(L, 1));
if (ret != 0) {
lua_pushstring(L, kr_strerror(ret));
lua_error(L);
}
return 0;
}
......@@ -77,9 +92,133 @@ int lib_modules(lua_State *L)
return 1;
}
int lib_config(lua_State *L)
/** Append 'addr = {port = int, udp = bool, tcp = bool}' */
static int net_list_add(const char *key, void *val, void *ext)
{
return 0;
lua_State *L = (lua_State *)ext;
endpoint_array_t *ep_array = val;
lua_newtable(L);
for (size_t i = ep_array->len; i--;) {
struct endpoint *ep = ep_array->at[i];
lua_pushinteger(L, ep->port);
lua_setfield(L, -2, "port");
lua_pushboolean(L, ep->flags & NET_UDP);
lua_setfield(L, -2, "udp");
lua_pushboolean(L, ep->flags & NET_TCP);
lua_setfield(L, -2, "tcp");
}
lua_setfield(L, -2, key);
return kr_ok();
}
/** List active endpoints. */
static int net_list(lua_State *L)
{
struct engine *engine = engine_luaget(L);
lua_newtable(L);
map_walk(&engine->net.endpoints, net_list_add, L);
return 1;
}
/** Listen on endpoint. */
static int net_listen(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n < 2) {
lua_pushstring(L, "expected (string addr, int port)");
lua_error(L);
}
/* Open resolution context cache */
struct engine *engine = engine_luaget(L);
int ret = network_listen(&engine->net, lua_tostring(L, 1), lua_tointeger(L, 2), NET_TCP|NET_UDP);
lua_pushboolean(L, ret == 0);
return 1;
}
/** Close endpoint. */
static int net_close(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n < 2) {
lua_pushstring(L, "expected (string addr, int port)");
lua_error(L);
}
/* Open resolution context cache */
struct engine *engine = engine_luaget(L);
int ret = network_close(&engine->net, lua_tostring(L, 1), lua_tointeger(L, 2));
lua_pushboolean(L, ret == 0);
return 1;
}
/** List available interfaces.
*/
static int net_interfaces(lua_State *L)
{
/* Retrieve interface list */
int count = 0;
char buf[INET6_ADDRSTRLEN]; /* http://tools.ietf.org/html/rfc4291 */
uv_interface_address_t *info = NULL;
uv_interface_addresses(&info, &count);
lua_newtable(L);
for (int i = 0; i < count; ++i) {
uv_interface_address_t iface = info[i];
lua_getfield(L, -1, iface.name);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_newtable(L);
}
/* Address */
lua_getfield(L, -1, "addr");
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_newtable(L);
}
if (iface.address.address4.sin_family == AF_INET) {
uv_ip4_name(&iface.address.address4, buf, sizeof(buf));
} else if (iface.address.address4.sin_family == AF_INET6) {
uv_ip6_name(&iface.address.address6, buf, sizeof(buf));
} else {
buf[0] = '\0';
}
lua_pushstring(L, buf);
lua_rawseti(L, -2, lua_len(L, -2) + 1);
lua_setfield(L, -2, "addr");
/* Hardware address. */
char *p = buf;
memset(buf, 0, sizeof(buf));
for (unsigned k = 0; k < sizeof(iface.phys_addr); ++k) {
sprintf(p, "%.2x:", iface.phys_addr[k] & 0xff);
p += 3;
}
*(p - 1) = '\0';
lua_pushstring(L, buf);
lua_setfield(L, -2, "mac");
/* Push table */
lua_setfield(L, -2, iface.name);
}
uv_free_interface_addresses(info, count);
return 1;
}
int lib_net(lua_State *L)
{
static const luaL_Reg lib[] = {
{ "list", net_list },
{ "listen", net_listen },
{ "close", net_close },
{ "interfaces", net_interfaces },
{ NULL, NULL }
};
register_lib(L, "net", lib);
return 1;
}
/** Open cache */
......
......@@ -33,11 +33,11 @@
int lib_modules(lua_State *L);
/**
* Load 'config' package.
* Load 'net' package.
* @param L scriptable
* @return number of packages to load
*/
int lib_config(lua_State *L);
int lib_net(lua_State *L);
/**
* Load 'cache' package.
......
kresolved_SOURCES := \
daemon/layer/query.c \
daemon/udp.c \
daemon/tcp.c \
daemon/io.c \
daemon/network.c \
daemon/engine.c \
daemon/worker.c \
daemon/bindings.c \
daemon/main.c
# Embed resources
daemon/engine.o: daemon/lua/init.inc
daemon/engine.o: daemon/lua/init.inc daemon/lua/config.inc
%.inc: %.lua
@$(call quiet,XXD,$<) -i < $< > $@
@echo ', 0x00' >> $@
......
......@@ -27,6 +27,13 @@
* Global bindings.
*/
/** Register module callback into Lua world. */
#define REGISTER_MODULE_CALL(L, module, cb, name) \
lua_pushlightuserdata((L), (module)); \
lua_pushlightuserdata((L), (cb)); \
lua_pushcclosure((L), l_trampoline, 2); \
lua_setfield((L), -2, (name))
/** Print help and available commands. */
static int l_help(lua_State *L)
{
......@@ -56,26 +63,23 @@ static int l_quit(lua_State *L)
/** Trampoline function for module properties. */
static int l_trampoline(lua_State *L)
{
const char *name = lua_tostring(L, lua_upvalueindex(1));
const char *property = lua_tostring(L, lua_upvalueindex(2));
struct kr_module *module = lua_touserdata(L, lua_upvalueindex(1));
void* callback = lua_touserdata(L, lua_upvalueindex(2));
struct engine *engine = engine_luaget(L);
/* Find module. */
for (unsigned i = 0; i < engine->modules.len; ++i) {
struct kr_module *module = &engine->modules.at[i];
if (strcmp(module->name, name) != 0) {
continue;
}
/* Find property. */
for (struct kr_prop *p = module->props; p && p->name; ++p) {
if (strcmp(p->name, property) == 0) {
auto_free char *ret = p->cb(engine, module, NULL);
lua_pushstring(L, ret);
return 1;
}
}
break;
/* Now we only have property callback or config,
* if we expand the callables, we might need a callback_type.
*/
if (callback == module->config) {
const char *param = lua_tostring(L, 1);
module->config(module, param);
} else {
kr_prop_cb *prop = (kr_prop_cb *)callback;
auto_free char *ret = prop(engine, module, lua_tostring(L, 1));
lua_pushstring(L, ret);
return 1;
}
/* No results */
return 0;
}
......@@ -134,6 +138,8 @@ int engine_init(struct engine *engine, mm_ctx_t *pool)
if (ret != 0) {
return ret;
}
/* Initialize network */
network_init(&engine->net, uv_default_loop());
return ret;
}
......@@ -144,6 +150,8 @@ void engine_deinit(struct engine *engine)
return;
}
network_deinit(&engine->net);
/* Unload modules. */
for (size_t i = 0; i < engine->modules.len; ++i) {
kr_module_unload(&engine->modules.at[i]);
......@@ -186,6 +194,14 @@ int engine_cmd(struct engine *engine, const char *str)
static int engine_loadconf(struct engine *engine)
{
/* Init environment */
static const char l_init[] = {
#include "daemon/lua/init.inc"
};
if (luaL_dostring(engine->L, l_init) != 0) {
return kr_error(ENOEXEC);
}
/* Load config file */
int ret = 0;
if(access("config", F_OK ) != -1 ) {
......@@ -193,7 +209,7 @@ static int engine_loadconf(struct engine *engine)
} else {
/* Load defaults */
static const char config_init[] = {
#include "daemon/lua/init.inc"
#include "daemon/lua/config.inc"
};
ret = luaL_dostring(engine->L, config_init);
}
......@@ -244,11 +260,13 @@ int engine_register(struct engine *engine, const char *name)
/* Register properties */
if (module->props) {
lua_newtable(engine->L);
if (module->config != NULL) {
REGISTER_MODULE_CALL(engine->L, module, module->config, "config");
}
for (struct kr_prop *p = module->props; p->name; ++p) {
lua_pushstring(engine->L, module->name);
lua_pushstring(engine->L, p->name);
lua_pushcclosure(engine->L, l_trampoline, 2);
lua_setfield(engine->L, -2, p->name);
if (p->cb != NULL && p->name != NULL) {
REGISTER_MODULE_CALL(engine->L, module, p->cb, p->name);
}
}
lua_setglobal(engine->L, module->name);
}
......@@ -259,7 +277,7 @@ int engine_register(struct engine *engine, const char *name)
int engine_unregister(struct engine *engine, const char *name)
{
/* Find matching module. */
modulelist_t *mod_list = &engine->modules;
module_array_t *mod_list = &engine->modules;
size_t found = mod_list->len;
for (size_t i = 0; i < mod_list->len; ++i) {
if (strcmp(mod_list->at[i].name, name) == 0) {
......
......@@ -20,14 +20,14 @@
* @internal These are forward decls to allow building modules with engine but without Lua.
*/
struct lua_State;
typedef int (*lua_CFunction) (struct lua_State *L);
#include "lib/resolve.h"
#include "lib/generic/array.h"
#include "daemon/network.h"
struct engine {
struct kr_context resolver;
modulelist_t modules;
struct network net;
module_array_t modules;
mm_ctx_t *pool;
struct lua_State *L;
};
......@@ -40,5 +40,5 @@ void engine_stop(struct engine *engine);
int engine_register(struct engine *engine, const char *module);
int engine_unregister(struct engine *engine, const char *module);
/** Return engine light userdata. */
void engine_lualib(struct engine *engine, const char *name, lua_CFunction lib_cb);
void engine_lualib(struct engine *engine, const char *name, int (*lib_cb) (struct lua_State *));
struct engine *engine_luaget(struct lua_State *L);
\ No newline at end of file
......@@ -17,43 +17,118 @@
#include <libknot/errcode.h>
#include <libknot/internal/utils.h>
#include "daemon/tcp.h"
#include "daemon/io.h"
#include "daemon/network.h"
#include "daemon/worker.h"
static void buf_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
#define ENDPOINT_BUFSIZE 512 /**< This is an artificial limit for DNS query. */
static void *buf_alloc(void)
{
struct endpoint_data *data = malloc(sizeof(*data) + ENDPOINT_BUFSIZE);
if (data == NULL) {
return NULL;
}
data->buf = uv_buf_init((char *)data + sizeof(*data), ENDPOINT_BUFSIZE);
return data;
}
static void buf_get(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
struct endpoint_data *data = handle->data;
*buf = data->buf;
}
static void buf_free(uv_handle_t* handle)
{
free(handle->data);
}
static void udp_send(uv_udp_t *handle, knot_pkt_t *answer, const struct sockaddr *addr)
{
uv_buf_t sendbuf = uv_buf_init((char *)answer->wire, answer->size);
uv_udp_try_send(handle, &sendbuf, 1, addr);
}
static void udp_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf,
const struct sockaddr *addr, unsigned flags)
{
uv_loop_t *loop = handle->loop;
struct worker_ctx *worker = loop->data;
/* Check the incoming wire length. */
if (nread < KNOT_WIRE_HEADER_SIZE) {
return;
}
/* Create packets */
knot_pkt_t *query = knot_pkt_new(buf->base, nread, worker->mm);
knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, worker->mm);
/* Resolve */
int ret = worker_exec(worker, answer, query);
if (ret == KNOT_EOK && answer->size > 0) {
udp_send(handle, answer, addr);
}
/* Cleanup */
knot_pkt_free(&query);
knot_pkt_free(&answer);
}
int udp_bind(struct endpoint *ep, struct sockaddr *addr)
{
uv_udp_t *handle = &ep->udp;
int ret = uv_udp_bind(handle, addr, 0);
if (ret != 0) {
return ret;
}
handle->data = buf_alloc();
if (handle->data == NULL) {
udp_unbind(ep);
return kr_error(ENOMEM);
}
uv_udp_recv_start(handle, &buf_get, &udp_recv);
return 0;
}
void udp_unbind(struct endpoint *ep)
{
struct worker_ctx *worker = handle->data;
buf->base = mm_alloc(worker->mm, suggested_size);
buf->len = suggested_size;
uv_udp_t *handle = &ep->udp;
uv_udp_recv_stop(handle);
buf_free((uv_handle_t *)handle);
uv_close((uv_handle_t *)handle, NULL);
}
static void buf_free(uv_handle_t* handle, const uv_buf_t* buf)
static void tcp_unbind_handle(uv_handle_t *handle)
{
struct worker_ctx *worker = handle->data;
mm_free(worker->mm, buf->base);
uv_read_stop((uv_stream_t *)handle);
buf_free(handle);
uv_close(handle, NULL);
}
static void tcp_send(uv_handle_t *handle, const knot_pkt_t *answer)
{
uint16_t pkt_size = 0;
uint16_t pkt_size = htons(answer->size);
uv_buf_t buf[2];
buf[0].base = (char *)&pkt_size;
buf[0].len = sizeof(pkt_size);
buf[1].base = (char *)answer->wire;
buf[1].len = answer->size;
wire_write_u16((uint8_t *)buf[0].base, answer->size);
uv_try_write((uv_stream_t *)handle, buf, 2);
}
static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
{
struct worker_ctx *worker = handle->data;
uv_loop_t *loop = handle->loop;
struct worker_ctx *worker = loop->data;
/* Check the incoming wire length (malformed, EOF or error). */
if (nread < (ssize_t) sizeof(uint16_t)) {
buf_free((uv_handle_t *)handle, buf);
tcp_unbind((uv_handle_t *)handle);
tcp_unbind_handle((uv_handle_t *)handle);
free(handle);
return;
}
......@@ -74,47 +149,60 @@ static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
/* Cleanup */
knot_pkt_free(&query);
knot_pkt_free(&answer);
buf_free((uv_handle_t *)handle, buf);
}
static void tcp_accept(uv_stream_t *server, int status)
static void tcp_accept(uv_stream_t *master, int status)
{
if (status != 0) {
return;
}
uv_tcp_t *client = malloc(sizeof(uv_tcp_t));
uv_tcp_init(server->loop, client);
client->data = server->data;
if (client == NULL) {
return;
}
if (uv_accept(server, (uv_stream_t*)client) != 0) {
uv_close((uv_handle_t*)client, NULL);
uv_tcp_init(master->loop, client);
client->data = buf_alloc();
if (client->data == NULL) {
tcp_unbind_handle((uv_handle_t *)client);
free(client);
return;
}
uv_read_start((uv_stream_t*)client, buf_alloc, tcp_recv);
if (uv_accept(master, (uv_stream_t*)client) != 0) {
tcp_unbind_handle((uv_handle_t *)client);
free(client);
return;
}
uv_read_start((uv_stream_t*)client, buf_get, tcp_recv);
}
int tcp_bind(uv_handle_t *handle, struct worker_ctx *worker, struct sockaddr *addr)
int tcp_bind(struct endpoint *ep, struct sockaddr *addr)
{
uv_tcp_t *sock = (uv_tcp_t *)handle;
int ret = uv_tcp_bind(sock, addr, 0);
uv_tcp_t *handle = &ep->tcp;
int ret = uv_tcp_bind(handle, addr, 0);
if (ret != 0) {
return KNOT_ERROR;
return ret;
}
ret = uv_listen((uv_stream_t *)sock, 128, tcp_accept);
ret = uv_listen((uv_stream_t *)handle, 16, tcp_accept);
if (ret != 0) {
return KNOT_ERROR;
tcp_unbind(ep);
return ret;
}
sock->data = worker;
return KNOT_EOK;
handle->data = buf_alloc();
if (handle->data == NULL) {
tcp_unbind(ep);
return kr_error(ENOMEM);
}
return 0;
}
void tcp_unbind(uv_handle_t *handle)
void tcp_unbind(struct endpoint *ep)
{
uv_udp_recv_stop((uv_udp_t *)handle);
uv_close(handle, NULL);
tcp_unbind_handle((uv_handle_t *)&ep->tcp);
}
......@@ -17,7 +17,9 @@
#pragma once
#include <uv.h>
#include "worker.h"
struct endpoint;
int tcp_bind(uv_handle_t *handle, struct worker_ctx *worker, struct sockaddr *addr);
void tcp_unbind(uv_handle_t *handle);
int udp_bind(struct endpoint *ep, struct sockaddr *addr);
void udp_unbind(struct endpoint *ep);
int tcp_bind(struct endpoint *ep, struct sockaddr *addr);
void tcp_unbind(struct endpoint *ep);
\ No newline at end of file
-- Default configuration
cache.open('.', 10485760)
-- Default configuration
cache.open('.', 10485760)
-- Syntactic sugar for module loading
-- `modules.<name> = <config>`
setmetatable(modules, {
__newindex = function (t,k,v)
modules.load(k)
if _G[k] then
local config_call = _G[k]['config']
if config_call and config_call[''] then
config_call(v)
end
end
end
})
-- Some services are append-only
function protect(defined)
local __protected = { ['modules'] = true }
return setmetatable({}, {
__index = defined,
__newindex = function (t, k, v)
if __protected[k] then
for k2,v2 in pairs(v) do
defined[k][k2] = v2
end
else
defined[k] = v
end
end
})
end
_G = protect(getfenv(0))
setfenv(0, _G)
......@@ -22,8 +22,8 @@
#include "lib/defines.h"
#include "lib/resolve.h"
#include "daemon/udp.h"
#include "daemon/tcp.h"
#include "daemon/network.h"
#include "daemon/worker.h"
#include "daemon/engine.h"
#include "daemon/bindings.h"
......@@ -63,27 +63,21 @@ static void help(int argc, char *argv[])
" [rundir] Path to the working directory (default: .)\n");
}
static int set_addr(struct sockaddr_storage *ss, char *addr)
static const char *set_addr(char *addr, int *port)
{
char *port = strchr(addr, '#');
if (port) {
sockaddr_port_set(ss, atoi(port + 1));
*port = '\0';
char *p = strchr(addr, '#');
if (p) {
*port = atoi(p + 1);
*p = '\0';
}
int family = AF_INET;
if (strchr(addr, ':')) {
family = AF_INET6;
}
return sockaddr_set(ss, family, addr, sockaddr_port(ss));
return addr;
}
int main(int argc, char **argv)
{
struct sockaddr_storage addr;
sockaddr_set(&addr, AF_INET, "127.0.0.1", 53);
const char *addr = "127.0.0.1";
int port = 53;
/* Long options. */
int c = 0, li = 0, ret = 0;
......@@ -97,11 +91,7 @@ int main(int argc, char **argv)
switch (c)
{
case 'a':
ret = set_addr(&addr, optarg);
if (ret != 0) {
fprintf(stderr, "[system]: address '%s': %s\n", optarg, knot_strerror(ret));
return EXIT_FAILURE;
}
addr = set_addr(optarg, &port);
break;
case 'v':
printf("%s, version %s\n", "Knot DNS Resolver", PACKAGE_VERSION);
......@@ -144,8 +134,8 @@ int main(int argc, char **argv)
/* Load bindings */
engine_lualib(&engine, "modules", lib_modules);
engine_lualib(&engine, "config", lib_config);
engine_lualib(&engine, "cache", lib_cache);
engine_lualib(&engine, "net", lib_net);
engine_lualib(&engine, "cache", lib_cache);
/* Create main worker. */
struct worker_ctx worker = {
......@@ -153,24 +143,12 @@ int main(int argc, char **argv)
.loop = loop,
.mm = NULL
};
loop->data = &worker;
/* Bind to sockets. */
char addr_str[SOCKADDR_STRLEN] = {'\0'};
sockaddr_tostr(addr_str, sizeof(addr_str), &addr);
uv_udp_t udp_sock;
uv_udp_init(loop, &udp_sock);
uv_tcp_t tcp_sock;
uv_tcp_init(loop, &tcp_sock);
printf("[system] listening on '%s/UDP'\n", addr_str);
ret = udp_bind((uv_handle_t *)&udp_sock, &worker, (struct sockaddr *)&addr);
if (ret == 0) {
printf("[system] listening on '%s/TCP'\n", addr_str);
ret = tcp_bind((uv_handle_t *)&tcp_sock, &worker, (struct sockaddr *)&addr);
}
/* Check results */
ret = network_listen(&engine.net, addr, (uint16_t)port, NET_UDP|NET_TCP);
if (ret != 0) {
fprintf(stderr, "[system] bind to '%s' %s\n", addr_str, knot_strerror(ret));
fprintf(stderr, "[system] bind to '%s#%d' %s\n", addr, port, knot_strerror(ret));
ret = EXIT_FAILURE;
} else {
/* Allocate TTY */
......@@ -181,6 +159,7 @@ int main(int argc, char **argv)
/* Interactive stdin */
if (!feof(stdin)) {
printf("[system] listening on '%s#%d'\n", addr, port);
printf("[system] started in interactive mode, type 'help()'\n");
tty_read(NULL, 0, NULL);
uv_read_start((uv_stream_t*) &pipe, tty_alloc, tty_read);
......@@ -191,8 +170,7 @@ int main(int argc, char **argv)