process.stdin.setRawMode(true);
process.stdin.on('data', function(data) {
key = String.fromCharCode(data[0]);
if(key == 't') console.log('test');
if(data[0] == 26) {
console.log("suspending");
process.kill(process.pid, 'SIGSTOP');
console.log("resuming");
}
if(data[0] == 3) process.exit();
});
console.log("press t to test, ctrl-z to suspend");
Run this process and press t.
It will immediately print out 'test'.
Then use ctrl-z to put the process into the background by sending it SIGSTOP.
On linux use $ fg to resume it to the foreground.
Press t again but it will not print 'test' as it did before until the return carriage is pressed. setRawMode(true) is no longer being honored.
$: ~/support/18416 > cat foo.c
#include <termios.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main() {
struct termios t1, *t2, t3;
int fd = open("/dev/pts/0", O_RDWR|O_CLOEXEC);
dup3(fd, 0, O_CLOEXEC);
tcgetattr(fd, &t1);
fprintf(stderr, "original: %d %d %d\n", t3.c_iflag, t3.c_oflag, t3.c_lflag);
t2 = &t1;
t2->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
t2->c_oflag &= ~OPOST;
t2->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
t2->c_cflag &= ~(CSIZE | PARENB);
t2->c_cflag |= CS8;
tcsetattr(fd, TCSADRAIN,t2);
while(1) {
if(getchar() == 26)
kill(getpid(), SIGSTOP);
else {
tcgetattr(fd, &t3);
fprintf(stderr, "modified: %d %d %d\n\r", t3.c_iflag, t3.c_oflag, t3.c_lflag);
}
}
}
$: ~/support/18416 > ./a.out
original: 0 0 0
//hit some key
modified: 10240 4 18960
[1]+ Stopped ./a.out
$: ~/support/18416 > fg
./a.out
t // hit enter
modified: 11520 5 51739
modified: 11520 5 51739
^C
if I use strace the issue is not reproducible - probably because the stdin was not a true tty in the first place, when running as child under strace, instead a psuedo-tty?
process.stdin.isRaw is true, not reflecting its true state.
Looks like we will need to re-apply the rawMode when returning from suspension.
trying to see if this one makes any difference:
--- a/deps/uv/src/unix/tty.c
+++ b/deps/uv/src/unix/tty.c
@@ -256,6 +256,7 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
tmp.c_cc[VMIN] = 1;
tmp.c_cc[VTIME] = 0;
+ tmp.c_cc[VSUSP] = 1;
break;
case UV_TTY_MODE_IO:
uv__tty_make_raw(&tmp);
no.
@gireeshpunathil I think you've already conclusively demonstrated that it's not a node.js issue. I would suggest closing this out.
It might be possible to reinitialize the TTY from a SIGCONT signal handler but that's adding workarounds for third-party bugs.
ok, closing based on @bnoordhuis suggestion, with a record of how this can be circumvented by resurrecting old attributes in a signal handler:
$: ~/support/18416 > cat foo.c
#include <termios.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
struct termios t1, *t2, t3;
int fd;
void cont_handler(int s) {
tcsetattr(fd, TCSADRAIN,t2);
}
int main() {
fd = open("/dev/pts/0", O_RDWR|O_CLOEXEC);
dup3(fd, 0, O_CLOEXEC);
tcgetattr(fd, &t1);
fprintf(stderr, "original: %d %d %d\n", t3.c_iflag, t3.c_oflag, t3.c_lflag);
t2 = &t1;
t2->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
t2->c_oflag &= ~OPOST;
t2->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
t2->c_cflag &= ~(CSIZE | PARENB);
t2->c_cflag |= CS8;
tcsetattr(fd, TCSADRAIN,t2);
signal(SIGCONT, cont_handler);
while(1) {
if(getchar() == 26)
kill(getpid(), SIGSTOP);
else {
tcgetattr(fd, &t3);
fprintf(stderr, "modified: %x %x %x\n\r", t3.c_iflag, t3.c_oflag, t3.c_lflag);
}
}
}
$: ~/support/18416 > ./a.out
original: 0 0 0
modified: 2800 4 4a10
modified: 2800 4 4a10
[1]+ Stopped ./a.out
$: ~/support/18416 > fg
./a.out
modified: 2800 4 4a10
modified: 2800 4 4a10
modified: 2800 4 4a10
So as a workaround in the program, I suggest adding:
process.on('SIGCONT', () => {
process.stdin.setRawMode(false)
process.stdin.setRawMode(true)
})
which re-instates the rawMode that was otherwise modified when the process was suspected.
Hope this helps.
Thank you! You guys are so fast and it was not really big problem. I tried doing process.stdin.setRawMode(true) but it didn't help probably because node thought it was already set. I didn't think to set it to false first and then back to true. You guys are smart.
https://github.com/libuv/libuv/issues/1292 looks like it might be why it's necessary to setRawMode(false) before setRawMode(true)