Commit 46122a59 authored by Marek Vavruša's avatar Marek Vavruša

daemon/main: cleanup, forking and remote tty

when the daemon starts in non-interactive mode, it creates a pipe in the ‘tty/<pid>’ which can be used to interact with it remotely - much wow

e.g. $ socat - UNIX-CONNECT:tty/38284
parent 47ce4434
......@@ -335,13 +335,7 @@ int engine_cmd(struct engine *engine, const char *str)
lua_pushstring(engine->L, str);
/* Check result. */
if (engine_pcall(engine->L, 1) != 0) {
fprintf(stderr, "%s\n", lua_tostring(engine->L, -1));
lua_pop(engine->L, 1);
return kr_error(EINVAL);
}
return kr_ok();
return engine_pcall(engine->L, 1);
}
/* Execute byte code */
......@@ -363,10 +357,10 @@ static int engine_loadconf(struct engine *engine)
return kr_error(ENOEXEC);
}
/* Use module path for including Lua scripts */
engine_cmd(engine, "package.path = package.path..';" PREFIX MODULEDIR "/?.lua'");
int ret = engine_cmd(engine, "package.path = package.path..';" PREFIX MODULEDIR "/?.lua'");
lua_pop(engine->L, 1);
/* Load config file */
int ret = 0;
if(access("config", F_OK ) != -1 ) {
ret = l_dosandboxfile(engine->L, "config");
} else {
......
......@@ -56,6 +56,7 @@ struct engine {
int engine_init(struct engine *engine, mm_ctx_t *pool);
void engine_deinit(struct engine *engine);
/** @warning This function leaves 1 string result on stack. */
int engine_cmd(struct engine *engine, const char *str);
int engine_start(struct engine *engine);
void engine_stop(struct engine *engine);
......
......@@ -119,15 +119,14 @@ function eval_cmd(line)
end
end
local status, err, chunk
chunk, err = load_code('table_print('..line..')')
chunk, err = load_code('return table_print('..line..')')
if err then
chunk, err = load_code(line)
end
if not err then
chunk()
end
if err then
print(err)
return chunk()
else
error(err)
end
end
......@@ -135,21 +134,23 @@ end
function table_print (tt, indent, done)
done = done or {}
indent = indent or 0
result = ""
if type(tt) == "table" then
for key, value in pairs (tt) do
io.write(string.rep (" ", indent))
result = result .. string.rep (" ", indent)
if type (value) == "table" and not done [value] then
done [value] = true
io.write(string.format("[%s] => {\n", tostring (key)));
result = result .. string.format("[%s] => {\n", tostring (key))
table_print (value, indent + 4, done)
io.write(string.rep (" ", indent))
io.write("}\n");
result = result .. string.rep (" ", indent)
result = result .. "}\n"
else
io.write(string.format("[%s] => %s\n",
tostring (key), tostring(value)))
result = result .. string.format("[%s] => %s\n",
tostring (key), tostring(value))
end
end
else
io.write(tostring(tt) .. "\n")
result = result .. tostring(tt) .. "\n"
end
return result
end
......@@ -19,8 +19,9 @@
#include <getopt.h>
#include <uv.h>
#include <libknot/internal/sockaddr.h>
#include <ucw/mempool.h>
#include "contrib/ucw/mempool.h"
#include "contrib/ccan/asprintf/asprintf.h"
#include "lib/defines.h"
#include "lib/resolve.h"
#include "daemon/network.h"
......@@ -29,19 +30,46 @@
#include "daemon/bindings.h"
#include "daemon/bindings/kres.h"
/*
* Globals
*/
static int g_interactive = 1;
/*
* TTY control
*/
static void tty_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
/* Set output streams */
FILE *out = stdout, *outerr = stderr;
uv_os_fd_t stream_fd = 0;
uv_fileno((uv_handle_t *)stream, &stream_fd);
if (stream_fd != STDIN_FILENO) {
if (nread <= 0) { /* Close if disconnected */
uv_close((uv_handle_t *)stream, (uv_close_cb) free);
return;
}
out = outerr = fdopen(dup(stream_fd), "w");
}
/* Execute */
if (stream && buf && nread > 0) {
/* Trim endln */
char *cmd = buf->base;
cmd[nread - 1] = '\0';
/* Execute */
engine_cmd((struct engine *)stream->data, cmd);
if (cmd[nread - 1] == '\n') {
cmd[nread - 1] = '\0';
}
struct engine *engine = stream->data;
lua_State *L = engine->L;
int ret = engine_cmd(engine, cmd);
fprintf(ret ? outerr : out, "%s\n> ", lua_tostring(L, -1));
lua_pop(L, 1);
free(buf->base);
}
fflush(out);
/* Close if redirected */
if (stream_fd != STDIN_FILENO) {
fclose(out); /* outerr is the same */
}
printf("> ");
fflush(stdout);
}
static void tty_alloc(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) {
......@@ -49,12 +77,44 @@ static void tty_alloc(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) {
buf->base = malloc(suggested);
}
void signal_handler(uv_signal_t *handle, int signum)
static void tty_accept(uv_stream_t *master, int status)
{
uv_tcp_t *client = malloc(sizeof(*client));
if (client) {
uv_tcp_init(master->loop, client);
if (uv_accept(master, (uv_stream_t *)client) != 0) {
free(client);
return;
}
client->data = master->data;
uv_read_start((uv_stream_t *)client, tty_alloc, tty_read);
/* Write command line */
uv_buf_t buf = { "> ", 2 };
uv_try_write((uv_stream_t *)client, &buf, 1);
}
}
static void signal_handler(uv_signal_t *handle, int signum)
{
uv_stop(uv_default_loop());
uv_signal_stop(handle);
}
static const char *set_addr(char *addr, int *port)
{
char *p = strchr(addr, '#');
if (p) {
*port = atoi(p + 1);
*p = '\0';
}
return addr;
}
/*
* Server operation.
*/
static void help(int argc, char *argv[])
{
printf("Usage: %s [parameters] [rundir]\n", argv[0]);
......@@ -67,15 +127,70 @@ static void help(int argc, char *argv[])
" [rundir] Path to the working directory (default: .)\n");
}
static const char *set_addr(char *addr, int *port)
static struct worker_ctx *init_worker(uv_loop_t *loop, struct engine *engine, mm_ctx_t *pool)
{
char *p = strchr(addr, '#');
if (p) {
*port = atoi(p + 1);
*p = '\0';
/* Load bindings */
engine_lualib(engine, "modules", lib_modules);
engine_lualib(engine, "net", lib_net);
engine_lualib(engine, "cache", lib_cache);
engine_lualib(engine, "event", lib_event);
engine_lualib(engine, "kres", lib_kres);
/* Create main worker. */
struct worker_ctx *worker = mm_alloc(pool, sizeof(*worker));
if(!worker) {
return NULL;
}
memset(worker, 0, sizeof(*worker));
worker->engine = engine,
worker->loop = loop;
loop->data = worker;
worker_reserve(worker, MP_FREELIST_SIZE);
return worker;
}
return addr;
static int run_worker(uv_loop_t *loop, struct engine *engine, int forks)
{
/* Fork subprocesses if requested */
while (--forks > 0) {
int pid = fork();
if (pid < 0) {
perror("[system] fork");
return EXIT_FAILURE;
}
/* Forked process */
if (pid == 0) {
break;
}
}
/* Control sockets or TTY */
auto_free char *sock_file = NULL;
uv_pipe_t pipe;
uv_pipe_init(loop, &pipe, 0);
pipe.data = engine;
if (g_interactive) {
printf("[system] interactive mode\n> ");
fflush(stdout);
uv_pipe_open(&pipe, 0);
uv_read_start((uv_stream_t*) &pipe, tty_alloc, tty_read);
} else {
(void) mkdir("tty", S_IRWXU|S_IRWXG);
sock_file = afmt("tty/%ld", getpid());
if (sock_file) {
uv_pipe_bind(&pipe, sock_file);
uv_listen((uv_stream_t *) &pipe, 16, tty_accept);
}
}
/* Run event loop */
int ret = engine_start(engine);
if (ret == 0) {
ret = uv_run(loop, UV_RUN_DEFAULT);
}
if (sock_file) {
unlink(sock_file);
}
return ret;
}
int main(int argc, char **argv)
......@@ -83,7 +198,6 @@ int main(int argc, char **argv)
const char *addr = NULL;
int port = 53;
int forks = 1;
int headless = 0;
/* Long options. */
int c = 0, li = 0, ret = 0;
......@@ -101,7 +215,7 @@ int main(int argc, char **argv)
addr = set_addr(optarg, &port);
break;
case 'f':
headless = 1;
g_interactive = 0;
forks = atoi(optarg);
if (forks == 0) {
fprintf(stderr, "[system] error '-f' requires number, not '%s'\n", optarg);
......@@ -140,7 +254,6 @@ int main(int argc, char **argv)
uv_signal_t sigint;
uv_signal_init(loop, &sigint);
uv_signal_start(&sigint, signal_handler, SIGINT);
/* Create a server engine. */
mm_ctx_t pool = {
.ctx = mp_new (4096),
......@@ -152,27 +265,13 @@ int main(int argc, char **argv)
fprintf(stderr, "[system] failed to initialize engine: %s\n", kr_strerror(ret));
return EXIT_FAILURE;
}
/* Load bindings */
engine_lualib(&engine, "modules", lib_modules);
engine_lualib(&engine, "net", lib_net);
engine_lualib(&engine, "cache", lib_cache);
engine_lualib(&engine, "event", lib_event);
engine_lualib(&engine, "kres", lib_kres);
/* Create main worker. */
struct worker_ctx *worker = mm_alloc(&pool, sizeof(*worker));
if(!worker) {
/* Create worker */
struct worker_ctx *worker = init_worker(loop, &engine, &pool);
if (!worker) {
fprintf(stderr, "[system] not enough memory\n");
return EXIT_FAILURE;
}
memset(worker, 0, sizeof(*worker));
worker->engine = &engine,
worker->loop = loop;
loop->data = worker;
worker_reserve(worker, MP_FREELIST_SIZE);
/* Bind to sockets. */
/* Bind to sockets and run */
if (addr != NULL) {
ret = network_listen(&engine.net, addr, (uint16_t)port, NET_UDP|NET_TCP);
if (ret != 0) {
......@@ -180,46 +279,14 @@ int main(int argc, char **argv)
ret = EXIT_FAILURE;
}
}
/* Fork subprocesses if requested */
while (--forks > 0) {
int pid = fork();
if (pid < 0) {
perror("[system] fork");
exit(1);
}
/* Forked process */
if (pid == 0) {
break;
}
printf("[system] forked PID %d\n", pid);
}
if (ret == 0) {
/* Interactive stdin */
uv_pipe_t pipe;
if (!headless) {
printf("[system] started in interactive mode, type 'help()'\n");
uv_pipe_init(loop, &pipe, 0);
uv_pipe_open(&pipe, 0);
pipe.data = &engine;
tty_read(NULL, 0, NULL);
uv_read_start((uv_stream_t*) &pipe, tty_alloc, tty_read);
}
/* Run the event loop. */
ret = engine_start(&engine);
if (ret == 0) {
ret = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}
ret = run_worker(loop, &engine, forks);
}
/* Cleanup. */
fprintf(stderr, "\n[system] quitting\n");
engine_deinit(&engine);
worker_reclaim(worker);
mp_delete(pool.ctx);
if (ret != 0) {
ret = EXIT_FAILURE;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment