Page MenuHomeFreeBSD

D46806.id143781.diff
No OneTemporary

D46806.id143781.diff

diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -852,6 +852,8 @@
..
pipe
..
+ tty
+ ..
..
kqueue
libkqueue
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -127,6 +127,7 @@
TESTS_SUBDIRS+= acct
TESTS_SUBDIRS+= execve
TESTS_SUBDIRS+= pipe
+TESTS_SUBDIRS+= tty
.include <netbsd-tests.test.mk>
diff --git a/tests/sys/kern/tty/Makefile b/tests/sys/kern/tty/Makefile
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/Makefile
@@ -0,0 +1,12 @@
+TESTSDIR= ${TESTSBASE}/sys/kern/tty
+BINDIR= ${TESTSDIR}
+
+PLAIN_TESTS_PORCH+= test_canon
+PLAIN_TESTS_PORCH+= test_canon_fullbuf
+PLAIN_TESTS_PORCH+= test_ncanon
+PLAIN_TESTS_PORCH+= test_recanon
+
+PROGS+= fionread
+PROGS+= readsz
+
+.include <bsd.test.mk>
diff --git a/tests/sys/kern/tty/fionread.c b/tests/sys/kern/tty/fionread.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/fionread.c
@@ -0,0 +1,21 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/ioctl.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ int nb;
+
+ assert(ioctl(STDIN_FILENO, FIONREAD, &nb) == 0);
+ printf("%d", nb);
+ return (0);
+}
diff --git a/tests/sys/kern/tty/readsz.c b/tests/sys/kern/tty/readsz.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/readsz.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s [-b bytes | -c lines | -e] [-s buffer-size]\n",
+ getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *buf;
+ const char *errstr;
+ size_t bufsz = 0, reps;
+ ssize_t ret;
+ enum { MODE_BYTES, MODE_COUNT, MODE_EOF } mode;
+ int ch;
+
+ /*
+ * -b specifies number of bytes.
+ * -c specifies number of read() calls.
+ * -e specifies eof (default)
+ * -s to pass a buffer size
+ *
+ * Reading N lines is the same as -c with a high buffer size.
+ */
+ mode = MODE_EOF;
+ while ((ch = getopt(argc, argv, "b:c:es:")) != -1) {
+ switch (ch) {
+ case 'b':
+ mode = MODE_BYTES;
+ reps = strtonum(optarg, 0, SSIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "strtonum: %s", errstr);
+ break;
+ case 'c':
+ mode = MODE_COUNT;
+ reps = strtonum(optarg, 1, SSIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "strtonum: %s", errstr);
+ break;
+ case 'e':
+ mode = MODE_EOF;
+ break;
+ case 's':
+ bufsz = strtonum(optarg, 1, SSIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "strtonum: %s", errstr);
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (bufsz == 0) {
+ if (mode == MODE_BYTES)
+ bufsz = reps;
+ else
+ bufsz = LINE_MAX;
+ }
+
+ buf = malloc(bufsz);
+ if (buf == NULL)
+ err(1, "malloc");
+
+ for (;;) {
+ size_t readsz;
+
+ /*
+ * Be careful not to over-read if we're in byte-mode. In every other
+ * mode, we'll read as much as we can.
+ */
+ if (mode == MODE_BYTES)
+ readsz = MIN(bufsz, reps);
+ else
+ readsz = bufsz;
+
+ ret = read(STDIN_FILENO, buf, readsz);
+ if (ret == -1 && errno == EINTR)
+ continue;
+ if (ret == -1)
+ err(1, "read");
+ if (ret == 0) {
+ if (mode == MODE_EOF)
+ return (0);
+ errx(1, "premature EOF");
+ }
+
+ /* Write out what we've got */
+ write(STDOUT_FILENO, buf, ret);
+
+ /*
+ * Bail out if we've hit our metric (byte mode / count mode).
+ */
+ switch (mode) {
+ case MODE_BYTES:
+ reps -= ret;
+ if (reps == 0)
+ return (0);
+ break;
+ case MODE_COUNT:
+ reps--;
+ if (reps == 0)
+ return (0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (0);
+}
diff --git a/tests/sys/kern/tty/test_canon.orch b/tests/sys/kern/tty/test_canon.orch
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/test_canon.orch
@@ -0,0 +1,102 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+spawn("cat")
+
+write "Complete\r"
+match "Complete\r"
+
+write "Basic\rIncomplete"
+match "Basic\r"
+
+-- We shouldn't see any of the "Incomplete" line
+fail(function()
+end)
+
+match "Incomp" {
+ callback = function()
+ exit(1)
+ end
+}
+
+fail(nil)
+
+-- Pushing a ^D along should force a flush of the tty, cat(1) will write the
+-- result without a trailing newline.
+write " line^D"
+match "Incomplete line$"
+
+-- Erase!
+write "Dog^H^D"
+match "Do$"
+
+-- More erase!
+write "Cat Dog^W^D"
+match "Cat $"
+
+write "^D"
+eof()
+
+local function fionread_test(str, expected)
+ spawn("fionread")
+
+ write(str)
+ match(expected)
+end
+
+-- Incomplete line
+fionread_test("Hello", "0")
+-- VEOF does not count
+fionread_test("Hello^D", "5")
+-- VEOF still doesn't count, even if the next line is an extra VEOF later
+fionread_test("Hello^D^D", "5")
+-- read(2) definitely won't return the second incomplete line
+fionread_test("Hello^Dther", "5")
+-- read(2) also won't return a second complete line at once
+fionread_test("Hello^Dthere^D", "5")
+-- Finally, send a VEOF to terminate a blank line and signal EOF in read(2)
+fionread_test("^D", "0")
+
+-- \r will instead show up in the input stream to the application, so we must
+-- make sure those are counted where VEOF generally wouldn't be.
+fionread_test("Hello\r", "6")
+fionread_test("Hello\rther", "6")
+fionread_test("Hello\rthere\r", "6")
+fionread_test("\r", "1")
+
+local function readsz_test(str, arg, expected)
+ spawn("readsz", table.unpack(arg))
+
+ if type(str) == "table" then
+ assert(#str == 2)
+ write(str[1])
+ release()
+
+ -- Give readsz a chance to consume the partial input before we send more
+ -- along.
+ sleep(1)
+ write(str[2])
+ else
+ write(str)
+ end
+ match(expected)
+end
+
+readsz_test("partial", {"-b", 3}, "^$")
+readsz_test("partial^D", {"-b", 3}, "^par$")
+readsz_test("partial^D", {"-c", 1}, "^partial$")
+for s = 1, #"partial" do
+ readsz_test("partial^D", {"-s", s}, "^partial$")
+end
+-- Send part of the line, release and pause, then finish it.
+readsz_test({"par", "tial^D"}, {"-c", 1}, "^partial$")
+-- line is incomplete, so we'll just see the "partial" even if we want two
+readsz_test("partial^Dline", {"-c", 2}, "^partial$")
+readsz_test("partial^Dline^D", {"-c", 1}, "^partial$")
+readsz_test("partial^Dline^D", {"-c", 2}, "^partialline$")
diff --git a/tests/sys/kern/tty/test_canon_fullbuf.orch b/tests/sys/kern/tty/test_canon_fullbuf.orch
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/test_canon_fullbuf.orch
@@ -0,0 +1,23 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+local TTYINQ_DATASIZE = 128
+local scream = string.rep("A", TTYINQ_DATASIZE - 1)
+
+spawn("cat")
+
+-- Fill up a whole block with screaming + VEOF
+write(scream .. "^D")
+match(scream .. "$")
+
+scream = scream .. "A"
+
+-- Now fill up the next block, but spill the VEOF over to a third block.
+write(scream .. "^D")
+match(scream .. "$")
diff --git a/tests/sys/kern/tty/test_ncanon.orch b/tests/sys/kern/tty/test_ncanon.orch
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/test_ncanon.orch
@@ -0,0 +1,39 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+local function spawn_one(...)
+ spawn(...)
+
+ stty("lflag", 0, tty.lflag.ICANON)
+end
+
+-- We can send one byte...
+spawn_one("readsz", "-c", 1)
+write "H"
+match "^H$"
+
+-- or many.
+spawn_one("readsz", "-c", 1)
+write "Hello"
+match "^Hello$"
+
+-- VEOF is a normal character here, passed through as-is.
+spawn_one("readsz", "-c", 1)
+write "Hello^D"
+match "^Hello\x04$"
+spawn_one("readsz", "-c", 1)
+write "^D"
+match "^\x04$"
+
+-- Confirm that FIONREAD agrees that VEOF will be returned, even if it was sent
+-- while the tty was still in canonical mode.
+spawn("fionread")
+write "^D"
+stty("lflag", 0, tty.lflag.ICANON)
+match "^1$"
diff --git a/tests/sys/kern/tty/test_recanon.orch b/tests/sys/kern/tty/test_recanon.orch
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/tty/test_recanon.orch
@@ -0,0 +1,90 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+local TTYINQ_DATASIZE = 128
+local scream = string.rep("A", TTYINQ_DATASIZE - 1)
+
+local function ncanon()
+ stty("lflag", nil, tty.lflag.ICANON)
+end
+
+local function canon()
+ stty("lflag", tty.lflag.ICANON)
+end
+
+spawn("readsz", "-e")
+ncanon()
+
+-- Fill up a whole block with screaming + VEOF; when it gets recanonicalized,
+-- the next line should be pointing to the beginning of the next block.
+write(scream .. "^D")
+
+canon()
+match(scream .. "$")
+
+-- The same as above, but spilling VEOF over to the next block.
+spawn("readsz", "-e")
+ncanon()
+
+write(scream .. "A^D")
+
+canon()
+match(scream .. "A$")
+
+-- We'll do it again, except with one character spilled over to the next block
+-- before we recanonicalize. We should then have the scream, followed by a
+-- partial line containing the spill over.
+spawn("cat")
+ncanon()
+
+write(scream .. "^DZ")
+
+canon()
+match(scream .. "$")
+
+-- Sending "B^D" should give us "ZB" to make sure that we didn't lose anything
+-- at the beginning of the next block.
+
+write("B^D")
+match("^ZB$")
+
+-- Next we'll VEOF at the beginning.
+spawn("readsz", "-e")
+ncanon()
+
+write("^D")
+match("^$")
+
+-- Finally, we'll trigger recanonicalization with an empty buffer. This one is
+-- just about avoiding a panic.
+spawn("true")
+
+ncanon()
+canon()
+release()
+eof()
+
+spawn("readsz", "-c", "1")
+
+write("Test^Dfoo")
+ncanon()
+
+match("^Test\x04foo$")
+
+-- Finally, swap VEOF out with ^F; before recent changes, we would remain
+-- canonicalized at Test^D and the kernel would block on it unless a short
+-- buffer was used since VEOF would not appear within the canonicalized bit.
+spawn("readsz", "-c", 1)
+
+write("Test^DLine^F")
+stty("cc", {
+ VEOF = "^F"
+})
+
+match("^Test\x04Line$")

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 17, 8:16 AM (20 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14673108
Default Alt Text
D46806.id143781.diff (10 KB)

Event Timeline