net-ssh 5.0.0.beta1 → 5.0.0.beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.rubocop_todo.yml +98 -258
  5. data/CHANGES.txt +8 -0
  6. data/Gemfile +1 -3
  7. data/Rakefile +37 -39
  8. data/lib/net/ssh.rb +26 -25
  9. data/lib/net/ssh/authentication/agent.rb +228 -225
  10. data/lib/net/ssh/authentication/certificate.rb +166 -164
  11. data/lib/net/ssh/authentication/constants.rb +17 -14
  12. data/lib/net/ssh/authentication/ed25519.rb +107 -104
  13. data/lib/net/ssh/authentication/ed25519_loader.rb +32 -28
  14. data/lib/net/ssh/authentication/key_manager.rb +5 -3
  15. data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
  16. data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
  17. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -4
  18. data/lib/net/ssh/authentication/methods/none.rb +10 -10
  19. data/lib/net/ssh/authentication/methods/password.rb +13 -13
  20. data/lib/net/ssh/authentication/methods/publickey.rb +54 -55
  21. data/lib/net/ssh/authentication/pageant.rb +468 -465
  22. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +44 -0
  23. data/lib/net/ssh/authentication/session.rb +127 -123
  24. data/lib/net/ssh/buffer.rb +305 -303
  25. data/lib/net/ssh/buffered_io.rb +163 -162
  26. data/lib/net/ssh/config.rb +230 -227
  27. data/lib/net/ssh/connection/channel.rb +659 -654
  28. data/lib/net/ssh/connection/constants.rb +30 -26
  29. data/lib/net/ssh/connection/event_loop.rb +108 -104
  30. data/lib/net/ssh/connection/keepalive.rb +54 -50
  31. data/lib/net/ssh/connection/session.rb +677 -678
  32. data/lib/net/ssh/connection/term.rb +180 -176
  33. data/lib/net/ssh/errors.rb +101 -99
  34. data/lib/net/ssh/key_factory.rb +108 -108
  35. data/lib/net/ssh/known_hosts.rb +148 -154
  36. data/lib/net/ssh/loggable.rb +56 -54
  37. data/lib/net/ssh/packet.rb +82 -78
  38. data/lib/net/ssh/prompt.rb +55 -53
  39. data/lib/net/ssh/proxy/command.rb +103 -102
  40. data/lib/net/ssh/proxy/errors.rb +12 -8
  41. data/lib/net/ssh/proxy/http.rb +92 -91
  42. data/lib/net/ssh/proxy/https.rb +42 -39
  43. data/lib/net/ssh/proxy/jump.rb +50 -47
  44. data/lib/net/ssh/proxy/socks4.rb +0 -2
  45. data/lib/net/ssh/proxy/socks5.rb +11 -11
  46. data/lib/net/ssh/ruby_compat.rb +1 -0
  47. data/lib/net/ssh/service/forward.rb +364 -362
  48. data/lib/net/ssh/test.rb +85 -83
  49. data/lib/net/ssh/test/channel.rb +146 -142
  50. data/lib/net/ssh/test/extensions.rb +148 -146
  51. data/lib/net/ssh/test/kex.rb +35 -31
  52. data/lib/net/ssh/test/local_packet.rb +48 -44
  53. data/lib/net/ssh/test/packet.rb +87 -84
  54. data/lib/net/ssh/test/remote_packet.rb +35 -31
  55. data/lib/net/ssh/test/script.rb +173 -171
  56. data/lib/net/ssh/test/socket.rb +59 -55
  57. data/lib/net/ssh/transport/algorithms.rb +413 -412
  58. data/lib/net/ssh/transport/cipher_factory.rb +108 -105
  59. data/lib/net/ssh/transport/constants.rb +35 -31
  60. data/lib/net/ssh/transport/ctr.rb +1 -1
  61. data/lib/net/ssh/transport/hmac.rb +1 -1
  62. data/lib/net/ssh/transport/hmac/abstract.rb +67 -64
  63. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +1 -1
  64. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +1 -1
  65. data/lib/net/ssh/transport/identity_cipher.rb +55 -51
  66. data/lib/net/ssh/transport/kex.rb +2 -4
  67. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +47 -40
  68. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +201 -197
  69. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -56
  70. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +94 -87
  71. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +17 -10
  72. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +17 -10
  73. data/lib/net/ssh/transport/key_expander.rb +29 -25
  74. data/lib/net/ssh/transport/openssl.rb +17 -30
  75. data/lib/net/ssh/transport/packet_stream.rb +193 -192
  76. data/lib/net/ssh/transport/server_version.rb +64 -66
  77. data/lib/net/ssh/transport/session.rb +286 -284
  78. data/lib/net/ssh/transport/state.rb +198 -196
  79. data/lib/net/ssh/verifiers/lenient.rb +29 -25
  80. data/lib/net/ssh/verifiers/null.rb +13 -9
  81. data/lib/net/ssh/verifiers/secure.rb +45 -45
  82. data/lib/net/ssh/verifiers/strict.rb +20 -16
  83. data/lib/net/ssh/version.rb +55 -53
  84. data/net-ssh.gemspec +4 -4
  85. data/support/ssh_tunnel_bug.rb +2 -2
  86. metadata +25 -24
  87. metadata.gz.sig +0 -0
@@ -2,671 +2,676 @@ require 'net/ssh/loggable'
2
2
  require 'net/ssh/connection/constants'
3
3
  require 'net/ssh/connection/term'
4
4
 
5
- module Net; module SSH; module Connection
6
-
7
- # The channel abstraction. Multiple "channels" can be multiplexed onto a
8
- # single SSH channel, each operating independently and seemingly in parallel.
9
- # This class represents a single such channel. Most operations performed
10
- # with the Net::SSH library will involve using one or more channels.
11
- #
12
- # Channels are intended to be used asynchronously. You request that one be
13
- # opened (via Connection::Session#open_channel), and when it is opened, your
14
- # callback is invoked. Then, you set various other callbacks on the newly
15
- # opened channel, which are called in response to the corresponding events.
16
- # Programming with Net::SSH works best if you think of your programs as
17
- # state machines. Complex programs are best implemented as objects that
18
- # wrap a channel. See Net::SCP and Net::SFTP for examples of how complex
19
- # state machines can be built on top of the SSH protocol.
20
- #
21
- # ssh.open_channel do |channel|
22
- # channel.exec("/invoke/some/command") do |ch, success|
23
- # abort "could not execute command" unless success
24
- #
25
- # channel.on_data do |ch, data|
26
- # puts "got stdout: #{data}"
27
- # channel.send_data "something for stdin\n"
28
- # end
29
- #
30
- # channel.on_extended_data do |ch, type, data|
31
- # puts "got stderr: #{data}"
32
- # end
33
- #
34
- # channel.on_close do |ch|
35
- # puts "channel is closing!"
36
- # end
37
- # end
38
- # end
39
- #
40
- # ssh.loop
41
- #
42
- # Channels also have a basic hash-like interface, that allows programs to
43
- # store arbitrary state information on a channel object. This helps simplify
44
- # the writing of state machines, especially when you may be juggling
45
- # multiple open channels at the same time.
46
- #
47
- # Note that data sent across SSH channels are governed by maximum packet
48
- # sizes and maximum window sizes. These details are managed internally
49
- # by Net::SSH::Connection::Channel, so you may remain blissfully ignorant
50
- # if you so desire, but you can always inspect the current maximums, as
51
- # well as the remaining window size, using the reader attributes for those
52
- # values.
53
- class Channel
54
- include Constants, Loggable
55
-
56
- # The local id for this channel, assigned by the Net::SSH::Connection::Session instance.
57
- attr_reader :local_id
58
-
59
- # The remote id for this channel, assigned by the remote host.
60
- attr_reader :remote_id
61
-
62
- # The type of this channel, usually "session".
63
- attr_reader :type
64
-
65
- # The underlying Net::SSH::Connection::Session instance that supports this channel.
66
- attr_reader :connection
67
-
68
- # The maximum packet size that the local host can receive.
69
- attr_reader :local_maximum_packet_size
70
-
71
- # The maximum amount of data that the local end of this channel can
72
- # receive. This is a total, not per-packet.
73
- attr_reader :local_maximum_window_size
74
-
75
- # The maximum packet size that the remote host can receive.
76
- attr_reader :remote_maximum_packet_size
77
-
78
- # The maximum amount of data that the remote end of this channel can
79
- # receive. This is a total, not per-packet.
80
- attr_reader :remote_maximum_window_size
81
-
82
- # This is the remaining window size on the local end of this channel. When
83
- # this reaches zero, no more data can be received.
84
- attr_reader :local_window_size
85
-
86
- # This is the remaining window size on the remote end of this channel. When
87
- # this reaches zero, no more data can be sent.
88
- attr_reader :remote_window_size
89
-
90
- # A hash of properties for this channel. These can be used to store state
91
- # information about this channel. See also #[] and #[]=.
92
- attr_reader :properties
93
-
94
- # The output buffer for this channel. Data written to the channel is
95
- # enqueued here, to be written as CHANNEL_DATA packets during each pass of
96
- # the event loop. See Connection::Session#process and #enqueue_pending_output.
97
- attr_reader :output #:nodoc:
98
-
99
- # The list of pending requests. Each time a request is sent which requires
100
- # a reply, the corresponding callback is pushed onto this queue. As responses
101
- # arrive, they are shifted off the front and handled.
102
- attr_reader :pending_requests #:nodoc:
103
-
104
- # Instantiates a new channel on the given connection, of the given type,
105
- # and with the given id. If a block is given, it will be remembered until
106
- # the channel is confirmed open by the server, and will be invoked at
107
- # that time (see #do_open_confirmation).
108
- #
109
- # This also sets the default maximum packet size and maximum window size.
110
- def initialize(connection, type, local_id, max_pkt_size = 0x8000, max_win_size = 0x20000, &on_confirm_open)
111
- self.logger = connection.logger
112
-
113
- @connection = connection
114
- @type = type
115
- @local_id = local_id
116
-
117
- @local_maximum_packet_size = max_pkt_size
118
- @local_window_size = @local_maximum_window_size = max_win_size
119
-
120
- @on_confirm_open = on_confirm_open
121
-
122
- @output = Buffer.new
123
-
124
- @properties = {}
125
-
126
- @pending_requests = []
127
- @on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil
128
- @on_request = {}
129
- @closing = @eof = @sent_eof = @local_closed = @remote_closed = false
130
- end
131
-
132
- # A shortcut for accessing properties of the channel (see #properties).
133
- def [](name)
134
- @properties[name]
135
- end
136
-
137
- # A shortcut for setting properties of the channel (see #properties).
138
- def []=(name, value)
139
- @properties[name] = value
140
- end
141
-
142
- # Syntactic sugar for executing a command. Sends a channel request asking
143
- # that the given command be invoked. If the block is given, it will be
144
- # called when the server responds. The first parameter will be the
145
- # channel, and the second will be true or false, indicating whether the
146
- # request succeeded or not. In this case, success means that the command
147
- # is being executed, not that it has completed, and failure means that the
148
- # command altogether failed to be executed.
149
- #
150
- # channel.exec "ls -l /home" do |ch, success|
151
- # if success
152
- # puts "command has begun executing..."
153
- # # this is a good place to hang callbacks like #on_data...
154
- # else
155
- # puts "alas! the command could not be invoked!"
156
- # end
157
- # end
158
- def exec(command, &block)
159
- send_channel_request("exec", :string, command, &block)
160
- end
161
-
162
- # Syntactic sugar for requesting that a subsystem be started. Subsystems
163
- # are a way for other protocols (like SFTP) to be run, using SSH as
164
- # the transport. Generally, you'll never need to call this directly unless
165
- # you are the implementor of something that consumes an SSH subsystem, like
166
- # SFTP.
167
- #
168
- # channel.subsystem("sftp") do |ch, success|
169
- # if success
170
- # puts "subsystem successfully started"
171
- # else
172
- # puts "subsystem could not be started"
173
- # end
174
- # end
175
- def subsystem(subsystem, &block)
176
- send_channel_request("subsystem", :string, subsystem, &block)
177
- end
178
-
179
- # Syntactic sugar for setting an environment variable in the remote
180
- # process' environment. Note that for security reasons, the server may
181
- # refuse to set certain environment variables, or all, at the server's
182
- # discretion. If you are connecting to an OpenSSH server, you will
183
- # need to update the AcceptEnv setting in the sshd_config to include the
184
- # environment variables you want to send.
185
- #
186
- # channel.env "PATH", "/usr/local/bin"
187
- def env(variable_name, variable_value, &block)
188
- send_channel_request("env", :string, variable_name, :string, variable_value, &block)
189
- end
190
-
191
- # A hash of the valid PTY options (see #request_pty).
192
- VALID_PTY_OPTIONS = { term: "xterm",
193
- chars_wide: 80,
194
- chars_high: 24,
195
- pixels_wide: 640,
196
- pixels_high: 480,
197
- modes: {} }
198
-
199
- # Requests that a pseudo-tty (or "pty") be made available for this channel.
200
- # This is useful when you want to invoke and interact with some kind of
201
- # screen-based program (e.g., vim, or some menuing system).
202
- #
203
- # Note, that without a pty some programs (e.g. sudo, or subversion) on
204
- # some systems, will not be able to run interactively, and will error
205
- # instead of prompt if they ever need some user interaction.
206
- #
207
- # Note, too, that when a pty is requested, user's shell configuration
208
- # scripts (.bashrc and such) are not run by default, whereas they are
209
- # run when a pty is not present.
210
- #
211
- # channel.request_pty do |ch, success|
212
- # if success
213
- # puts "pty successfully obtained"
214
- # else
215
- # puts "could not obtain pty"
216
- # end
217
- # end
218
- def request_pty(opts={}, &block)
219
- extra = opts.keys - VALID_PTY_OPTIONS.keys
220
- raise ArgumentError, "invalid option(s) to request_pty: #{extra.inspect}" if extra.any?
221
-
222
- opts = VALID_PTY_OPTIONS.merge(opts)
223
-
224
- modes = opts[:modes].inject(Buffer.new) do |memo, (mode, data)|
225
- memo.write_byte(mode).write_long(data)
226
- end
227
- # mark the end of the mode opcode list with a 0 byte
228
- modes.write_byte(0)
229
-
230
- send_channel_request("pty-req", :string, opts[:term],
231
- :long, opts[:chars_wide], :long, opts[:chars_high],
232
- :long, opts[:pixels_wide], :long, opts[:pixels_high],
233
- :string, modes.to_s, &block)
234
- end
235
-
236
- # Sends data to the channel's remote endpoint. This usually has the
237
- # effect of sending the given string to the remote process' stdin stream.
238
- # Note that it does not immediately send the data across the channel,
239
- # but instead merely appends the given data to the channel's output buffer,
240
- # preparatory to being packaged up and sent out the next time the connection
241
- # is accepting data. (A connection might not be accepting data if, for
242
- # instance, it has filled its data window and has not yet been resized by
243
- # the remote end-point.)
244
- #
245
- # This will raise an exception if the channel has previously declared
246
- # that no more data will be sent (see #eof!).
247
- #
248
- # channel.send_data("the password\n")
249
- def send_data(data)
250
- raise EOFError, "cannot send data if channel has declared eof" if eof?
251
- output.append(data.to_s)
252
- end
253
-
254
- # Returns true if the channel exists in the channel list of the session,
255
- # and false otherwise. This can be used to determine whether a channel has
256
- # been closed or not.
257
- #
258
- # ssh.loop { channel.active? }
259
- def active?
260
- connection.channels.key?(local_id)
261
- end
262
-
263
- # Runs the SSH event loop until the channel is no longer active. This is
264
- # handy for blocking while you wait for some channel to finish.
265
- #
266
- # channel.exec("grep ...") { ... }
267
- # channel.wait
268
- def wait
269
- connection.loop { active? }
270
- end
271
-
272
- # True if close() has been called; NOTE: if the channel has data waiting to
273
- # be sent then the channel will close after all the data is sent. See
274
- # closed?() to determine if we have actually sent CHANNEL_CLOSE to server.
275
- # This may be true for awhile before closed? returns true if we are still
276
- # sending buffered output to server.
277
- def closing?
278
- @closing
279
- end
280
-
281
- # True if we have sent CHANNEL_CLOSE to the remote server.
282
- def local_closed?
283
- @local_closed
284
- end
285
-
286
- def remote_closed?
287
- @remote_closed
288
- end
289
-
290
- def remote_closed!
291
- @remote_closed = true
292
- end
293
-
294
- # Requests that the channel be closed. It only marks the channel to be closed
295
- # the CHANNEL_CLOSE message will be sent from event loop
296
- def close
297
- return if @closing
298
- @closing = true
299
- end
300
-
301
- # Returns true if the local end of the channel has declared that no more
302
- # data is forthcoming (see #eof!). Trying to send data via #send_data when
303
- # this is true will result in an exception being raised.
304
- def eof?
305
- @eof
306
- end
307
-
308
- # Tells the remote end of the channel that no more data is forthcoming
309
- # from this end of the channel. The remote end may still send data.
310
- # The CHANNEL_EOF packet will be sent once the output buffer is empty.
311
- def eof!
312
- return if eof?
313
- @eof = true
314
- end
315
-
316
- # If an #on_process handler has been set up, this will cause it to be
317
- # invoked (passing the channel itself as an argument). It also causes all
318
- # pending output to be enqueued as CHANNEL_DATA packets (see #enqueue_pending_output).
319
- def process
320
- @on_process.call(self) if @on_process
321
- enqueue_pending_output
322
-
323
- if @eof and not @sent_eof and output.empty? and remote_id and not @local_closed
324
- connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id))
325
- @sent_eof = true
326
- end
327
-
328
- if @closing and not @local_closed and output.empty? and remote_id
329
- connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
330
- @local_closed = true
331
- connection.cleanup_channel(self)
332
- end
333
- end
334
-
335
- # Registers a callback to be invoked when data packets are received by the
336
- # channel. The callback is called with the channel as the first argument,
337
- # and the data as the second.
338
- #
339
- # channel.on_data do |ch, data|
340
- # puts "got data: #{data.inspect}"
341
- # end
342
- #
343
- # Data received this way is typically the data written by the remote
344
- # process to its +stdout+ stream.
345
- def on_data(&block)
346
- old, @on_data = @on_data, block
347
- old
348
- end
349
-
350
- # Registers a callback to be invoked when extended data packets are received
351
- # by the channel. The callback is called with the channel as the first
352
- # argument, the data type (as an integer) as the second, and the data as
353
- # the third. Extended data is almost exclusively used to send +stderr+ data
354
- # (+type+ == 1). Other extended data types are not defined by the SSH
355
- # protocol.
356
- #
357
- # channel.on_extended_data do |ch, type, data|
358
- # puts "got stderr: #{data.inspect}"
359
- # end
360
- def on_extended_data(&block)
361
- old, @on_extended_data = @on_extended_data, block
362
- old
363
- end
364
-
365
- # Registers a callback to be invoked for each pass of the event loop for
366
- # this channel. There are no guarantees on timeliness in the event loop,
367
- # but it will be called roughly once for each packet received by the
368
- # connection (not the channel). This callback is invoked with the channel
369
- # as the sole argument.
370
- #
371
- # Here's an example that accumulates the channel data into a variable on
372
- # the channel itself, and displays individual lines in the input one
373
- # at a time when the channel is processed:
374
- #
375
- # channel[:data] = ""
376
- #
377
- # channel.on_data do |ch, data|
378
- # channel[:data] << data
379
- # end
380
- #
381
- # channel.on_process do |ch|
382
- # if channel[:data] =~ /^.*?\n/
383
- # puts $&
384
- # channel[:data] = $'
385
- # end
386
- # end
387
- def on_process(&block)
388
- old, @on_process = @on_process, block
389
- old
390
- end
391
-
392
- # Registers a callback to be invoked when the server acknowledges that a
393
- # channel is closed. This is invoked with the channel as the sole argument.
394
- #
395
- # channel.on_close do |ch|
396
- # puts "remote end is closing!"
397
- # end
398
- def on_close(&block)
399
- old, @on_close = @on_close, block
400
- old
401
- end
402
-
403
- # Registers a callback to be invoked when the server indicates that no more
404
- # data will be sent to the channel (although the channel can still send
405
- # data to the server). The channel is the sole argument to the callback.
406
- #
407
- # channel.on_eof do |ch|
408
- # puts "remote end is done sending data"
409
- # end
410
- def on_eof(&block)
411
- old, @on_eof = @on_eof, block
412
- old
413
- end
414
-
415
- # Registers a callback to be invoked when the server was unable to open
416
- # the requested channel. The channel itself will be passed to the block,
417
- # along with the integer "reason code" for the failure, and a textual
418
- # description of the failure from the server.
419
- #
420
- # channel = session.open_channel do |ch|
421
- # # ..
422
- # end
423
- #
424
- # channel.on_open_failed { |ch, code, desc| ... }
425
- def on_open_failed(&block)
426
- old, @on_open_failed = @on_open_failed, block
427
- old
428
- end
429
-
430
- # Registers a callback to be invoked when a channel request of the given
431
- # type is received. The callback will receive the channel as the first
432
- # argument, and the associated (unparsed) data as the second. The data
433
- # will be a Net::SSH::Buffer that you will need to parse, yourself,
434
- # according to the kind of request you are watching.
435
- #
436
- # By default, if the request wants a reply, Net::SSH will send a
437
- # CHANNEL_SUCCESS response for any request that was handled by a registered
438
- # callback, and CHANNEL_FAILURE for any that wasn't, but if you want your
439
- # registered callback to result in a CHANNEL_FAILURE response, just raise
440
- # Net::SSH::ChannelRequestFailed.
441
- #
442
- # Some common channel requests that your programs might want to listen
443
- # for are:
444
- #
445
- # * "exit-status" : the exit status of the remote process will be reported
446
- # as a long integer in the data buffer, which you can grab via
447
- # data.read_long.
448
- # * "exit-signal" : if the remote process died as a result of a signal
449
- # being sent to it, the signal will be reported as a string in the
450
- # data, via data.read_string. (Not all SSH servers support this channel
451
- # request type.)
452
- #
453
- # channel.on_request "exit-status" do |ch, data|
454
- # puts "process terminated with exit status: #{data.read_long}"
455
- # end
456
- def on_request(type, &block)
457
- old, @on_request[type] = @on_request[type], block
458
- old
459
- end
460
-
461
- # Sends a new channel request with the given name. The extra +data+
462
- # parameter must either be empty, or consist of an even number of
463
- # arguments. See Net::SSH::Buffer.from for a description of their format.
464
- # If a block is given, it is registered as a callback for a pending
465
- # request, and the packet will be flagged so that the server knows a
466
- # reply is required. If no block is given, the server will send no
467
- # response to this request. Responses, where required, will cause the
468
- # callback to be invoked with the channel as the first argument, and
469
- # either true or false as the second, depending on whether the request
470
- # succeeded or not. The meaning of "success" and "failure" in this context
471
- # is dependent on the specific request that was sent.
472
- #
473
- # channel.send_channel_request "shell" do |ch, success|
474
- # if success
475
- # puts "user shell started successfully"
476
- # else
477
- # puts "could not start user shell"
478
- # end
479
- # end
480
- #
481
- # Most channel requests you'll want to send are already wrapped in more
482
- # convenient helper methods (see #exec and #subsystem).
483
- def send_channel_request(request_name, *data, &callback)
484
- info { "sending channel request #{request_name.inspect}" }
485
- fail "Channel open not yet confirmed, please call send_channel_request(or exec) from block of open_channel" unless remote_id
486
- msg = Buffer.from(:byte, CHANNEL_REQUEST,
487
- :long, remote_id, :string, request_name,
488
- :bool, !callback.nil?, *data)
489
- connection.send_message(msg)
490
- pending_requests << callback if callback
491
- end
492
-
493
- public # these methods are public, but for Net::SSH internal use only
494
-
495
- # Enqueues pending output at the connection as CHANNEL_DATA packets. This
496
- # does nothing if the channel has not yet been confirmed open (see
497
- # #do_open_confirmation). This is called automatically by #process, which
498
- # is called from the event loop (Connection::Session#process). You will
499
- # generally not need to invoke it directly.
500
- def enqueue_pending_output #:nodoc:
501
- return unless remote_id
502
-
503
- while output.length > 0
504
- length = output.length
505
- length = remote_window_size if length > remote_window_size
506
- length = remote_maximum_packet_size if length > remote_maximum_packet_size
507
-
508
- if length > 0
509
- connection.send_message(Buffer.from(:byte, CHANNEL_DATA, :long, remote_id, :string, output.read(length)))
510
- output.consume!
511
- @remote_window_size -= length
512
- else
513
- break
5
+ module Net
6
+ module SSH
7
+ module Connection
8
+
9
+ # The channel abstraction. Multiple "channels" can be multiplexed onto a
10
+ # single SSH channel, each operating independently and seemingly in parallel.
11
+ # This class represents a single such channel. Most operations performed
12
+ # with the Net::SSH library will involve using one or more channels.
13
+ #
14
+ # Channels are intended to be used asynchronously. You request that one be
15
+ # opened (via Connection::Session#open_channel), and when it is opened, your
16
+ # callback is invoked. Then, you set various other callbacks on the newly
17
+ # opened channel, which are called in response to the corresponding events.
18
+ # Programming with Net::SSH works best if you think of your programs as
19
+ # state machines. Complex programs are best implemented as objects that
20
+ # wrap a channel. See Net::SCP and Net::SFTP for examples of how complex
21
+ # state machines can be built on top of the SSH protocol.
22
+ #
23
+ # ssh.open_channel do |channel|
24
+ # channel.exec("/invoke/some/command") do |ch, success|
25
+ # abort "could not execute command" unless success
26
+ #
27
+ # channel.on_data do |ch, data|
28
+ # puts "got stdout: #{data}"
29
+ # channel.send_data "something for stdin\n"
30
+ # end
31
+ #
32
+ # channel.on_extended_data do |ch, type, data|
33
+ # puts "got stderr: #{data}"
34
+ # end
35
+ #
36
+ # channel.on_close do |ch|
37
+ # puts "channel is closing!"
38
+ # end
39
+ # end
40
+ # end
41
+ #
42
+ # ssh.loop
43
+ #
44
+ # Channels also have a basic hash-like interface, that allows programs to
45
+ # store arbitrary state information on a channel object. This helps simplify
46
+ # the writing of state machines, especially when you may be juggling
47
+ # multiple open channels at the same time.
48
+ #
49
+ # Note that data sent across SSH channels are governed by maximum packet
50
+ # sizes and maximum window sizes. These details are managed internally
51
+ # by Net::SSH::Connection::Channel, so you may remain blissfully ignorant
52
+ # if you so desire, but you can always inspect the current maximums, as
53
+ # well as the remaining window size, using the reader attributes for those
54
+ # values.
55
+ class Channel
56
+ include Loggable
57
+ include Constants
58
+
59
+ # The local id for this channel, assigned by the Net::SSH::Connection::Session instance.
60
+ attr_reader :local_id
61
+
62
+ # The remote id for this channel, assigned by the remote host.
63
+ attr_reader :remote_id
64
+
65
+ # The type of this channel, usually "session".
66
+ attr_reader :type
67
+
68
+ # The underlying Net::SSH::Connection::Session instance that supports this channel.
69
+ attr_reader :connection
70
+
71
+ # The maximum packet size that the local host can receive.
72
+ attr_reader :local_maximum_packet_size
73
+
74
+ # The maximum amount of data that the local end of this channel can
75
+ # receive. This is a total, not per-packet.
76
+ attr_reader :local_maximum_window_size
77
+
78
+ # The maximum packet size that the remote host can receive.
79
+ attr_reader :remote_maximum_packet_size
80
+
81
+ # The maximum amount of data that the remote end of this channel can
82
+ # receive. This is a total, not per-packet.
83
+ attr_reader :remote_maximum_window_size
84
+
85
+ # This is the remaining window size on the local end of this channel. When
86
+ # this reaches zero, no more data can be received.
87
+ attr_reader :local_window_size
88
+
89
+ # This is the remaining window size on the remote end of this channel. When
90
+ # this reaches zero, no more data can be sent.
91
+ attr_reader :remote_window_size
92
+
93
+ # A hash of properties for this channel. These can be used to store state
94
+ # information about this channel. See also #[] and #[]=.
95
+ attr_reader :properties
96
+
97
+ # The output buffer for this channel. Data written to the channel is
98
+ # enqueued here, to be written as CHANNEL_DATA packets during each pass of
99
+ # the event loop. See Connection::Session#process and #enqueue_pending_output.
100
+ attr_reader :output #:nodoc:
101
+
102
+ # The list of pending requests. Each time a request is sent which requires
103
+ # a reply, the corresponding callback is pushed onto this queue. As responses
104
+ # arrive, they are shifted off the front and handled.
105
+ attr_reader :pending_requests #:nodoc:
106
+
107
+ # Instantiates a new channel on the given connection, of the given type,
108
+ # and with the given id. If a block is given, it will be remembered until
109
+ # the channel is confirmed open by the server, and will be invoked at
110
+ # that time (see #do_open_confirmation).
111
+ #
112
+ # This also sets the default maximum packet size and maximum window size.
113
+ def initialize(connection, type, local_id, max_pkt_size = 0x8000, max_win_size = 0x20000, &on_confirm_open)
114
+ self.logger = connection.logger
115
+
116
+ @connection = connection
117
+ @type = type
118
+ @local_id = local_id
119
+
120
+ @local_maximum_packet_size = max_pkt_size
121
+ @local_window_size = @local_maximum_window_size = max_win_size
122
+
123
+ @on_confirm_open = on_confirm_open
124
+
125
+ @output = Buffer.new
126
+
127
+ @properties = {}
128
+
129
+ @pending_requests = []
130
+ @on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil
131
+ @on_request = {}
132
+ @closing = @eof = @sent_eof = @local_closed = @remote_closed = false
133
+ end
134
+
135
+ # A shortcut for accessing properties of the channel (see #properties).
136
+ def [](name)
137
+ @properties[name]
138
+ end
139
+
140
+ # A shortcut for setting properties of the channel (see #properties).
141
+ def []=(name, value)
142
+ @properties[name] = value
143
+ end
144
+
145
+ # Syntactic sugar for executing a command. Sends a channel request asking
146
+ # that the given command be invoked. If the block is given, it will be
147
+ # called when the server responds. The first parameter will be the
148
+ # channel, and the second will be true or false, indicating whether the
149
+ # request succeeded or not. In this case, success means that the command
150
+ # is being executed, not that it has completed, and failure means that the
151
+ # command altogether failed to be executed.
152
+ #
153
+ # channel.exec "ls -l /home" do |ch, success|
154
+ # if success
155
+ # puts "command has begun executing..."
156
+ # # this is a good place to hang callbacks like #on_data...
157
+ # else
158
+ # puts "alas! the command could not be invoked!"
159
+ # end
160
+ # end
161
+ def exec(command, &block)
162
+ send_channel_request("exec", :string, command, &block)
163
+ end
164
+
165
+ # Syntactic sugar for requesting that a subsystem be started. Subsystems
166
+ # are a way for other protocols (like SFTP) to be run, using SSH as
167
+ # the transport. Generally, you'll never need to call this directly unless
168
+ # you are the implementor of something that consumes an SSH subsystem, like
169
+ # SFTP.
170
+ #
171
+ # channel.subsystem("sftp") do |ch, success|
172
+ # if success
173
+ # puts "subsystem successfully started"
174
+ # else
175
+ # puts "subsystem could not be started"
176
+ # end
177
+ # end
178
+ def subsystem(subsystem, &block)
179
+ send_channel_request("subsystem", :string, subsystem, &block)
180
+ end
181
+
182
+ # Syntactic sugar for setting an environment variable in the remote
183
+ # process' environment. Note that for security reasons, the server may
184
+ # refuse to set certain environment variables, or all, at the server's
185
+ # discretion. If you are connecting to an OpenSSH server, you will
186
+ # need to update the AcceptEnv setting in the sshd_config to include the
187
+ # environment variables you want to send.
188
+ #
189
+ # channel.env "PATH", "/usr/local/bin"
190
+ def env(variable_name, variable_value, &block)
191
+ send_channel_request("env", :string, variable_name, :string, variable_value, &block)
192
+ end
193
+
194
+ # A hash of the valid PTY options (see #request_pty).
195
+ VALID_PTY_OPTIONS = { term: "xterm",
196
+ chars_wide: 80,
197
+ chars_high: 24,
198
+ pixels_wide: 640,
199
+ pixels_high: 480,
200
+ modes: {} }
201
+
202
+ # Requests that a pseudo-tty (or "pty") be made available for this channel.
203
+ # This is useful when you want to invoke and interact with some kind of
204
+ # screen-based program (e.g., vim, or some menuing system).
205
+ #
206
+ # Note, that without a pty some programs (e.g. sudo, or subversion) on
207
+ # some systems, will not be able to run interactively, and will error
208
+ # instead of prompt if they ever need some user interaction.
209
+ #
210
+ # Note, too, that when a pty is requested, user's shell configuration
211
+ # scripts (.bashrc and such) are not run by default, whereas they are
212
+ # run when a pty is not present.
213
+ #
214
+ # channel.request_pty do |ch, success|
215
+ # if success
216
+ # puts "pty successfully obtained"
217
+ # else
218
+ # puts "could not obtain pty"
219
+ # end
220
+ # end
221
+ def request_pty(opts={}, &block)
222
+ extra = opts.keys - VALID_PTY_OPTIONS.keys
223
+ raise ArgumentError, "invalid option(s) to request_pty: #{extra.inspect}" if extra.any?
224
+
225
+ opts = VALID_PTY_OPTIONS.merge(opts)
226
+
227
+ modes = opts[:modes].inject(Buffer.new) do |memo, (mode, data)|
228
+ memo.write_byte(mode).write_long(data)
514
229
  end
230
+ # mark the end of the mode opcode list with a 0 byte
231
+ modes.write_byte(0)
232
+
233
+ send_channel_request("pty-req", :string, opts[:term],
234
+ :long, opts[:chars_wide], :long, opts[:chars_high],
235
+ :long, opts[:pixels_wide], :long, opts[:pixels_high],
236
+ :string, modes.to_s, &block)
515
237
  end
516
- end
517
-
518
- # Invoked when the server confirms that a channel has been opened.
519
- # The remote_id is the id of the channel as assigned by the remote host,
520
- # and max_window and max_packet are the maximum window and maximum
521
- # packet sizes, respectively. If an open-confirmation callback was
522
- # given when the channel was created, it is invoked at this time with
523
- # the channel itself as the sole argument.
524
- def do_open_confirmation(remote_id, max_window, max_packet) #:nodoc:
525
- @remote_id = remote_id
526
- @remote_window_size = @remote_maximum_window_size = max_window
527
- @remote_maximum_packet_size = max_packet
528
- connection.forward.agent(self) if connection.options[:forward_agent] && type == "session"
529
- forward_local_env(connection.options[:send_env]) if connection.options[:send_env]
530
- @on_confirm_open.call(self) if @on_confirm_open
531
- end
532
-
533
- # Invoked when the server failed to open the channel. If an #on_open_failed
534
- # callback was specified, it will be invoked with the channel, reason code,
535
- # and description as arguments. Otherwise, a ChannelOpenFailed exception
536
- # will be raised.
537
- def do_open_failed(reason_code, description)
538
- if @on_open_failed
539
- @on_open_failed.call(self, reason_code, description)
540
- else
541
- raise ChannelOpenFailed.new(reason_code, description)
238
+
239
+ # Sends data to the channel's remote endpoint. This usually has the
240
+ # effect of sending the given string to the remote process' stdin stream.
241
+ # Note that it does not immediately send the data across the channel,
242
+ # but instead merely appends the given data to the channel's output buffer,
243
+ # preparatory to being packaged up and sent out the next time the connection
244
+ # is accepting data. (A connection might not be accepting data if, for
245
+ # instance, it has filled its data window and has not yet been resized by
246
+ # the remote end-point.)
247
+ #
248
+ # This will raise an exception if the channel has previously declared
249
+ # that no more data will be sent (see #eof!).
250
+ #
251
+ # channel.send_data("the password\n")
252
+ def send_data(data)
253
+ raise EOFError, "cannot send data if channel has declared eof" if eof?
254
+ output.append(data.to_s)
542
255
  end
543
- end
544
-
545
- # Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and
546
- # causes the remote window size to be adjusted upwards by the given
547
- # number of bytes. This has the effect of allowing more data to be sent
548
- # from the local end to the remote end of the channel.
549
- def do_window_adjust(bytes) #:nodoc:
550
- @remote_maximum_window_size += bytes
551
- @remote_window_size += bytes
552
- end
553
-
554
- # Invoked when the server sends a channel request. If any #on_request
555
- # callback has been registered for the specific type of this request,
556
- # it is invoked. If +want_reply+ is true, a packet will be sent of
557
- # either CHANNEL_SUCCESS or CHANNEL_FAILURE type. If there was no callback
558
- # to handle the request, CHANNEL_FAILURE will be sent. Otherwise,
559
- # CHANNEL_SUCCESS, unless the callback raised ChannelRequestFailed. The
560
- # callback should accept the channel as the first argument, and the
561
- # request-specific data as the second.
562
- def do_request(request, want_reply, data) #:nodoc:
563
- result = true
564
-
565
- begin
566
- callback = @on_request[request] or raise ChannelRequestFailed
567
- callback.call(self, data)
568
- rescue ChannelRequestFailed
569
- result = false
256
+
257
+ # Returns true if the channel exists in the channel list of the session,
258
+ # and false otherwise. This can be used to determine whether a channel has
259
+ # been closed or not.
260
+ #
261
+ # ssh.loop { channel.active? }
262
+ def active?
263
+ connection.channels.key?(local_id)
570
264
  end
571
-
572
- if want_reply
573
- msg = Buffer.from(:byte, result ? CHANNEL_SUCCESS : CHANNEL_FAILURE, :long, remote_id)
574
- connection.send_message(msg)
265
+
266
+ # Runs the SSH event loop until the channel is no longer active. This is
267
+ # handy for blocking while you wait for some channel to finish.
268
+ #
269
+ # channel.exec("grep ...") { ... }
270
+ # channel.wait
271
+ def wait
272
+ connection.loop { active? }
575
273
  end
576
- end
577
-
578
- # Invokes the #on_data callback when the server sends data to the
579
- # channel. This will reduce the available window size on the local end,
580
- # but does not actually throttle requests that come in illegally when
581
- # the window size is too small. The callback is invoked with the channel
582
- # as the first argument, and the data as the second.
583
- def do_data(data) #:nodoc:
584
- update_local_window_size(data.length)
585
- @on_data.call(self, data) if @on_data
586
- end
587
-
588
- # Invokes the #on_extended_data callback when the server sends
589
- # extended data to the channel. This will reduce the available window
590
- # size on the local end. The callback is invoked with the channel,
591
- # type, and data.
592
- def do_extended_data(type, data)
593
- update_local_window_size(data.length)
594
- @on_extended_data.call(self, type, data) if @on_extended_data
595
- end
596
-
597
- # Invokes the #on_eof callback when the server indicates that no
598
- # further data is forthcoming. The callback is invoked with the channel
599
- # as the argument.
600
- def do_eof
601
- @on_eof.call(self) if @on_eof
602
- end
603
-
604
- # Invokes the #on_close callback when the server closes a channel.
605
- # The channel is the only argument.
606
- def do_close
607
- @on_close.call(self) if @on_close
608
- end
609
-
610
- # Invokes the next pending request callback with +false+ as the second
611
- # argument.
612
- def do_failure
613
- if callback = pending_requests.shift
614
- callback.call(self, false)
615
- else
616
- error { "channel failure received with no pending request to handle it (bug?)" }
274
+
275
+ # True if close() has been called; NOTE: if the channel has data waiting to
276
+ # be sent then the channel will close after all the data is sent. See
277
+ # closed?() to determine if we have actually sent CHANNEL_CLOSE to server.
278
+ # This may be true for awhile before closed? returns true if we are still
279
+ # sending buffered output to server.
280
+ def closing?
281
+ @closing
617
282
  end
618
- end
619
-
620
- # Invokes the next pending request callback with +true+ as the second
621
- # argument.
622
- def do_success
623
- if callback = pending_requests.shift
624
- callback.call(self, true)
625
- else
626
- error { "channel success received with no pending request to handle it (bug?)" }
283
+
284
+ # True if we have sent CHANNEL_CLOSE to the remote server.
285
+ def local_closed?
286
+ @local_closed
627
287
  end
628
- end
629
-
630
- private
631
-
632
- # Runs the SSH event loop until the remote confirmed channel open
633
- # experimental api
634
- def wait_until_open_confirmed
635
- connection.loop { !remote_id }
636
- end
637
-
638
- # Updates the local window size by the given amount. If the window
639
- # size drops to less than half of the local maximum (an arbitrary
640
- # threshold), a CHANNEL_WINDOW_ADJUST message will be sent to the
641
- # server telling it that the window size has grown.
642
- def update_local_window_size(size)
643
- @local_window_size -= size
644
- if local_window_size < local_maximum_window_size/2
645
- connection.send_message(Buffer.from(:byte, CHANNEL_WINDOW_ADJUST,
646
- :long, remote_id, :long, 0x20000))
647
- @local_window_size += 0x20000
648
- @local_maximum_window_size += 0x20000
288
+
289
+ def remote_closed?
290
+ @remote_closed
649
291
  end
650
- end
651
-
652
- # Gets an +Array+ of local environment variables in the remote process'
653
- # environment.
654
- # A variable name can either be described by a +Regexp+ or +String+.
655
- #
656
- # channel.forward_local_env [/^GIT_.*$/, "LANG"]
657
- def forward_local_env(env_variable_patterns)
658
- Array(env_variable_patterns).each do |env_variable_pattern|
659
- matched_variables = ENV.find_all do |env_name, _|
660
- case env_variable_pattern
661
- when Regexp then env_name =~ env_variable_pattern
662
- when String then env_name == env_variable_pattern
292
+
293
+ def remote_closed!
294
+ @remote_closed = true
295
+ end
296
+
297
+ # Requests that the channel be closed. It only marks the channel to be closed
298
+ # the CHANNEL_CLOSE message will be sent from event loop
299
+ def close
300
+ return if @closing
301
+ @closing = true
302
+ end
303
+
304
+ # Returns true if the local end of the channel has declared that no more
305
+ # data is forthcoming (see #eof!). Trying to send data via #send_data when
306
+ # this is true will result in an exception being raised.
307
+ def eof?
308
+ @eof
309
+ end
310
+
311
+ # Tells the remote end of the channel that no more data is forthcoming
312
+ # from this end of the channel. The remote end may still send data.
313
+ # The CHANNEL_EOF packet will be sent once the output buffer is empty.
314
+ def eof!
315
+ return if eof?
316
+ @eof = true
317
+ end
318
+
319
+ # If an #on_process handler has been set up, this will cause it to be
320
+ # invoked (passing the channel itself as an argument). It also causes all
321
+ # pending output to be enqueued as CHANNEL_DATA packets (see #enqueue_pending_output).
322
+ def process
323
+ @on_process.call(self) if @on_process
324
+ enqueue_pending_output
325
+
326
+ if @eof and not @sent_eof and output.empty? and remote_id and not @local_closed
327
+ connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id))
328
+ @sent_eof = true
329
+ end
330
+
331
+ if @closing and not @local_closed and output.empty? and remote_id
332
+ connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
333
+ @local_closed = true
334
+ connection.cleanup_channel(self)
335
+ end
336
+ end
337
+
338
+ # Registers a callback to be invoked when data packets are received by the
339
+ # channel. The callback is called with the channel as the first argument,
340
+ # and the data as the second.
341
+ #
342
+ # channel.on_data do |ch, data|
343
+ # puts "got data: #{data.inspect}"
344
+ # end
345
+ #
346
+ # Data received this way is typically the data written by the remote
347
+ # process to its +stdout+ stream.
348
+ def on_data(&block)
349
+ old, @on_data = @on_data, block
350
+ old
351
+ end
352
+
353
+ # Registers a callback to be invoked when extended data packets are received
354
+ # by the channel. The callback is called with the channel as the first
355
+ # argument, the data type (as an integer) as the second, and the data as
356
+ # the third. Extended data is almost exclusively used to send +stderr+ data
357
+ # (+type+ == 1). Other extended data types are not defined by the SSH
358
+ # protocol.
359
+ #
360
+ # channel.on_extended_data do |ch, type, data|
361
+ # puts "got stderr: #{data.inspect}"
362
+ # end
363
+ def on_extended_data(&block)
364
+ old, @on_extended_data = @on_extended_data, block
365
+ old
366
+ end
367
+
368
+ # Registers a callback to be invoked for each pass of the event loop for
369
+ # this channel. There are no guarantees on timeliness in the event loop,
370
+ # but it will be called roughly once for each packet received by the
371
+ # connection (not the channel). This callback is invoked with the channel
372
+ # as the sole argument.
373
+ #
374
+ # Here's an example that accumulates the channel data into a variable on
375
+ # the channel itself, and displays individual lines in the input one
376
+ # at a time when the channel is processed:
377
+ #
378
+ # channel[:data] = ""
379
+ #
380
+ # channel.on_data do |ch, data|
381
+ # channel[:data] << data
382
+ # end
383
+ #
384
+ # channel.on_process do |ch|
385
+ # if channel[:data] =~ /^.*?\n/
386
+ # puts $&
387
+ # channel[:data] = $'
388
+ # end
389
+ # end
390
+ def on_process(&block)
391
+ old, @on_process = @on_process, block
392
+ old
393
+ end
394
+
395
+ # Registers a callback to be invoked when the server acknowledges that a
396
+ # channel is closed. This is invoked with the channel as the sole argument.
397
+ #
398
+ # channel.on_close do |ch|
399
+ # puts "remote end is closing!"
400
+ # end
401
+ def on_close(&block)
402
+ old, @on_close = @on_close, block
403
+ old
404
+ end
405
+
406
+ # Registers a callback to be invoked when the server indicates that no more
407
+ # data will be sent to the channel (although the channel can still send
408
+ # data to the server). The channel is the sole argument to the callback.
409
+ #
410
+ # channel.on_eof do |ch|
411
+ # puts "remote end is done sending data"
412
+ # end
413
+ def on_eof(&block)
414
+ old, @on_eof = @on_eof, block
415
+ old
416
+ end
417
+
418
+ # Registers a callback to be invoked when the server was unable to open
419
+ # the requested channel. The channel itself will be passed to the block,
420
+ # along with the integer "reason code" for the failure, and a textual
421
+ # description of the failure from the server.
422
+ #
423
+ # channel = session.open_channel do |ch|
424
+ # # ..
425
+ # end
426
+ #
427
+ # channel.on_open_failed { |ch, code, desc| ... }
428
+ def on_open_failed(&block)
429
+ old, @on_open_failed = @on_open_failed, block
430
+ old
431
+ end
432
+
433
+ # Registers a callback to be invoked when a channel request of the given
434
+ # type is received. The callback will receive the channel as the first
435
+ # argument, and the associated (unparsed) data as the second. The data
436
+ # will be a Net::SSH::Buffer that you will need to parse, yourself,
437
+ # according to the kind of request you are watching.
438
+ #
439
+ # By default, if the request wants a reply, Net::SSH will send a
440
+ # CHANNEL_SUCCESS response for any request that was handled by a registered
441
+ # callback, and CHANNEL_FAILURE for any that wasn't, but if you want your
442
+ # registered callback to result in a CHANNEL_FAILURE response, just raise
443
+ # Net::SSH::ChannelRequestFailed.
444
+ #
445
+ # Some common channel requests that your programs might want to listen
446
+ # for are:
447
+ #
448
+ # * "exit-status" : the exit status of the remote process will be reported
449
+ # as a long integer in the data buffer, which you can grab via
450
+ # data.read_long.
451
+ # * "exit-signal" : if the remote process died as a result of a signal
452
+ # being sent to it, the signal will be reported as a string in the
453
+ # data, via data.read_string. (Not all SSH servers support this channel
454
+ # request type.)
455
+ #
456
+ # channel.on_request "exit-status" do |ch, data|
457
+ # puts "process terminated with exit status: #{data.read_long}"
458
+ # end
459
+ def on_request(type, &block)
460
+ old, @on_request[type] = @on_request[type], block
461
+ old
462
+ end
463
+
464
+ # Sends a new channel request with the given name. The extra +data+
465
+ # parameter must either be empty, or consist of an even number of
466
+ # arguments. See Net::SSH::Buffer.from for a description of their format.
467
+ # If a block is given, it is registered as a callback for a pending
468
+ # request, and the packet will be flagged so that the server knows a
469
+ # reply is required. If no block is given, the server will send no
470
+ # response to this request. Responses, where required, will cause the
471
+ # callback to be invoked with the channel as the first argument, and
472
+ # either true or false as the second, depending on whether the request
473
+ # succeeded or not. The meaning of "success" and "failure" in this context
474
+ # is dependent on the specific request that was sent.
475
+ #
476
+ # channel.send_channel_request "shell" do |ch, success|
477
+ # if success
478
+ # puts "user shell started successfully"
479
+ # else
480
+ # puts "could not start user shell"
481
+ # end
482
+ # end
483
+ #
484
+ # Most channel requests you'll want to send are already wrapped in more
485
+ # convenient helper methods (see #exec and #subsystem).
486
+ def send_channel_request(request_name, *data, &callback)
487
+ info { "sending channel request #{request_name.inspect}" }
488
+ fail "Channel open not yet confirmed, please call send_channel_request(or exec) from block of open_channel" unless remote_id
489
+ msg = Buffer.from(:byte, CHANNEL_REQUEST,
490
+ :long, remote_id, :string, request_name,
491
+ :bool, !callback.nil?, *data)
492
+ connection.send_message(msg)
493
+ pending_requests << callback if callback
494
+ end
495
+
496
+ public # these methods are public, but for Net::SSH internal use only
497
+
498
+ # Enqueues pending output at the connection as CHANNEL_DATA packets. This
499
+ # does nothing if the channel has not yet been confirmed open (see
500
+ # #do_open_confirmation). This is called automatically by #process, which
501
+ # is called from the event loop (Connection::Session#process). You will
502
+ # generally not need to invoke it directly.
503
+ def enqueue_pending_output #:nodoc:
504
+ return unless remote_id
505
+
506
+ while output.length > 0
507
+ length = output.length
508
+ length = remote_window_size if length > remote_window_size
509
+ length = remote_maximum_packet_size if length > remote_maximum_packet_size
510
+
511
+ if length > 0
512
+ connection.send_message(Buffer.from(:byte, CHANNEL_DATA, :long, remote_id, :string, output.read(length)))
513
+ output.consume!
514
+ @remote_window_size -= length
515
+ else
516
+ break
663
517
  end
664
518
  end
665
- matched_variables.each do |env_name, env_value|
666
- self.env(env_name, env_value)
519
+ end
520
+
521
+ # Invoked when the server confirms that a channel has been opened.
522
+ # The remote_id is the id of the channel as assigned by the remote host,
523
+ # and max_window and max_packet are the maximum window and maximum
524
+ # packet sizes, respectively. If an open-confirmation callback was
525
+ # given when the channel was created, it is invoked at this time with
526
+ # the channel itself as the sole argument.
527
+ def do_open_confirmation(remote_id, max_window, max_packet) #:nodoc:
528
+ @remote_id = remote_id
529
+ @remote_window_size = @remote_maximum_window_size = max_window
530
+ @remote_maximum_packet_size = max_packet
531
+ connection.forward.agent(self) if connection.options[:forward_agent] && type == "session"
532
+ forward_local_env(connection.options[:send_env]) if connection.options[:send_env]
533
+ @on_confirm_open.call(self) if @on_confirm_open
534
+ end
535
+
536
+ # Invoked when the server failed to open the channel. If an #on_open_failed
537
+ # callback was specified, it will be invoked with the channel, reason code,
538
+ # and description as arguments. Otherwise, a ChannelOpenFailed exception
539
+ # will be raised.
540
+ def do_open_failed(reason_code, description)
541
+ if @on_open_failed
542
+ @on_open_failed.call(self, reason_code, description)
543
+ else
544
+ raise ChannelOpenFailed.new(reason_code, description)
545
+ end
546
+ end
547
+
548
+ # Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and
549
+ # causes the remote window size to be adjusted upwards by the given
550
+ # number of bytes. This has the effect of allowing more data to be sent
551
+ # from the local end to the remote end of the channel.
552
+ def do_window_adjust(bytes) #:nodoc:
553
+ @remote_maximum_window_size += bytes
554
+ @remote_window_size += bytes
555
+ end
556
+
557
+ # Invoked when the server sends a channel request. If any #on_request
558
+ # callback has been registered for the specific type of this request,
559
+ # it is invoked. If +want_reply+ is true, a packet will be sent of
560
+ # either CHANNEL_SUCCESS or CHANNEL_FAILURE type. If there was no callback
561
+ # to handle the request, CHANNEL_FAILURE will be sent. Otherwise,
562
+ # CHANNEL_SUCCESS, unless the callback raised ChannelRequestFailed. The
563
+ # callback should accept the channel as the first argument, and the
564
+ # request-specific data as the second.
565
+ def do_request(request, want_reply, data) #:nodoc:
566
+ result = true
567
+
568
+ begin
569
+ callback = @on_request[request] or raise ChannelRequestFailed
570
+ callback.call(self, data)
571
+ rescue ChannelRequestFailed
572
+ result = false
573
+ end
574
+
575
+ if want_reply
576
+ msg = Buffer.from(:byte, result ? CHANNEL_SUCCESS : CHANNEL_FAILURE, :long, remote_id)
577
+ connection.send_message(msg)
578
+ end
579
+ end
580
+
581
+ # Invokes the #on_data callback when the server sends data to the
582
+ # channel. This will reduce the available window size on the local end,
583
+ # but does not actually throttle requests that come in illegally when
584
+ # the window size is too small. The callback is invoked with the channel
585
+ # as the first argument, and the data as the second.
586
+ def do_data(data) #:nodoc:
587
+ update_local_window_size(data.length)
588
+ @on_data.call(self, data) if @on_data
589
+ end
590
+
591
+ # Invokes the #on_extended_data callback when the server sends
592
+ # extended data to the channel. This will reduce the available window
593
+ # size on the local end. The callback is invoked with the channel,
594
+ # type, and data.
595
+ def do_extended_data(type, data)
596
+ update_local_window_size(data.length)
597
+ @on_extended_data.call(self, type, data) if @on_extended_data
598
+ end
599
+
600
+ # Invokes the #on_eof callback when the server indicates that no
601
+ # further data is forthcoming. The callback is invoked with the channel
602
+ # as the argument.
603
+ def do_eof
604
+ @on_eof.call(self) if @on_eof
605
+ end
606
+
607
+ # Invokes the #on_close callback when the server closes a channel.
608
+ # The channel is the only argument.
609
+ def do_close
610
+ @on_close.call(self) if @on_close
611
+ end
612
+
613
+ # Invokes the next pending request callback with +false+ as the second
614
+ # argument.
615
+ def do_failure
616
+ if callback = pending_requests.shift
617
+ callback.call(self, false)
618
+ else
619
+ error { "channel failure received with no pending request to handle it (bug?)" }
620
+ end
621
+ end
622
+
623
+ # Invokes the next pending request callback with +true+ as the second
624
+ # argument.
625
+ def do_success
626
+ if callback = pending_requests.shift
627
+ callback.call(self, true)
628
+ else
629
+ error { "channel success received with no pending request to handle it (bug?)" }
630
+ end
631
+ end
632
+
633
+ private
634
+
635
+ # Runs the SSH event loop until the remote confirmed channel open
636
+ # experimental api
637
+ def wait_until_open_confirmed
638
+ connection.loop { !remote_id }
639
+ end
640
+
641
+ # Updates the local window size by the given amount. If the window
642
+ # size drops to less than half of the local maximum (an arbitrary
643
+ # threshold), a CHANNEL_WINDOW_ADJUST message will be sent to the
644
+ # server telling it that the window size has grown.
645
+ def update_local_window_size(size)
646
+ @local_window_size -= size
647
+ if local_window_size < local_maximum_window_size / 2
648
+ connection.send_message(Buffer.from(:byte, CHANNEL_WINDOW_ADJUST,
649
+ :long, remote_id, :long, 0x20000))
650
+ @local_window_size += 0x20000
651
+ @local_maximum_window_size += 0x20000
652
+ end
653
+ end
654
+
655
+ # Gets an +Array+ of local environment variables in the remote process'
656
+ # environment.
657
+ # A variable name can either be described by a +Regexp+ or +String+.
658
+ #
659
+ # channel.forward_local_env [/^GIT_.*$/, "LANG"]
660
+ def forward_local_env(env_variable_patterns)
661
+ Array(env_variable_patterns).each do |env_variable_pattern|
662
+ matched_variables = ENV.find_all do |env_name, _|
663
+ case env_variable_pattern
664
+ when Regexp then env_name =~ env_variable_pattern
665
+ when String then env_name == env_variable_pattern
666
+ end
667
+ end
668
+ matched_variables.each do |env_name, env_value|
669
+ self.env(env_name, env_value)
670
+ end
667
671
  end
668
672
  end
669
673
  end
670
- end
671
674
 
672
- end; end; end
675
+ end
676
+ end
677
+ end