summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt1
-rw-r--r--lib/net/ssh/proxy/command.rb26
-rw-r--r--test/proxy/test_command.rb23
-rw-r--r--test/start/test_connection.rb9
4 files changed, 48 insertions, 11 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 5cede5e..ab6a889 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
=== 5.0.0.beta1
+ * Don't leave proxy command as zombie on timeout [DimitriosLisenko, #560]
* Use OpenSSL for aes*-ctr for up to 5x throughput improvement [Miklós Fazekas, Harald Sitter, #570]
* Optimize slice! usage in CTR for up to 2x throughput improvement [Harald Sitter, #569]
* Replace RbNaCl dependency with ed25519 gem [Tony Arcieri ,#563]
diff --git a/lib/net/ssh/proxy/command.rb b/lib/net/ssh/proxy/command.rb
index 4c9eb62..6e362bd 100644
--- a/lib/net/ssh/proxy/command.rb
+++ b/lib/net/ssh/proxy/command.rb
@@ -23,6 +23,9 @@ module Net; module SSH; module Proxy
# The command line for the session
attr_reader :command_line
+ # Timeout in seconds in open, defaults to 60
+ attr_accessor :timeout
+
# Create a new socket factory that tunnels via a command executed
# with the user's shell, which is composed from the given command
# template. In the command template, `%h' will be substituted by
@@ -30,6 +33,7 @@ module Net; module SSH; module Proxy
def initialize(command_line_template)
@command_line_template = command_line_template
@command_line = nil
+ @timeout = 60
end
# Return a new socket connected to the given host and port via the
@@ -56,13 +60,17 @@ module Net; module SSH; module Proxy
}
begin
io = IO.popen(command_line, "r+")
- if result = IO.select([io], nil, [io], 60)
- if result.last.any? || io.eof?
- io.close
- raise "command failed"
+ begin
+ if result = IO.select([io], nil, [io], @timeout)
+ if result.last.any? || io.eof?
+ raise "command failed"
+ end
+ else
+ raise "command timed out"
end
- else
- raise "command timed out"
+ rescue
+ close_on_error(io)
+ raise
end
rescue => e
raise ConnectError, "#{e}: #{command_line}"
@@ -104,6 +112,12 @@ module Net; module SSH; module Proxy
end
io
end
+
+ def close_on_error(io)
+ Process.kill('TERM', io.pid)
+ Thread.new { io.close }
+ end
+
end
end; end; end
diff --git a/test/proxy/test_command.rb b/test/proxy/test_command.rb
new file mode 100644
index 0000000..22dd17e
--- /dev/null
+++ b/test/proxy/test_command.rb
@@ -0,0 +1,23 @@
+require_relative '../common'
+require 'net/ssh'
+require 'net/ssh/proxy/command'
+
+module NetSSH
+ class TestProxy < NetSSHTest
+ unless Gem.win_platform?
+ def test_process_is_stopped_on_timeout
+ proxy = Net::SSH::Proxy::Command.new('sleep 10')
+ proxy.timeout = 2
+ host = 'foo'
+ port = 1
+ assert_raises Net::SSH::Proxy::ConnectError do
+ proxy.open(host, port)
+ end
+ sleep 0.1
+ assert_raises Errno::ECHILD do
+ Process.waitpid(0, Process::WNOHANG)
+ end
+ end
+ end
+ end
+end
diff --git a/test/start/test_connection.rb b/test/start/test_connection.rb
index eaf826e..47546ee 100644
--- a/test/start/test_connection.rb
+++ b/test/start/test_connection.rb
@@ -1,10 +1,10 @@
-require 'common'
+require_relative '../common'
require 'net/ssh'
module NetSSH
class TestConnection < NetSSHTest
attr_reader :connection_session
-
+
def setup
authentication_session = mock('authentication_session')
authentication_session.stubs(:authenticate).returns(true)
@@ -40,15 +40,14 @@ module NetSSH
# We aren't interested in the exception
end
end
-
+
def test_return_value_is_returned
@connection_session.expects(:closed?).returns(false)
@connection_session.expects(:close).once
-
+
val = 1
retval = Net::SSH.start('localhost', 'testuser') { val }
assert_equal(val, retval)
end
end
end
-