class Net::SSH::Proxy::Command
An implementation of a command proxy. To use it, instantiate it, then pass the instantiated object via the :proxy key to Net::SSH.start:
require 'net/ssh/proxy/command' proxy = Net::SSH::Proxy::Command.new('ssh relay nc %h %p') Net::SSH.start('host', 'user', :proxy => proxy) do |ssh| ... end
Attributes
command_line[R]
The command line for the session
command_line_template[R]
The command line template
timeout[RW]
Timeout in seconds in open, defaults to 60
Public Class Methods
new(command_line_template)
click to toggle source
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 the host name to connect and `%p' by the port.
# File lib/net/ssh/proxy/command.rb, line 34 def initialize(command_line_template) @command_line_template = command_line_template @command_line = nil @timeout = 60 end
Public Instance Methods
close_on_error(io)
click to toggle source
# File lib/net/ssh/proxy/command.rb, line 117 def close_on_error(io) Process.kill('TERM', io.pid) Thread.new { io.close } end
open(host, port, connection_options = nil)
click to toggle source
Return a new socket connected to the given host and port via the proxy that was requested when the socket factory was instantiated.
# File lib/net/ssh/proxy/command.rb, line 42 def open(host, port, connection_options = nil) command_line = @command_line_template.gsub(/%(.)/) { case $1 when 'h' host when 'p' port.to_s when 'r' remote_user = connection_options && connection_options[:remote_user] if remote_user remote_user else raise ArgumentError, "remote user name not available" end when '%' '%' else raise ArgumentError, "unknown key: #{$1}" end } begin io = IO.popen(command_line, "r+") 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 rescue StandardError close_on_error(io) raise end rescue StandardError => e raise ConnectError, "#{e}: #{command_line}" end @command_line = command_line if Gem.win_platform? # read_nonblock and write_nonblock are not available on Windows # pipe. Use sysread and syswrite as a replacement works. def io.send(data, flag) syswrite(data) end def io.recv(size) sysread(size) end else def io.send(data, flag) begin result = write_nonblock(data) rescue IO::WaitWritable, Errno::EINTR IO.select(nil, [self]) retry end result end def io.recv(size) begin result = read_nonblock(size) rescue IO::WaitReadable, Errno::EINTR timeout_in_seconds = 20 if IO.select([self], nil, [self], timeout_in_seconds) == nil raise "Unexpected spurious read wakeup" end retry end result end end io end