Class: Net::SSH::Connection::Channel
- Inherits:
-
Object
- Object
- Net::SSH::Connection::Channel
- Defined in:
- lib/net/ssh/connection/channel.rb
Overview
The channel abstraction. Multiple “channels” can be multiplexed onto a single SSH channel, each operating independently and seemingly in parallel. This class represents a single such channel. Most operations performed with the Net::SSH library will involve using one or more channels.
Channels are intended to be used asynchronously. You request that one be opened (via Connection::Session#open_channel), and when it is opened, your callback is invoked. Then, you set various other callbacks on the newly opened channel, which are called in response to the corresponding events. Programming with Net::SSH works best if you think of your programs as state machines. Complex programs are best implemented as objects that wrap a channel. See Net::SCP and Net::SFTP for examples of how complex state machines can be built on top of the SSH protocol.
ssh.open_channel do |channel|
channel.exec("/invoke/some/command") do |ch, success|
abort "could not execute command" unless success
channel.on_data do |ch, data|
puts "got stdout: #{data}"
channel.send_data "something for stdin\n"
end
channel.on_extended_data do |ch, type, data|
puts "got stderr: #{data}"
end
channel.on_close do |ch|
puts "channel is closing!"
end
end
end
ssh.loop
Channels also have a basic hash-like interface, that allows programs to store arbitrary state information on a channel object. This helps simplify the writing of state machines, especially when you may be juggling multiple open channels at the same time.
Note that data sent across SSH channels are governed by maximum packet sizes and maximum window sizes. These details are managed internally by Net::SSH::Connection::Channel, so you may remain blissfully ignorant if you so desire, but you can always inspect the current maximums, as well as the remaining window size, using the reader attributes for those values.
Constant Summary collapse
- VALID_PTY_OPTIONS =
A hash of the valid PTY options (see #request_pty).
{ term: "xterm", chars_wide: 80, chars_high: 24, pixels_wide: 640, pixels_high: 480, modes: {} }
Constants included from Constants
Net::SSH::Connection::Constants::CHANNEL_CLOSE, Net::SSH::Connection::Constants::CHANNEL_DATA, Net::SSH::Connection::Constants::CHANNEL_EOF, Net::SSH::Connection::Constants::CHANNEL_EXTENDED_DATA, Net::SSH::Connection::Constants::CHANNEL_FAILURE, Net::SSH::Connection::Constants::CHANNEL_OPEN, Net::SSH::Connection::Constants::CHANNEL_OPEN_CONFIRMATION, Net::SSH::Connection::Constants::CHANNEL_OPEN_FAILURE, Net::SSH::Connection::Constants::CHANNEL_REQUEST, Net::SSH::Connection::Constants::CHANNEL_SUCCESS, Net::SSH::Connection::Constants::CHANNEL_WINDOW_ADJUST, Net::SSH::Connection::Constants::GLOBAL_REQUEST, Net::SSH::Connection::Constants::REQUEST_FAILURE, Net::SSH::Connection::Constants::REQUEST_SUCCESS
Instance Attribute Summary collapse
-
#connection ⇒ Object
readonly
The underlying Net::SSH::Connection::Session instance that supports this channel.
-
#local_id ⇒ Object
readonly
The local id for this channel, assigned by the Net::SSH::Connection::Session instance.
-
#local_maximum_packet_size ⇒ Object
readonly
The maximum packet size that the local host can receive.
-
#local_maximum_window_size ⇒ Object
readonly
The maximum amount of data that the local end of this channel can receive.
-
#local_window_size ⇒ Object
readonly
This is the remaining window size on the local end of this channel.
-
#output ⇒ Object
readonly
The output buffer for this channel.
-
#pending_requests ⇒ Object
readonly
The list of pending requests.
-
#properties ⇒ Object
readonly
A hash of properties for this channel.
-
#remote_id ⇒ Object
readonly
The remote id for this channel, assigned by the remote host.
-
#remote_maximum_packet_size ⇒ Object
readonly
The maximum packet size that the remote host can receive.
-
#remote_maximum_window_size ⇒ Object
readonly
The maximum amount of data that the remote end of this channel can receive.
-
#remote_window_size ⇒ Object
readonly
This is the remaining window size on the remote end of this channel.
-
#type ⇒ Object
readonly
The type of this channel, usually “session”.
Attributes included from Loggable
Instance Method Summary collapse
-
#[](name) ⇒ Object
A shortcut for accessing properties of the channel (see #properties).
-
#[]=(name, value) ⇒ Object
A shortcut for setting properties of the channel (see #properties).
-
#active? ⇒ Boolean
Returns true if the channel exists in the channel list of the session, and false otherwise.
-
#close ⇒ Object
Requests that the channel be closed.
-
#closing? ⇒ Boolean
True if close() has been called; NOTE: if the channel has data waiting to be sent then the channel will close after all the data is sent.
-
#do_close ⇒ Object
Invokes the #on_close callback when the server closes a channel.
-
#do_data(data) ⇒ Object
Invokes the #on_data callback when the server sends data to the channel.
-
#do_eof ⇒ Object
Invokes the #on_eof callback when the server indicates that no further data is forthcoming.
-
#do_extended_data(type, data) ⇒ Object
Invokes the #on_extended_data callback when the server sends extended data to the channel.
-
#do_failure ⇒ Object
Invokes the next pending request callback with
false
as the second argument. -
#do_open_confirmation(remote_id, max_window, max_packet) ⇒ Object
Invoked when the server confirms that a channel has been opened.
-
#do_open_failed(reason_code, description) ⇒ Object
Invoked when the server failed to open the channel.
-
#do_request(request, want_reply, data) ⇒ Object
Invoked when the server sends a channel request.
-
#do_success ⇒ Object
Invokes the next pending request callback with
true
as the second argument. -
#do_window_adjust(bytes) ⇒ Object
Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and causes the remote window size to be adjusted upwards by the given number of bytes.
-
#enqueue_pending_output ⇒ Object
Enqueues pending output at the connection as CHANNEL_DATA packets.
-
#env(variable_name, variable_value, &block) ⇒ Object
Syntactic sugar for setting an environment variable in the remote process’ environment.
-
#eof! ⇒ Object
Tells the remote end of the channel that no more data is forthcoming from this end of the channel.
-
#eof? ⇒ Boolean
Returns true if the local end of the channel has declared that no more data is forthcoming (see #eof!).
-
#exec(command, &block) ⇒ Object
Syntactic sugar for executing a command.
-
#initialize(connection, type, local_id, max_pkt_size = 0x8000, max_win_size = 0x20000, &on_confirm_open) ⇒ Channel
constructor
Instantiates a new channel on the given connection, of the given type, and with the given id.
-
#local_closed? ⇒ Boolean
True if we have sent CHANNEL_CLOSE to the remote server.
-
#on_close(&block) ⇒ Object
Registers a callback to be invoked when the server acknowledges that a channel is closed.
-
#on_data(&block) ⇒ Object
Registers a callback to be invoked when data packets are received by the channel.
-
#on_eof(&block) ⇒ Object
Registers a callback to be invoked when the server indicates that no more data will be sent to the channel (although the channel can still send data to the server).
-
#on_extended_data(&block) ⇒ Object
Registers a callback to be invoked when extended data packets are received by the channel.
-
#on_open_failed(&block) ⇒ Object
Registers a callback to be invoked when the server was unable to open the requested channel.
-
#on_process(&block) ⇒ Object
Registers a callback to be invoked for each pass of the event loop for this channel.
-
#on_request(type, &block) ⇒ Object
Registers a callback to be invoked when a channel request of the given type is received.
-
#process ⇒ Object
If an #on_process handler has been set up, this will cause it to be invoked (passing the channel itself as an argument).
- #remote_closed! ⇒ Object
- #remote_closed? ⇒ Boolean
-
#request_pty(opts = {}, &block) ⇒ Object
Requests that a pseudo-tty (or “pty”) be made available for this channel.
-
#send_channel_request(request_name, *data, &callback) ⇒ Object
Sends a new channel request with the given name.
-
#send_data(data) ⇒ Object
Sends data to the channel’s remote endpoint.
-
#subsystem(subsystem, &block) ⇒ Object
Syntactic sugar for requesting that a subsystem be started.
-
#wait ⇒ Object
Runs the SSH event loop until the channel is no longer active.
Methods included from Loggable
#debug, #error, #fatal, #info, #lwarn
Constructor Details
#initialize(connection, type, local_id, max_pkt_size = 0x8000, max_win_size = 0x20000, &on_confirm_open) ⇒ Channel
Instantiates a new channel on the given connection, of the given type, and with the given id. If a block is given, it will be remembered until the channel is confirmed open by the server, and will be invoked at that time (see #do_open_confirmation).
This also sets the default maximum packet size and maximum window size.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/net/ssh/connection/channel.rb', line 112 def initialize(connection, type, local_id, max_pkt_size = 0x8000, max_win_size = 0x20000, &on_confirm_open) self.logger = connection.logger @connection = connection @type = type @local_id = local_id @local_maximum_packet_size = max_pkt_size @local_window_size = @local_maximum_window_size = max_win_size @on_confirm_open = on_confirm_open @output = Buffer.new @properties = {} @pending_requests = [] @on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil @on_request = {} @closing = @eof = @sent_eof = @local_closed = @remote_closed = false end |
Instance Attribute Details
#connection ⇒ Object (readonly)
The underlying Net::SSH::Connection::Session instance that supports this channel.
68 69 70 |
# File 'lib/net/ssh/connection/channel.rb', line 68 def connection @connection end |
#local_id ⇒ Object (readonly)
The local id for this channel, assigned by the Net::SSH::Connection::Session instance.
59 60 61 |
# File 'lib/net/ssh/connection/channel.rb', line 59 def local_id @local_id end |
#local_maximum_packet_size ⇒ Object (readonly)
The maximum packet size that the local host can receive.
71 72 73 |
# File 'lib/net/ssh/connection/channel.rb', line 71 def local_maximum_packet_size @local_maximum_packet_size end |
#local_maximum_window_size ⇒ Object (readonly)
The maximum amount of data that the local end of this channel can receive. This is a total, not per-packet.
75 76 77 |
# File 'lib/net/ssh/connection/channel.rb', line 75 def local_maximum_window_size @local_maximum_window_size end |
#local_window_size ⇒ Object (readonly)
This is the remaining window size on the local end of this channel. When this reaches zero, no more data can be received.
86 87 88 |
# File 'lib/net/ssh/connection/channel.rb', line 86 def local_window_size @local_window_size end |
#output ⇒ Object (readonly)
The output buffer for this channel. Data written to the channel is enqueued here, to be written as CHANNEL_DATA packets during each pass of the event loop. See Connection::Session#process and #enqueue_pending_output.
99 100 101 |
# File 'lib/net/ssh/connection/channel.rb', line 99 def output @output end |
#pending_requests ⇒ Object (readonly)
The list of pending requests. Each time a request is sent which requires a reply, the corresponding callback is pushed onto this queue. As responses arrive, they are shifted off the front and handled.
104 105 106 |
# File 'lib/net/ssh/connection/channel.rb', line 104 def pending_requests @pending_requests end |
#properties ⇒ Object (readonly)
A hash of properties for this channel. These can be used to store state information about this channel. See also #[] and #[]=.
94 95 96 |
# File 'lib/net/ssh/connection/channel.rb', line 94 def properties @properties end |
#remote_id ⇒ Object (readonly)
The remote id for this channel, assigned by the remote host.
62 63 64 |
# File 'lib/net/ssh/connection/channel.rb', line 62 def remote_id @remote_id end |
#remote_maximum_packet_size ⇒ Object (readonly)
The maximum packet size that the remote host can receive.
78 79 80 |
# File 'lib/net/ssh/connection/channel.rb', line 78 def remote_maximum_packet_size @remote_maximum_packet_size end |
#remote_maximum_window_size ⇒ Object (readonly)
The maximum amount of data that the remote end of this channel can receive. This is a total, not per-packet.
82 83 84 |
# File 'lib/net/ssh/connection/channel.rb', line 82 def remote_maximum_window_size @remote_maximum_window_size end |
#remote_window_size ⇒ Object (readonly)
This is the remaining window size on the remote end of this channel. When this reaches zero, no more data can be sent.
90 91 92 |
# File 'lib/net/ssh/connection/channel.rb', line 90 def remote_window_size @remote_window_size end |
#type ⇒ Object (readonly)
The type of this channel, usually “session”.
65 66 67 |
# File 'lib/net/ssh/connection/channel.rb', line 65 def type @type end |
Instance Method Details
#[](name) ⇒ Object
A shortcut for accessing properties of the channel (see #properties).
135 136 137 |
# File 'lib/net/ssh/connection/channel.rb', line 135 def [](name) @properties[name] end |
#[]=(name, value) ⇒ Object
A shortcut for setting properties of the channel (see #properties).
140 141 142 |
# File 'lib/net/ssh/connection/channel.rb', line 140 def []=(name, value) @properties[name] = value end |
#active? ⇒ Boolean
Returns true if the channel exists in the channel list of the session, and false otherwise. This can be used to determine whether a channel has been closed or not.
ssh.loop { channel.active? }
262 263 264 |
# File 'lib/net/ssh/connection/channel.rb', line 262 def active? connection.channels.key?(local_id) end |
#close ⇒ Object
Requests that the channel be closed. It only marks the channel to be closed the CHANNEL_CLOSE message will be sent from event loop
299 300 301 302 303 |
# File 'lib/net/ssh/connection/channel.rb', line 299 def close return if @closing @closing = true end |
#closing? ⇒ Boolean
True if close() has been called; NOTE: if the channel has data waiting to be sent then the channel will close after all the data is sent. See closed?() to determine if we have actually sent CHANNEL_CLOSE to server. This may be true for awhile before closed? returns true if we are still sending buffered output to server.
280 281 282 |
# File 'lib/net/ssh/connection/channel.rb', line 280 def closing? @closing end |
#do_close ⇒ Object
Invokes the #on_close callback when the server closes a channel. The channel is the only argument.
613 614 615 |
# File 'lib/net/ssh/connection/channel.rb', line 613 def do_close @on_close.call(self) if @on_close end |
#do_data(data) ⇒ Object
Invokes the #on_data callback when the server sends data to the channel. This will reduce the available window size on the local end, but does not actually throttle requests that come in illegally when the window size is too small. The callback is invoked with the channel as the first argument, and the data as the second.
590 591 592 593 |
# File 'lib/net/ssh/connection/channel.rb', line 590 def do_data(data) # :nodoc: update_local_window_size(data.length) @on_data.call(self, data) if @on_data end |
#do_eof ⇒ Object
Invokes the #on_eof callback when the server indicates that no further data is forthcoming. The callback is invoked with the channel as the argument.
607 608 609 |
# File 'lib/net/ssh/connection/channel.rb', line 607 def do_eof @on_eof.call(self) if @on_eof end |
#do_extended_data(type, data) ⇒ Object
Invokes the #on_extended_data callback when the server sends extended data to the channel. This will reduce the available window size on the local end. The callback is invoked with the channel, type, and data.
599 600 601 602 |
# File 'lib/net/ssh/connection/channel.rb', line 599 def do_extended_data(type, data) update_local_window_size(data.length) @on_extended_data.call(self, type, data) if @on_extended_data end |
#do_failure ⇒ Object
Invokes the next pending request callback with false
as the second argument.
619 620 621 622 623 624 625 |
# File 'lib/net/ssh/connection/channel.rb', line 619 def do_failure if callback = pending_requests.shift callback.call(self, false) else error { "channel failure received with no pending request to handle it (bug?)" } end end |
#do_open_confirmation(remote_id, max_window, max_packet) ⇒ Object
Invoked when the server confirms that a channel has been opened. The remote_id is the id of the channel as assigned by the remote host, and max_window and max_packet are the maximum window and maximum packet sizes, respectively. If an open-confirmation callback was given when the channel was created, it is invoked at this time with the channel itself as the sole argument.
530 531 532 533 534 535 536 537 538 |
# File 'lib/net/ssh/connection/channel.rb', line 530 def do_open_confirmation(remote_id, max_window, max_packet) # :nodoc: @remote_id = remote_id @remote_window_size = @remote_maximum_window_size = max_window @remote_maximum_packet_size = max_packet connection.forward.agent(self) if connection.[:forward_agent] && type == "session" forward_local_env(connection.[:send_env]) if connection.[:send_env] set_remote_env(connection.[:set_env]) if connection.[:set_env] @on_confirm_open.call(self) if @on_confirm_open end |
#do_open_failed(reason_code, description) ⇒ Object
Invoked when the server failed to open the channel. If an #on_open_failed callback was specified, it will be invoked with the channel, reason code, and description as arguments. Otherwise, a ChannelOpenFailed exception will be raised.
544 545 546 547 548 549 550 |
# File 'lib/net/ssh/connection/channel.rb', line 544 def do_open_failed(reason_code, description) if @on_open_failed @on_open_failed.call(self, reason_code, description) else raise ChannelOpenFailed.new(reason_code, description) end end |
#do_request(request, want_reply, data) ⇒ Object
Invoked when the server sends a channel request. If any #on_request callback has been registered for the specific type of this request, it is invoked. If want_reply
is true, a packet will be sent of either CHANNEL_SUCCESS or CHANNEL_FAILURE type. If there was no callback to handle the request, CHANNEL_FAILURE will be sent. Otherwise, CHANNEL_SUCCESS, unless the callback raised ChannelRequestFailed. The callback should accept the channel as the first argument, and the request-specific data as the second.
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 |
# File 'lib/net/ssh/connection/channel.rb', line 569 def do_request(request, want_reply, data) # :nodoc: result = true begin callback = @on_request[request] or raise ChannelRequestFailed callback.call(self, data) rescue ChannelRequestFailed result = false end if want_reply msg = Buffer.from(:byte, result ? CHANNEL_SUCCESS : CHANNEL_FAILURE, :long, remote_id) connection.(msg) end end |
#do_success ⇒ Object
Invokes the next pending request callback with true
as the second argument.
629 630 631 632 633 634 635 |
# File 'lib/net/ssh/connection/channel.rb', line 629 def do_success if callback = pending_requests.shift callback.call(self, true) else error { "channel success received with no pending request to handle it (bug?)" } end end |
#do_window_adjust(bytes) ⇒ Object
Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and causes the remote window size to be adjusted upwards by the given number of bytes. This has the effect of allowing more data to be sent from the local end to the remote end of the channel.
556 557 558 559 |
# File 'lib/net/ssh/connection/channel.rb', line 556 def do_window_adjust(bytes) # :nodoc: @remote_maximum_window_size += bytes @remote_window_size += bytes end |
#enqueue_pending_output ⇒ Object
Enqueues pending output at the connection as CHANNEL_DATA packets. This does nothing if the channel has not yet been confirmed open (see #do_open_confirmation). This is called automatically by #process, which is called from the event loop (Connection::Session#process). You will generally not need to invoke it directly.
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
# File 'lib/net/ssh/connection/channel.rb', line 506 def enqueue_pending_output # :nodoc: return unless remote_id while output.length > 0 length = output.length length = remote_window_size if length > remote_window_size length = remote_maximum_packet_size if length > remote_maximum_packet_size if length > 0 connection.(Buffer.from(:byte, CHANNEL_DATA, :long, remote_id, :string, output.read(length))) output.consume! @remote_window_size -= length else break end end end |
#env(variable_name, variable_value, &block) ⇒ Object
Syntactic sugar for setting an environment variable in the remote process’ environment. Note that for security reasons, the server may refuse to set certain environment variables, or all, at the server’s discretion. If you are connecting to an OpenSSH server, you will need to update the AcceptEnv setting in the sshd_config to include the environment variables you want to send.
channel.env "PATH", "/usr/local/bin"
189 190 191 |
# File 'lib/net/ssh/connection/channel.rb', line 189 def env(variable_name, variable_value, &block) send_channel_request("env", :string, variable_name, :string, variable_value, &block) end |
#eof! ⇒ Object
Tells the remote end of the channel that no more data is forthcoming from this end of the channel. The remote end may still send data. The CHANNEL_EOF packet will be sent once the output buffer is empty.
315 316 317 318 319 |
# File 'lib/net/ssh/connection/channel.rb', line 315 def eof! return if eof? @eof = true end |
#eof? ⇒ Boolean
Returns true if the local end of the channel has declared that no more data is forthcoming (see #eof!). Trying to send data via #send_data when this is true will result in an exception being raised.
308 309 310 |
# File 'lib/net/ssh/connection/channel.rb', line 308 def eof? @eof end |
#exec(command, &block) ⇒ Object
Syntactic sugar for executing a command. Sends a channel request asking that the given command be invoked. If the block is given, it will be called when the server responds. The first parameter will be the channel, and the second will be true or false, indicating whether the request succeeded or not. In this case, success means that the command is being executed, not that it has completed, and failure means that the command altogether failed to be executed.
channel.exec "ls -l /home" do |ch, success|
if success
puts "command has begun executing..."
# this is a good place to hang callbacks like #on_data...
else
puts "alas! the command could not be invoked!"
end
end
160 161 162 |
# File 'lib/net/ssh/connection/channel.rb', line 160 def exec(command, &block) send_channel_request("exec", :string, command, &block) end |
#local_closed? ⇒ Boolean
True if we have sent CHANNEL_CLOSE to the remote server.
285 286 287 |
# File 'lib/net/ssh/connection/channel.rb', line 285 def local_closed? @local_closed end |
#on_close(&block) ⇒ Object
Registers a callback to be invoked when the server acknowledges that a channel is closed. This is invoked with the channel as the sole argument.
channel.on_close do |ch|
puts "remote end is closing!"
end
403 404 405 406 |
# File 'lib/net/ssh/connection/channel.rb', line 403 def on_close(&block) old, @on_close = @on_close, block old end |
#on_data(&block) ⇒ Object
Registers a callback to be invoked when data packets are received by the channel. The callback is called with the channel as the first argument, and the data as the second.
channel.on_data do |ch, data|
puts "got data: #{data.inspect}"
end
Data received this way is typically the data written by the remote process to its stdout
stream.
350 351 352 353 |
# File 'lib/net/ssh/connection/channel.rb', line 350 def on_data(&block) old, @on_data = @on_data, block old end |
#on_eof(&block) ⇒ Object
Registers a callback to be invoked when the server indicates that no more data will be sent to the channel (although the channel can still send data to the server). The channel is the sole argument to the callback.
channel.on_eof do |ch|
puts "remote end is done sending data"
end
415 416 417 418 |
# File 'lib/net/ssh/connection/channel.rb', line 415 def on_eof(&block) old, @on_eof = @on_eof, block old end |
#on_extended_data(&block) ⇒ Object
Registers a callback to be invoked when extended data packets are received by the channel. The callback is called with the channel as the first argument, the data type (as an integer) as the second, and the data as the third. Extended data is almost exclusively used to send stderr
data (type
== 1). Other extended data types are not defined by the SSH protocol.
channel.on_extended_data do |ch, type, data|
puts "got stderr: #{data.inspect}"
end
365 366 367 368 |
# File 'lib/net/ssh/connection/channel.rb', line 365 def on_extended_data(&block) old, @on_extended_data = @on_extended_data, block old end |
#on_open_failed(&block) ⇒ Object
Registers a callback to be invoked when the server was unable to open the requested channel. The channel itself will be passed to the block, along with the integer “reason code” for the failure, and a textual description of the failure from the server.
channel = session.open_channel do |ch|
# ..
end
channel.on_open_failed { |ch, code, desc| ... }
430 431 432 433 |
# File 'lib/net/ssh/connection/channel.rb', line 430 def on_open_failed(&block) old, @on_open_failed = @on_open_failed, block old end |
#on_process(&block) ⇒ Object
Registers a callback to be invoked for each pass of the event loop for this channel. There are no guarantees on timeliness in the event loop, but it will be called roughly once for each packet received by the connection (not the channel). This callback is invoked with the channel as the sole argument.
Here’s an example that accumulates the channel data into a variable on the channel itself, and displays individual lines in the input one at a time when the channel is processed:
channel[:data] = ""
channel.on_data do |ch, data|
channel[:data] << data
end
channel.on_process do |ch|
if channel[:data] =~ /^.*?\n/
puts $&
channel[:data] = $'
end
end
392 393 394 395 |
# File 'lib/net/ssh/connection/channel.rb', line 392 def on_process(&block) old, @on_process = @on_process, block old end |
#on_request(type, &block) ⇒ Object
Registers a callback to be invoked when a channel request of the given type is received. The callback will receive the channel as the first argument, and the associated (unparsed) data as the second. The data will be a Net::SSH::Buffer that you will need to parse, yourself, according to the kind of request you are watching.
By default, if the request wants a reply, Net::SSH will send a CHANNEL_SUCCESS response for any request that was handled by a registered callback, and CHANNEL_FAILURE for any that wasn’t, but if you want your registered callback to result in a CHANNEL_FAILURE response, just raise Net::SSH::ChannelRequestFailed.
Some common channel requests that your programs might want to listen for are:
-
“exit-status” : the exit status of the remote process will be reported as a long integer in the data buffer, which you can grab via data.read_long.
-
“exit-signal” : if the remote process died as a result of a signal being sent to it, the signal will be reported as a string in the data, via data.read_string. (Not all SSH servers support this channel request type.)
channel.on_request "exit-status" do |ch, data| puts "process terminated with exit status: #{data.read_long}" end
461 462 463 464 |
# File 'lib/net/ssh/connection/channel.rb', line 461 def on_request(type, &block) old, @on_request[type] = @on_request[type], block old end |
#process ⇒ Object
If an #on_process handler has been set up, this will cause it to be invoked (passing the channel itself as an argument). It also causes all pending output to be enqueued as CHANNEL_DATA packets (see #enqueue_pending_output).
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/net/ssh/connection/channel.rb', line 324 def process @on_process.call(self) if @on_process enqueue_pending_output if @eof and not @sent_eof and output.empty? and remote_id and not @local_closed connection.(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id)) @sent_eof = true end if @closing and not @local_closed and output.empty? and remote_id connection.(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id)) @local_closed = true connection.cleanup_channel(self) end end |
#remote_closed! ⇒ Object
293 294 295 |
# File 'lib/net/ssh/connection/channel.rb', line 293 def remote_closed! @remote_closed = true end |
#remote_closed? ⇒ Boolean
289 290 291 |
# File 'lib/net/ssh/connection/channel.rb', line 289 def remote_closed? @remote_closed end |
#request_pty(opts = {}, &block) ⇒ Object
Requests that a pseudo-tty (or “pty”) be made available for this channel. This is useful when you want to invoke and interact with some kind of screen-based program (e.g., vim, or some menuing system).
Note, that without a pty some programs (e.g. sudo, or subversion) on some systems, will not be able to run interactively, and will error instead of prompt if they ever need some user interaction.
Note, too, that when a pty is requested, user’s shell configuration scripts (.bashrc and such) are not run by default, whereas they are run when a pty is not present.
channel.request_pty do |ch, success|
if success
puts "pty successfully obtained"
else
puts "could not obtain pty"
end
end
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/net/ssh/connection/channel.rb', line 220 def request_pty(opts = {}, &block) extra = opts.keys - VALID_PTY_OPTIONS.keys raise ArgumentError, "invalid option(s) to request_pty: #{extra.inspect}" if extra.any? opts = VALID_PTY_OPTIONS.merge(opts) modes = opts[:modes].inject(Buffer.new) do |memo, (mode, data)| memo.write_byte(mode).write_long(data) end # mark the end of the mode opcode list with a 0 byte modes.write_byte(0) send_channel_request("pty-req", :string, opts[:term], :long, opts[:chars_wide], :long, opts[:chars_high], :long, opts[:pixels_wide], :long, opts[:pixels_high], :string, modes.to_s, &block) end |
#send_channel_request(request_name, *data, &callback) ⇒ Object
Sends a new channel request with the given name. The extra data
parameter must either be empty, or consist of an even number of arguments. See Net::SSH::Buffer.from for a description of their format. If a block is given, it is registered as a callback for a pending request, and the packet will be flagged so that the server knows a reply is required. If no block is given, the server will send no response to this request. Responses, where required, will cause the callback to be invoked with the channel as the first argument, and either true or false as the second, depending on whether the request succeeded or not. The meaning of “success” and “failure” in this context is dependent on the specific request that was sent.
channel.send_channel_request "shell" do |ch, success|
if success
puts "user shell started successfully"
else
puts "could not start user shell"
end
end
Most channel requests you’ll want to send are already wrapped in more convenient helper methods (see #exec and #subsystem).
488 489 490 491 492 493 494 495 496 497 |
# File 'lib/net/ssh/connection/channel.rb', line 488 def send_channel_request(request_name, *data, &callback) info { "sending channel request #{request_name.inspect}" } fail "Channel open not yet confirmed, please call send_channel_request(or exec) from block of open_channel" unless remote_id msg = Buffer.from(:byte, CHANNEL_REQUEST, :long, remote_id, :string, request_name, :bool, !callback.nil?, *data) connection.(msg) pending_requests << callback if callback end |
#send_data(data) ⇒ Object
Sends data to the channel’s remote endpoint. This usually has the effect of sending the given string to the remote process’ stdin stream. Note that it does not immediately send the data across the channel, but instead merely appends the given data to the channel’s output buffer, preparatory to being packaged up and sent out the next time the connection is accepting data. (A connection might not be accepting data if, for instance, it has filled its data window and has not yet been resized by the remote end-point.)
This will raise an exception if the channel has previously declared that no more data will be sent (see #eof!).
channel.send_data("the password\n")
251 252 253 254 255 |
# File 'lib/net/ssh/connection/channel.rb', line 251 def send_data(data) raise EOFError, "cannot send data if channel has declared eof" if eof? output.append(data.to_s) end |
#subsystem(subsystem, &block) ⇒ Object
Syntactic sugar for requesting that a subsystem be started. Subsystems are a way for other protocols (like SFTP) to be run, using SSH as the transport. Generally, you’ll never need to call this directly unless you are the implementor of something that consumes an SSH subsystem, like SFTP.
channel.subsystem("sftp") do |ch, success|
if success
puts "subsystem successfully started"
else
puts "subsystem could not be started"
end
end
177 178 179 |
# File 'lib/net/ssh/connection/channel.rb', line 177 def subsystem(subsystem, &block) send_channel_request("subsystem", :string, subsystem, &block) end |
#wait ⇒ Object
Runs the SSH event loop until the channel is no longer active. This is handy for blocking while you wait for some channel to finish.
channel.exec("grep ...") { ... }
channel.wait
271 272 273 |
# File 'lib/net/ssh/connection/channel.rb', line 271 def wait connection.loop { active? } end |