Tarantool development patches archive
 help / color / mirror / Atom feed
From: Alexander Turenko <alexander.turenko@tarantool.org>
To: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH 12/12] popen: allow to close parent's end of std* fds
Date: Wed, 15 Apr 2020 07:21:12 +0300	[thread overview]
Message-ID: <20200415042112.ur5anngvrhn7mmrl@tkn_work_nb> (raw)
In-Reply-To: <20200414130507.GH3072@uranus>

> > +int
> > +popen_shutdown(struct popen_handle *handle, unsigned int flags)
> > +{
> > +	assert(handle != NULL);
> > +
> > +	if ((flags & (POPEN_FLAG_FD_STDIN |
> > +		      POPEN_FLAG_FD_STDOUT |
> > +		      POPEN_FLAG_FD_STDERR)) == 0) {
> > +		diag_set(IllegalParams,
> > +			 "popen: neither stdin, stdout nor stderr is choosen");
> > +		return -1;
> > +	}
> > +
> > +	/* Verify the operation. */
> > +	for (int idx = STDIN_FILENO; idx < POPEN_FLAG_FD_STDEND_BIT; ++idx) {
> 
> 	for (size_t i = 0; i < lengthof(pfd_map); i++)
> 
> We already do a build time check for STDIN_x proper mapping to numbers,
> lets make it shorter.

Sure, changed.

The diff at bottom of the email.

> 
> 
> > +		/* Operate only on asked fds. */
> > +		unsigned int op_mask = pfd_map[idx].mask;
> > +		if ((flags & op_mask) == 0)
> > +			continue;
> > +
> > +		if (popen_may_io(handle, idx, op_mask, true) != 0)
> > +			return -1;
> > +	}
> > +
> > +	/* Perform the operation. */
> > +	for (int idx = STDIN_FILENO; idx < POPEN_FLAG_FD_STDEND_BIT; ++idx) {
> > +		/* Operate only on asked fds. */
> > +		unsigned int op_mask = pfd_map[idx].mask;
> > +		if ((flags & op_mask) == 0)
> > +			continue;
> > +
> > +		/* Skip already closed fds. */
> > +		if (handle->ios[idx].fd < 0)
> > +			continue;
> > +
> > +		say_debug("popen: %d: shutdown idx [%s:%d] fd %s",
> > +			  handle->pid, stdX_str(idx), idx,
> > +			  handle->ios[idx].fd);
> > +		coio_close_io(loop(), &handle->ios[idx]);
> > +	}
> 
> I don't get why we need two for() cycles?

The first loop is to check that we able to close all fds: all checks are
made first, then all close()'s are called. But the first loop actually
may be replaced with the following check:

 | flags &= POPEN_FLAG_FD_STDIN  |
 |          POPEN_FLAG_FD_STDOUT |
 |          POPEN_FLAG_FD_STDERR;
 | if ((handle->flags & flags) != flags)
 |         /* Give an error here. */

It looks more clean, so I'll use this way. Thanks for catching this!

> Also, I don't like that we
> mangle popen_may_io(). The shutdown is special. Why not do something like

We should check for closed fd in popen_may_io(), because read / write
should give proper error in the case. I guess that here we agreed.

But I made this check conditional in popen_may_io() and I guess it is
what you complain about.

I agree that it looks a bit artificial and so I extracted setting of the
'not supported IO operation' error into a helper function and use it in
popen_shutdown() directly. This really looks better.

See the diff below.

> 
> for (size_t idx = 0; i < lengthof(pfd_map); i++) {
> 	unsigned int op_mask = pfd_map[idx].mask;
> 	if ((flags & op_mask) == 0)
> 		continue;
> 
> 	if (handle->ios[idx].fd < 0)
> 		continue;
> 
> 	...
> }
> 
> Can't we do something like that?

Not exactly, but a kind of this. See below.

WBR, Alexander Turenko.

----

Incremental diff:

--- a/src/lib/core/popen.c	2020-04-15 01:44:51.387489580 +0300
+++ b/src/lib/core/popen.c	2020-04-15 01:44:35.118490605 +0300
@@ -245,21 +245,30 @@
 }
 
 /**
+ * Generate an error about IO operation that is not supported by
+ * a popen handle.
+ */
+static inline int
+popen_set_unsupported_io_error(void)
+{
+	diag_set(IllegalParams, "popen: handle does not support the "
+		 "requested IO operation");
+	return -1;
+}
+
+/**
  * Test if the handle can run a requested IO operation.
  *
  * Returns 0 if so and -1 otherwise (and set a diag).
  */
 static inline int
 popen_may_io(struct popen_handle *handle, unsigned int idx,
-	     unsigned int io_flags, bool allow_closed)
+	     unsigned int io_flags)
 {
-	if (!(io_flags & handle->flags)) {
-		diag_set(IllegalParams, "popen: handle does not support the "
-			 "requested IO operation");
-		return -1;
-	}
+	if (!(io_flags & handle->flags))
+		return popen_set_unsupported_io_error();
 
-       if (! allow_closed && handle->ios[idx].fd < 0) {
+	if (handle->ios[idx].fd < 0) {
 		diag_set(IllegalParams, "popen: attempt to operate "
 			 "on a closed file descriptor");
 		return -1;
@@ -374,7 +383,7 @@
 
 	int idx = STDIN_FILENO;
 
-	if (popen_may_io(handle, idx, flags, false) != 0)
+	if (popen_may_io(handle, idx, flags) != 0)
 		return -1;
 
 	say_debug("popen: %d: write idx [%s:%d] buf %p count %zu "
@@ -439,7 +448,7 @@
 	int idx = flags & POPEN_FLAG_FD_STDOUT ?
 		STDOUT_FILENO : STDERR_FILENO;
 
-	if (popen_may_io(handle, idx, flags, false) != 0)
+	if (popen_may_io(handle, idx, flags) != 0)
 		return -1;
 
 	say_debug("popen: %d: read idx [%s:%d] buf %p count %zu "
@@ -486,27 +495,29 @@
 {
 	assert(handle != NULL);
 
-	if ((flags & (POPEN_FLAG_FD_STDIN |
-		      POPEN_FLAG_FD_STDOUT |
-		      POPEN_FLAG_FD_STDERR)) == 0) {
+	/* Ignore irrelevant flags. */
+	flags &= POPEN_FLAG_FD_STDIN	|
+		 POPEN_FLAG_FD_STDOUT	|
+		 POPEN_FLAG_FD_STDERR;
+
+	/* At least one ..._FD_STDx flag should be set. */
+	if (flags == 0) {
 		diag_set(IllegalParams,
 			 "popen: neither stdin, stdout nor stderr is choosen");
 		return -1;
 	}
 
-	/* Verify the operation. */
-	for (int idx = STDIN_FILENO; idx < POPEN_FLAG_FD_STDEND_BIT; ++idx) {
-		/* Operate only on asked fds. */
-		unsigned int op_mask = pfd_map[idx].mask;
-		if ((flags & op_mask) == 0)
-			continue;
-
-		if (popen_may_io(handle, idx, op_mask, true) != 0)
-			return -1;
-	}
+	/*
+	 * The handle should have all std*, which are asked to
+	 * close, be piped.
+	 *
+	 * Otherwise the operation has no sense: we should close
+	 * parent's end of a pipe, but it was not created at all.
+	 */
+	if ((handle->flags & flags) != flags)
+		return popen_set_unsupported_io_error();
 
-	/* Perform the operation. */
-	for (int idx = STDIN_FILENO; idx < POPEN_FLAG_FD_STDEND_BIT; ++idx) {
+	for (size_t idx = 0; idx < lengthof(pfd_map); ++idx) {
 		/* Operate only on asked fds. */
 		unsigned int op_mask = pfd_map[idx].mask;
 		if ((flags & op_mask) == 0)

  reply	other threads:[~2020-04-15  4:21 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-04-14 11:38 [Tarantool-patches] [PATCH 00/12] Popen Lua API: preliminary patches 2 Alexander Turenko
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 01/12] popen: allow to kill process group Alexander Turenko
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 02/12] popen: add ability to keep child on deletion Alexander Turenko
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 03/12] popen: log a reason of close inherited fds failure Alexander Turenko
2020-04-14 11:52   ` Cyrill Gorcunov
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 04/12] popen: add missed diag_set() in popen_new() Alexander Turenko
2020-04-14 11:54   ` Cyrill Gorcunov
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 05/12] popen: remove retval from popen_stat() Alexander Turenko
2020-04-14 11:54   ` Cyrill Gorcunov
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 06/12] popen: quote multiword command arguments Alexander Turenko
2020-04-14 11:58   ` Cyrill Gorcunov
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 07/12] popen: add logging of duplicated logger fd Alexander Turenko
2020-04-14 11:58   ` Cyrill Gorcunov
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 08/12] popen: fix close-on-exec flag setting Alexander Turenko
2020-04-14 12:03   ` Cyrill Gorcunov
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 09/12] popen: clarify popen_{signal, delete} contract Alexander Turenko
2020-04-14 12:29   ` Cyrill Gorcunov
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 10/12] popen: add FIXME re group signal flaw Alexander Turenko
2020-04-14 13:19   ` Cyrill Gorcunov
2020-04-15  4:21     ` Alexander Turenko
2020-04-15  7:27       ` Cyrill Gorcunov
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 11/12] popen: clarify popen_read_timeout error message Alexander Turenko
2020-04-14 12:32   ` Cyrill Gorcunov
2020-04-15  4:21   ` Alexander Turenko
2020-04-15  7:39     ` Cyrill Gorcunov
2020-04-15 21:45       ` Alexander Turenko
2020-04-14 11:38 ` [Tarantool-patches] [PATCH 12/12] popen: allow to close parent's end of std* fds Alexander Turenko
2020-04-14 13:05   ` Cyrill Gorcunov
2020-04-15  4:21     ` Alexander Turenko [this message]
2020-04-15  7:43       ` Cyrill Gorcunov
2020-04-15 21:45         ` Alexander Turenko
2020-04-15  4:25 ` [Tarantool-patches] [PATCH 13/13] popen: add caution comment for popen_may_io() Alexander Turenko
2020-04-15  7:44   ` Cyrill Gorcunov
2020-04-15  4:52 ` [Tarantool-patches] [PATCH 14/14] popen: fix popen_write_timeout retval type Alexander Turenko
2020-04-15 23:57 ` [Tarantool-patches] [PATCH 00/12] Popen Lua API: preliminary patches 2 Alexander Turenko
2020-04-16  0:00   ` Alexander Turenko
2020-04-16 11:52   ` Cyrill Gorcunov
2020-04-17  6:58 ` Kirill Yukhin

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=20200415042112.ur5anngvrhn7mmrl@tkn_work_nb \
    --to=alexander.turenko@tarantool.org \
    --cc=gorcunov@gmail.com \
    --cc=tarantool-patches@dev.tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH 12/12] popen: allow to close parent'\''s end of std* fds' \
    /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