Commit 954e0ee6 authored by Marek Vavruša's avatar Marek Vavruša

Merge branch 'lua'

parents 00ff09a7 5e91a0b6
......@@ -11,10 +11,15 @@
*.swp
*.d
*.db
*.out
*.6
*.log
*.inc
.dirstamp
.libs
.deps
_obj
tmp*
/autom4te.cache/*
/config.log
/config.h
......
......@@ -17,11 +17,23 @@ endif
# Dependencies
$(eval $(call find_lib,libknot))
$(eval $(call find_lib,libuv))
$(eval $(call find_alt,lua,luajit))
$(eval $(call find_alt,lua,lua5.2))
$(eval $(call find_alt,lua,lua))
$(eval $(call find_lib,cmocka))
$(eval $(call find_bin,doxygen))
$(eval $(call find_bin,sphinx-build))
$(eval $(call find_bin,gccgo))
$(eval $(call find_python))
CFLAGS += $(libknot_CFLAGS) $(libuv_CFLAGS) $(cmocka_CFLAGS) $(python_CFLAGS)
# Work around luajit on OS X
ifeq ($(PLATFORM), Darwin)
ifneq (,$(findstring luajit, $(lua_LIBS)))
lua_LIBS += -pagezero_size 10000 -image_base 100000000
endif
endif
CFLAGS += $(libknot_CFLAGS) $(libuv_CFLAGS) $(cmocka_CFLAGS) $(python_CFLAGS) $(lua_CFLAGS)
# Sub-targets
include help.mk
......
......@@ -11,12 +11,11 @@ INCLUDEDIR := /include
MODULEDIR := $(LIBDIR)/kdns_modules
# Tools
ifndef CC
CC := cc
endif
CC ?= cc
CFLAGS += -std=c99 -D_GNU_SOURCE -Wall -fPIC -I$(abspath .)
CFLAGS += -DPACKAGE_VERSION="\"$(MAJOR).$(MINOR)\"" -DPREFIX="\"$(PREFIX)\"" -DMODULEDIR="\"$(MODULEDIR)\""
RM := rm -f
LN := ln -s
LN := ln -s
XXD ?= xxd
INSTALL := install
PYTHON := python
PYTHON := python
/* 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/>.
*/
#include "lib/cache.h"
#include "daemon/bindings.h"
/** @internal Compatibility wrapper for Lua 5.0 - 5.2 */
#if LUA_VERSION_NUM < 502
#define register_lib(L, name, lib) \
luaL_openlib((L), (name), (lib), 0)
#else
#define register_lib(L, name, lib) \
luaL_newlib((L), (lib))
#endif
/** List loaded modules */
static int mod_list(lua_State *L)
{
struct engine *engine = engine_luaget(L);
for (unsigned i = 0; i < engine->modules.len; ++i) {
struct kr_module *module = &engine->modules.at[i];
lua_pushstring(L, module->name);
}
return engine->modules.len;
}
/** Load module. */
static int mod_load(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n != 1 || !lua_isstring(L, 1)) {
lua_pushstring(L, "expected module name");
lua_error(L);
}
/* Load engine module */
struct engine *engine = engine_luaget(L);
int ret = engine_register(engine, lua_tostring(L, 1));
if (ret != 0) {
lua_pushstring(L, kr_strerror(ret));
lua_error(L);
}
return 0;
}
/** Unload module. */
static int mod_unload(lua_State *L)
{
lua_pushstring(L, "not implemented");
lua_error(L);
return 0;
}
int lib_modules(lua_State *L)
{
static const luaL_Reg lib[] = {
{ "list", mod_list },
{ "load", mod_load },
{ "unload", mod_unload },
{ NULL, NULL }
};
register_lib(L, "modules", lib);
return 1;
}
int lib_config(lua_State *L)
{
return 0;
}
/** Open cache */
static int cache_open(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n < 2) {
lua_pushstring(L, "expected (string path, int size)");
lua_error(L);
}
/* Open resolution context cache */
struct engine *engine = engine_luaget(L);
engine->resolver.cache = kr_cache_open(lua_tostring(L, 1), engine->pool, lua_tointeger(L, 2));
if (engine->resolver.cache == NULL) {
lua_pushstring(L, "invalid cache directory: ");
lua_pushstring(L, lua_tostring(L, 1));
lua_concat(L, 2);
lua_error(L);
}
lua_pushboolean(L, 1);
return 1;
}
static int cache_close(lua_State *L)
{
struct engine *engine = engine_luaget(L);
if (engine->resolver.cache != NULL) {
kr_cache_close(engine->resolver.cache);
engine->resolver.cache = NULL;
}
return 0;
}
int lib_cache(lua_State *L)
{
static const luaL_Reg lib[] = {
{ "open", cache_open },
{ "close", cache_close },
{ NULL, NULL }
};
register_lib(L, "cache", lib);
return 0;
}
\ No newline at end of file
......@@ -14,9 +14,30 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Bindings to engine services, see \a http://www.lua.org/manual/5.2/manual.html#luaL_newlib for the reference.
*/
#pragma once
#include <uv.h>
#include "daemon/worker.h"
#include "daemon/engine.h"
/**
* Load 'modules' package.
* @param L scriptable
* @return number of packages to load
*/
int lib_modules(lua_State *L);
/**
* Load 'config' package.
* @param L scriptable
* @return number of packages to load
*/
int lib_config(lua_State *L);
int cmd_exec(struct worker_ctx *worker, char *cmd);
\ No newline at end of file
/**
* Load 'cache' package.
* @param L scriptable
* @return number of packages to load
*/
int lib_cache(lua_State *L);
\ No newline at end of file
/* 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/>.
*/
#include <libknot/internal/mempool.h>
#include "daemon/cmd.h"
#include "lib/defines.h"
struct cmd {
const char *name;
int (*cb)(struct worker_ctx *, char *);
};
static int help(struct worker_ctx *worker, char *args)
{
printf("help:\n show this help\n");
printf("context:\n show context information\n");
printf("load:\n load module\n");
printf("unload:\n unload module\n");
struct kr_context *ctx = &worker->resolve;
for (unsigned i = 0; i < ctx->mod_loaded; ++i) {
struct kr_module *mod = &ctx->modules[i];
for (struct kr_prop *p = mod->props; p && p->name; ++p) {
printf("%s.%s:\n %s\n", mod->name, p->name, p->info);
}
}
return kr_ok();
}
static int context(struct worker_ctx *worker, char *args)
{
struct kr_context *ctx = &worker->resolve;
/* Modules */
printf("modules:\n");
for (unsigned i = 0; i < ctx->mod_loaded; ++i) {
struct kr_module *mod = &ctx->modules[i];
printf(" %s\n", mod->name);
}
/* Options */
printf("options: 0x%x\n", ctx->options);
return kr_ok();
}
static int mod_load(struct worker_ctx *worker, char *args)
{
struct kr_context *ctx = &worker->resolve;
char *saveptr = NULL;
char *prop_name = strtok_r(args, " \t\n\r", &saveptr);
return kr_context_register(ctx, prop_name);
}
static int mod_unload(struct worker_ctx *worker, char *args)
{
return kr_error(ENOTSUP);
}
static int cmd_exec_prop(struct worker_ctx *worker, char *name, char *prop, char *args)
{
struct kr_context *ctx = &worker->resolve;
for (unsigned i = 0; i < ctx->mod_loaded; ++i) {
struct kr_module *mod = &ctx->modules[i];
if (strncmp(mod->name, name, strlen(mod->name)) != 0) {
continue;
}
for (struct kr_prop *p = mod->props; p && p->name; ++p) {
if (strncmp(p->name, prop, strlen(p->name)) == 0) {
auto_free char *res = p->cb(ctx, mod, args);
printf("%s\n", res);
return kr_ok();
}
}
}
return kr_error(ENOENT);
}
int cmd_exec(struct worker_ctx *worker, char *cmd)
{
static struct cmd cmd_table[] = {
{ "help", &help },
{ "context", &context },
{ "load", &mod_load },
{ "unload", &mod_unload },
{ NULL, NULL }
};
int ret = kr_error(ENOENT);
char *args = strchr(cmd, ' ');
if (args != NULL) {
*args = '\0';
args += 1;
}
/* Search builtin namespace. */
for (struct cmd *c = cmd_table; c->name; ++c) {
if (strncmp(cmd, c->name, strlen(c->name)) == 0) {
return c->cb(worker, args);
}
}
/* Search module namespace. */
char *prop = strchr(cmd, '.');
if (prop != NULL) {
ret = cmd_exec_prop(worker, cmd, prop + 1, args);
}
return ret;
}
\ No newline at end of file
......@@ -2,18 +2,30 @@ kresolved_SOURCES := \
daemon/layer/query.c \
daemon/udp.c \
daemon/tcp.c \
daemon/cmd.c \
daemon/engine.c \
daemon/worker.c \
daemon/bindings.c \
daemon/main.c
# Embed resources
daemon/engine.o: daemon/lua/init.inc
%.inc: %.lua
@$(call quiet,XXD,$<) -i < $< > $@
@echo ', 0x00' >> $@
# Dependencies
kresolved_DEPEND := libkresolve libknot libuv
kresolved_LIBS := $(libkresolve_TARGET) $(libknot_LIBS) $(libuv_LIBS)
kresolved_DEPEND := $(libkresolve)
kresolved_LIBS := $(libkresolve_TARGET) $(libknot_LIBS) $(libuv_LIBS) $(lua_LIBS)
# Make binary
ifeq ($(HAS_lua)|$(HAS_libuv), yes|yes)
$(eval $(call make_bin,kresolved,daemon))
endif
# Targets
daemon: kresolved
daemon: $(kresolved)
daemon-install: kresolved-install
daemon-clean: kresolved-clean
@$(RM) daemon/lua/*.inc
.PHONY: daemon daemon-install daemon-clean
/* 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/>.
*/
#include <uv.h>
#include <unistd.h>
#include <libknot/internal/mem.h>
#include "daemon/engine.h"
#include "lib/cache.h"
#include "lib/defines.h"
/*
* Global bindings.
*/
/** Print help and available commands. */
static int l_help(lua_State *L)
{
static const char *help_str =
"help()\n show this help\n"
"quit()\n quit\n"
"modules.list()\n list modules\n"
"modules.load()\n load module\n"
"modules.unload()\n unload module\n"
"cache.open(path, max_size)\n open cache\n"
"cache.close()\n close cache\n"
;
puts(help_str);
/* No results */
return 0;
}
/** Quit current executable. */
static int l_quit(lua_State *L)
{
/* Stop engine */
engine_stop(engine_luaget(L));
/* No results */
return 0;
}
/** 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 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;
}
/* No results */
return 0;
}
/*
* Engine API.
*/
static int init_resolver(struct engine *engine)
{
/* Open resolution context */
engine->resolver.modules = &engine->modules;
/* Load basic modules */
engine_register(engine, "iterate");
engine_register(engine, "itercache");
return kr_ok();
}
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. */
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");
lua_pushlightuserdata(engine->L, engine);
lua_setglobal(engine->L, "__engine");
return kr_ok();
}
int engine_init(struct engine *engine, mm_ctx_t *pool)
{
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) {
return ret;
}
return ret;
}
void engine_deinit(struct engine *engine)
{
if (engine == NULL) {
return;
}
/* Unload modules. */
for (size_t i = 0; i < engine->modules.len; ++i) {
kr_module_unload(&engine->modules.at[i]);
}
array_clear(engine->modules);
if (engine->L) {
lua_close(engine->L);
}
kr_cache_close(engine->resolver.cache);
}
int engine_cmd(struct engine *engine, const char *str)
{
if (engine == NULL || engine->L == NULL) {
return kr_error(ENOEXEC);
}
/* Evaluate results */
int ret = luaL_dostring(engine->L, str);
/* Print results. */
int nres = lua_gettop(engine->L);
for (int i = 0; i < nres; ++i) {
const char *out = lua_tostring(engine->L, -1);
if (out != NULL) {
printf("%s\n", out);
}
lua_pop(engine->L, 1);
}
/* Check result. */
if (ret != 0) {
return kr_error(EINVAL);
}
return kr_ok();
}
static int engine_loadconf(struct engine *engine)
{
/* Load config file */
int ret = 0;
if(access("config", F_OK ) != -1 ) {
ret = luaL_dofile(engine->L, "config");
} else {
/* Load defaults */
static const char config_init[] = {
#include "daemon/lua/init.inc"
};
ret = luaL_dostring(engine->L, config_init);
}
/* Evaluate */
if (ret != 0) {
fprintf(stderr, "error: %s\n", lua_tostring(engine->L, -1));
lua_pop(engine->L, 1);
return kr_error(EINVAL);
}
return kr_ok();
}
int engine_start(struct engine *engine)
{
/* Load configuration. */
int ret = engine_loadconf(engine);
if (ret != 0) {
return ret;
}
return uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}
void engine_stop(struct engine *engine)
{
uv_stop(uv_default_loop());
}
int engine_register(struct engine *engine, const char *name)
{
if (engine == NULL || name == NULL) {
return kr_error(EINVAL);
}
/* Load module */
size_t next = engine->modules.len;
array_reserve(engine->modules, next + 1);
struct kr_module *module = &engine->modules.at[next];
int ret = kr_module_load(module, name, NULL);
if (ret != 0) {
return ret;
} else {
engine->modules.len += 1;
}
/* Register properties */
if (module->props) {
lua_newtable(engine->L);
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);
}
lua_setglobal(engine->L, module->name);