From 46122a59570cc4ec759c6afb5a1fc4f6af38a5a1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marek=20Vavru=C5=A1a?= Date: Tue, 30 Jun 2015 00:27:00 +0200 Subject: [PATCH] daemon/main: cleanup, forking and remote tty MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit when the daemon starts in non-interactive mode, it creates a pipe in the ‘tty/’ which can be used to interact with it remotely - much wow e.g. $ socat - UNIX-CONNECT:tty/38284 --- daemon/engine.c | 12 +-- daemon/engine.h | 1 + daemon/lua/sandbox.lua | 25 ++--- daemon/main.c | 203 +++++++++++++++++++++++++++-------------- 4 files changed, 152 insertions(+), 89 deletions(-) diff --git a/daemon/engine.c b/daemon/engine.c index dd36156a7..d9b078c70 100644 --- a/daemon/engine.c +++ b/daemon/engine.c @@ -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 { diff --git a/daemon/engine.h b/daemon/engine.h index 4c64d61af..e82474cd9 100644 --- a/daemon/engine.h +++ b/daemon/engine.h @@ -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); diff --git a/daemon/lua/sandbox.lua b/daemon/lua/sandbox.lua index a94b050cb..a7ca501c0 100644 --- a/daemon/lua/sandbox.lua +++ b/daemon/lua/sandbox.lua @@ -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 diff --git a/daemon/main.c b/daemon/main.c index 9ad4dba7d..791ec2688 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -19,8 +19,9 @@ #include #include #include -#include +#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; } -- 2.47.2