Tarantool development patches archive
 help / color / mirror / Atom feed
From: Cyrill Gorcunov <gorcunov@gmail.com>
To: tml <tarantool-patches@dev.tarantool.org>
Subject: [Tarantool-patches] [PATCH 3/5] popen/fio: Merge popen engine into fio internal module
Date: Thu, 28 Nov 2019 23:45:10 +0300	[thread overview]
Message-ID: <20191128204512.19732-4-gorcunov@gmail.com> (raw)
In-Reply-To: <20191128204512.19732-1-gorcunov@gmail.com>

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 <gorcunov@gmail.com>
---
 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 <time.h>
 #include <errno.h>
 #include "coio_task.h"
+#include "popen.h"
 
 #include <lua.h>
 #include <lauxlib.h>
@@ -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

  parent reply	other threads:[~2019-11-28 20:46 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-28 20:45 [Tarantool-patches] [PATCH 0/5] popen: Add ability to run external process Cyrill Gorcunov
2019-11-28 20:45 ` [Tarantool-patches] [PATCH 1/5] popen: Introduce a backend engine Cyrill Gorcunov
2019-11-29  5:52   ` Konstantin Osipov
2019-11-29  9:57     ` Cyrill Gorcunov
2019-11-29  5:59   ` Konstantin Osipov
2019-11-29  9:40     ` Cyrill Gorcunov
2019-11-29 11:19       ` Konstantin Osipov
2019-11-29 11:36         ` Cyrill Gorcunov
2019-11-29 14:50           ` Konstantin Osipov
2019-11-29 15:14             ` Cyrill Gorcunov
2019-11-29 15:17               ` Cyrill Gorcunov
2019-11-29 18:31               ` Konstantin Osipov
2019-11-29 19:17                 ` Cyrill Gorcunov
2019-11-29 22:36                   ` Cyrill Gorcunov
2019-11-30  4:21                     ` Konstantin Osipov
2019-11-30  7:48                       ` Cyrill Gorcunov
2019-11-30  4:14                   ` Konstantin Osipov
2019-11-30  7:36                     ` Cyrill Gorcunov
2019-11-30 10:04                       ` Konstantin Osipov
2019-11-30 10:47                         ` Cyrill Gorcunov
2019-11-30 10:54                           ` Cyrill Gorcunov
2019-11-30 12:16                             ` Cyrill Gorcunov
2019-11-30 20:30                             ` Konstantin Osipov
2019-11-30 20:36                               ` Cyrill Gorcunov
2019-12-13  2:50   ` Alexander Turenko
2019-11-28 20:45 ` [Tarantool-patches] [PATCH 2/5] lua/fio: Add lbox_fio_push_error as a separate helper Cyrill Gorcunov
2019-11-29  6:02   ` Konstantin Osipov
2019-11-29  9:47     ` Cyrill Gorcunov
2019-11-29 11:22       ` Konstantin Osipov
2019-11-29 11:42         ` Cyrill Gorcunov
2019-11-29 14:51           ` Konstantin Osipov
2019-11-28 20:45 ` Cyrill Gorcunov [this message]
2019-11-28 20:45 ` [Tarantool-patches] [PATCH 4/5] popen/fio: Implement lua interface for a popen object Cyrill Gorcunov
2019-11-28 20:45 ` [Tarantool-patches] [PATCH 5/5] test: Add app/popen test Cyrill Gorcunov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191128204512.19732-4-gorcunov@gmail.com \
    --to=gorcunov@gmail.com \
    --cc=tarantool-patches@dev.tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH 3/5] popen/fio: Merge popen engine into fio internal module' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox