Skip to content

Commit b5dda32

Browse files
bnoordhuisBridgeAR
authored andcommitted
src: restore stdio on program exit
Record the state of the stdio file descriptors on start-up and restore them to that state on exit. This should prevent issues where node.js sometimes leaves stdio in raw or non-blocking mode. This is a reworked version of commit c2c9c0c from May 2018 that was reverted in commit 14dc17d from June 2018. The revert was a little light on details but I infer that the problem was caused by a missing call to `uv_tty_reset_mode()`. Apropos the NOLINT comments: cpplint doesn't understand do/while statements, it thinks they're while statements without a body. Fixes: #14752 Fixes: #21020 Original-PR-URL: #20592 PR-URL: #24260 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Refael Ackermann (רפאל פלחי) <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent abe5d05 commit b5dda32

File tree

4 files changed

+90
-7
lines changed

4 files changed

+90
-7
lines changed

src/node.cc

+87-5
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
#else
9898
#include <pthread.h>
9999
#include <sys/resource.h> // getrlimit, setrlimit
100+
#include <termios.h> // tcgetattr, tcsetattr
100101
#include <unistd.h> // STDIN_FILENO, STDERR_FILENO
101102
#endif
102103

@@ -182,7 +183,7 @@ void WaitForInspectorDisconnect(Environment* env) {
182183
}
183184

184185
void SignalExit(int signo) {
185-
uv_tty_reset_mode();
186+
ResetStdio();
186187
#ifdef __FreeBSD__
187188
// FreeBSD has a nasty bug, see RegisterSignalHandler for details
188189
struct sigaction sa;
@@ -490,6 +491,16 @@ void RegisterSignalHandler(int signal,
490491

491492
#endif // __POSIX__
492493

494+
#ifdef __POSIX__
495+
static struct {
496+
int flags;
497+
bool isatty;
498+
struct stat stat;
499+
struct termios termios;
500+
} stdio[1 + STDERR_FILENO];
501+
#endif // __POSIX__
502+
503+
493504
inline void PlatformInit() {
494505
#ifdef __POSIX__
495506
#if HAVE_INSPECTOR
@@ -500,16 +511,18 @@ inline void PlatformInit() {
500511
#endif // HAVE_INSPECTOR
501512

502513
// Make sure file descriptors 0-2 are valid before we start logging anything.
503-
for (int fd = STDIN_FILENO; fd <= STDERR_FILENO; fd += 1) {
504-
struct stat ignored;
505-
if (fstat(fd, &ignored) == 0)
514+
for (auto& s : stdio) {
515+
const int fd = &s - stdio;
516+
if (fstat(fd, &s.stat) == 0)
506517
continue;
507518
// Anything but EBADF means something is seriously wrong. We don't
508519
// have to special-case EINTR, fstat() is not interruptible.
509520
if (errno != EBADF)
510521
ABORT();
511522
if (fd != open("/dev/null", O_RDWR))
512523
ABORT();
524+
if (fstat(fd, &s.stat) != 0)
525+
ABORT();
513526
}
514527

515528
#if HAVE_INSPECTOR
@@ -532,6 +545,27 @@ inline void PlatformInit() {
532545
}
533546
#endif // !NODE_SHARED_MODE
534547

548+
// Record the state of the stdio file descriptors so we can restore it
549+
// on exit. Needs to happen before installing signal handlers because
550+
// they make use of that information.
551+
for (auto& s : stdio) {
552+
const int fd = &s - stdio;
553+
int err;
554+
555+
do
556+
s.flags = fcntl(fd, F_GETFL);
557+
while (s.flags == -1 && errno == EINTR); // NOLINT
558+
CHECK_NE(s.flags, -1);
559+
560+
if (!isatty(fd)) continue;
561+
s.isatty = true;
562+
563+
do
564+
err = tcgetattr(fd, &s.termios);
565+
while (err == -1 && errno == EINTR); // NOLINT
566+
CHECK_EQ(err, 0);
567+
}
568+
535569
RegisterSignalHandler(SIGINT, SignalExit, true);
536570
RegisterSignalHandler(SIGTERM, SignalExit, true);
537571

@@ -571,6 +605,54 @@ inline void PlatformInit() {
571605
#endif // _WIN32
572606
}
573607

608+
609+
// Safe to call more than once and from signal handlers.
610+
void ResetStdio() {
611+
uv_tty_reset_mode();
612+
#ifdef __POSIX__
613+
for (auto& s : stdio) {
614+
const int fd = &s - stdio;
615+
616+
struct stat tmp;
617+
if (-1 == fstat(fd, &tmp)) {
618+
CHECK_EQ(errno, EBADF); // Program closed file descriptor.
619+
continue;
620+
}
621+
622+
bool is_same_file =
623+
(s.stat.st_dev == tmp.st_dev && s.stat.st_ino == tmp.st_ino);
624+
if (!is_same_file) continue; // Program reopened file descriptor.
625+
626+
int flags;
627+
do
628+
flags = fcntl(fd, F_GETFL);
629+
while (flags == -1 && errno == EINTR); // NOLINT
630+
CHECK_NE(flags, -1);
631+
632+
// Restore the O_NONBLOCK flag if it changed.
633+
if (O_NONBLOCK & (flags ^ s.flags)) {
634+
flags &= ~O_NONBLOCK;
635+
flags |= s.flags & O_NONBLOCK;
636+
637+
int err;
638+
do
639+
err = fcntl(fd, F_SETFL, flags);
640+
while (err == -1 && errno == EINTR); // NOLINT
641+
CHECK_NE(err, -1);
642+
}
643+
644+
if (s.isatty) {
645+
int err;
646+
do
647+
err = tcsetattr(fd, TCSANOW, &s.termios);
648+
while (err == -1 && errno == EINTR); // NOLINT
649+
CHECK_NE(err, -1);
650+
}
651+
}
652+
#endif // __POSIX__
653+
}
654+
655+
574656
int ProcessGlobalArgs(std::vector<std::string>* args,
575657
std::vector<std::string>* exec_args,
576658
std::vector<std::string>* errors,
@@ -826,7 +908,7 @@ void Init(int* argc,
826908
}
827909

828910
InitializationResult InitializeOncePerProcess(int argc, char** argv) {
829-
atexit([] () { uv_tty_reset_mode(); });
911+
atexit(ResetStdio);
830912
PlatformInit();
831913
per_process::node_start_time = uv_hrtime();
832914

src/node_errors.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ void AppendExceptionLine(Environment* env,
230230
Mutex::ScopedLock lock(per_process::tty_mutex);
231231
env->set_printed_error(true);
232232

233-
uv_tty_reset_mode();
233+
ResetStdio();
234234
PrintErrorString("\n%s", source.c_str());
235235
return;
236236
}

src/node_internals.h

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ void PrintCaughtException(v8::Isolate* isolate,
9191
const v8::TryCatch& try_catch);
9292

9393
void WaitForInspectorDisconnect(Environment* env);
94+
void ResetStdio(); // Safe to call more than once and from signal handlers.
9495
void SignalExit(int signo);
9596
#ifdef __POSIX__
9697
void RegisterSignalHandler(int signal,

src/node_main_instance.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ int NodeMainInstance::Run() {
145145

146146
env->set_can_call_into_js(false);
147147
env->stop_sub_worker_contexts();
148-
uv_tty_reset_mode();
148+
ResetStdio();
149149
env->RunCleanup();
150150
RunAtExit(env.get());
151151

0 commit comments

Comments
 (0)