Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node doesn't reset tty on early/aborted exit #41143

Open
gotchoices opened this issue Dec 11, 2021 · 11 comments
Open

Node doesn't reset tty on early/aborted exit #41143

gotchoices opened this issue Dec 11, 2021 · 11 comments
Labels
tty Issues and PRs related to the tty subsystem.

Comments

@gotchoices
Copy link

Version

v16.13.1, v17.2.0 tested

Platform

Linux lux0 5.11.22-100.fc32.x86_64 #1 SMP Wed May 19 18:58:25 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

No response

What steps will reproduce the bug?

Create this script file in test.js:

#!/usr/bin/env node
for (let i = 0; i < 100000; i++) {
console.log("i:", i)
}

In a shell window, run:

test.js |less

Hit "q" to quit less (after only one page of output), which also kills test.js while it still has undelivered data in the stdout buffer.

Your tty will be left in raw mode (no echo, other command characters disabled). Must do a "stty sane" to restore usability of the console.

How often does it reproduce? Is there a required condition?

Happens every time. Have tested multiple versions of node on Linux and MacOS.

What is the expected behavior?

Should restore my tty settings no matter how ugly the exit conditions.

What do you see instead?

Tty is left in raw mode with no echo. Can't see my typed commands.

Additional information

Is there a standard way of dealing with this in case a script is killed before it can write its output? Or is this a bug in node?

@lpinca lpinca added the tty Issues and PRs related to the tty subsystem. label Dec 12, 2021
@shachaf
Copy link

shachaf commented Mar 14, 2022

Based on a bit of strace investigation, I think the issue isn't that node doesn't reset the tty state, it's that it does (and shouldn't). Here's what I think is happening:

  • less starts, saves tty state, sets -echo
  • node starts, saves tty state (for some reason?)
  • less exits, restores +echo
  • node gets SIGPIPE, exits, restores "original" tty state of -echo

node should probably not touch the tty state at all in this sort of situation where it's just being used to print to stdout.

@gotchoices
Copy link
Author

Seems like isatty() can/should be called (if its not already) to determine if stdout is a terminal or a file/pipe...

I can confirm that if I redirect stdin and stderr to/from /dev/null, the problem goes away. If I redirect either one (but not both) the problem persists. It looks like node is checking to see if any one of the three (stdin, stdout, stderr) is a terminal. If any one of the three is, it stores/restores the tty. I'm not sure what the correct logic is, but probably not that.

Here's an article with a bit of guidance about 20% in (search for "pipeline"):
https://www.linusakesson.net/programming/tty/

Sounds like in a pipeline, there is a foreground process (less in this case) that is "in charge" of dealing with the terminal. I wonder if there is some more meaningful measure than isatty()? Or may it is as simple as testing stdout and ignoring what stdin and stderr are doing?

@shachaf
Copy link

shachaf commented Mar 15, 2022

I think the right thing is to just not touch the tty state at all if the program doesn't request it. That's what most programs do by default and it behaves correctly.

From a quick search of the code, my guess is that it's happening here: https://github.com/nodejs/node/blob/master/lib/repl.js#L547-L591
If the REPL evaluation code is being used to run scripts, then it's saving and restoring the terminal mode as if it was being used interactively. But there's no reason for it to do that for non-interactive use.

@bnoordhuis
Copy link
Member

For anyone who wants to work on this, the relevant logic is in src/node.cc:

  • node restores the tty's state most of the time, see ResetStdio (called on normal program exit and SIGINT or SIGTERM but not other signals)

  • the state is captured at startup in PlatformInit

the right thing is to just not touch the tty state at all if the program doesn't request it

Node has to work this way because native add-ons can also change the tty's state.

@gotchoices
Copy link
Author

I like shachaf's opinion of leaving the tty alone unless node is explicitly launched in interactive mode (and therefore not buried in a pipeline somewhere). If you're not running some kind of command-line input, there's probably no reason to diddle the tty settings.

If an add-on wants to change tty settings, it should probably register its own exit hook (assuming that can be done) to restore settings. But it doesn't seem to me like node should always assume the tty needs to be restored just because some add-on might have done something to it.

@bnoordhuis
Copy link
Member

Anything that requires the ecosystem to move is pretty much DOA.

The yubikey openssl engine (provides TLS keys to node) is a good example of why that isn't workable. It leaves the tty in a bad state if you press ^C on the passphrase prompt and an exit hook won't fix that because node and the yubikey are unaware of each other's existence.

(And let's not get into the perils of cleaning up from signal handlers!)

Basically, if you want to see this fixed, work within the existing framework. Happy to review pull requests, just cc me. :)

@shachaf
Copy link

shachaf commented Mar 15, 2022 via email

@brendon
Copy link

brendon commented Oct 7, 2023

It could be a coincidence but updating to Node 18 fixed this for me.

@JosephHalter
Copy link

JosephHalter commented May 7, 2024

I just want to confirm that it's still happening on node 20.12.1, running yarn run serve and Ctrl+C messes up my terminal every time.

@brendon
Copy link

brendon commented May 7, 2024

It could be a coincidence but updating to Node 18 fixed this for me.

Turns out I was too hasty. It still occurs in 18. Haven't checked 20.

@gotchoices
Copy link
Author

I just checked with 20.13.0 and the bug is still present.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tty Issues and PRs related to the tty subsystem.
Projects
None yet
Development

No branches or pull requests

7 participants
@brendon @JosephHalter @shachaf @bnoordhuis @lpinca @gotchoices and others