0
@@ -27,14 +27,14 @@ module Net; module SSH; module Connection
0
# The underlying transport layer abstraction (see Net::SSH::Transport::Session).
0
+ # The map of options that were used to initialize this instance.
0
# The map of channels, each key being the local-id for the channel.
0
+ attr_reader :channels
#:nodoc:0
# The map of listeners that the event loop knows about. See #listen_to.
0
- attr_reader :listeners
0
- # The map of options that were used to initialize this instance.
0
+ attr_reader :listeners #:nodoc:
0
# The map of specialized handlers for opening specific channel types. See
0
@@ -63,7 +63,7 @@ module Net; module SSH; module Connection
0
# successfully closed, and then closes the underlying transport layer
0
-
debug { "closing remaining channels (#{channels.length} open)" }
0
+
info { "closing remaining channels (#{channels.length} open)" }
0
channels.each { |id, channel| channel.close }
0
loop(0) { channels.any? }
0
@@ -72,33 +72,84 @@ module Net; module SSH; module Connection
0
# preserve a reference to Kernel#loop
0
alias :loop_forever :loop
0
+ # Returns +true+ if there are any channels currently active on this
0
+ # session. By default, this will not include "invisible" channels
0
+ # (such as those created by forwarding ports and such), but if you pass
0
+ # a +true+ value for +include_invisible+, then those will be counted.
0
+ # This can be useful for determining whether the event loop should continue
0
+ # ssh.loop { ssh.busy? }
0
+ def busy?(include_invisible=false)
0
+ channels.any? { |id, ch| !ch[:invisible] }
0
# The main event loop. Calls #process until #process returns false. If a
0
# block is given, it is passed to #process, otherwise a default proc is
0
- # used that just returns true if there are any channels active. The +wait+
0
- # parameter is also passed through to #process.
0
+ # used that just returns true if there are any channels active (see #busy?).
0
+ # The # +wait+ parameter is also passed through to #process (where it is
0
+ # interpreted as the maximum number of seconds to wait for IO.select to return).
0
+ # # loop for as long as there are any channels active
0
+ # # loop for as long as there are any channels active, but make sure
0
+ # # the event loop runs at least once per 0.1 second
0
+ # # loop until ctrl-C is pressed
0
+ # trap("INT") { int_pressed = true }
0
+ # ssh.loop(0.1) { not int_pressed }
0
def loop(wait=nil, &block)
0
- running = block || Proc.new {
channels.any? { |id,ch| !ch[:invisible] } }
0
+ running = block || Proc.new {
busy? }
0
loop_forever { break unless process(wait, &running) }
0
# The core of the event loop. It processes a single iteration of the event
0
# loop. If a block is given, it should return false when the processing
0
# should abort, which causes #process to return false. Otherwise,
0
- # #process returns true.
0
+ # #process returns true. The session itself is yielded to the block as its
0
# If +wait+ is nil (the default), this method will block until any of the
0
# monitored IO objects are ready to be read from or written to. If you want
0
# it to not block, you can pass 0, or you can pass any other numeric value
0
# to indicate that it should block for no more than that many seconds.
0
+ # Passing 0 is a good way to poll the connection, but if you do it too
0
+ # frequently it can make your CPU quite busy!
0
+ # This will also cause all active channels to be processed once each (see
0
+ # Net::SSH::Connection::Channel#on_process).
0
+ # # process multiple Net::SSH connections in parallel
0
+ # Net::SSH.start("host1", ...),
0
+ # Net::SSH.start("host2", ...)
0
+ # connections.each do |ssh|
0
+ # ssh.exec "grep something /in/some/files"
0
+ # condition = Proc.new { |s| s.busy? }
0
- # This will cause all active channels to be processed once each.
0
+ # connections.delete_if { |ssh| !ssh.process(0.1, &condition) }
0
+ # break if connections.empty?
0
- return false if block_given? && !yield
0
+ return false if block_given? && !yield
(self)0
dispatch_incoming_packets
0
channels.each { |id, channel| channel.process unless channel.closing? }
0
- return false if block_given? && !yield
0
+ return false if block_given? && !yield
(self)0
w = r.select { |w| w.pending_write? }
0
@@ -132,6 +183,14 @@ module Net; module SSH; module Connection
0
# success or failure is indicated by the callback being invoked, with the
0
# first parameter being true or false (success, or failure), and the second
0
# being the packet itself.
0
+ # Generally, Net::SSH will manage global requests that need to be sent
0
+ # (e.g. port forward requests and such are handled in the Net::SSH::Service::Forward
0
+ # class, for instance). However, there may be times when you need to
0
+ # send a global request that isn't explicitly handled by Net::SSH, and so
0
+ # this method is available to you.
0
+ # ssh.send_global_request("keep-alive@openssh.com")
0
def send_global_request(type, *extra, &callback)
0
info { "sending global request #{type}" }
0
msg = Buffer.from(:byte, GLOBAL_REQUEST, :string, type.to_s, :bool, !callback.nil?, *extra)
0
@@ -147,6 +206,18 @@ module Net; module SSH; module Connection
0
# Net::SSH::Buffer.from. If a callback is given, it will be invoked when
0
# the server confirms that the channel opened successfully. The sole parameter
0
# for the callback is the channel object itself.
0
+ # In general, you'll use #open_channel without any arguments; the only
0
+ # time you'd want to set the channel type or pass additional initialization
0
+ # data is if you were implementing an SSH extension.
0
+ # channel = ssh.open_channel do |ch|
0
+ # ch.exec "grep something /some/files" do |ch, success|
0
def open_channel(type="session", *extra, &on_confirm)
0
local_id = get_next_channel_id
0
channel = Channel.new(self, type, local_id, &on_confirm)
0
@@ -160,13 +231,25 @@ module Net; module SSH; module Connection
0
# A convenience method for executing a command and interacting with it. If
0
- # no block is given, all output
printed via $stdout and $stderr. Otherwise,
0
+ # no block is given, all output
is printed via $stdout and $stderr. Otherwise,
0
# the block is called for each data and extended data packet, with three
0
# arguments: the channel object, a symbol indicating the data type
0
# (:stdout or :stderr), and the data (as a string).
0
# Note that this method returns immediately, and requires an event loop
0
# (see Session#loop) in order for the command to actually execute.
0
+ # This is effectively identical to calling #open_channel, and then
0
+ # Net::SSH::Connection::Channel#exec, and then setting up the channel
0
+ # callbacks. However, for most uses, this will be sufficient.
0
+ # ssh.exec "grep something /some/files" do |ch, stream, data|
0
+ # if stream == :stderr
0
+ # puts "ERROR: #{data}"
0
def exec(command, &block)
0
open_channel do |channel|
0
channel.exec(command) do |ch, success|
0
@@ -194,6 +277,8 @@ module Net; module SSH; module Connection
0
# Same as #exec, except this will block until the command finishes. Also,
0
# if a block is not given, this will return all output (stdout and stderr)
0
+ # matches = ssh.exec!("grep something /some/files")
0
def exec!(command, &block)
0
block ||= Proc.new do |ch, type, data|
0
@@ -201,13 +286,18 @@ module Net; module SSH; module Connection
0
channel = exec(command, &block)
0
-
loop { channel.active? }0
return channel[:result]
0
# Enqueues a message to be sent to the server as soon as the socket is
0
- # available for writing.
0
+ # available for writing. Most programs will never need to call this, but
0
+ # if you are implementing an extension to the SSH protocol, or if you
0
+ # need to send a packet that Net::SSH does not directly support, you can
0
+ # use this to send it.
0
+ # ssh.send_message(Buffer.from(:byte, REQUEST_SUCCESS).to_s)
0
def send_message(message)
0
transport.enqueue_message(message)
0
@@ -215,6 +305,37 @@ module Net; module SSH; module Connection
0
# Adds an IO object for the event loop to listen to. If a callback
0
# is given, it will be invoked when the io is ready to be read, otherwise,
0
# the io will merely have its #fill method invoked.
0
+ # Any +io+ value passed to this method _must_ have mixed into it the
0
+ # Net::SSH::BufferedIo functionality, typically by calling #extend on the
0
+ # The following example executes a process on the remote server, opens
0
+ # a socket to somewhere, and then pipes data from that socket to the
0
+ # remote process' stdin stream:
0
+ # channel = ssh.open_channel do |ch|
0
+ # ch.exec "/some/process/that/wants/input" do |ch, success|
0
+ # abort "can't execute!" unless success
0
+ # io = TCPSocket.new(somewhere, port)
0
+ # io.extend(Net::SSH::BufferedIo)
0
+ # ch.send_data(io.read_available)
0
+ # ssh.stop_listening_to(io)
0
def listen_to(io, &callback)
0
listeners[io] = callback
0
@@ -225,8 +346,8 @@ module Net; module SSH; module Connection
0
- # Returns a reference to the Service::Forward service, which can be used
0
- # for forwarding ports over SSH.
0
+ # Returns a reference to the Net::SSH::Service::Forward service, which can
0
+ # be used for forwarding ports over SSH.
0
@forward ||= Service::Forward.new(self)
0
@@ -237,6 +358,10 @@ module Net; module SSH; module Connection
0
# raise ChannelOpenFailed if it is unable to open the channel for some
0
# reason. Otherwise, the channel will be opened and a confirmation message
0
+ # This is used by the Net::SSH::Service::Forward service to open a channel
0
+ # when a remote forwarded port receives a connection. However, you are
0
+ # welcome to register handlers for other channel types, as needed.
0
def on_open_channel(type, &block)
0
channel_open_handlers[type] = block
Comments
No one has commented yet.