From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-lf1-f46.google.com (mail-lf1-f46.google.com [209.85.167.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 513FA46970F for ; Thu, 28 Nov 2019 23:46:02 +0300 (MSK) Received: by mail-lf1-f46.google.com with SMTP id r15so18002742lff.2 for ; Thu, 28 Nov 2019 12:46:02 -0800 (PST) From: Cyrill Gorcunov Date: Thu, 28 Nov 2019 23:45:10 +0300 Message-Id: <20191128204512.19732-4-gorcunov@gmail.com> In-Reply-To: <20191128204512.19732-1-gorcunov@gmail.com> References: <20191128204512.19732-1-gorcunov@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH 3/5] popen/fio: Merge popen engine into fio internal module List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: tml In the patch we wrap popen calls with lua C interface which will allow us to use popen inside plain lua code. They are wrapped via coio_custom calls thus each routine takes into account current libev priority state. Worth to note again that the creation of a new popen object may cause coio thread to stall until exec/exit called. Should not be a huge problem, after all exec itself is extremelly heavy syscall. Part-of #4031 Signed-off-by: Cyrill Gorcunov --- src/lib/core/coio_file.c | 155 ++++++++++++++++++++++++++ src/lib/core/coio_file.h | 10 ++ src/lua/fio.c | 235 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 400 insertions(+) diff --git a/src/lib/core/coio_file.c b/src/lib/core/coio_file.c index 62388344e..39e4ed193 100644 --- a/src/lib/core/coio_file.c +++ b/src/lib/core/coio_file.c @@ -30,6 +30,7 @@ */ #include "coio_file.h" #include "coio_task.h" +#include "popen.h" #include "fiber.h" #include "say.h" #include "fio.h" @@ -103,6 +104,16 @@ struct coio_file_task { const char *source; const char *dest; } copyfile; + + struct { + struct popen_handle *handle; + const char *command; + unsigned int flags; + int timeout_msecs; + size_t count; + int wstatus; + void *buf; + } popen; }; }; @@ -639,3 +650,147 @@ coio_utime(const char *pathname, double atime, double mtime) eio_req *req = eio_utime(pathname, atime, mtime, 0, coio_complete, &eio); return coio_wait_done(req, &eio); } + +static void +do_coio_popen_create(eio_req *req) +{ + struct coio_file_task *eio = req->data; + + eio->popen.handle = popen_create(eio->popen.command, + eio->popen.flags); + req->result = eio->popen.handle ? 0 : -1; +} + +void * +coio_popen_create(const char *command, unsigned int flags) +{ + INIT_COEIO_FILE(eio); + eio.popen.command = command; + eio.popen.flags = flags; + + eio_req *req = eio_custom(do_coio_popen_create, + EIO_PRI_DEFAULT, + coio_complete, &eio); + coio_wait_done(req, &eio); + + return eio.popen.handle; +} + +static void +coio_do_popen_destroy(eio_req *req) +{ + struct coio_file_task *eio = req->data; + req->result = popen_destroy(eio->popen.handle); +} + +int +coio_popen_destroy(void *handle) +{ + INIT_COEIO_FILE(eio); + eio.popen.handle = handle; + + eio_req *req = eio_custom(coio_do_popen_destroy, + EIO_PRI_DEFAULT, + coio_complete, &eio); + return coio_wait_done(req, &eio); +} + +static void +coio_do_popen_kill(eio_req *req) +{ + struct coio_file_task *eio = req->data; + req->result = popen_kill(eio->popen.handle); +} + +int +coio_popen_kill(void *handle) +{ + INIT_COEIO_FILE(eio); + eio.popen.handle = handle; + + eio_req *req = eio_custom(coio_do_popen_kill, + EIO_PRI_DEFAULT, + coio_complete, &eio); + return coio_wait_done(req, &eio); +} + +static void +coio_do_popen_status(eio_req *req) +{ + struct coio_file_task *eio = req->data; + req->result = popen_wstatus(eio->popen.handle, + &eio->popen.wstatus); +} + +int +coio_popen_status(void *handle, int *reason, int *exit_code) +{ + INIT_COEIO_FILE(eio); + eio.popen.handle = handle; + + eio_req *req = eio_custom(coio_do_popen_status, + EIO_PRI_DEFAULT, + coio_complete, &eio); + int ret = coio_wait_done(req, &eio); + if (ret > 0) { + *reason = WIFEXITED(eio.popen.wstatus) ? 1 : 2; + *exit_code = WIFEXITED(eio.popen.wstatus) ? + WEXITSTATUS(eio.popen.wstatus) : + WTERMSIG(eio.popen.wstatus); + } + return ret; +} + +static void +coio_do_popen_read(eio_req *req) +{ + struct coio_file_task *eio = req->data; + req->result = popen_read_timeout(eio->popen.handle, + eio->popen.buf, + eio->popen.count, + eio->popen.flags, + eio->popen.timeout_msecs); +} + +ssize_t +coio_popen_read(void *handle, void *buf, size_t count, + unsigned int flags, int timeout_msecs) +{ + INIT_COEIO_FILE(eio); + eio.popen.timeout_msecs = timeout_msecs; + eio.popen.handle = handle; + eio.popen.flags = flags; + eio.popen.count = count; + eio.popen.buf = buf; + + eio_req *req = eio_custom(coio_do_popen_read, + EIO_PRI_DEFAULT, + coio_complete, &eio); + return coio_wait_done(req, &eio); +} + +static void +coio_do_popen_write(eio_req *req) +{ + struct coio_file_task *eio = req->data; + req->result = popen_write(eio->popen.handle, + eio->popen.buf, + eio->popen.count, + eio->popen.flags); +} + +ssize_t +coio_popen_write(void *handle, void *buf, size_t count, + unsigned int flags) +{ + INIT_COEIO_FILE(eio); + eio.popen.handle = handle; + eio.popen.flags = flags; + eio.popen.count = count; + eio.popen.buf = buf; + + eio_req *req = eio_custom(coio_do_popen_write, + EIO_PRI_DEFAULT, + coio_complete, &eio); + return coio_wait_done(req, &eio); +} diff --git a/src/lib/core/coio_file.h b/src/lib/core/coio_file.h index ac7b1aacf..fb385737d 100644 --- a/src/lib/core/coio_file.h +++ b/src/lib/core/coio_file.h @@ -85,6 +85,16 @@ int coio_tempdir(char *path, size_t path_len); int coio_readdir(const char *path, char **buf); int coio_copyfile(const char *source, const char *dest); int coio_utime(const char *pathname, double atime, double mtime); + +void *coio_popen_create(const char *command, unsigned int flags); +int coio_popen_destroy(void *handle); +int coio_popen_kill(void *handle); +int coio_popen_status(void *handle, int *reason, int *exit_code); +ssize_t coio_popen_read(void *handle, void *buf, size_t count, + unsigned int flags, int timeout_msecs); +ssize_t coio_popen_write(void *handle, void *buf, size_t count, + unsigned int flags); + #if defined(__cplusplus) } /* extern "C" */ #endif /* defined(__cplusplus) */ diff --git a/src/lua/fio.c b/src/lua/fio.c index 33ccd5d71..3cf52f2d5 100644 --- a/src/lua/fio.c +++ b/src/lua/fio.c @@ -39,6 +39,7 @@ #include #include #include "coio_task.h" +#include "popen.h" #include #include @@ -683,6 +684,233 @@ lbox_fio_utime(struct lua_State *L) return lbox_fio_pushbool(L, coio_utime(pathname, atime, mtime) == 0); } +/** + * lbox_fio_popen_create - creates a new popen handle and runs a command inside + * @command: a command to run + * @flags: child pipe end specification + * + * Returns pair @handle = data, @err = nil on success, + * @handle = nil, err ~= nil on error. + */ +static int +lbox_fio_popen_create(struct lua_State *L) +{ + struct popen_handle *handle; + unsigned int flags; + const char *command; + + if (lua_gettop(L) < 1) { +usage: + luaL_error(L, "Usage: fio.popen(command [, rw, {opts}])"); + } + + command = lua_tostring(L, 1); + if (!command) + goto usage; + flags = lua_tonumber(L, 2); + + handle = coio_popen_create(command, flags); + if (!handle) + return lbox_fio_pushsyserror(L); + + lua_pushlightuserdata(L, handle); + return 1; +} + +/** + * lbox_fio_popen_destroy - close a popen handle + * @handle: a handle to close + * + * If there is a running child it get killed first. + * + * Returns true if a handle is closeed, false otherwise. + */ +static int +lbox_fio_popen_destroy(struct lua_State *L) +{ + void *handle = lua_touserdata(L, 1); + return lbox_fio_pushbool(L, coio_popen_destroy(handle) == 0); +} + +/** + * lbox_fio_popen_kill - kill popen's child process + * @handle: a handle carries child process to kill + * + * Returns true if process is killed and false + * otherwise. Note the process is simply signaled + * and it doesn't mean it is killed immediately, + * Poll lbox_fio_pstatus if need to find out when + * exactly the child is reaped out. + */ +static int +lbox_fio_popen_kill(struct lua_State *L) +{ + struct popen_handle *p = lua_touserdata(L, 1); + return lbox_fio_pushbool(L, coio_popen_kill(p) == 0); +} + +/** + * lbox_fio_popen_status - fetch popen child process status + * @handle: a handle to fetch status from + * + * Returns @err = nil, @reason = [1|2], @exit_code = 'number' + * on success, where reason = 1 means process has been calling + * exit(@exit_code); and reason = 2 when process has been killed + * by @exit_code signal (either explicitly via lbox_fio_pkill + * or implicitly by a third side, say node admin via kill command + * in a shell). + */ +static int +lbox_fio_popen_status(struct lua_State *L) +{ + struct popen_handle *p = lua_touserdata(L, 1); + int reason = 0, exit_code = 0, ret; + + ret = coio_popen_status(p, &reason, &exit_code); + if (ret < 0) { + return lbox_fio_push_error(L); + } else if (ret == 0) { + lua_pushnil(L); + return 1; + } + + lua_pushnil(L); + lua_pushinteger(L, reason); + lua_pushinteger(L, exit_code); + return 3; +} + +/** + * lbox_fio_popen_read - read data from a child peer + * @handle: a handle of a child process + * @buf: destination buffer + * @count: number of bytes to read + * @timeout_msecs: read timeout, in microseconds, -1 to not use + * @flags: which peer to read (stdout,stderr) + * + * Returns @size = 'read bytes', @err = nil on success, + * @size = nil, @err = nil if timeout expired, and + * @size = nil, @err ~= nil on error. + */ +static int +lbox_fio_popen_read(struct lua_State *L) +{ + struct popen_handle *handle = lua_touserdata(L, 1); + uint32_t ctypeid; + void *buf = *(char **)luaL_checkcdata(L, 2, &ctypeid); + size_t count = lua_tonumber(L, 3); + int timeout_msecs = lua_tonumber(L, 4); + unsigned int flags = lua_tonumber(L, 5); + ssize_t ret; + + ret = coio_popen_read(handle, buf, count, flags, timeout_msecs); + if (ret < 0) { + if (ret == -EAGAIN) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } else + return lbox_fio_pushsyserror(L); + } + + lua_pushinteger(L, ret); + return 1; +} + +/** + * lbox_fio_popen_write - write data to a child peer + * @handle: a handle of a child process + * @buf: source buffer + * @count: number of bytes to write + * @flags: which peer to write (stdin) + * + * Returns @size = 'bytes wrote', @err = nil on succes, + * @size = nil, @err ~= nil on error. + */ +static int +lbox_fio_popen_write(struct lua_State *L) +{ + struct popen_handle *handle = lua_touserdata(L, 1); + void *buf = (void *)lua_tostring(L, 2); + uint32_t ctypeid = 0; + if (buf == NULL) + buf = *(char **)luaL_checkcdata(L, 2, &ctypeid); + size_t count = lua_tonumber(L, 3); + unsigned int flags = lua_tonumber(L, 4); + ssize_t ret; + + ret = coio_popen_write(handle, buf, count, flags); + if (ret < 0) + return lbox_fio_pushsyserror(L); + + lua_pushinteger(L, ret); + return 1; +} + +/** + * lbox_fio_popen_info - return information about popen handle + * @handle: a handle of a child process + * + * Returns a @table ~= nil, @err = nil on success, + * @table = nil, @err ~= nil on error. + */ +static int +lbox_fio_popen_info(struct lua_State *L) +{ + struct popen_handle *handle = lua_touserdata(L, 1); + int reason = 0, exit_code = 0, ret; + struct popen_stat st = { }; + char *status; + + if (popen_stat(handle, &st)) + return lbox_fio_pushsyserror(L); + + ret = coio_popen_status(handle, &reason, &exit_code); + if (ret < 0) + return lbox_fio_pushsyserror(L); + + if (ret == 0) + status = "alive"; + else if (ret == 1) + status = "exited"; + else if (ret == 2) + status = "signaled"; + else + status = "unknown"; + + lua_newtable(L); + + lua_pushliteral(L, "pid"); + lua_pushinteger(L, st.pid); + lua_settable(L, -3); + + lua_pushliteral(L, "flags"); + lua_pushinteger(L, st.flags); + lua_settable(L, -3); + + lua_pushliteral(L, "state"); + lua_pushstring(L, status); + lua_settable(L, -3); + + lua_pushliteral(L, "exit_code"); + lua_pushinteger(L, exit_code); + lua_settable(L, -3); + + lua_pushliteral(L, "stdin"); + lua_pushinteger(L, st.fds[POPEN_FLAG_FD_STDIN_BIT]); + lua_settable(L, -3); + + lua_pushliteral(L, "stdout"); + lua_pushinteger(L, st.fds[POPEN_FLAG_FD_STDOUT_BIT]); + lua_settable(L, -3); + + lua_pushliteral(L, "stderr"); + lua_pushinteger(L, st.fds[POPEN_FLAG_FD_STDERR_BIT]); + lua_settable(L, -3); + + return 1; +} + void tarantool_lua_fio_init(struct lua_State *L) { @@ -726,6 +954,13 @@ tarantool_lua_fio_init(struct lua_State *L) { "fstat", lbox_fio_fstat }, { "copyfile", lbox_fio_copyfile, }, { "utime", lbox_fio_utime }, + { "popen_create", lbox_fio_popen_create }, + { "popen_destroy", lbox_fio_popen_destroy }, + { "popen_kill", lbox_fio_popen_kill }, + { "popen_status", lbox_fio_popen_status }, + { "popen_read", lbox_fio_popen_read }, + { "popen_write", lbox_fio_popen_write }, + { "popen_info", lbox_fio_popen_info }, { NULL, NULL } }; luaL_register(L, NULL, internal_methods); -- 2.20.1