Verified Commit dd9e33dc authored by Vladimír Čunát's avatar Vladimír Čunát Committed by Petr Špaček

split daemon/bindings.c file

It's now a directory, one C file for each lua table of functions.
We get more total lines due to per-file copyright headers,
but the original file was just too long (nearing 2k lines).
The layout was inspired by the lib/cache/ split.

Disadvantage: git operations (e.g. blame) will stumble on this commit.

During the move, rename the "str" and "xstr" macros, too.
Otherwise there are no real changes, as they would be hard to spot.
parent 0f962b7b
/* Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <lua.h>
int lib_cache (lua_State *L);
int lib_event (lua_State *L);
int lib_modules (lua_State *L);
int lib_net (lua_State *L);
int lib_worker (lua_State *L);
This diff is collapsed.
/* Copyright (C) 2015-2019 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 <https://www.gnu.org/licenses/>.
*/
#include "daemon/bindings/impl.h"
#include "daemon/worker.h"
#include <unistd.h>
#include <uv.h>
static void event_free(uv_timer_t *timer)
{
struct worker_ctx *worker = timer->loop->data;
lua_State *L = worker->engine->L;
int ref = (intptr_t) timer->data;
luaL_unref(L, LUA_REGISTRYINDEX, ref);
free(timer);
}
static void event_callback(uv_timer_t *timer)
{
struct worker_ctx *worker = timer->loop->data;
lua_State *L = worker->engine->L;
/* Retrieve callback and execute */
lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) timer->data);
lua_rawgeti(L, -1, 1);
lua_pushinteger(L, (intptr_t) timer->data);
int ret = execute_callback(L, 1);
/* Free callback if not recurrent or an error */
if (ret != 0 || (uv_timer_get_repeat(timer) == 0 && uv_is_active((uv_handle_t *)timer) == 0)) {
if (!uv_is_closing((uv_handle_t *)timer)) {
uv_close((uv_handle_t *)timer, (uv_close_cb) event_free);
}
}
}
static void event_fdcallback(uv_poll_t* handle, int status, int events)
{
struct worker_ctx *worker = handle->loop->data;
lua_State *L = worker->engine->L;
/* Retrieve callback and execute */
lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) handle->data);
lua_rawgeti(L, -1, 1);
lua_pushinteger(L, (intptr_t) handle->data);
lua_pushinteger(L, status);
lua_pushinteger(L, events);
int ret = execute_callback(L, 3);
/* Free callback if not recurrent or an error */
if (ret != 0) {
if (!uv_is_closing((uv_handle_t *)handle)) {
uv_close((uv_handle_t *)handle, (uv_close_cb) event_free);
}
}
}
static int event_sched(lua_State *L, unsigned timeout, unsigned repeat)
{
uv_timer_t *timer = malloc(sizeof(*timer));
if (!timer) {
format_error(L, "out of memory");
lua_error(L);
}
/* Start timer with the reference */
uv_loop_t *loop = uv_default_loop();
uv_timer_init(loop, timer);
int ret = uv_timer_start(timer, event_callback, timeout, repeat);
if (ret != 0) {
free(timer);
format_error(L, "couldn't start the event");
lua_error(L);
}
/* Save callback and timer in registry */
lua_newtable(L);
lua_pushvalue(L, 2);
lua_rawseti(L, -2, 1);
lua_pushlightuserdata(L, timer);
lua_rawseti(L, -2, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
/* Save reference to the timer */
timer->data = (void *) (intptr_t)ref;
lua_pushinteger(L, ref);
return 1;
}
static int event_after(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2)) {
format_error(L, "expected 'after(number timeout, function)'");
lua_error(L);
}
return event_sched(L, lua_tonumber(L, 1), 0);
}
static int event_recurrent(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2)) {
format_error(L, "expected 'recurrent(number interval, function)'");
lua_error(L);
}
return event_sched(L, 0, lua_tonumber(L, 1));
}
static int event_cancel(lua_State *L)
{
int n = lua_gettop(L);
if (n < 1 || !lua_isnumber(L, 1)) {
format_error(L, "expected 'cancel(number event)'");
lua_error(L);
}
/* Fetch event if it exists */
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_tointeger(L, 1));
if (!lua_istable(L, -1)) {
lua_pushboolean(L, false);
return 1;
}
/* Close the timer */
lua_rawgeti(L, -1, 2);
uv_handle_t *timer = lua_touserdata(L, -1);
if (!uv_is_closing(timer)) {
uv_close(timer, (uv_close_cb) event_free);
}
lua_pushboolean(L, true);
return 1;
}
static int event_reschedule(lua_State *L)
{
int n = lua_gettop(L);
if (n < 2 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2)) {
format_error(L, "expected 'reschedule(number event, number timeout)'");
lua_error(L);
}
/* Fetch event if it exists */
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_tointeger(L, 1));
if (!lua_istable(L, -1)) {
lua_pushboolean(L, false);
return 1;
}
/* Reschedule the timer */
lua_rawgeti(L, -1, 2);
uv_handle_t *timer = lua_touserdata(L, -1);
if (!uv_is_closing(timer)) {
if (uv_is_active(timer)) {
uv_timer_stop((uv_timer_t *)timer);
}
int ret = uv_timer_start((uv_timer_t *)timer, event_callback, lua_tointeger(L, 2), 0);
if (ret != 0) {
event_cancel(L);
lua_pushboolean(L, false);
return 1;
}
}
lua_pushboolean(L, true);
return 1;
}
static int event_fdwatch(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2)) {
format_error(L, "expected 'socket(number fd, function)'");
lua_error(L);
}
uv_poll_t *handle = malloc(sizeof(*handle));
if (!handle) {
format_error(L, "out of memory");
lua_error(L);
}
/* Start timer with the reference */
int sock = lua_tonumber(L, 1);
uv_loop_t *loop = uv_default_loop();
#if defined(__APPLE__) || defined(__FreeBSD__)
/* libuv is buggy and fails to create poller for
* kqueue sockets as it can't be fcntl'd to non-blocking mode,
* so we pass it a copy of standard input and then
* switch it with real socket before starting the poller
*/
int decoy_fd = dup(STDIN_FILENO);
int ret = uv_poll_init(loop, handle, decoy_fd);
if (ret == 0) {
handle->io_watcher.fd = sock;
}
close(decoy_fd);
#else
int ret = uv_poll_init(loop, handle, sock);
#endif
if (ret == 0) {
ret = uv_poll_start(handle, UV_READABLE, event_fdcallback);
}
if (ret != 0) {
free(handle);
format_error(L, "couldn't start event poller");
lua_error(L);
}
/* Save callback and timer in registry */
lua_newtable(L);
lua_pushvalue(L, 2);
lua_rawseti(L, -2, 1);
lua_pushlightuserdata(L, handle);
lua_rawseti(L, -2, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
/* Save reference to the timer */
handle->data = (void *) (intptr_t)ref;
lua_pushinteger(L, ref);
return 1;
}
int lib_event(lua_State *L)
{
static const luaL_Reg lib[] = {
{ "after", event_after },
{ "recurrent", event_recurrent },
{ "cancel", event_cancel },
{ "socket", event_fdwatch },
{ "reschedule", event_reschedule },
{ NULL, NULL }
};
register_lib(L, "event", lib);
return 1;
}
/* Copyright (C) 2015-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2015-2019 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
......@@ -14,18 +14,17 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Bindings to engine services, see \a https://www.lua.org/manual/5.2/manual.html#luaL_newlib for the reference.
*/
#pragma once
#include "daemon/engine.h"
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "daemon/engine.h"
/** @internal Compatibility wrapper for Lua 5.0 - 5.2 */
/** @internal Compatibility wrapper for Lua 5.0 - 5.2
https://www.lua.org/manual/5.2/manual.html#luaL_newlib
*/
#if LUA_VERSION_NUM >= 502
#define register_lib(L, name, lib) \
luaL_newlib((L), (lib))
......@@ -36,53 +35,31 @@
luaL_openlib((L), (name), (lib), 0)
#endif
#if !LUA_HAS_SETFUNCS
/* Adapted from Lua 5.2.0 */
static inline void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
luaL_checkstack(L, nup+1, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */
int i;
lua_pushstring(L, l->name);
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -(nup+1));
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
lua_settable(L, -(nup + 3));
}
lua_pop(L, nup); /* remove upvalues */
}
#endif
/** Useful to stringify #defines into error strings. */
#define STR(s) STRINGIFY_TOKEN(s)
#define STRINGIFY_TOKEN(s) #s
/**
* Load 'modules' package.
* @param L scriptable
* @return number of packages to load
*/
int lib_modules(lua_State *L);
/** @internal Prefix error with file:line
* Implementation lumped into ./worker.c */
int KR_COLD format_error(lua_State* L, const char *err);
/** @internal Annotate for static checkers. */
KR_NORETURN int lua_error(lua_State *L);
/**
* Load 'net' package.
* @param L scriptable
* @return number of packages to load
*/
int lib_net(lua_State *L);
/**
* Load 'cache' package.
* @param L scriptable
* @return number of packages to load
*/
int lib_cache(lua_State *L);
static inline struct worker_ctx *wrk_luaget(lua_State *L) {
lua_getglobal(L, "__worker");
struct worker_ctx *worker = lua_touserdata(L, -1);
lua_pop(L, 1);
return worker;
}
/**
* Load 'event' package.
* @param L scriptable
* @return number of packages to load
*/
int lib_event(lua_State *L);
static inline int execute_callback(lua_State *L, int argc)
{
int ret = engine_pcall(L, argc);
if (ret != 0) {
fprintf(stderr, "error: %s\n", lua_tostring(L, -1));
}
/* Clear the stack, there may be event a/o enything returned */
lua_settop(L, 0);
return ret;
}
/**
* Load worker API.
* @param L scriptable
* @return number of packages to load
*/
int lib_worker(lua_State *L);
/* Copyright (C) 2015-2019 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 <https://www.gnu.org/licenses/>.
*/
#include "daemon/bindings/impl.h"
/** List loaded modules */
static int mod_list(lua_State *L)
{
struct engine *engine = engine_luaget(L);
lua_newtable(L);
for (unsigned i = 0; i < engine->modules.len; ++i) {
struct kr_module *module = engine->modules.at[i];
lua_pushstring(L, module->name);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
/** Load module. */
static int mod_load(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n != 1 || !lua_isstring(L, 1)) {
format_error(L, "expected 'load(string name)'");
lua_error(L);
}
/* Parse precedence declaration */
char *declaration = strdup(lua_tostring(L, 1));
if (!declaration) {
return kr_error(ENOMEM);
}
const char *name = strtok(declaration, " ");
const char *precedence = strtok(NULL, " ");
const char *ref = strtok(NULL, " ");
/* Load engine module */
struct engine *engine = engine_luaget(L);
int ret = engine_register(engine, name, precedence, ref);
free(declaration);
if (ret != 0) {
if (ret == kr_error(EIDRM)) {
format_error(L, "referenced module not found");
} else {
format_error(L, kr_strerror(ret));
}
lua_error(L);
}
lua_pushboolean(L, 1);
return 1;
}
/** Unload module. */
static int mod_unload(lua_State *L)
{
/* Check parameters */
int n = lua_gettop(L);
if (n != 1 || !lua_isstring(L, 1)) {
format_error(L, "expected 'unload(string 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) {
format_error(L, kr_strerror(ret));
lua_error(L);
}
lua_pushboolean(L, 1);
return 1;
}
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;
}
This diff is collapsed.
/* Copyright (C) 2015-2019 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 <https://www.gnu.org/licenses/>.
*/
#include "daemon/bindings/impl.h"
#include "daemon/worker.h"
#include <string.h>
int format_error(lua_State* L, const char *err)
{
lua_Debug d;
lua_getstack(L, 1, &d);
/* error message prefix */
lua_getinfo(L, "Sln", &d);
if (strncmp(d.short_src, "[", 1) != 0) {
lua_pushstring(L, d.short_src);
lua_pushstring(L, ":");
lua_pushnumber(L, d.currentline);
lua_pushstring(L, ": error: ");
lua_concat(L, 4);
} else {
lua_pushstring(L, "error: ");
}
/* error message */
lua_pushstring(L, err);
lua_concat(L, 2);
return 1;
}
static int wrk_resolve(lua_State *L)
{
struct worker_ctx *worker = wrk_luaget(L);
if (!worker) {
return 0;
}
uint8_t dname[KNOT_DNAME_MAXLEN];
if (!knot_dname_from_str(dname, lua_tostring(L, 1), sizeof(dname))) {
lua_pushstring(L, "invalid qname");
lua_error(L);
};
/* Check class and type */
uint16_t rrtype = lua_tointeger(L, 2);
if (!lua_isnumber(L, 2)) {
lua_pushstring(L, "invalid RR type");
lua_error(L);
}
uint16_t rrclass = lua_tointeger(L, 3);
if (!lua_isnumber(L, 3)) { /* Default class is IN */
rrclass = KNOT_CLASS_IN;
}
/* Add query options */
const struct kr_qflags *options = lua_topointer(L, 4);
if (!options) { /* but we rely on the lua wrapper when dereferencing non-NULL */
lua_pushstring(L, "invalid options");
lua_error(L);
}
/* Create query packet */
knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_EDNS_MAX_UDP_PAYLOAD, NULL);
if (!pkt) {
lua_pushstring(L, kr_strerror(ENOMEM));
lua_error(L);
}
knot_pkt_put_question(pkt, dname, rrclass, rrtype);
knot_wire_set_rd(pkt->wire);
knot_wire_set_ad(pkt->wire);
/* Add OPT RR */
pkt->opt_rr = knot_rrset_copy(worker->engine->resolver.opt_rr, NULL);
if (!pkt->opt_rr) {
knot_pkt_free(pkt);
return kr_error(ENOMEM);
}
if (options->DNSSEC_WANT) {
knot_edns_set_do(pkt->opt_rr);
}
if (options->DNSSEC_CD) {
knot_wire_set_cd(pkt->wire);
}
/* Create task and start with a first question */
struct qr_task *task = worker_resolve_start(worker, pkt, *options);
if (!task) {
knot_rrset_free(pkt->opt_rr, NULL);
knot_pkt_free(pkt);
lua_pushstring(L, "couldn't create a resolution request");
lua_error(L);
}
/* Add initialisation callback */
if (lua_isfunction(L, 5)) {
lua_pushvalue(L, 5);
lua_pushlightuserdata(L, worker_task_request(task));
(void) execute_callback(L, 1);
}
/* Start execution */
int ret = worker_resolve_exec(task, pkt);
lua_pushboolean(L, ret == 0);
knot_rrset_free(pkt->opt_rr, NULL);
knot_pkt_free(pkt);
return 1;
}
static inline double getseconds(uv_timeval_t *tv)
{
return (double)tv->tv_sec + 0.000001*((double)tv->tv_usec);
}
/** Return worker statistics. */
static int wrk_stats(lua_State *L)
{
struct worker_ctx *worker = wrk_luaget(L);
if (!worker) {
return 0;
}
lua_newtable(L);
lua_pushnumber(L, worker->stats.concurrent);
lua_setfield(L, -2, "concurrent");
lua_pushnumber(L, worker->stats.udp);
lua_setfield(L, -2, "udp");
lua_pushnumber(L, worker->stats.tcp);
lua_setfield(L, -2, "tcp");
lua_pushnumber(L, worker->stats.tls);
lua_setfield(L, -2, "tls");
lua_pushnumber(L, worker->stats.ipv6);
lua_setfield(L, -2, "ipv6");
lua_pushnumber(L, worker->stats.ipv4);
lua_setfield(L, -2, "ipv4");
lua_pushnumber(L, worker->stats.queries);
lua_setfield(L, -2, "queries");
lua_pushnumber(L, worker->stats.dropped);
lua_setfield(L, -2, "dropped");
lua_pushnumber(L, worker->stats.timeout);
lua_setfield(L, -2, "timeout");
/* Add subset of rusage that represents counters. */
uv_rusage_t rusage;
if (uv_getrusage(&rusage) == 0) {
lua_pushnumber(L, getseconds(&rusage.ru_utime));
lua_setfield(L, -2, "usertime");
lua_pushnumber(L, getseconds(&rusage.ru_stime));
lua_setfield(L, -2, "systime");
lua_pushnumber(L, rusage.ru_majflt);
lua_setfield(L, -2, "pagefaults");
lua_pushnumber(L, rusage.ru_nswap);
lua_setfield(L, -2, "swaps");
lua_pushnumber(L, rusage.ru_nvcsw + rusage.ru_nivcsw);
lua_setfield(L, -2, "csw");
}
/* Get RSS */
size_t rss = 0;
if (uv_resident_set_memory(&rss) == 0) {
lua_pushnumber(L, rss);
lua_setfield(L, -2, "rss");
}
return 1;
}
int lib_worker(lua_State *L)
{
static const luaL_Reg lib[] = {
{ "resolve_unwrapped", wrk_resolve },
{ "stats", wrk_stats },
{ NULL, NULL }
};
register_lib(L, "worker", lib);
return 1;
}
kresd_SOURCES := \
daemon/bindings/cache.c \
daemon/bindings/event.c \
daemon/bindings/modules.c \
daemon/bindings/net.c \
daemon/bindings/worker.c \
daemon/io.c \
daemon/network.c \
daemon/engine.c \
daemon/worker.c \
daemon/bindings.c \
daemon/ffimodule.c \
daemon/tls.c \
daemon/tls_ephemeral_credentials.c \
......
......@@ -24,8 +24,11 @@
#include <sys/param.h>
#include <libzscanner/scanner.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "daemon/engine.h"
#include "daemon/bindings.h"
#include "daemon/ffimodule.h"
#include "lib/nsrep.h"
#include "lib/cache/api.h"
......
......@@ -15,10 +15,11 @@
*/
#include <uv.h>
#include <lua.h>
#include <lauxlib.h>
#include "daemon/engine.h"
#include "daemon/ffimodule.h"
#include "daemon/bindings.h"
#include "lib/module.h"
#include "lib/layer.h"
......
......@@ -38,7 +38,6 @@
#include "daemon/network.h"
#include "daemon/worker.h"
#include "daemon/engine.h"
#include "daemon/bindings.h"
#include "daemon/tls.h"
#include "lib/dnssec/ta.h"
......
......@@ -16,6 +16,7 @@
#include <uv.h>
#include <lua.h>