* [Tarantool-patches] [PATCH 01/13] popen: require popen handle to be non-NULL
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  7:16   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 02/13] popen: remove retval from popen_state() Alexander Turenko
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
Further commits will add proper entries into the diagnostics area for
failures inside popen functions. We should either report handle == NULL
case via the diagnostics area or ensure that the NULL handle case is not
possible.
The latter approach is implemented in this commit. There are two
reasons for this:
* This way simplifies function contracts (one less kind of failure).
* The popen Lua module (that will be implemented in the further commits)
  will not construct any logic using NULL as a handle. When 'NULL
  handle' error is not possible in the C API, it will be easier to
  verify that this failure is not possible the Lua API.
A user of the C API should take care to don't call those functions with
NULL handle.
Part of #4031
---
 src/lib/core/popen.c | 33 +++++++++++++--------------------
 1 file changed, 13 insertions(+), 20 deletions(-)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index 6b6062215..5f74bc3ce 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -163,13 +163,13 @@ popen_may_io(struct popen_handle *handle, unsigned int idx,
 }
 
 /**
- * Test if the handle is not nil and still have
- * a living child process.
+ * Test if the handle still have a living child process.
  */
 static inline bool
 popen_may_pidop(struct popen_handle *handle)
 {
-	if (!handle || handle->pid == -1) {
+	assert(handle != NULL);
+	if (handle->pid == -1) {
 		errno = ESRCH;
 		return false;
 	}
@@ -182,10 +182,7 @@ popen_may_pidop(struct popen_handle *handle)
 int
 popen_stat(struct popen_handle *handle, struct popen_stat *st)
 {
-	if (!handle) {
-		errno = ESRCH;
-		return -1;
-	}
+	assert(handle != NULL);
 
 	st->pid		= handle->pid;
 	st->flags	= handle->flags;
@@ -204,11 +201,7 @@ popen_stat(struct popen_handle *handle, struct popen_stat *st)
 const char *
 popen_command(struct popen_handle *handle)
 {
-	if (!handle) {
-		errno = ESRCH;
-		return NULL;
-	}
-
+	assert(handle != NULL);
 	return (const char *)handle->command;
 }
 
@@ -236,6 +229,8 @@ popen_write_timeout(struct popen_handle *handle, void *buf,
 		    size_t count, unsigned int flags,
 		    ev_tstamp timeout)
 {
+	assert(handle != NULL);
+
 	int idx = STDIN_FILENO;
 
 	if (!(flags & POPEN_FLAG_FD_STDIN)) {
@@ -268,6 +263,8 @@ popen_read_timeout(struct popen_handle *handle, void *buf,
 		   size_t count, unsigned int flags,
 		   ev_tstamp timeout)
 {
+	assert(handle != NULL);
+
 	int idx = flags & POPEN_FLAG_FD_STDOUT ?
 		STDOUT_FILENO : STDERR_FILENO;
 
@@ -380,10 +377,7 @@ popen_sigchld_handler(EV_P_ ev_child *w, int revents)
 int
 popen_state(struct popen_handle *handle, int *state, int *exit_code)
 {
-	if (!handle) {
-		errno = ESRCH;
-		return -1;
-	}
+	assert(handle != NULL);
 
 	if (handle->pid != -1) {
 		*state = POPEN_STATE_ALIVE;
@@ -437,6 +431,8 @@ popen_send_signal(struct popen_handle *handle, int signo)
 {
 	int ret;
 
+	assert(handle != NULL);
+
 	/*
 	 * A child may be killed or exited already.
 	 */
@@ -464,10 +460,7 @@ popen_delete(struct popen_handle *handle)
 {
 	size_t i;
 
-	if (!handle) {
-		errno = ESRCH;
-		return -1;
-	}
+	assert(handle != NULL);
 
 	if (popen_send_signal(handle, SIGKILL) && errno != ESRCH)
 		return -1;
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 01/13] popen: require popen handle to be non-NULL
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 01/13] popen: require popen handle to be non-NULL Alexander Turenko
@ 2020-04-10  7:16   ` Cyrill Gorcunov
  0 siblings, 0 replies; 36+ messages in thread
From: Cyrill Gorcunov @ 2020-04-10  7:16 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 05:50:39AM +0300, Alexander Turenko wrote:
> Further commits will add proper entries into the diagnostics area for
> failures inside popen functions. We should either report handle == NULL
> case via the diagnostics area or ensure that the NULL handle case is not
> possible.
> 
> The latter approach is implemented in this commit. There are two
> reasons for this:
> 
> * This way simplifies function contracts (one less kind of failure).
> * The popen Lua module (that will be implemented in the further commits)
>   will not construct any logic using NULL as a handle. When 'NULL
>   handle' error is not possible in the C API, it will be easier to
>   verify that this failure is not possible the Lua API.
> 
> A user of the C API should take care to don't call those functions with
> NULL handle.
> 
> Part of #4031
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
^ permalink raw reply	[flat|nested] 36+ messages in thread
* [Tarantool-patches] [PATCH 02/13] popen: remove retval from popen_state()
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 01/13] popen: require popen handle to be non-NULL Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  7:17   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 03/13] popen: add missed diag_set in popen_signal/delete Alexander Turenko
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
After the previous commit ('popen: require popen handle to be non-NULL')
it turns out that popen_state() function always succeeds. There is no
reason to return a success / failure value from it.
Part of #4031
---
 src/lib/core/popen.c   |  4 +---
 src/lib/core/popen.h   |  2 +-
 test/unit/popen.c      |  8 +++-----
 test/unit/popen.result | 13 ++++++-------
 4 files changed, 11 insertions(+), 16 deletions(-)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index 5f74bc3ce..3a0ac4b53 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -374,7 +374,7 @@ popen_sigchld_handler(EV_P_ ev_child *w, int revents)
 /**
  * Get current child state.
  */
-int
+void
 popen_state(struct popen_handle *handle, int *state, int *exit_code)
 {
 	assert(handle != NULL);
@@ -391,8 +391,6 @@ popen_state(struct popen_handle *handle, int *state, int *exit_code)
 			*exit_code = WTERMSIG(handle->wstatus);
 		}
 	}
-
-	return 0;
 }
 
 /**
diff --git a/src/lib/core/popen.h b/src/lib/core/popen.h
index 9cbfed60c..587d6f7df 100644
--- a/src/lib/core/popen.h
+++ b/src/lib/core/popen.h
@@ -163,7 +163,7 @@ popen_read_timeout(struct popen_handle *handle, void *buf,
 		   size_t count, unsigned int flags,
 		   ev_tstamp timeout);
 
-extern int
+extern void
 popen_state(struct popen_handle *handle, int *state, int *exit_code);
 
 extern const char *
diff --git a/test/unit/popen.c b/test/unit/popen.c
index caea9d552..b71e5485b 100644
--- a/test/unit/popen.c
+++ b/test/unit/popen.c
@@ -26,8 +26,7 @@ static int
 wait_exit(struct popen_handle *handle, int *state, int *exit_code)
 {
 	for (;;) {
-		if (popen_state(handle, state, exit_code))
-			return -1;
+		popen_state(handle, state, exit_code);
 		if (*state == POPEN_STATE_EXITED ||
 		    *state == POPEN_STATE_SIGNALED)
 			break;
@@ -61,7 +60,7 @@ popen_write_exit(void)
 	};
 	int rc;
 
-	plan(7);
+	plan(6);
 	header();
 
 	handle = popen_new(&opts);
@@ -69,8 +68,7 @@ popen_write_exit(void)
 	if (handle == NULL)
 		goto out;
 
-	rc = popen_state(handle, &state, &exit_code);
-	ok(rc == 0, "popen_state");
+	popen_state(handle, &state, &exit_code);
 
 	ok(state == POPEN_STATE_ALIVE, "state %s",
 	   popen_state_str(state));
diff --git a/test/unit/popen.result b/test/unit/popen.result
index d7894d1db..f5e6bc2ca 100644
--- a/test/unit/popen.result
+++ b/test/unit/popen.result
@@ -1,14 +1,13 @@
 1..3
 	*** main_f ***
-    1..7
+    1..6
 	*** popen_write_exit ***
     ok 1 - popen_new
-    ok 2 - popen_state
-    ok 3 - state alive
-    ok 4 - write flag check
-    ok 5 - write to pipe
-    ok 6 - child exited
-    ok 7 - popen_delete
+    ok 2 - state alive
+    ok 3 - write flag check
+    ok 4 - write to pipe
+    ok 5 - child exited
+    ok 6 - popen_delete
 	*** popen_write_exit: done ***
 ok 1 - subtests
     1..5
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* [Tarantool-patches] [PATCH 03/13] popen: add missed diag_set in popen_signal/delete
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 01/13] popen: require popen handle to be non-NULL Alexander Turenko
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 02/13] popen: remove retval from popen_state() Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  7:23   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 04/13] popen: add logging of fds closed in a child Alexander Turenko
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
Lua API will use content of the diagnostics area to report an error to a
caller, so it is critical to always have proper diagnostics at failure.
Part of #4031
---
 src/lib/core/popen.c | 37 +++++++++++++++++++++++++++----------
 1 file changed, 27 insertions(+), 10 deletions(-)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index 3a0ac4b53..3fcbc325a 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -164,16 +164,19 @@ popen_may_io(struct popen_handle *handle, unsigned int idx,
 
 /**
  * Test if the handle still have a living child process.
+ *
+ * Return -1 and set errno to ESRCH when a process does not
+ * exist. Otherwise return 0.
  */
-static inline bool
+static inline int
 popen_may_pidop(struct popen_handle *handle)
 {
 	assert(handle != NULL);
 	if (handle->pid == -1) {
 		errno = ESRCH;
-		return false;
+		return -1;
 	}
-	return true;
+	return 0;
 }
 
 /**
@@ -423,6 +426,10 @@ popen_state_str(unsigned int state)
 
 /**
  * Send a signal to a child process.
+ *
+ * Return 0 at success or -1 at failure (and set a diag).
+ *
+ * Set errno to ESRCH when a process does not exist.
  */
 int
 popen_send_signal(struct popen_handle *handle, int signo)
@@ -434,16 +441,22 @@ popen_send_signal(struct popen_handle *handle, int signo)
 	/*
 	 * A child may be killed or exited already.
 	 */
-	if (!popen_may_pidop(handle))
+	ret = popen_may_pidop(handle);
+	if (ret == 0) {
+		say_debug("popen: kill %d signo %d", handle->pid, signo);
+		assert(handle->pid != -1);
+		ret = kill(handle->pid, signo);
+	}
+	if (ret < 0 && errno == ESRCH) {
+		diag_set(SystemError, "Attempt to send a signal %d to a "
+			 "process that does not exist anymore", signo);
 		return -1;
-
-	say_debug("popen: kill %d signo %d", handle->pid, signo);
-	ret = kill(handle->pid, signo);
-	if (ret < 0) {
+	} else if (ret < 0) {
 		diag_set(SystemError, "Unable to kill %d signo %d",
 			 handle->pid, signo);
+		return -1;
 	}
-	return ret;
+	return 0;
 }
 
 /**
@@ -460,7 +473,11 @@ popen_delete(struct popen_handle *handle)
 
 	assert(handle != NULL);
 
-	if (popen_send_signal(handle, SIGKILL) && errno != ESRCH)
+	/*
+	 * Unable to kill the process -- give an error.
+	 * The process is not exist already -- pass over.
+	 */
+	if (popen_send_signal(handle, SIGKILL) != 0 && errno != ESRCH)
 		return -1;
 
 	for (i = 0; i < lengthof(handle->ios); i++) {
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* [Tarantool-patches] [PATCH 04/13] popen: add logging of fds closed in a child
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (2 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 03/13] popen: add missed diag_set in popen_signal/delete Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  7:46   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 05/13] say: allow to set a logger file descriptor Alexander Turenko
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
It is useful for debugging popen behaviour around file descriptors.
Part of #4031
---
 src/lib/core/popen.c | 2 ++
 1 file changed, 2 insertions(+)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index 3fcbc325a..9d4e6ef3a 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -579,6 +579,8 @@ close_inherited_fds(int *skip_fds, size_t nr_skip_fds)
 		if (fd_no == -1)
 			continue;
 
+		say_debug("popen: close inherited fd [%s:%d]", stdX_str(fd_no),
+			  fd_no);
 		if (close(fd_no)) {
 			int saved_errno = errno;
 			diag_set(SystemError, "fdin: Can't close %d", fd_no);
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 04/13] popen: add logging of fds closed in a child
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 04/13] popen: add logging of fds closed in a child Alexander Turenko
@ 2020-04-10  7:46   ` Cyrill Gorcunov
  2020-04-10 12:19     ` Alexander Turenko
  0 siblings, 1 reply; 36+ messages in thread
From: Cyrill Gorcunov @ 2020-04-10  7:46 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 05:50:42AM +0300, Alexander Turenko wrote:
> It is useful for debugging popen behaviour around file descriptors.
> 
> Part of #4031
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
> ---
>  src/lib/core/popen.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
> index 3fcbc325a..9d4e6ef3a 100644
> --- a/src/lib/core/popen.c
> +++ b/src/lib/core/popen.c
> @@ -579,6 +579,8 @@ close_inherited_fds(int *skip_fds, size_t nr_skip_fds)
>  		if (fd_no == -1)
>  			continue;
>  
> +		say_debug("popen: close inherited fd [%s:%d]", stdX_str(fd_no),
> +			  fd_no);
Can we please shift args a bit, like
		say_debug("popen: close inherited fd [%s:%d]",
			  stdX_str(fd_no), fd_no);
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 04/13] popen: add logging of fds closed in a child
  2020-04-10  7:46   ` Cyrill Gorcunov
@ 2020-04-10 12:19     ` Alexander Turenko
  0 siblings, 0 replies; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10 12:19 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 10:46:42AM +0300, Cyrill Gorcunov wrote:
> On Fri, Apr 10, 2020 at 05:50:42AM +0300, Alexander Turenko wrote:
> > It is useful for debugging popen behaviour around file descriptors.
> > 
> > Part of #4031
> Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
> 
> > ---
> >  src/lib/core/popen.c | 2 ++
> >  1 file changed, 2 insertions(+)
> > 
> > diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
> > index 3fcbc325a..9d4e6ef3a 100644
> > --- a/src/lib/core/popen.c
> > +++ b/src/lib/core/popen.c
> > @@ -579,6 +579,8 @@ close_inherited_fds(int *skip_fds, size_t nr_skip_fds)
> >  		if (fd_no == -1)
> >  			continue;
> >  
> > +		say_debug("popen: close inherited fd [%s:%d]", stdX_str(fd_no),
> > +			  fd_no);
> 
> Can we please shift args a bit, like
> 
> 		say_debug("popen: close inherited fd [%s:%d]",
> 			  stdX_str(fd_no), fd_no);
My approach is to keep lines within 80 symbols, but don't break lines
prematurely.
I asked Cyrill what is the general approach he use, because I don't want
change code back and forth due to different styles within one file.
The answer is that one dangling word looks ugly. It is not strict
criteria, however I will try to follow it (at least when editing
Cyrill's code).
Changed:
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index 9d4e6ef3a..e5e7e5cfc 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -579,8 +579,8 @@ close_inherited_fds(int *skip_fds, size_t nr_skip_fds)
                if (fd_no == -1)
                        continue;
 
-               say_debug("popen: close inherited fd [%s:%d]", stdX_str(fd_no),
-                         fd_no);
+               say_debug("popen: close inherited fd [%s:%d]",
+                         stdX_str(fd_no), fd_no);
                if (close(fd_no)) {
                        int saved_errno = errno;
                        diag_set(SystemError, "fdin: Can't close %d", fd_no);
^ permalink raw reply	[flat|nested] 36+ messages in thread
* [Tarantool-patches] [PATCH 05/13] say: allow to set a logger file descriptor
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (3 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 04/13] popen: add logging of fds closed in a child Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  8:33   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 06/13] popen: decouple logger fd from stderr Alexander Turenko
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
It is necessary to decouple stderr from a logger file descriptor in the
popen implementation.
Part of #4031
---
 src/lib/core/say.c | 6 ++++++
 src/lib/core/say.h | 7 ++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/lib/core/say.c b/src/lib/core/say.c
index dd05285a6..5d572e1ab 100644
--- a/src/lib/core/say.c
+++ b/src/lib/core/say.c
@@ -174,6 +174,12 @@ log_get_fd(void)
 	return log_default->fd;
 }
 
+void
+log_set_fd(int new_fd)
+{
+	log_default->fd = new_fd;
+}
+
 void
 log_set_level(struct log *log, enum say_level level)
 {
diff --git a/src/lib/core/say.h b/src/lib/core/say.h
index e17de659c..3ce7724c4 100644
--- a/src/lib/core/say.h
+++ b/src/lib/core/say.h
@@ -204,10 +204,15 @@ enum say_logger_type
 log_type();
 
 /**
- * Default logger file descriptor.
+ * Accessors for default logger file descriptor.
+ *
+ * It is needed for dark magic inside popen implementation.
+ * Unlikely it is what you want to use anywhere else.
  */
 int
 log_get_fd(void);
+void
+log_set_fd(int new_fd);
 
 /**
  * Set log level. Can be used dynamically.
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 05/13] say: allow to set a logger file descriptor
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 05/13] say: allow to set a logger file descriptor Alexander Turenko
@ 2020-04-10  8:33   ` Cyrill Gorcunov
  2020-04-10 12:19     ` Alexander Turenko
  0 siblings, 1 reply; 36+ messages in thread
From: Cyrill Gorcunov @ 2020-04-10  8:33 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 05:50:43AM +0300, Alexander Turenko wrote:
...
>  
>  /**
> - * Default logger file descriptor.
> + * Accessors for default logger file descriptor.
> + *
> + * It is needed for dark magic inside popen implementation.
> + * Unlikely it is what you want to use anywhere else.
>   */
I would rather comment it as "It is needed to keep the
logger fd alive if a child process in the popen engine
needs own fd intersecting with logger." But up to you.
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
^ permalink raw reply	[flat|nested] 36+ messages in thread
* Re: [Tarantool-patches] [PATCH 05/13] say: allow to set a logger file descriptor
  2020-04-10  8:33   ` Cyrill Gorcunov
@ 2020-04-10 12:19     ` Alexander Turenko
  0 siblings, 0 replies; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10 12:19 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 11:33:49AM +0300, Cyrill Gorcunov wrote:
> On Fri, Apr 10, 2020 at 05:50:43AM +0300, Alexander Turenko wrote:
> ...
> >  
> >  /**
> > - * Default logger file descriptor.
> > + * Accessors for default logger file descriptor.
> > + *
> > + * It is needed for dark magic inside popen implementation.
> > + * Unlikely it is what you want to use anywhere else.
> >   */
> 
> I would rather comment it as "It is needed to keep the
> logger fd alive if a child process in the popen engine
> needs own fd intersecting with logger." But up to you.
Keep it alive is one of two problems we should solve: the second one is
about capturing of stderr in a parent process. I would not bury into
those details here, but give few common words: we need decouple
descriptors in popen, let's use those functions with caution.
The wording is actually too informal, so it worth to rewrite in more
serious way :)
Changed:
diff --git a/src/lib/core/say.h b/src/lib/core/say.h
index 3ce7724c4..43883f0da 100644
--- a/src/lib/core/say.h
+++ b/src/lib/core/say.h
@@ -206,8 +206,11 @@ log_type();
 /**
  * Accessors for default logger file descriptor.
  *
- * It is needed for dark magic inside popen implementation.
- * Unlikely it is what you want to use anywhere else.
+ * It is needed for decoupling of a logger file descriptor from
+ * stderr in the popen implementation.
+ *
+ * Those functions break logger incapsulation, so use them with
+ * caution.
  */
 int
 log_get_fd(void);
^ permalink raw reply	[flat|nested] 36+ messages in thread
* [Tarantool-patches] [PATCH 06/13] popen: decouple logger fd from stderr
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (4 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 05/13] say: allow to set a logger file descriptor Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  9:18   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 07/13] popen: add const qualifier to popen_write_timeout Alexander Turenko
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
The default logger configuration writes logs to stderr.
Popen implementation holds a logger fd until execve() to be able to
write debug entries or information about a failure from a child. However
when popen flags requires to close stderr in the child, the logger fd
becomes closed: logging will fail.
Another problem appears when a user want to capture stderr and
tarantool's log level is set to debug (7). Since the logger uses stderr
and it is fed to the parent using a pipe, the logger output will not
shown on the 'real' stderr, but will be captured together with child's
program debugging output.
This commit duplicates a logger file descriptor that allows to close or
redirect child's stderr without described side effects.
See also 86ec3a5c4792ea1bba9f644da1e8f301314c8d29 ('popen: add logging
in child process').
Areas for improvements:
* Copy logger fd at module initialization time instead of copying of
  each popen call.
Alternatives:
* Extend logger to allow to accumulate log entries in a buffer. Flush
  the buffer from the parent process. (It is possible since vfork does
  not split a virtual memory space).
Part of #4031
---
 src/lib/core/popen.c | 124 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 115 insertions(+), 9 deletions(-)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index 9d4e6ef3a..62920e0c8 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -74,6 +74,59 @@ popen_unregister(struct popen_handle *handle)
 	mh_i32ptr_remove(popen_pids_map, &node, NULL);
 }
 
+/**
+ * Duplicate a file descriptor, but not to std{in,out,err}.
+ *
+ * Return a new fd at success, otherwise return -1 and set a diag.
+ */
+static int
+dup_not_std_streams(int fd)
+{
+	int res = -1;
+	int save_errno = 0;
+
+	/*
+	 * We will call dup() in a loop until it will return
+	 * fd > STDERR_FILENO. The array `discarded_fds` stores
+	 * intermediate fds to close them after all dup() calls.
+	 */
+	static_assert(STDERR_FILENO + 1 == 3,
+		      "Unexpected STDERR_FILENO constant");
+	int discarded_fds[STDERR_FILENO + 1] = {-1, -1, -1};
+
+	for (size_t i = 0; i < lengthof(discarded_fds) + 1; ++i) {
+		int new_fd = dup(fd);
+		if (new_fd < 0) {
+			save_errno = errno;
+			break;
+		}
+
+		/* Found wanted fd. */
+		if (new_fd > STDERR_FILENO) {
+			res = new_fd;
+			break;
+		}
+
+		/* Save to close then. */
+		assert(i < lengthof(discarded_fds));
+		discarded_fds[i] = new_fd;
+	}
+
+	/* Close all intermediate fds (if any). */
+	for (size_t i = 0; i < lengthof(discarded_fds); ++i)
+		if (discarded_fds[i] >= 0)
+			close(discarded_fds[i]);
+
+	/* Report an error if it occurs. */
+	if (res < 0) {
+		errno = save_errno;
+		diag_set(SystemError, "Unable to duplicate an fd %d", fd);
+		return -1;
+	}
+
+	return res;
+}
+
 /**
  * Allocate new popen hanldle with flags specified.
  */
@@ -746,9 +799,34 @@ popen_new(struct popen_opts *opts)
 	 * plus dev/null variants and logfd
 	 */
 	int skip_fds[POPEN_FLAG_FD_STDEND_BIT * 2 + 2 + 1];
-	int log_fd = log_get_fd();
 	size_t nr_skip_fds = 0;
 
+	/*
+	 * We must decouple log file descriptor from stderr in order to
+	 * close or redirect stderr, but keep logging as is until
+	 * execve() call.
+	 *
+	 * The new file descriptor should not have the same number as
+	 * stdin, stdout or stderr.
+	 *
+	 * NB: It is better to acquire it from the parent to catch
+	 * possible error sooner and don't ever call vfork() if we
+	 * reached a file descriptor limit.
+	 */
+	int old_log_fd = log_get_fd();
+	int log_fd = -1;
+	if (old_log_fd >= 0) {
+		log_fd = dup_not_std_streams(old_log_fd);
+		if (log_fd < 0)
+			return NULL;
+		if (fcntl(log_fd, F_SETFL, O_CLOEXEC) != 0) {
+			diag_set(SystemError,
+				 "Unable to set O_CLOEXEC on temporary logfd");
+			close(log_fd);
+			return NULL;
+		}
+	}
+
 	/*
 	 * A caller must preserve space for this.
 	 */
@@ -771,8 +849,11 @@ popen_new(struct popen_opts *opts)
 		      "Popen flags do not match stdX");
 
 	handle = handle_new(opts);
-	if (!handle)
+	if (!handle) {
+		if (log_fd >= 0)
+			close(log_fd);
 		return NULL;
+	}
 
 	if (log_fd >= 0)
 		skip_fds[nr_skip_fds++] = log_fd;
@@ -839,6 +920,20 @@ popen_new(struct popen_opts *opts)
 		 * do anything special.
 		 */
 
+		/*
+		 * Replace the logger fd to its duplicate. It
+		 * should be done before we'll close inherited
+		 * fds: old logger fd may be stderr and stderr may
+		 * be subject to close.
+		 *
+		 * We should also do it before a first call to a
+		 * say_*() function, because otherwise a user may
+		 * capture our debug logs as stderr of the child
+		 * process.
+		 */
+		if (log_fd >= 0)
+			log_set_fd(log_fd);
+
 		/*
 		 * Also don't forget to drop signal handlers
 		 * to default inside a child process since we're
@@ -944,19 +1039,21 @@ popen_new(struct popen_opts *opts)
 			goto exit_child;
 		}
 
-		if (log_fd >= 0) {
-			if (close(log_fd)) {
-				say_error("child: can't close logfd %d",
-					  log_fd);
-				goto exit_child;
-			}
-		}
+		/*
+		 * Return the logger back, because we're in the
+		 * same virtual memory address space as the
+		 * parent.
+		 */
+		if (log_fd >= 0)
+			log_set_fd(old_log_fd);
 
 		if (opts->flags & POPEN_FLAG_SHELL)
 			execve(_PATH_BSHELL, opts->argv, envp);
 		else
 			execve(opts->argv[0], opts->argv, envp);
 exit_child:
+		if (log_fd >= 0)
+			log_set_fd(old_log_fd);
 		_exit(errno);
 		unreachable();
 	}
@@ -987,6 +1084,13 @@ exit_child:
 		}
 	}
 
+	/* Close the temporary logger fd. */
+	if (log_fd >= 0 && close(log_fd) != 0) {
+		diag_set(SystemError, "Can't close temporary logfd %d", log_fd);
+		log_fd = -1;
+		goto out_err;
+	}
+
 	/*
 	 * Link it into global list for force
 	 * cleanup on exit. Note we use this
@@ -1018,6 +1122,8 @@ out_err:
 		if (pfd[i][1] != -1)
 			close(pfd[i][1]);
 	}
+	if (log_fd >= 0)
+		close(log_fd);
 	errno = saved_errno;
 	return NULL;
 }
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 06/13] popen: decouple logger fd from stderr
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 06/13] popen: decouple logger fd from stderr Alexander Turenko
@ 2020-04-10  9:18   ` Cyrill Gorcunov
  2020-04-10 12:20     ` Alexander Turenko
  0 siblings, 1 reply; 36+ messages in thread
From: Cyrill Gorcunov @ 2020-04-10  9:18 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 05:50:44AM +0300, Alexander Turenko wrote:
> The default logger configuration writes logs to stderr.
> 
> Popen implementation holds a logger fd until execve() to be able to
> write debug entries or information about a failure from a child. However
> when popen flags requires to close stderr in the child, the logger fd
> becomes closed: logging will fail.
> 
> Another problem appears when a user want to capture stderr and
> tarantool's log level is set to debug (7). Since the logger uses stderr
> and it is fed to the parent using a pipe, the logger output will not
> shown on the 'real' stderr, but will be captured together with child's
> program debugging output.
> 
> This commit duplicates a logger file descriptor that allows to close or
> redirect child's stderr without described side effects.
> 
> See also 86ec3a5c4792ea1bba9f644da1e8f301314c8d29 ('popen: add logging
> in child process').
> 
> Areas for improvements:
> 
> * Copy logger fd at module initialization time instead of copying of
>   each popen call.
> 
> Alternatives:
> 
> * Extend logger to allow to accumulate log entries in a buffer. Flush
>   the buffer from the parent process. (It is possible since vfork does
>   not split a virtual memory space).
> 
> Part of #4031
> ---
>  src/lib/core/popen.c | 124 +++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 115 insertions(+), 9 deletions(-)
> 
> diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
> index 9d4e6ef3a..62920e0c8 100644
> --- a/src/lib/core/popen.c
> +++ b/src/lib/core/popen.c
> @@ -74,6 +74,59 @@ popen_unregister(struct popen_handle *handle)
>  	mh_i32ptr_remove(popen_pids_map, &node, NULL);
>  }
>  
> +/**
> + * Duplicate a file descriptor, but not to std{in,out,err}.
> + *
> + * Return a new fd at success, otherwise return -1 and set a diag.
> + */
> +static int
> +dup_not_std_streams(int fd)
> +{
> +	int res = -1;
> +	int save_errno = 0;
> +
> +	/*
> +	 * We will call dup() in a loop until it will return
> +	 * fd > STDERR_FILENO. The array `discarded_fds` stores
> +	 * intermediate fds to close them after all dup() calls.
> +	 */
> +	static_assert(STDERR_FILENO + 1 == 3,
> +		      "Unexpected STDERR_FILENO constant");
We already have (in popen_new)
	static_assert(STDIN_FILENO == 0 &&
		      STDOUT_FILENO == 1 &&
		      STDERR_FILENO == 2,
		      "stdin/out/err are not posix compatible");
no need for this again.
> +	int discarded_fds[STDERR_FILENO + 1] = {-1, -1, -1};
And here we could
	int discarded_fds[POPEN_FLAG_FD_STDEND_BIT]
the POPEN_FLAG_FD_STDEND_BIT constant introduced exactly for that.
> +
> +	for (size_t i = 0; i < lengthof(discarded_fds) + 1; ++i) {
> +		int new_fd = dup(fd);
> +		if (new_fd < 0) {
> +			save_errno = errno;
> +			break;
> +		}
> +
> +		/* Found wanted fd. */
> +		if (new_fd > STDERR_FILENO) {
> +			res = new_fd;
> +			break;
> +		}
> +
> +		/* Save to close then. */
> +		assert(i < lengthof(discarded_fds));
> +		discarded_fds[i] = new_fd;
> +	}
> +
> +	/* Close all intermediate fds (if any). */
> +	for (size_t i = 0; i < lengthof(discarded_fds); ++i)
> +		if (discarded_fds[i] >= 0)
> +			close(discarded_fds[i]);
Wrap for() with {} please.
Otherwise looks good.
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 06/13] popen: decouple logger fd from stderr
  2020-04-10  9:18   ` Cyrill Gorcunov
@ 2020-04-10 12:20     ` Alexander Turenko
  0 siblings, 0 replies; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10 12:20 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
> > +	/*
> > +	 * We will call dup() in a loop until it will return
> > +	 * fd > STDERR_FILENO. The array `discarded_fds` stores
> > +	 * intermediate fds to close them after all dup() calls.
> > +	 */
> > +	static_assert(STDERR_FILENO + 1 == 3,
> > +		      "Unexpected STDERR_FILENO constant");
> 
> We already have (in popen_new)
> 
> 	static_assert(STDIN_FILENO == 0 &&
> 		      STDOUT_FILENO == 1 &&
> 		      STDERR_FILENO == 2,
> 		      "stdin/out/err are not posix compatible");
> 
> no need for this again.
> 
> > +	int discarded_fds[STDERR_FILENO + 1] = {-1, -1, -1};
> 
> And here we could
> 
> 	int discarded_fds[POPEN_FLAG_FD_STDEND_BIT]
> 
> the POPEN_FLAG_FD_STDEND_BIT constant introduced exactly for that.
Okay, thanks!
> 
> > +
> > +	for (size_t i = 0; i < lengthof(discarded_fds) + 1; ++i) {
> > +		int new_fd = dup(fd);
> > +		if (new_fd < 0) {
> > +			save_errno = errno;
> > +			break;
> > +		}
> > +
> > +		/* Found wanted fd. */
> > +		if (new_fd > STDERR_FILENO) {
> > +			res = new_fd;
> > +			break;
> > +		}
> > +
> > +		/* Save to close then. */
> > +		assert(i < lengthof(discarded_fds));
> > +		discarded_fds[i] = new_fd;
> > +	}
> > +
> > +	/* Close all intermediate fds (if any). */
> > +	for (size_t i = 0; i < lengthof(discarded_fds); ++i)
> > +		if (discarded_fds[i] >= 0)
> > +			close(discarded_fds[i]);
> 
> Wrap for() with {} please.
No problem.
Changed:
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index b8ce77494..ef2f8e2aa 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -90,9 +90,7 @@ dup_not_std_streams(int fd)
         * fd > STDERR_FILENO. The array `discarded_fds` stores
         * intermediate fds to close them after all dup() calls.
         */
-       static_assert(STDERR_FILENO + 1 == 3,
-                     "Unexpected STDERR_FILENO constant");
-       int discarded_fds[STDERR_FILENO + 1] = {-1, -1, -1};
+       int discarded_fds[POPEN_FLAG_FD_STDEND_BIT] = {-1, -1, -1};
 
        for (size_t i = 0; i < lengthof(discarded_fds) + 1; ++i) {
                int new_fd = dup(fd);
@@ -113,9 +111,10 @@ dup_not_std_streams(int fd)
        }
 
        /* Close all intermediate fds (if any). */
-       for (size_t i = 0; i < lengthof(discarded_fds); ++i)
+       for (size_t i = 0; i < lengthof(discarded_fds); ++i) {
                if (discarded_fds[i] >= 0)
                        close(discarded_fds[i]);
+       }
 
        /* Report an error if it occurs. */
        if (res < 0) {
^ permalink raw reply	[flat|nested] 36+ messages in thread
* [Tarantool-patches] [PATCH 07/13] popen: add const qualifier to popen_write_timeout
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (5 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 06/13] popen: decouple logger fd from stderr Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  8:04   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 08/13] popen: unblock popen_read_timeout at a first byte Alexander Turenko
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
The buffer is for reading, we're not intend to change it.
Part of #4031
---
 src/lib/core/popen.c | 2 +-
 src/lib/core/popen.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index 62920e0c8..dbc700287 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -281,7 +281,7 @@ stdX_str(unsigned int index)
  * Write data to the child stdin.
  */
 int
-popen_write_timeout(struct popen_handle *handle, void *buf,
+popen_write_timeout(struct popen_handle *handle, const void *buf,
 		    size_t count, unsigned int flags,
 		    ev_tstamp timeout)
 {
diff --git a/src/lib/core/popen.h b/src/lib/core/popen.h
index 587d6f7df..97c581c13 100644
--- a/src/lib/core/popen.h
+++ b/src/lib/core/popen.h
@@ -154,7 +154,7 @@ extern const char *
 popen_command(struct popen_handle *handle);
 
 extern int
-popen_write_timeout(struct popen_handle *handle, void *buf,
+popen_write_timeout(struct popen_handle *handle, const void *buf,
 		    size_t count, unsigned int flags,
 		    ev_tstamp timeout);
 
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* [Tarantool-patches] [PATCH 08/13] popen: unblock popen_read_timeout at a first byte
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (6 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 07/13] popen: add const qualifier to popen_write_timeout Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  8:10   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 09/13] popen: remove redundant fd check before perform IO Alexander Turenko
                   ` (5 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
Before this change popen_read_timeout() waits until a passed buffer will
be fully filled (or until EOF / timeout / IO error occurs). Now it waits
for any amount of data (but at least one byte).
It allows to communicate with an interactive child program: write, read,
repeat.
Part of #4031
---
 src/lib/core/popen.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index dbc700287..660ace0d5 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -345,8 +345,8 @@ popen_read_timeout(struct popen_handle *handle, void *buf,
 		  handle->pid, stdX_str(idx), idx, buf, count,
 		  handle->ios[idx].fd, timeout);
 
-	return coio_read_timeout(&handle->ios[idx], buf,
-				 count, timeout);
+	return coio_read_ahead_timeout(&handle->ios[idx], buf, 1, count,
+				       timeout);
 }
 
 /**
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* [Tarantool-patches] [PATCH 09/13] popen: remove redundant fd check before perform IO
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (7 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 08/13] popen: unblock popen_read_timeout at a first byte Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  8:18   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 10/13] popen: add missed diag_set() in popen IO functions Alexander Turenko
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
The function already checks flags to find out whether the file
descriptor should be available for reading / writing. When it is so, the
corresponding fd is great or equal to zero.
The further commits will add missed diagnostics for IO functions and it
is hard to write a meaningful error message for a situation that is not
possible. Moreover, we would obligated to document the error as one of
possible failures in a function contract (while it can't occur).
Part of #4031
---
 src/lib/core/popen.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index 660ace0d5..c54e0b211 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -194,8 +194,7 @@ handle_free(struct popen_handle *handle)
  * Test if the handle can run io operation.
  */
 static inline bool
-popen_may_io(struct popen_handle *handle, unsigned int idx,
-	     unsigned int io_flags)
+popen_may_io(struct popen_handle *handle, unsigned int io_flags)
 {
 	if (!handle) {
 		errno = ESRCH;
@@ -207,11 +206,6 @@ popen_may_io(struct popen_handle *handle, unsigned int idx,
 		return false;
 	}
 
-	if (handle->ios[idx].fd < 0) {
-		errno = EPIPE;
-		return false;
-	}
-
 	return true;
 }
 
@@ -294,7 +288,7 @@ popen_write_timeout(struct popen_handle *handle, const void *buf,
 	    return -1;
 	}
 
-	if (!popen_may_io(handle, STDIN_FILENO, flags))
+	if (!popen_may_io(handle, flags))
 		return -1;
 
 	if (count > (size_t)SSIZE_MAX) {
@@ -329,7 +323,7 @@ popen_read_timeout(struct popen_handle *handle, void *buf,
 	    return -1;
 	}
 
-	if (!popen_may_io(handle, idx, flags))
+	if (!popen_may_io(handle, flags))
 		return -1;
 
 	if (count > (size_t)SSIZE_MAX) {
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 09/13] popen: remove redundant fd check before perform IO
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 09/13] popen: remove redundant fd check before perform IO Alexander Turenko
@ 2020-04-10  8:18   ` Cyrill Gorcunov
  0 siblings, 0 replies; 36+ messages in thread
From: Cyrill Gorcunov @ 2020-04-10  8:18 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 05:50:47AM +0300, Alexander Turenko wrote:
> The function already checks flags to find out whether the file
> descriptor should be available for reading / writing. When it is so, the
> corresponding fd is great or equal to zero.
> 
> The further commits will add missed diagnostics for IO functions and it
> is hard to write a meaningful error message for a situation that is not
> possible. Moreover, we would obligated to document the error as one of
> possible failures in a function contract (while it can't occur).
> 
> Part of #4031
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
^ permalink raw reply	[flat|nested] 36+ messages in thread
* [Tarantool-patches] [PATCH 10/13] popen: add missed diag_set() in popen IO functions
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (8 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 09/13] popen: remove redundant fd check before perform IO Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  8:28   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 11/13] coio: fix obsoleted comment in coio_write_timeout Alexander Turenko
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
Our usual convention for C code is to return a negative value at failure
and set an entry to the diagnostics area.
When code uses this convention consistently, it is much easier to handle
failures when using it: you always know where to find an error type and
message and how to pass the error to a C or Lua caller.
See also the previous commit ('popen: add missed diag_set in
popen_signal/delete').
Part of #4031
---
 src/lib/core/popen.c | 100 +++++++++++++++++++++++++++++++------------
 1 file changed, 72 insertions(+), 28 deletions(-)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index c54e0b211..bf7d597bd 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -191,22 +191,20 @@ handle_free(struct popen_handle *handle)
 }
 
 /**
- * Test if the handle can run io operation.
+ * Test if the handle can run a requested IO operation.
+ *
+ * Returns 0 if so and -1 otherwise (and set a diag).
  */
-static inline bool
+static inline int
 popen_may_io(struct popen_handle *handle, unsigned int io_flags)
 {
-	if (!handle) {
-		errno = ESRCH;
-		return false;
-	}
-
 	if (!(io_flags & handle->flags)) {
-		errno = EINVAL;
-		return false;
+		diag_set(IllegalParams, "popen: handle does not support the "
+			 "requested IO operation");
+		return -1;
 	}
 
-	return true;
+	return 0;
 }
 
 /**
@@ -273,6 +271,27 @@ stdX_str(unsigned int index)
 
 /**
  * Write data to the child stdin.
+ *
+ * Yield until all @a count bytes will be written.
+ *
+ * Returns @a count at success, otherwise returns -1 and set a
+ * diag.
+ *
+ * Possible errors:
+ *
+ * - IllegalParams: a parameter check fails:
+ *   - count: data is too big.
+ *   - flags: POPEN_FLAG_FD_STDIN bit is unset.
+ *   - handle: handle does not support the requested IO operation.
+ * - SocketError: an IO error occurs at write().
+ * - TimedOut: @a timeout quota is exceeded.
+ * - FiberIsCancelled: cancelled by an outside code.
+ *
+ * An error may occur after a partial write. There is not way to
+ * enquire amount of written bytes in the case.
+ *
+ * FIXME: Provide an info re amount written bytes in the case.
+ *        Say, return -(written) in the case.
  */
 int
 popen_write_timeout(struct popen_handle *handle, const void *buf,
@@ -281,20 +300,21 @@ popen_write_timeout(struct popen_handle *handle, const void *buf,
 {
 	assert(handle != NULL);
 
-	int idx = STDIN_FILENO;
+	if (count > (size_t)SSIZE_MAX) {
+		diag_set(IllegalParams, "popen: data is too big");
+		return -1;
+	}
 
 	if (!(flags & POPEN_FLAG_FD_STDIN)) {
-	    errno = EINVAL;
-	    return -1;
+		diag_set(IllegalParams,
+			 "popen: POPEN_FLAG_FD_STDIN bit is unset");
+		return -1;
 	}
 
-	if (!popen_may_io(handle, flags))
+	if (popen_may_io(handle, flags) != 0)
 		return -1;
 
-	if (count > (size_t)SSIZE_MAX) {
-		errno = E2BIG;
-		return -1;
-	}
+	int idx = STDIN_FILENO;
 
 	say_debug("popen: %d: write idx [%s:%d] buf %p count %zu "
 		  "fds %d timeout %.9g",
@@ -307,6 +327,26 @@ popen_write_timeout(struct popen_handle *handle, const void *buf,
 
 /**
  * Read data from a child's peer with timeout.
+ *
+ * Yield until some data will be available for read.
+ *
+ * Returns amount of read bytes at success, otherwise returns -1
+ * and set a diag.
+ *
+ * Zero return value means EOF.
+ *
+ * Note: Less then @a count bytes may be available for read at a
+ * moment, so a return value less then @a count does not mean EOF.
+ *
+ * Possible errors:
+ *
+ * - IllegalParams: a parameter check fails:
+ *   - count: buffer is too big.
+ *   - flags: POPEN_FLAG_FD_STD{OUT,ERR} are unset both.
+ *   - handle: handle does not support the requested IO operation.
+ * - SocketError: an IO error occurs at read().
+ * - TimedOut: @a timeout quota is exceeded.
+ * - FiberIsCancelled: cancelled by an outside code.
  */
 ssize_t
 popen_read_timeout(struct popen_handle *handle, void *buf,
@@ -315,24 +355,28 @@ popen_read_timeout(struct popen_handle *handle, void *buf,
 {
 	assert(handle != NULL);
 
-	int idx = flags & POPEN_FLAG_FD_STDOUT ?
-		STDOUT_FILENO : STDERR_FILENO;
+	if (count > (size_t)SSIZE_MAX) {
+		diag_set(IllegalParams, "popen: buffer is too big");
+		return -1;
+	}
 
 	if (!(flags & (POPEN_FLAG_FD_STDOUT | POPEN_FLAG_FD_STDERR))) {
-	    errno = EINVAL;
-	    return -1;
+		diag_set(IllegalParams, "popen: POPEN_FLAG_FD_STD{OUT,ERR} are "
+			 "unset both");
+		return -1;
 	}
 
-	if (!popen_may_io(handle, flags))
+	if (flags & POPEN_FLAG_FD_STDOUT && flags & POPEN_FLAG_FD_STDERR) {
+		diag_set(IllegalParams, "popen: reading from both stdout and "
+			 "stderr at one call is not supported");
 		return -1;
+	}
 
-	if (count > (size_t)SSIZE_MAX) {
-		errno = E2BIG;
+	if (popen_may_io(handle, flags) != 0)
 		return -1;
-	}
 
-	if (timeout < 0.)
-		timeout = TIMEOUT_INFINITY;
+	int idx = flags & POPEN_FLAG_FD_STDOUT ?
+		STDOUT_FILENO : STDERR_FILENO;
 
 	say_debug("popen: %d: read idx [%s:%d] buf %p count %zu "
 		  "fds %d timeout %.9g",
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 10/13] popen: add missed diag_set() in popen IO functions
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 10/13] popen: add missed diag_set() in popen IO functions Alexander Turenko
@ 2020-04-10  8:28   ` Cyrill Gorcunov
  0 siblings, 0 replies; 36+ messages in thread
From: Cyrill Gorcunov @ 2020-04-10  8:28 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 05:50:48AM +0300, Alexander Turenko wrote:
> Our usual convention for C code is to return a negative value at failure
> and set an entry to the diagnostics area.
> 
> When code uses this convention consistently, it is much easier to handle
> failures when using it: you always know where to find an error type and
> message and how to pass the error to a C or Lua caller.
> 
> See also the previous commit ('popen: add missed diag_set in
> popen_signal/delete').
> 
> Part of #4031
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
^ permalink raw reply	[flat|nested] 36+ messages in thread
* [Tarantool-patches] [PATCH 11/13] coio: fix obsoleted comment in coio_write_timeout
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (9 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 10/13] popen: add missed diag_set() in popen IO functions Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  8:28   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions Alexander Turenko
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
The comment was added in 52765de65b8304424ef3d7014952f9fa55ee46f4, but
becomes non-actual since 1.6.6-21-gc74abc786 ('Implement special
TimedOut exception type and use it in coio and latch.')
Part of #4031
---
 src/lib/core/coio.cc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/lib/core/coio.cc b/src/lib/core/coio.cc
index e88d724d5..74e6240ce 100644
--- a/src/lib/core/coio.cc
+++ b/src/lib/core/coio.cc
@@ -388,8 +388,8 @@ coio_readn_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz
  * fiber until the socket becomes ready, until
  * all data is written.
  *
- * @retval the number of bytes written. Can be less than
- * requested only in case of timeout.
+ * @retval the number of bytes written. Always
+ * equal to @a sz.
  */
 ssize_t
 coio_write_timeout(struct ev_io *coio, const void *buf, size_t sz,
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (10 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 11/13] coio: fix obsoleted comment in coio_write_timeout Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10  8:05   ` Konstantin Osipov
  2020-04-10  8:29   ` Cyrill Gorcunov
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 13/13] popen: use of exception safe functions for IO Alexander Turenko
  2020-04-10 16:36 ` [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Kirill Yukhin
  13 siblings, 2 replies; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
The popen implementation is written in C and uses coio read / write
functions. If an exception occurs, it'll pass through the C code. It
should be catched to proceed correctly.
We usually have foo() and foo_xc() (exception) functions when both
variants are necessary. Here I added non-conventional *_noxc() functions
as the temporary solution to postpone refactoring of the code and all
its usages.
Part of #4031
---
 src/lib/core/coio.cc | 39 +++++++++++++++++++++++++++++++++++++++
 src/lib/core/coio.h  | 41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)
diff --git a/src/lib/core/coio.cc b/src/lib/core/coio.cc
index 74e6240ce..6a113aa47 100644
--- a/src/lib/core/coio.cc
+++ b/src/lib/core/coio.cc
@@ -381,6 +381,26 @@ coio_readn_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz
 	return nrd;
 }
 
+/*
+ * FIXME: Rewrite coio_read_ahead_timeout() w/o C++ exceptions and
+ * drop this function.
+ */
+ssize_t
+coio_read_ahead_timeout_noxc(struct ev_io *coio, void *buf, size_t sz,
+			     size_t bufsiz, ev_tstamp timeout)
+{
+	try {
+		return coio_read_ahead_timeout(coio, buf, sz, bufsiz, timeout);
+	} catch (Exception *) {
+		/*
+		 * The exception is already in the diagnostics
+		 * area. Nothing to do.
+		 */
+	}
+	return -1;
+}
+
+
 /** Write sz bytes to socket.
  *
  * Throws SocketError in case of write error. If
@@ -437,6 +457,25 @@ coio_write_timeout(struct ev_io *coio, const void *buf, size_t sz,
 	}
 }
 
+/*
+ * FIXME: Rewrite coio_write_timeout() w/o C++ exceptions and drop
+ * this function.
+ */
+ssize_t
+coio_write_timeout_noxc(struct ev_io *coio, const void *buf, size_t sz,
+			ev_tstamp timeout)
+{
+	try {
+		return coio_write_timeout(coio, buf, sz, timeout);
+	} catch (Exception *) {
+		/*
+		 * The exception is already in the diagnostics
+		 * area. Nothing to do.
+		 */
+	}
+	return -1;
+}
+
 /*
  * Write iov using sio API.
  * Put in an own function to workaround gcc bug with @finally
diff --git a/src/lib/core/coio.h b/src/lib/core/coio.h
index c323955d7..7875a0da1 100644
--- a/src/lib/core/coio.h
+++ b/src/lib/core/coio.h
@@ -130,6 +130,29 @@ coio_read_timeout(struct ev_io *coio, void *buf, size_t sz, ev_tstamp timeout)
 	return coio_read_ahead_timeout(coio, buf, sz, sz, timeout);
 }
 
+/**
+ * Read data with timeout.
+ *
+ * Yield until some data will be available for read.
+ *
+ * Returns amount of read bytes at success, otherwise returns -1
+ * and set a diag.
+ *
+ * Zero return value means EOF.
+ *
+ * Note: Less then @a count bytes may be available for read at a
+ * moment, so a return value less then @a count does not mean EOF.
+ *
+ * Possible errors:
+ *
+ * - SocketError: an IO error occurs at read().
+ * - TimedOut: @a timeout quota is exceeded.
+ * - FiberIsCancelled: cancelled by an outside code.
+ */
+ssize_t
+coio_read_ahead_timeout_noxc(struct ev_io *coio, void *buf, size_t sz,
+			     size_t bufsiz, ev_tstamp timeout);
+
 static inline ssize_t
 coio_readn(struct ev_io *coio, void *buf, size_t sz)
 {
@@ -144,6 +167,24 @@ ssize_t
 coio_write_timeout(struct ev_io *coio, const void *buf, size_t sz,
 		   ev_tstamp timeout);
 
+/**
+ * Write @a count bytes with timeout.
+ *
+ * Yield until all @a count bytes will be written.
+ *
+ * Returns @a count at success, otherwise returns -1 and set a
+ * diag.
+ *
+ * Possible errors:
+ *
+ * - SocketError: an IO error occurs at write().
+ * - TimedOut: @a timeout quota is exceeded.
+ * - FiberIsCancelled: cancelled by an outside code.
+ */
+ssize_t
+coio_write_timeout_noxc(struct ev_io *coio, const void *buf, size_t sz,
+			ev_tstamp timeout);
+
 static inline void
 coio_write(struct ev_io *coio, const void *buf, size_t sz)
 {
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions Alexander Turenko
@ 2020-04-10  8:05   ` Konstantin Osipov
  2020-04-10  8:17     ` Cyrill Gorcunov
                       ` (2 more replies)
  2020-04-10  8:29   ` Cyrill Gorcunov
  1 sibling, 3 replies; 36+ messages in thread
From: Konstantin Osipov @ 2020-04-10  8:05 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
* Alexander Turenko <alexander.turenko@tarantool.org> [20/04/10 09:28]:
> The popen implementation is written in C and uses coio read / write
> functions. If an exception occurs, it'll pass through the C code. It
> should be catched to proceed correctly.
> 
> We usually have foo() and foo_xc() (exception) functions when both
> variants are necessary. Here I added non-conventional *_noxc() functions
> as the temporary solution to postpone refactoring of the code and all
> its usages.
> 
> Part of #4031
> ---
>  src/lib/core/coio.cc | 39 +++++++++++++++++++++++++++++++++++++++
>  src/lib/core/coio.h  | 41 +++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 80 insertions(+)
> 
> diff --git a/src/lib/core/coio.cc b/src/lib/core/coio.cc
> index 74e6240ce..6a113aa47 100644
> --- a/src/lib/core/coio.cc
> +++ b/src/lib/core/coio.cc
> @@ -381,6 +381,26 @@ coio_readn_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz
>  	return nrd;
>  }
>  
> +/*
> + * FIXME: Rewrite coio_read_ahead_timeout() w/o C++ exceptions and
> + * drop this function.
> + */
Ugh.
Why can't you do it instead? This is necessary for a lot of other
patches, I believe Georgy's patches are doing it as well.
-- 
Konstantin Osipov, Moscow, Russia
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions
  2020-04-10  8:05   ` Konstantin Osipov
@ 2020-04-10  8:17     ` Cyrill Gorcunov
  2020-04-10 11:57     ` Alexander Turenko
  2020-04-12 12:51     ` Alexander Turenko
  2 siblings, 0 replies; 36+ messages in thread
From: Cyrill Gorcunov @ 2020-04-10  8:17 UTC (permalink / raw)
  To: Konstantin Osipov; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 11:05:13AM +0300, Konstantin Osipov wrote:
> >  
> > +/*
> > + * FIXME: Rewrite coio_read_ahead_timeout() w/o C++ exceptions and
> > + * drop this function.
> > + */
> 
> Ugh.
> 
> Why can't you do it instead? This is necessary for a lot of other
> patches, I believe Georgy's patches are doing it as well.
Simply because we want a functional popen engine in the upcoming
release. We gonna cleanup/fixup and etc in next iteration. And
unweaving service functions from exception is nontrivial. As
to me such things should be done by separate series.
If you take a look on Georgy's patches with more attention
you'll see that the changes are far more than just modifying
the helpers, they affect context.
^ permalink raw reply	[flat|nested] 36+ messages in thread
* Re: [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions
  2020-04-10  8:05   ` Konstantin Osipov
  2020-04-10  8:17     ` Cyrill Gorcunov
@ 2020-04-10 11:57     ` Alexander Turenko
  2020-04-12 12:51     ` Alexander Turenko
  2 siblings, 0 replies; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10 11:57 UTC (permalink / raw)
  To: Konstantin Osipov; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 11:05:13AM +0300, Konstantin Osipov wrote:
> * Alexander Turenko <alexander.turenko@tarantool.org> [20/04/10 09:28]:
> > The popen implementation is written in C and uses coio read / write
> > functions. If an exception occurs, it'll pass through the C code. It
> > should be catched to proceed correctly.
> > 
> > We usually have foo() and foo_xc() (exception) functions when both
> > variants are necessary. Here I added non-conventional *_noxc() functions
> > as the temporary solution to postpone refactoring of the code and all
> > its usages.
> > 
> > Part of #4031
> > ---
> >  src/lib/core/coio.cc | 39 +++++++++++++++++++++++++++++++++++++++
> >  src/lib/core/coio.h  | 41 +++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 80 insertions(+)
> > 
> > diff --git a/src/lib/core/coio.cc b/src/lib/core/coio.cc
> > index 74e6240ce..6a113aa47 100644
> > --- a/src/lib/core/coio.cc
> > +++ b/src/lib/core/coio.cc
> > @@ -381,6 +381,26 @@ coio_readn_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz
> >  	return nrd;
> >  }
> >  
> > +/*
> > + * FIXME: Rewrite coio_read_ahead_timeout() w/o C++ exceptions and
> > + * drop this function.
> > + */
> 
> Ugh.
> 
> Why can't you do it instead? This is necessary for a lot of other
> patches, I believe Georgy's patches are doing it as well.
I dislike all those "let's do it dirty for now, later we'll return here"
too. But now I really have no choice: there is no time I can pay here
now.
Sorry.
^ permalink raw reply	[flat|nested] 36+ messages in thread
* Re: [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions
  2020-04-10  8:05   ` Konstantin Osipov
  2020-04-10  8:17     ` Cyrill Gorcunov
  2020-04-10 11:57     ` Alexander Turenko
@ 2020-04-12 12:51     ` Alexander Turenko
  2 siblings, 0 replies; 36+ messages in thread
From: Alexander Turenko @ 2020-04-12 12:51 UTC (permalink / raw)
  To: Konstantin Osipov; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 11:05:13AM +0300, Konstantin Osipov wrote:
> * Alexander Turenko <alexander.turenko@tarantool.org> [20/04/10 09:28]:
> > The popen implementation is written in C and uses coio read / write
> > functions. If an exception occurs, it'll pass through the C code. It
> > should be catched to proceed correctly.
> > 
> > We usually have foo() and foo_xc() (exception) functions when both
> > variants are necessary. Here I added non-conventional *_noxc() functions
> > as the temporary solution to postpone refactoring of the code and all
> > its usages.
> > 
> > Part of #4031
> > ---
> >  src/lib/core/coio.cc | 39 +++++++++++++++++++++++++++++++++++++++
> >  src/lib/core/coio.h  | 41 +++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 80 insertions(+)
> > 
> > diff --git a/src/lib/core/coio.cc b/src/lib/core/coio.cc
> > index 74e6240ce..6a113aa47 100644
> > --- a/src/lib/core/coio.cc
> > +++ b/src/lib/core/coio.cc
> > @@ -381,6 +381,26 @@ coio_readn_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz
> >  	return nrd;
> >  }
> >  
> > +/*
> > + * FIXME: Rewrite coio_read_ahead_timeout() w/o C++ exceptions and
> > + * drop this function.
> > + */
> 
> Ugh.
> 
> Why can't you do it instead? This is necessary for a lot of other
> patches, I believe Georgy's patches are doing it as well.
Filed https://github.com/tarantool/tarantool/issues/4881
WBR, Alexander Turenko.
^ permalink raw reply	[flat|nested] 36+ messages in thread
* Re: [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions Alexander Turenko
  2020-04-10  8:05   ` Konstantin Osipov
@ 2020-04-10  8:29   ` Cyrill Gorcunov
  1 sibling, 0 replies; 36+ messages in thread
From: Cyrill Gorcunov @ 2020-04-10  8:29 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 05:50:50AM +0300, Alexander Turenko wrote:
> The popen implementation is written in C and uses coio read / write
> functions. If an exception occurs, it'll pass through the C code. It
> should be catched to proceed correctly.
> 
> We usually have foo() and foo_xc() (exception) functions when both
> variants are necessary. Here I added non-conventional *_noxc() functions
> as the temporary solution to postpone refactoring of the code and all
> its usages.
> 
> Part of #4031
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
^ permalink raw reply	[flat|nested] 36+ messages in thread
* [Tarantool-patches] [PATCH 13/13] popen: use of exception safe functions for IO
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (11 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 12/13] coio: add *_noxc read / write functions Alexander Turenko
@ 2020-04-10  2:50 ` Alexander Turenko
  2020-04-10 11:50   ` Cyrill Gorcunov
  2020-04-10 16:36 ` [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Kirill Yukhin
  13 siblings, 1 reply; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10  2:50 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
popen read / write functions provides are written in C and intended to
be used from C: the contract is to return -1 at failure and set an entry
to the diagnostics area. However a C++ exception from coio read / write
functions would pass over a popen function stack frame.
The solution is to use the recently introduced coio exception safe
functions.
Part of #4031
---
 src/lib/core/popen.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/lib/core/popen.c b/src/lib/core/popen.c
index bf7d597bd..d39d8640a 100644
--- a/src/lib/core/popen.c
+++ b/src/lib/core/popen.c
@@ -321,8 +321,10 @@ popen_write_timeout(struct popen_handle *handle, const void *buf,
 		  handle->pid, stdX_str(idx), idx, buf, count,
 		  handle->ios[idx].fd, timeout);
 
-	return coio_write_timeout(&handle->ios[idx], buf,
-				  count, timeout);
+	int rc = coio_write_timeout_noxc(&handle->ios[idx], buf, count,
+					 timeout);
+	assert(rc < 0 || rc == (ssize_t)count);
+	return rc;
 }
 
 /**
@@ -383,8 +385,8 @@ popen_read_timeout(struct popen_handle *handle, void *buf,
 		  handle->pid, stdX_str(idx), idx, buf, count,
 		  handle->ios[idx].fd, timeout);
 
-	return coio_read_ahead_timeout(&handle->ios[idx], buf, 1, count,
-				       timeout);
+	return coio_read_ahead_timeout_noxc(&handle->ios[idx], buf, 1, count,
+					    timeout);
 }
 
 /**
-- 
2.25.0
^ permalink raw reply	[flat|nested] 36+ messages in thread* Re: [Tarantool-patches] [PATCH 13/13] popen: use of exception safe functions for IO
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 13/13] popen: use of exception safe functions for IO Alexander Turenko
@ 2020-04-10 11:50   ` Cyrill Gorcunov
  2020-04-10 12:21     ` Alexander Turenko
  0 siblings, 1 reply; 36+ messages in thread
From: Cyrill Gorcunov @ 2020-04-10 11:50 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 05:50:51AM +0300, Alexander Turenko wrote:
> popen read / write functions provides are written in C and intended to
> be used from C: the contract is to return -1 at failure and set an entry
> to the diagnostics area. However a C++ exception from coio read / write
> functions would pass over a popen function stack frame.
> 
> The solution is to use the recently introduced coio exception safe
> functions.
> 
> Part of #4031
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
^ permalink raw reply	[flat|nested] 36+ messages in thread
* Re: [Tarantool-patches] [PATCH 13/13] popen: use of exception safe functions for IO
  2020-04-10 11:50   ` Cyrill Gorcunov
@ 2020-04-10 12:21     ` Alexander Turenko
  0 siblings, 0 replies; 36+ messages in thread
From: Alexander Turenko @ 2020-04-10 12:21 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches
On Fri, Apr 10, 2020 at 02:50:00PM +0300, Cyrill Gorcunov wrote:
> On Fri, Apr 10, 2020 at 05:50:51AM +0300, Alexander Turenko wrote:
> > popen read / write functions provides are written in C and intended to
Fixed typo: removed 'provides'.
> > be used from C: the contract is to return -1 at failure and set an entry
> > to the diagnostics area. However a C++ exception from coio read / write
> > functions would pass over a popen function stack frame.
> > 
> > The solution is to use the recently introduced coio exception safe
> > functions.
> > 
> > Part of #4031
> Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
Thanks for the review!
^ permalink raw reply	[flat|nested] 36+ messages in thread
* Re: [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches
  2020-04-10  2:50 [Tarantool-patches] [PATCH 00/13] Popen Lua API: preliminary patches Alexander Turenko
                   ` (12 preceding siblings ...)
  2020-04-10  2:50 ` [Tarantool-patches] [PATCH 13/13] popen: use of exception safe functions for IO Alexander Turenko
@ 2020-04-10 16:36 ` Kirill Yukhin
  13 siblings, 0 replies; 36+ messages in thread
From: Kirill Yukhin @ 2020-04-10 16:36 UTC (permalink / raw)
  To: Alexander Turenko; +Cc: tarantool-patches
Hello,
On 10 апр 05:50, Alexander Turenko wrote:
> https://github.com/tarantool/tarantool/issues/4031
> https://github.com/tarantool/tarantool/tree/Totktonada/gh-4031-popen-lua-api-preparation
> 
> I working on Lua API for popen. During the development I found that
> several changes are necessary for the underlying popen engine. This
> patch series accumulates them.
> 
> Here are bugfixes, API and behaviour changes. Hope they are explained
> enough in the commit messages.
> 
> Please, give more attention to the 'popen: decouple logger fd from
> stderr' patch: it is easy to make a mistake with those fds juggling and
> I have very sparse time to test it as it deserves (I made manual testing
> for the described cases however).
> 
> Those changes can be partially tested via Lua API draft. It is on my
> temporary branch Totktonada/gh-4031-popen-12-workbranch (see also
> test/app-tap/popen.test.lua on the branch).
> 
> Alexander Turenko (13):
>   popen: require popen handle to be non-NULL
>   popen: remove retval from popen_state()
>   popen: add missed diag_set in popen_signal/delete
>   popen: add logging of fds closed in a child
>   say: allow to set a logger file descriptor
>   popen: decouple logger fd from stderr
>   popen: add const qualifier to popen_write_timeout
>   popen: unblock popen_read_timeout at a first byte
>   popen: remove redundant fd check before perform IO
>   popen: add missed diag_set() in popen IO functions
>   coio: fix obsoleted comment in coio_write_timeout
>   coio: add *_noxc read / write functions
>   popen: use of exception safe functions for IO
The patchset LGTM.
I've checked it into master.
--
Regards, Kirill Yukhin
^ permalink raw reply	[flat|nested] 36+ messages in thread