script input redirection / eof handling
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
I came across an old bug filed in debian about script being redirected from a file misbehaving. It seems that when the master ( input ) process hits eof, it exits immediately, orphaning the child ( output ) and user processes.
I believe the correct thing to do is signal the eof to the user process and wait for the children to exit, but I'm not sure how to make a pty signal eof.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.17 (MingW32)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/
iQEcBAEBAgAGBQJScmdWAAoJEJrBOlT6nu75JIQH/1Eqt6gsG+vKGc8xpybIE6F7 UGgo8sLyn++PE3U6MZQcsWj2nHZ6p76uHOp4Kn1sk8xrb2zetueQTF1idtgJO7nS PKhwbfInD3ocSuEQB1HygMmKTKuXT9qYCBj18AT9ZQp7eQ5ocz+nvNnGHQuR4KPR /CsI0D0/BnTO32ayh6qgE1HtxD0sUY4K1Qw3MyyChINwFAU9q8p3Y9Lz5RqQTTZk 2j3kc6jDdNaGy1b6w1exhEMrhs7zC1LRvz5GhqM+ODVAN/YiMh4eKYH9a4OrYXru l8Df0gDWNRrQhqrJxvOMKph/DZO1sZEZP3ZG/kOXak/cpbHVy0MkGepP0qId91g= =ZaJO
-----END PGP SIGNATURE-----
Karel Zak
maintainerQuoted 5 lines · Phillip Susi
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1I came across an old bug filed in debian about script being redirected from a file misbehaving.
Do you mean something like:
echo "ps uf" | script
.. if yes, then it's pretty unexpected usage. The code expects that stdin is terminal. (For example it copy the current terminal attributes the new pseudo-terminal.)
Unfortunately it does not check tcgetatt() return code so the problem is not visible. I'll fix it.
Quoted 3 lines
It seems that when the master ( input )
process hits eof, it exits immediately, orphaning the child ( output ) and user processes.
Yep, I think it would be enough to add
kill(0, SIGTERM);
before done() in doinput() to avoid orphans.
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com
Karel Zak
maintainerQuoted 9 lines · Karel Zak
Quoted 5 lines · Phillip Susi
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1I came across an old bug filed in debian about script being redirected from a file misbehaving.
Do you mean something like:
Quoted 8 lines
echo "ps uf" | script
.. if yes, then it's pretty unexpected usage. The code expects that stdin is terminal. (For example it copy the current terminal attributes the new pseudo-terminal.)
Unfortunately it does not check
tcgetatt()return code so the problem is not visible. I'll fix it.
Note, that if you want to support this scenario (stdin is not a terminal) then send a patch ;-)
I guess you have to initialize the terminal (see inlude/ttyutils.h, reset_virtual_console()) and use this dummy struct termios instead of the current 'tt'.
Quoted 5 lines
Yep, I think it would be enough to add
kill(0, SIGTERM);before
done()indoinput()to avoid orphans.
This is probably not the best solution (because shell will print "Terminated" etc.), but it's definitely better than orphans.
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Quoted 2 lines · Karel Zak
Note, that if you want to support this scenario (stdin is not a terminal) then send a patch ;-)
It seems to work fine up until it hits eof, which can also happen on a tty and you'll get the same bad behavior, so I think refusing to run on a non tty isn't a proper fix.
Quoted 5 lines
Quoted 5 lines
Yep, I think it would be enough to add
kill(0, SIGTERM);before
done()indoinput()to avoid orphans.
You don't want to just make sure the children die, you want to make sure that the child sees the eof and capture any final output it may have.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.17 (MingW32)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/
iQEcBAEBAgAGBQJSnJz9AAoJEI5FoCIzSKrw6j0IAKZvkym0kJQfWghombuTnV1z 5FCcWPz3Jyo9V96X+fnD3cXpkkPjIM5ZUIBaRJubUYX65AKebXaXw+FkE9YyXsIR Jeegewp5IlSlF3zbZzCSUchI/gK0KCHv1yKe21xD20iGAy9MGApXBJszsOaREbU2 nG9mFYwviFDQnURBfwHxvS7Bf4S+6i1wgKeS58EqSeXpMpMDCimTw1/mv8MLnGdS 0nzCrPZvgRZcc2avYPSWYs8ZLtFgu5rDuJ7J9TfEuiEzGWCqlPXQYF1a6nHvpT0W KSdu+1bzsasdrEXFF6Iai2NvFndg9JXqiLCGfKdELbJayYPjwltOtl41PfalG/w= =2lT4
-----END PGP SIGNATURE-----
Karel Zak
maintainerQuoted 8 lines · Phillip Susi
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1Quoted 2 lines · Karel Zak
Note, that if you want to support this scenario (stdin is not a terminal) then send a patch ;-)
It seems to work fine up until it hits eof, which can also happen on a
well, it's nasty that the code ignores return codes and it call's tty ioctls/functions for non-tty file descriptors.
Quoted 11 lines
tty and you'll get the same bad behavior, so I think refusing to run on a non tty isn't a proper fix.
Quoted 5 lines
Quoted 5 lines
Yep, I think it would be enough to add
kill(0, SIGTERM);before
done()indoinput()to avoid orphans.You don't want to just make sure the children die, you want to make sure that the child sees the eof and capture any final output it may have.
never ending orphans suck...
The possible solution (hack) to emulate end-of-file is by write "exit\n" to the master tty. For example patch below works for me (it also make it more robust for non-ttys).
Karel
term-utils/script.c +44 -16
@@ -107,6 +107,7 @@ int fflg = 0;int qflg =0 ;int tflg =0 ;int forceflg =0 ; +int isterm;int die;int resized;
@@ -230,9 +231,6 @@ main(int argc, char **argv) {die_if_link (fname); } -if (!isatty (STDIN_FILENO )) -errx (EXIT_FAILURE ,_ ("The stdin is not a terminal." )); -if ((fscript =fopen (fname, aflg ?"a" :"w" )) ==NULL ) {warn (_ ("cannot open %s" ), fname);fail ();
@@ -314,11 +312,21 @@ doinput(void) {else if (cc <0 && errno ==EINTR && resized) {/* transmit window change information to the child */ -ioctl (STDIN_FILENO ,TIOCGWINSZ , (char *)&win); -ioctl (slave,TIOCSWINSZ , (char *)&win); +if (isterm) { +ioctl (STDIN_FILENO ,TIOCGWINSZ , (char *)&win); +ioctl (slave,TIOCSWINSZ , (char *)&win); + } resized =0 ; - } -else + }else if (cc ==0 && errno ==0 ) { +/* end-of-file, probably "foo | script" */ +strcpy (ibuf,"exit\n" ); +if (write (master, ibuf,5 ) <0 ) { +warn (_ ("write failed" )); +fail (); + } +pause ();/* wait for SIGCHLD */ +break ; + }else break ; }
@@ -473,6 +481,9 @@ voidfixtty (void) {struct termios rtt; +if (!isterm) +return ; + rtt = tt;cfmakeraw (&rtt); rtt.c_lflag &= ~ECHO ;
@@ -503,7 +514,8 @@ done(void) { master =-1 ; }else { -tcsetattr (STDIN_FILENO ,TCSADRAIN , &tt); +if (isterm) +tcsetattr (STDIN_FILENO ,TCSADRAIN , &tt);if (!qflg)printf (_ ("Script done, file is %s\n" ), fname);#ifdef HAVE_LIBUTEMPTER
@@ -524,9 +536,19 @@ done(void) {void getmaster (void ) {#if defined(HAVE_LIBUTIL ) && defined(HAVE_PTY_H ) -tcgetattr (STDIN_FILENO , &tt); -ioctl (STDIN_FILENO ,TIOCGWINSZ , (char *)&win); -if (openpty (&master, &slave,NULL , &tt, &win) <0 ) { +int rc; + + isterm =isatty (STDIN_FILENO ); + +if (isterm) { +if (tcgetattr (STDIN_FILENO , &tt) !=0 ) +err (EXIT_FAILURE ,_ ("failed to get terminal attributes" )); +ioctl (STDIN_FILENO ,TIOCGWINSZ , (char *) &win); + rc =openpty (&master, &slave,NULL , &tt, &win); + }else + rc =openpty (&master, &slave,NULL ,NULL ,NULL ); + +if (rc <0 ) {warn (_ ("openpty failed" ));fail (); }
@@ -534,6 +556,8 @@ getmaster(void) {char *pty, *bank, *cp;struct stat stb; + isterm =isatty (STDIN_FILENO ); + pty = &line[strlen ("/dev/ptyp" )];for (bank ="pqrs" ; *bank; bank++) { line[strlen ("/dev/pty" )] = *bank;
@@ -552,9 +576,11 @@ getmaster(void) { ok =access (line,R_OK |W_OK ) ==0 ; *tp ='p' ;if (ok) { -tcgetattr (STDIN_FILENO , &tt); -ioctl (STDIN_FILENO ,TIOCGWINSZ , - (char *)&win); +if (isterm) { +tcgetattr (STDIN_FILENO , &tt); +ioctl (STDIN_FILENO ,TIOCGWINSZ , + (char *)&win); + }return ; }close (master);
@@ -577,8 +603,10 @@ getslave(void) {warn (_ ("cannot open %s" ), line); fail(); } -tcsetattr (slave,TCSANOW , &tt); -ioctl (slave,TIOCSWINSZ , (char *)&win); +if (isterm) { +tcsetattr (slave,TCSANOW , &tt); +ioctl (slave,TIOCSWINSZ , (char *)&win); + }#endif setsid ();ioctl (slave,TIOCSCTTY ,0 );
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Quoted 1 line · Karel Zak
never ending orphans suck...
Yes, but the idea is not to orphan them in the first place and wait for them to terminate instead.
Quoted 3 lines
The possible solution (hack) to emulate end-of-file is by write "exit\n" to the master tty. For example patch below works for me (it also make it more robust for non-ttys).
That only works if the program on the other end knows what "exit" means, which is basically only true if it's a shell. Surely there must be a proper way to signal EOF over a pty? Can you use shutdown() on a pty?
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.17 (MingW32)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/
iQEcBAEBAgAGBQJSnMwNAAoJEI5FoCIzSKrwwvYH+wRd0hXZwZKhElWgpLIjWNOO 82ulPzt1Cld085qllaG8VvO0CSN3JERHD5Eo8iB+YIuI32lNO2dGuGw0MwYJ72cn whUEqT7BNcnDCDqhPba/ZYcGJ8mCp+3+58BDth8NPu+nma7d6V0n4sAbqO+w6uCS VZOO9qo2cKYGqs7E2YYWLHBadMGkyLjpDd9j7ZsLxEe6ezb1vFLxSmJQ3CiNLz+i q2okm0GUipkhMAMCC8zuV1GnUf5sJuSx6QiSkIE5Mok9d8HqOtXTt8F7URAa+YVC tBtM7dMIfXEQCJil16hTn+KdgiAtNE4BaGkyevW2kohwmKQgKU51/CHlofWGK1s= =6Ihg
-----END PGP SIGNATURE-----
Karel Zak
maintainerQuoted 1 line · Phillip Susi
That only works if the program on the other end knows what "exit"
yep, fingers have been faster than brain.. (I forgot we have --command).
Quoted 3 lines
means, which is basically only true if it's a shell. Surely there must be a proper way to signal EOF over a pty? Can you use
shutdown()on a pty?
I'm going to play with that tomorrow.
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com
Karel Zak
maintainerQuoted 4 lines · Phillip Susi
That only works if the program on the other end knows what "exit" means, which is basically only true if it's a shell. Surely there must be a proper way to signal EOF over a pty? Can you use
shutdown()on a pty?
Oh, now it reminds me... in include/ttyutils.h we have macros to generate tty control chars, for example ^C
char eof = DEF_EOF;
if (write(master, &eof, 1) < 0) {
warn (_("write failed"));
fail();
}
pause();is what we need.
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com
Karel Zak
maintainerQuoted 17 lines · Karel Zak
Quoted 4 lines · Phillip Susi
That only works if the program on the other end knows what "exit" means, which is basically only true if it's a shell. Surely there must be a proper way to signal EOF over a pty? Can you use
shutdown()on a pty?Oh, now it reminds me... in
include/ttyutils.hwe have macros to generate tty control chars, for example ^Cchar eof = DEF_EOF; if (write(master, &eof, 1) < 0) { warn (_("write failed")); fail(); } pause();
Quoted 1 line
is what we need.
It was more tricky than I have originally expected, but it seems it works. (It seems that it is not enough to send EOF, you have to be sure that someone already listens on slave side.)
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com