|
require 'pty'
|
|
|
|
#############################################################################
|
|
# $ ruby --version
|
|
# ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin10.8.0]
|
|
# $ ruby pty_no_eof_example.rb 10000
|
|
# ..pty_no_eof_example.rb:35:in `block (2 levels) in <main>': timeout waiting for slave EOF (cmd: /bin/bash i:258 pid:37196) (RuntimeError)
|
|
# from pty_no_eof_example.rb:23:in `spawn'
|
|
# from pty_no_eof_example.rb:23:in `block in <main>'
|
|
# from pty_no_eof_example.rb:21:in `upto'
|
|
# from pty_no_eof_example.rb:21:in `<main>
|
|
#############################################################################
|
|
# $ ruby --version
|
|
# ruby 1.8.7 (2010-08-16 patchlevel 302) [i686-darwin10.4.0]
|
|
# $ ruby pty_no_eof_example.rb 10000
|
|
# ....................................................................................................
|
|
#############################################################################
|
|
|
|
max = ARGV.shift || 10000
|
|
shell = ARGV.shift || '/bin/bash'
|
|
timeout = 3
|
|
|
|
1.upto(max.to_i) do |i|
|
|
exitstatus = nil
|
|
PTY.spawn(shell) do |slave, master, pid|
|
|
begin
|
|
|
|
# exit as soon as possible
|
|
unless IO.select(nil,[master],nil,timeout)
|
|
raise "timeout waiting for master (cmd: #{shell} i:#{i} pid:#{pid})"
|
|
end
|
|
master.write("exit 8\n")
|
|
|
|
# read to EOF
|
|
while true
|
|
unless IO.select([slave],nil,nil,timeout)
|
|
raise "timeout waiting for slave EOF (cmd: #{shell} i:#{i} pid:#{pid})"
|
|
end
|
|
|
|
begin
|
|
c = slave.read(1)
|
|
rescue(Errno::EIO)
|
|
c = nil
|
|
end
|
|
|
|
if c.nil?
|
|
break
|
|
end
|
|
end
|
|
|
|
# Cleanup and capture the exit status to validate the exit worked
|
|
Process.wait(pid)
|
|
exitstatus = $?.exitstatus
|
|
|
|
rescue PTY::ChildExited
|
|
# Wait can cause a ChildExited error on 1.8.6 and 1.8.7 so handle it as
|
|
# a normal exit route. 1.9.2 does not exit this way.
|
|
exitstatus = $!.status.exitstatus
|
|
|
|
rescue Exception
|
|
# Cleanup on error - note PTY::ChildExited must be accounted for again
|
|
Process.kill(9, pid)
|
|
Process.wait(pid) rescue PTY::ChildExited
|
|
raise
|
|
end
|
|
end
|
|
|
|
unless exitstatus == 8
|
|
raise "\nexpected exit status 8 but was #{exitstatus.inspect}"
|
|
end
|
|
|
|
if i % 100 == 0
|
|
$stdout.print '.'
|
|
$stdout.flush
|
|
end
|
|
end
|
|
puts
|