All of lore.kernel.org
 help / color / mirror / Atom feed
From: Herbert Xu <herbert@gondor.apana.org.au>
To: наб <nabijaczleweli@nabijaczleweli.xyz>
Cc: dash@vger.kernel.org, harald@gigawatt.nl
Subject: [v3 PATCH] input: Read standard input byte-wise
Date: Tue, 3 Jan 2023 14:15:58 +0800	[thread overview]
Message-ID: <Y7PIHqN4ic4UBOBb@gondor.apana.org.au> (raw)
In-Reply-To: <42d2b629549d8be07d14acb6cdd7d0f853ed1a6e.1670979949.git.nabijaczleweli@nabijaczleweli.xyz>

наб <nabijaczleweli@nabijaczleweli.xyz> wrote:
> 
> POSIX Issue 7, XCU, sh, STDIN says:

Your patch breaks history support since that relies on there
being a whole line in the input buffer.

This works for me:

---8<---
POSIX Issue 7, XCU, sh, STDIN says:
 When the shell is using standard input and it invokes a command that
 also uses standard input, the shell shall ensure that the standard
 input file pointer points directly after the command it has read when
 the command begins execution. It shall not read ahead in such a manner
 that any characters intended to be read by the invoked command are
 consumed by the shell (whether interpreted by the shell or not) or
 that characters that are not read by the invoked command are not seen
 by the shell.

I.e.
 sh <<EOF
 id
 cat
 good!
 EOF
must execute id, then execute cat, then the cat must copy "good!"
to the standard output stream, and similarly
 sh <<"EOF"
 id
 read Q
 good!
 echo Q$Q
 EOF
must execute id, then read "good!" into Q, then echo "Qgood!".

Heretofor the output was as such:
 uid=1000(nabijaczleweli) gid=100(users) groups=100(users)
 ./dash: 3: good!: not found
and as such (with -x):
 + id
 uid=1000(nabijaczleweli) gid=100(users) groups=100(users)
 + read Q
 + good!
 sh: 3: good!: not found
 + echo Q
 Q
and a strace confirms:
 read(0, "id\ncat\ngood!\n", 8192)       = 13
 read(0, "id\nread Q\ngood!\necho Q$Q\n", 8192) = 25

Reading the standard input byte-by-byte is the obvious solution to this
issue, Just Works, and is how all of shells do it on non-seekable input
(we could, theoretically, read regular files block-wise,
then seek within them after parsing, but the complexity out-weighs
the rarity of running sh < program; we could also do whole-line reads
on teletypes in icanon mode, but, again, the gain here is miniscule
for an interactive session, and the mode can change at any time, so...).
Naturally, we keep reading block-wise from not-standard-input.

With this patch, we observe the correct
 uid=1000(nabijaczleweli) gid=100(users) groups=100(users)
 good!
and
 + id
 uid=1000(nabijaczleweli) gid=100(users) groups=100(users)
 + read Q
 + echo Qgood!
 Qgood!

Link: https://bugs.debian.org/862907
Reported-by: наб <nabijaczleweli@nabijaczleweli.xyz>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

diff --git a/src/input.c b/src/input.c
index ec075f5..43c9161 100644
--- a/src/input.c
+++ b/src/input.c
@@ -159,6 +159,17 @@ int pgetc(void)
 	return __pgetc();
 }
 
+static int stdin_clear_nonblock(void)
+{
+	int flags = fcntl(0, F_GETFL, 0);
+
+	if (flags >= 0) {
+		flags &=~ O_NONBLOCK;
+		flags = fcntl(0, F_SETFL, flags);
+	}
+
+	return flags;
+}
 
 static int
 preadfd(void)
@@ -195,22 +206,38 @@ retry:
 
 	} else
 #endif
+	if (parsefile->fd)
 		nr = read(parsefile->fd, buf, IBUFSIZ - 1);
+	else {
+		unsigned len = IBUFSIZ - 1;
+
+		nr = 0;
+
+		do {
+			int err;
 
+			err = read(0, buf, 1);
+			if (err <= 0) {
+				if (nr)
+					break;
+
+				nr = err;
+				if (errno != EWOULDBLOCK)
+					break;
+				if (stdin_clear_nonblock() < 0)
+					break;
+
+				out2str("sh: turning off NDELAY mode\n");
+				goto retry;
+			}
+
+			nr++;
+		} while (!IS_DEFINED_SMALL && *buf++ != '\n' && --len);
+	}
 
 	if (nr < 0) {
 		if (errno == EINTR)
 			goto retry;
-		if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
-			int flags = fcntl(0, F_GETFL, 0);
-			if (flags >= 0 && flags & O_NONBLOCK) {
-				flags &=~ O_NONBLOCK;
-				if (fcntl(0, F_SETFL, flags) >= 0) {
-					out2str("sh: turning off NDELAY mode\n");
-					goto retry;
-				}
-			}
-		}
 	}
 	return nr;
 }
diff --git a/src/input.h b/src/input.h
index 8c39f33..8830b66 100644
--- a/src/input.h
+++ b/src/input.h
@@ -34,6 +34,12 @@
  *	@(#)input.h	8.2 (Berkeley) 5/4/95
  */
 
+#ifdef SMALL
+#define IS_DEFINED_SMALL 1
+#else
+#define IS_DEFINED_SMALL 0
+#endif
+
 /* PEOF (the end of file marker) is defined in syntax.h */
 
 enum {
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

  reply	other threads:[~2023-01-03  6:16 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-13 22:17 [PATCH] input: preadfd: read standard input byte-wise наб
2022-12-13 22:37 ` Harald van Dijk
2022-12-14  1:05   ` наб
2022-12-14  1:06     ` [PATCH v2 1/3] parser: fixredir: invalid redirections are run-time, not syntax наб
2023-01-05  9:43       ` Herbert Xu
2022-12-14  1:06     ` [PATCH v2 2/3] parser: synerror: explicitly consume the entire invalid line наб
2023-01-03  1:53       ` Herbert Xu
2023-01-03  5:32         ` [PATCH] input: Eat rest of line upon reset Herbert Xu
2023-01-03 11:47         ` [PATCH v2 2/3] parser: synerror: explicitly consume the entire invalid line Harald van Dijk
2023-01-04  9:51           ` Herbert Xu
2023-01-04 11:25             ` Harald van Dijk
2023-01-04 14:10               ` Herbert Xu
2023-01-04 14:30                 ` Harald van Dijk
2023-01-04 14:41                   ` Herbert Xu
2023-01-04 14:59                     ` Harald van Dijk
2023-01-05  7:12                       ` [PATCH] input: Check for int_pending while clearing input Herbert Xu
2022-12-14  1:06     ` [PATCH v2 3/3] input: preadfd: read standard input byte-wise наб
2023-01-03  6:15       ` Herbert Xu [this message]
2023-01-03 11:54         ` [v3 PATCH] input: Read " Harald van Dijk
2023-01-04  8:59           ` Herbert Xu
2023-01-04 11:18             ` Harald van Dijk
2023-01-05  7:26               ` [PATCH] input: Only skip blank lines on PS1 Herbert Xu
2023-01-05  8:33                 ` Harald van Dijk

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=Y7PIHqN4ic4UBOBb@gondor.apana.org.au \
    --to=herbert@gondor.apana.org.au \
    --cc=dash@vger.kernel.org \
    --cc=harald@gigawatt.nl \
    --cc=nabijaczleweli@nabijaczleweli.xyz \
    /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
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.