[Bug #3673] PTY.getpty with IO.pipe doesn't finish on FreeBSD

Bug #3673: PTY.getpty with IO.pipe doesn’t finish on FreeBSD
http://redmine.ruby-lang.org/issues/show/3673

e$B5/I<<Te(B: Yui NARUSE
e$B%9%F!<%?%9e(B: Open, e$BM%@hEYe(B: Normal
e$B%+%F%4%je(B: core, Target version: 1.9.x
ruby -v: ruby 1.9.3dev (2010-08-09 trunk 28938) [x86_64-freebsd8.1]

e$B0J2<$N%W%m%0%i%`$,e(B FreeBSD e$B$G=N;$7$^$;$s!#e(B
(test/ruby/test_rubyoptions.rb e$B$Ne(B test_script_from_stdin
e$B$h$je(B)
Ubuntu 8.04 e$B$de(B Mac OS X 10.6
e$B$G$O=
$o$k$3$H$r3NG’$7$F$$$^$9!#e(B

require ‘pty’
#require ‘timeout’
s, w = IO.pipe
PTY.getpty(‘./ruby’, out: w) do |r, m|
w.close
#m.print(“print ‘abc’\n”)
m.print(“\C-d”)
p s.read

result = Timeout.timeout(3) {s.read}

end
puts :fin

2010e$BG/e(B8e$B7ne(B10e$BF|e(B10:53 Yui NARUSE [email protected]:

e$B0J2<$N%W%m%0%i%`$,e(B FreeBSD e$B$G=N;$7$^$;$s!#e(B
(test/ruby/test_rubyoptions.rb e$B$Ne(B test_script_from_stdin e$B$h$je(B)
Ubuntu 8.04 e$B$de(B Mac OS X 10.6 e$B$G$O=
$o$k$3$H$r3NG’$7$F$$$^$9!#e(B

e$BC1=c2=$7$F$_$^$7$?!#e(B
e$B%Q%$%W$O4X78$"$j$^$;$s!#e(B

freebsd8(16:07:56)% cat z.rb
require ‘pty’
PTY.getpty(‘sleep 1’) do |r, w, pid|
p pid
w.print(“a”)
Process.wait pid
end
puts :fin

freebsd8(16:07:58)% ./ruby -v z.rb
ruby 1.9.3dev (2010-08-07 trunk 28906) [i386-freebsd8.1]
32576
(e$B$3$3$G%O%s%0e(B)

e$BB>$NC<Kv$+$ie(B ps e$B$7$F$_$k$H!"0J2<$N$h$&$K$J$j$^$9!#e(B

freebsd8(16:07:25)% ps u32576
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
akr 32576 0.0 0.1 1536 504 7- SEs+ 4:07PM 0:00.00 sleep 1

STAT e$B$N0UL#$Oe(B

S Marks a process that is sleeping for less than about 20
seconds.
E The process is trying to exit.
s The process is a session leader.

  •   The process is in the foreground process group of its
    
    control terminal.

e$B$H$$$&$3$H$G!"e(BE e$B$N$^$^$G=*$o$i$J$$$N$,$o$+$j$^$;$s!#e(B

e$B$^$?!"e(Bsleep 1 e$B$re(B ktrace sleep 1 e$B$K$7$Fe(B kdump
e$B$9$k$H!"e(B

freebsd8(16:18:10)% kdump -E

33428 sleep 0.001572 RET sigprocmask 0
33428 sleep 0.001611 CALL nanosleep(0xbfbfeaac,0)
33428 sleep 1.001607 RET nanosleep 0
33428 sleep 1.001672 CALL
sigprocmask(SIG_BLOCK,0x2807acc0,0xbfbfea10)
33428 sleep 1.001681 RET sigprocmask 0
33428 sleep 1.001688 CALL sigprocmask(SIG_SETMASK,0x2807acd0,0)
33428 sleep 1.001693 RET sigprocmask 0
33428 sleep 1.001712 CALL
sigprocmask(SIG_BLOCK,0x2807acc0,0xbfbfe9d0)
33428 sleep 1.001718 RET sigprocmask 0
33428 sleep 1.001723 CALL sigprocmask(SIG_SETMASK,0x2807acd0,0)
33428 sleep 1.001742 RET sigprocmask 0
33428 sleep 1.001757 CALL exit(0)

e$B$H$$$&$h$&$Ke(B nanosleep e$B$Ge(B 1e$BICBT$C$?8e!"e(Bexit(0)
e$B$r8F$s$G$$$k$h$&$J$N$,e(B
e$B4Q;!$5$l$^$9!#e(B

2010e$BG/e(B8e$B7ne(B10e$BF|e(B16:21 Tanaka A. [email protected]:

freebsd8(16:07:56)% cat z.rb
require ‘pty’
PTY.getpty(‘sleep 1’) do |r, w, pid|
p pid
w.print(“a”)
Process.wait pid
end
puts :fin

C e$B$K$7$F$5$i$KC1=c2=$9$k$H$3$&$G$9$+$M!#e(B

freebsd8(23:26:18)% cat t.c
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
int m, s;
char *slavedev;
pid_t pid;
int status;

if ((m = posix_openpt(O_RDWR|O_NOCTTY)) == -1) {
perror(“posix_openpt”); exit(1); }
if (grantpt(m) == -1) { perror(“grantpt”); exit(1); }
if (unlockpt(m) == -1) { perror(“unlockpt”); exit(1); }
if ((slavedev = ptsname(m)) == NULL) { perror(“ptsname”); exit(1); }
if ((s = open(slavedev, O_RDWR|O_NOCTTY, 0)) == -1) {
perror(“open(slavedev)”); exit(1); }

pid = fork();
if (pid == -1) { perror(“fork”); exit(1); }
if (pid == 0) {
sleep(1);
exit(0);
}
if (close(s) == -1) { perror(“close”); exit(1); }

if (write(m, “a”, 1) == -1) { perror(“write”); exit(1); }

fprintf(stderr, “pid=%d\n”, (int)pid);
if (waitpid(pid, &status, 0) == -1) { perror(“waitpid”); exit(1); }

return 0;
}
freebsd8(23:26:22)% gcc -Wall t.c
freebsd8(23:28:02)% time ./a.out
pid=68602
^C
./a.out 0.00s user 0.00s system 0% cpu 19.719 total

^C e$B$9$kA0$Ke(B ps e$B$7$?7k2Le(B:
freebsd8(23:28:08)% ps u68602
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
akr 68602 0.0 0.1 1536 664 3 SE+ 11:28PM 0:00.00 ./a.out

e$B$d$C$Qe(B E e$B$G$9$,!"e(BE
e$B$C$F6qBNE*$K$O$I$&$$$&>u67$J$N$+$J$!!#e(B

e$B$7$+$7!"e(Bsetsid e$B$7$J$/$F$b:F8=$9$k$J$ie(B

% ./ruby -rpty -e ’
m, s = PTY.open
pid = spawn(“sleep 1”)
s.close
m.write “a”
Process.wait pid

e$B$GH/>I$7$F$b$*$+$7$/$J$$$H;W$&$s$G$9$,:F8=$7$J$$e(B…

e$B@.@%$G$9!#e(B

(2010/08/10 23:32), Tanaka A. wrote:

C e$B$K$7$F$5$i$KC1=c2=$9$k$H$3$&$G$9$+$M!#e(B

e$B$I$&$b$G$9!#e(B

^C e$B$9$kA0$Ke(B ps e$B$7$?7k2Le(B:
freebsd8(23:28:08)% ps u68602
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
akr 68602 0.0 0.1 1536 664 3 SE+ 11:28PM 0:00.00 ./a.out

e$B$d$C$Qe(B E e$B$G$9$,!"e(BE e$B$C$F6qBNE*$K$O$I$&$$$&>u67$J$N$+$J$!!#e(B

e$B$H$j$"$($:e(B ps(3) e$B$N%=!<%9$r$_$k$H!“e(Bexit
e$B$7$F$$$k$,%>%s%S$G$O$J$$!”$H!#e(B
if (flag & P_WEXIT && k->ki_p->ki_stat != SZOMB)
*cp++ = ‘E’;
http://svn.freebsd.org/viewvc/base/head/bin/ps/print.c?revision=205271&view=markup

e$B$"$H!"e(Bps e$B$Ge(B wchan e$B$r8+$F$_$k$H!"0J2<$N$h$&$K$J$j$^$9$Me(B
% pgrep a.out|xargs procstat
PID PPID PGID SID TSID THR LOGIN WCHAN EMUL COMM
35305 35304 35304 47112 47112 1 naruse ttyout FreeBSD ELF64 a.out
35304 47112 35304 47112 47112 1 naruse wait FreeBSD ELF64 a.out

At Tue, 10 Aug 2010 23:32:24 +0900,
Tanaka A. wrote:

^C e$B$9$kA0$Ke(B ps e$B$7$?7k2Le(B:
freebsd8(23:28:08)% ps u68602
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
akr 68602 0.0 0.1 1536 664 3 SE+ 11:28PM 0:00.00 ./a.out

e$B$d$C$Qe(B E e$B$G$9$,!"e(BE e$B$C$F6qBNE*$K$O$I$&$$$&>u67$J$N$+$J$!!#e(B

FreeBSDe$B$OFI$s$@$3$H$,$J$$$N$G?dB,$G$9$,!De(B

  1. e$B0lHL$K!“e(Bexit()e$B$7$?%W%m%;%9$Ne(Bprocess
    IDe$B$O!”?7$?$K:n$i$l$?%W%m%;%9$K:Fe(B
    e$B3dEv$F$7$^$9!#e(B
  2. sessnion leadere$B$Ne(Bprocess IDe$B$Oe(Bsession
    IDe$B$G$b$"$k$N$G!"Ev3:e(Bsessione$B$KB0e(B
    e$B$9$k%W%m%;%9$,B8:_$9$k4|4V!"e(Bexit()e$B$7$?e(Bsession
    leadere$B$Ne(Bprocess IDe$B$r:Fe(B
    e$B3dEv$F$F$O$$$1$^$;$s!#e(B
  3. 2e$B$N7gHV07$$$N$?$a$K!“e(Bprocess
    IDe$B$r5-O?$7$F$$$k%G!<%?9=B$$K$=$N;]5-O?e(B
    e$B$7$F$*$/$7$+$”$j$^$;$s!#e(Bexit()e$B$7$F$bEv3:e(Bentrye$B$O3+J|$G$-$J$$$N$G$9!#e(B

3e$B$Ne(Bentrye$B$N2D;k@-$re(BPOSIXe$B$O5,Dj$7$F$$$J$$e(B(e$B$h$&$KFI$a$ke(B)e$B$N$G!"e(B1e$B<B8=<jK!$He(B
e$B$7$F$3$&$$$&$3$H$r$7$F$k$s$8$c$J$$$G$7$g$&$+!#e(B
exit(2)e$B$,0z$-?-$P$5$l$F$$$l$P!"e(B3e$B$,<B8=$G$-$F$k$3$H$K$J$j$^$9$+$i!#e(B

e$B$3$N2r<a$,@5$7$$$J$i!"e(Bsetpgid()e$B$G$bF1$87k2L$K$J$k$H;W$o$l$^$9!#e(B

e$B%A%1%C%He(B #3673 e$B$,99?7$5$l$^$7$?!#e(B (by Akira T.)

e$B%9%F!<%?%9e(B Opene$B$+$ie(BClosede$B$KJQ99e(B
e$B?JD=e(B % 0e$B$+$ie(B100e$B$KJQ99e(B

This issue was solved with changeset r28965.
Yui, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


http://redmine.ruby-lang.org/issues/show/3673

2010e$BG/e(B8e$B7ne(B11e$BF|e(B8:05 NARUSE, Yui [email protected]:

e$B$"$H!"e(Bps e$B$Ge(B wchan e$B$r8+$F$_$k$H!"0J2<$N$h$&$K$J$j$^$9$Me(B
% pgrep a.out|xargs procstat
PID PPID PGID SID TSID THR LOGIN WCHAN EMUL COMM
35305 35304 35304 47112 47112 1 naruse ttyout FreeBSD ELF64 a.out
35304 47112 35304 47112 47112 1 naruse wait FreeBSD ELF64 a.out

e$B$3$l$ONI$$>pJs$G$9!#e(B
ttyout e$B$C$F$3$H$O$J$K$+=PNO$rBT$C$F$$$k$s$G$9$M!#e(B

e$B;R%W%m%;%9$b<h$j=|$1$^$7$?!#e(B

freebsd8% cat tst.c
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
int m, s;
char *slavedev;

if ((m = posix_openpt(O_RDWR|O_NOCTTY)) == -1) {
perror(“posix_openpt”); exit(1); }
if (grantpt(m) == -1) { perror(“grantpt”); exit(1); }
if (unlockpt(m) == -1) { perror(“unlockpt”); exit(1); }
if ((slavedev = ptsname(m)) == NULL) { perror(“ptsname”); exit(1); }
if ((s = open(slavedev, O_RDWR|O_NOCTTY, 0)) == -1) {
perror(“open”); exit(1); }

if (write(m, “a”, 1) == -1) { perror(“write”); exit(1); }

fprintf(stderr, “before close(s)\n”);
if (close(s) == -1) { perror(“close”); exit(1); }
fprintf(stderr, “after close(s)\n”);

return 0;
}
freebsd8% gcc -Wall tst.c
freebsd8% ./a.out
before close(s)
(e$B$3$3$G%O%s%0e(B)

e$B$I$&$d$i!"e(Bclose e$B$,%V%m%C%/$7$F$$$k$h$&$G$9$M!#e(B
exit e$B$bFbItE*$K$Oe(B close e$BAjEv$N$3$H$r$9$k$G$7$g$&$+$i!"e(B
e$B$=$3$G%V%m%C%/$7$F$$$k$N$G$7$g$&!#e(B

e$B0J2<$N$h$&$K$7$F$b%O%s%0$7$^$9!#e(B

% ./ruby -rpty -e ’
m, s = PTY.open
m.write “a”
s.close

e$B$J$s$G=PNO$,$“$k$+$H$$$&$H!”$*$=$i$/e(B tty
e$B$N%(%3!<$@$m$&$H$$$&$3$H$G!"e(B
e$B%(%3!<$rM^@)$9$k$H%O%s%0$7$^$;$s!#e(B

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <termios.h>

int main(int argc, char *argv[])
{
int m, s;
char *slavedev;
struct termios t;

if ((m = posix_openpt(O_RDWR|O_NOCTTY)) == -1) {
perror(“posix_openpt”); exit(1); }
if (grantpt(m) == -1) { perror(“grantpt”); exit(1); }
if (unlockpt(m) == -1) { perror(“unlockpt”); exit(1); }
if ((slavedev = ptsname(m)) == NULL) { perror(“ptsname”); exit(1); }
if ((s = open(slavedev, O_RDWR|O_NOCTTY, 0)) == -1) {
perror(“open(slavedev)”); exit(1); }

if (tcgetattr(s, &t) == -1) { perror(“tcgetattr”); }
t.c_lflag &= ~(tcflag_t)(ECHO|ECHOE|ECHOK|ECHONL);
if (tcsetattr(s, TCSANOW, &t) == -1) { perror(“tcsetattr”); }

if (write(m, “a”, 1) == -1) { perror(“write”); exit(1); }

if (close(s) == -1) { perror(“close”); exit(1); }

return 0;
}

ruby e$B$J$i$3$&$G$9!#e(B

% ./ruby -rio/console -rpty -e ’
m, s = PTY.open
s.echo = false
m.write “a”
s.close

e$B$G$O!“e(Btest_script_from_stdin
e$B$G$b!”$H$$$&$H!"$=$3$,HyL/$G$9!#e(B
PTY.spawn e$B$Oe(B slave tty e$B$r65$($F$/$l$J$$$N$G!"e(B
slave tty e$B$Ke(B tcsetattr e$B$,$G$-$^$;$s!#e(B
FreeBSD e$B$@$H0J2<$N$h$&$Ke(B master e$BB&$Ke(B tcsetattr
e$B$rH/9T$7$F$bF0$/$s$G$9$,!“e(B
e$B$3$l$O%]!<%?%V%k$G$O$”$j$^$;$s!#e(B[ruby-list:28382]

% svn diff --diff-cmd diff -x -u test/ruby/test_rubyoptions.rb
Index: test/ruby/test_rubyoptions.rb

— test/ruby/test_rubyoptions.rb (revision 28906)
+++ test/ruby/test_rubyoptions.rb (working copy)
@@ -436,6 +436,7 @@
result = nil
s, w = IO.pipe
PTY.spawn(EnvUtil.rubybin, out: w) do |r, m|

  •  m.echo = false
     w.close
     m.print("\C-d")
     assert_nothing_raised('[ruby-dev:37798]') do
    

@@ -446,6 +447,7 @@
assert_equal(“”, result, ‘[ruby-dev:37798]’)
s, w = IO.pipe
PTY.spawn(EnvUtil.rubybin, out: w) do |r, m|

  •  m.echo = false
     w.close
     m.print("$stdin.read; p $stdin.gets\n\C-d")
     m.print("abc\n\C-d")
    

e$B$3$N%F%9%H$G$O@)8fC<Kv$rJQ$($kI,MW$O$J$$$H;W$&$N$G!"e(B
PTY.open e$B$r;H$C$F=q$-D>$9$N$,$$$$$+$J$!!#e(B

e$B%A%1%C%He(B #3673 e$B$,99?7$5$l$^$7$?!#e(B (by Yui NARUSE)

e$B$^$:$O%F%9%H$N=$@5$"$j$,$H$&$4$6$$$^$9!#e(B

e$B3N$+$K0J2<$NDL$je(B close(2)
e$B$NA0$K%(%3!<$K$h$C$F=PNO$5$l$?J8;zNs$rFI$s$G$"$2$k$H$A$c$s$HJD$8$i$l$^$9$M!#e(B
e$B$&!<$s!“A0$Ne(B Bug #3515 e$B$H4X78$”$k$N$+$J$!!#e(B

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <termios.h>

int main(int argc, char *argv[])
{
int m, s;
char *slavedev;
struct termios t;

  if ((m = posix_openpt(O_RDWR|O_NOCTTY)) == -1) {
      perror("posix_openpt"); exit(1); }
  if (grantpt(m) == -1) { perror("grantpt"); exit(1); }
  if (unlockpt(m) == -1) { perror("unlockpt"); exit(1); }
  if ((slavedev = ptsname(m)) == NULL) { perror("ptsname"); exit(1); 

}
if ((s = open(slavedev, O_RDWR|O_NOCTTY, 0)) == -1) {
perror(“open(slavedev)”); exit(1); }

  if (tcgetattr(s, &t) == -1) { perror("tcgetattr"); }
  t.c_lflag &= ~(tcflag_t)(ECHO|ECHOE|ECHOK|ECHONL);
  //if (tcsetattr(s, TCSANOW, &t) == -1) { perror("tcsetattr"); }

  if (write(m, "a", 1) == -1) { perror("write"); exit(1); }

  {
      char buf[100];
      if (read(m, buf, 1) == -1) { perror("write"); exit(1); }
      printf("echo: %c\n", buf[0]);
  }

  if (close(s) == -1) { perror("close"); exit(1); }

  return 0;

}


http://redmine.ruby-lang.org/issues/show/3673

2010e$BG/e(B8e$B7ne(B14e$BF|e(B19:55 Yui NARUSE [email protected]:

e$B$&!<$s!“A0$Ne(B Bug #3515 e$B$H4X78$”$k$N$+$J$!!#e(B

NetBSD e$B$de(B OpenBSD e$B$G$b:F8=$9$k$N$G4X78$J$$$H;W$$$^$9!#e(B