From 7928ff6f0c5ce47b3a9a4e0773ca68a5df6d7c93 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Tue, 5 Sep 2023 09:53:50 -0700 Subject: [PATCH] tty: enable `TCSETSF` While there is a difference between flavors of this ioctl (`TCSETS`, `TCSETSF`, `TCSETSW`), in practice it seems not to matter. We should let users call `TCSETSF` and can implement those differences if necessary. --- pkg/sentry/fsimpl/devpts/master.go | 4 ++++ pkg/sentry/fsimpl/devpts/replica.go | 4 ++++ test/syscalls/linux/pty.cc | 34 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/pkg/sentry/fsimpl/devpts/master.go b/pkg/sentry/fsimpl/devpts/master.go index 62b651706..131875972 100644 --- a/pkg/sentry/fsimpl/devpts/master.go +++ b/pkg/sentry/fsimpl/devpts/master.go @@ -158,6 +158,10 @@ func (mfd *masterFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysn case linux.TCSETSW: // TODO(b/29356795): This should drain the output queue first. return mfd.t.ld.setTermios(t, args) + case linux.TCSETSF: + // TODO(b/29356795): This should drain the output queue and + // clear the input queue first. + return mfd.t.ld.setTermios(t, args) case linux.TIOCGPTN: nP := primitive.Uint32(mfd.t.n) _, err := nP.CopyOut(t, args[2].Pointer()) diff --git a/pkg/sentry/fsimpl/devpts/replica.go b/pkg/sentry/fsimpl/devpts/replica.go index 68614690f..67f9edccc 100644 --- a/pkg/sentry/fsimpl/devpts/replica.go +++ b/pkg/sentry/fsimpl/devpts/replica.go @@ -170,6 +170,10 @@ func (rfd *replicaFileDescription) Ioctl(ctx context.Context, io usermem.IO, sys case linux.TCSETSW: // TODO(b/29356795): This should drain the output queue first. return rfd.inode.t.ld.setTermios(t, args) + case linux.TCSETSF: + // TODO(b/29356795): This should drain the output queue and + // clear the input queue first. + return rfd.inode.t.ld.setTermios(t, args) case linux.TIOCGPTN: nP := primitive.Uint32(rfd.inode.t.n) _, err := nP.CopyOut(t, args[2].Pointer()) diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc index f2dc04440..bb34dc86e 100644 --- a/test/syscalls/linux/pty.cc +++ b/test/syscalls/linux/pty.cc @@ -777,6 +777,40 @@ TEST_F(PtyTest, TermiosONLCR) { ExpectFinished(replica_); } +// ICRNL rewrites input \r to \n. +TEST_F(PtyTest, TCSETSFTermiosICRNL) { + struct kernel_termios t = DefaultTermios(); + t.c_iflag |= ICRNL; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(replica_.get(), TCSETSF, &t), SyscallSucceeds()); + + char c = '\r'; + ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + ExpectReadable(replica_, 1, &c); + EXPECT_EQ(c, '\n'); + + ExpectFinished(replica_); +} + +// ONLCR rewrites output \n to \r\n. +TEST_F(PtyTest, TCSETSFTermiosONLCR) { + struct kernel_termios t = DefaultTermios(); + t.c_oflag |= ONLCR; + t.c_lflag &= ~ICANON; // for byte-by-byte reading. + ASSERT_THAT(ioctl(replica_.get(), TCSETSF, &t), SyscallSucceeds()); + + char c = '\n'; + ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1)); + + // Extra byte for NUL for EXPECT_STREQ. + char buf[3] = {}; + ExpectReadable(master_, 2, buf); + EXPECT_STREQ(buf, "\r\n"); + + ExpectFinished(replica_); +} + TEST_F(PtyTest, TermiosIGNCR) { struct kernel_termios t = DefaultTermios(); t.c_iflag |= IGNCR;