Skip to content

Commit af06581

Browse files
bnoordhuisktaborski
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. Co-authored-by: Krzysztof Taborski <[email protected]> PR-URL: #20592 Fixes: #14752 Fixes: #21020 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 182c73b commit af06581

File tree

1 file changed

+94
-6
lines changed

1 file changed

+94
-6
lines changed

src/node.cc

+94-6
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ typedef int mode_t;
100100
#else
101101
#include <pthread.h>
102102
#include <sys/resource.h> // getrlimit, setrlimit
103+
#include <termios.h> // tcgetattr, tcsetattr
103104
#include <unistd.h> // setuid, getuid
104105
#endif
105106

@@ -172,6 +173,9 @@ using v8::Value;
172173
static Mutex process_mutex;
173174
static Mutex environ_mutex;
174175

176+
// Safe to call more than once and from signal handlers.
177+
inline void PlatformExit();
178+
175179
static bool print_eval = false;
176180
static bool force_repl = false;
177181
static bool syntax_check_only = false;
@@ -1091,7 +1095,7 @@ void AppendExceptionLine(Environment* env,
10911095
Mutex::ScopedLock lock(process_mutex);
10921096
env->set_printed_error(true);
10931097

1094-
uv_tty_reset_mode();
1098+
PlatformExit();
10951099
PrintErrorString("\n%s", arrow);
10961100
return;
10971101
}
@@ -3025,7 +3029,7 @@ void SetupProcessObject(Environment* env,
30253029

30263030

30273031
void SignalExit(int signo) {
3028-
uv_tty_reset_mode();
3032+
PlatformExit();
30293033
v8_platform.StopTracingAgent();
30303034
#ifdef __FreeBSD__
30313035
// FreeBSD has a nasty bug, see RegisterSignalHandler for details
@@ -3846,6 +3850,27 @@ static void DebugEnd(const FunctionCallbackInfo<Value>& args) {
38463850
}
38473851

38483852

3853+
#ifdef __POSIX__
3854+
static struct {
3855+
int flags;
3856+
bool isatty;
3857+
struct stat stat;
3858+
struct termios termios;
3859+
} stdio[1 + STDERR_FILENO];
3860+
3861+
3862+
inline int GetFileDescriptorFlags(int fd) {
3863+
int flags;
3864+
3865+
do {
3866+
flags = fcntl(fd, F_GETFL);
3867+
} while (flags == -1 && errno == EINTR);
3868+
3869+
return flags;
3870+
}
3871+
#endif // __POSIX__
3872+
3873+
38493874
inline void PlatformInit() {
38503875
#ifdef __POSIX__
38513876
#if HAVE_INSPECTOR
@@ -3856,16 +3881,18 @@ inline void PlatformInit() {
38563881
#endif // HAVE_INSPECTOR
38573882

38583883
// Make sure file descriptors 0-2 are valid before we start logging anything.
3859-
for (int fd = STDIN_FILENO; fd <= STDERR_FILENO; fd += 1) {
3860-
struct stat ignored;
3861-
if (fstat(fd, &ignored) == 0)
3884+
for (auto& s : stdio) {
3885+
const int fd = &s - stdio;
3886+
if (fstat(fd, &s.stat) == 0)
38623887
continue;
38633888
// Anything but EBADF means something is seriously wrong. We don't
38643889
// have to special-case EINTR, fstat() is not interruptible.
38653890
if (errno != EBADF)
38663891
ABORT();
38673892
if (fd != open("/dev/null", O_RDWR))
38683893
ABORT();
3894+
if (fstat(fd, &s.stat) != 0)
3895+
ABORT();
38693896
}
38703897

38713898
#if HAVE_INSPECTOR
@@ -3888,6 +3915,24 @@ inline void PlatformInit() {
38883915
}
38893916
#endif // !NODE_SHARED_MODE
38903917

3918+
// Record the state of the stdio file descriptors so we can restore it
3919+
// on exit. Needs to happen before installing signal handlers because
3920+
// they make use of that information.
3921+
for (auto& s : stdio) {
3922+
const int fd = &s - stdio;
3923+
int err;
3924+
3925+
s.flags = GetFileDescriptorFlags(fd);
3926+
CHECK_NE(s.flags, -1);
3927+
3928+
if (!isatty(fd)) continue;
3929+
s.isatty = true;
3930+
do {
3931+
err = tcgetattr(fd, &s.termios);
3932+
} while (err == -1 && errno == EINTR);
3933+
CHECK_EQ(err, 0);
3934+
}
3935+
38913936
RegisterSignalHandler(SIGINT, SignalExit, true);
38923937
RegisterSignalHandler(SIGTERM, SignalExit, true);
38933938

@@ -3928,6 +3973,49 @@ inline void PlatformInit() {
39283973
}
39293974

39303975

3976+
// This function must be safe to call more than once and from signal handlers.
3977+
inline void PlatformExit() {
3978+
#ifdef __POSIX__
3979+
for (auto& s : stdio) {
3980+
const int fd = &s - stdio;
3981+
3982+
struct stat tmp;
3983+
if (-1 == fstat(fd, &tmp)) {
3984+
CHECK_EQ(errno, EBADF); // Program closed file descriptor.
3985+
continue;
3986+
}
3987+
3988+
bool is_same_file =
3989+
(s.stat.st_dev == tmp.st_dev && s.stat.st_ino == tmp.st_ino);
3990+
if (!is_same_file) continue; // Program reopened file descriptor.
3991+
3992+
int flags = GetFileDescriptorFlags(fd);
3993+
CHECK_NE(flags, -1);
3994+
3995+
// Restore the O_NONBLOCK flag if it changed.
3996+
if (O_NONBLOCK & (flags ^ s.flags)) {
3997+
flags &= ~O_NONBLOCK;
3998+
flags |= s.flags & O_NONBLOCK;
3999+
4000+
int err;
4001+
do {
4002+
err = fcntl(fd, F_SETFL, flags);
4003+
} while (err == -1 && errno == EINTR);
4004+
CHECK_NE(err, -1);
4005+
}
4006+
4007+
if (s.isatty) {
4008+
int err;
4009+
do {
4010+
err = tcsetattr(fd, TCSANOW, &s.termios);
4011+
} while (err == -1 && errno == EINTR);
4012+
CHECK_NE(err, -1);
4013+
}
4014+
}
4015+
#endif // __POSIX__
4016+
}
4017+
4018+
39314019
void ProcessArgv(int* argc,
39324020
const char** argv,
39334021
int* exec_argc,
@@ -4392,7 +4480,7 @@ inline int Start(uv_loop_t* event_loop,
43924480
}
43934481

43944482
int Start(int argc, char** argv) {
4395-
atexit([] () { uv_tty_reset_mode(); });
4483+
atexit([] () { PlatformExit(); });
43964484
PlatformInit();
43974485
performance::performance_node_start = PERFORMANCE_NOW();
43984486

0 commit comments

Comments
 (0)