From 7fbd9f73be238b15d9ac1746f5c9bcb858ba64a1 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Sun, 8 Mar 2026 16:28:20 -0700 Subject: [PATCH] Fix `isatty()` under `NODERAWFS` --- src/lib/libnoderawfs.js | 13 +++++++------ test/test_other.py | 9 ++++++++- test/unistd/isatty.c | 38 +++++++++++++++++++++++++++++--------- 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/lib/libnoderawfs.js b/src/lib/libnoderawfs.js index b805c43302b4f..57387ad233674 100644 --- a/src/lib/libnoderawfs.js +++ b/src/lib/libnoderawfs.js @@ -10,6 +10,7 @@ addToLibrary({ if (!ENVIRONMENT_IS_NODE) { throw new Error("NODERAWFS is currently only supported on Node.js environment.") } + var nodeTTY = require('node:tty'); function _wrapNodeError(func) { return (...args) => { try { @@ -64,11 +65,10 @@ addToLibrary({ return { path, node: { id: st.ino, mode, node_ops: NODERAWFS, path }}; }, createStandardStreams() { - // FIXME: tty is set to true to appease isatty(), the underlying ioctl syscalls still need to be implemented, see issue #22264. - FS.createStream({ nfd: 0, position: 0, path: '/dev/stdin', flags: 0, tty: true, seekable: false }, 0); + FS.createStream({ nfd: 0, position: 0, path: '/dev/stdin', flags: 0, seekable: false }, 0); var paths = [,'/dev/stdout', '/dev/stderr']; for (var i = 1; i < 3; i++) { - FS.createStream({ nfd: i, position: 0, path: paths[i], flags: {{{ cDefs.O_TRUNC | cDefs.O_CREAT | cDefs.O_WRONLY }}}, tty: true, seekable: false }, i); + FS.createStream({ nfd: i, position: 0, path: paths[i], flags: {{{ cDefs.O_TRUNC | cDefs.O_CREAT | cDefs.O_WRONLY }}}, seekable: false }, i); } }, // generic function for all node creation @@ -179,10 +179,11 @@ addToLibrary({ createStream(stream, fd) { // Call the original FS.createStream var rtn = VFS.createStream(stream, fd); - if (typeof rtn.shared.refcnt == 'undefined') { - rtn.shared.refcnt = 1; - } else { + // Detect PIPEFS streams and skip the refcnt/tty initialization in that case. + if (!stream.stream_ops) { + rtn.shared.refcnt ??= 0; rtn.shared.refcnt++; + rtn.tty = nodeTTY.isatty(rtn.nfd); } return rtn; }, diff --git a/test/test_other.py b/test/test_other.py index 936dff4783e1c..a453365822a67 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -1653,7 +1653,7 @@ def test_export_all_and_exported_functions(self): self.do_runf('lib.c', 'libfunc\n', cflags=['-sEXPORTED_FUNCTIONS=_libfunc2', '-sEXPORT_ALL', '--pre-js', 'pre.js']) @all_engines - @also_with_wasmfs + @with_all_fs @crossplatform @parameterized({ '': ([],), @@ -13128,7 +13128,14 @@ def test_unistd_pathconf(self): def test_unistd_swab(self): self.do_run_in_out_file_test('unistd/swab.c') + @also_with_noderawfs def test_unistd_isatty(self): + if '-DNODERAWFS' in self.cflags: + # Under NODERAWFS istty reports accurate information about the file descriptors + # of the node process. When we run tests we always capture stdout so we never expect + # stdout to be a tty. + stdin_isatty = os.isatty(0) + self.cflags += ['-DEXPECT_STDOUT=0', f'-DEXPECT_STDIN={int(stdin_isatty)}'] self.do_runf('unistd/isatty.c', 'success') def test_unistd_login(self): diff --git a/test/unistd/isatty.c b/test/unistd/isatty.c index 8d4ad2f3a8100..c4c858e0c04a9 100644 --- a/test/unistd/isatty.c +++ b/test/unistd/isatty.c @@ -13,21 +13,41 @@ #include #include +#ifndef EXPECT_STDIN +#define EXPECT_STDIN 1 +#endif + +#ifndef EXPECT_STDOUT +#define EXPECT_STDOUT 1 +#endif + int main() { - int err; + printf("EXPECT_STDIN: %d\n", EXPECT_STDIN); + printf("EXPECT_STDOUT: %d\n", EXPECT_STDOUT); + + assert(isatty(0) == EXPECT_STDIN); + assert(isatty(1) == EXPECT_STDOUT); - err = isatty(-1); - assert(!err); + int err, fd; + + assert(isatty(-1) == 0); assert(errno == EBADF); - err = isatty(open("/dev/stdin", O_RDONLY)); - assert(err == 1); + fd = open("/dev/stdin", O_RDONLY); + assert(fd >= 0); + assert(isatty(fd) == EXPECT_STDIN); + + fd = open("/dev/stdout", O_RDONLY); + assert(fd >= 0); + assert(isatty(fd) == EXPECT_STDOUT); - err = isatty(open("/dev/null", O_RDONLY)); - assert(!err); + fd = open("/dev/null", O_RDONLY); + assert(fd >= 0); + assert(isatty(fd) == 0); - err = isatty(open("/dev", O_RDONLY)); - assert(!err); + fd = open("/dev", O_RDONLY); + assert(fd >= 0); + assert(isatty(fd) == 0); puts("success");