net-ssh-open3 0.1.3 → 0.1.4

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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/lib/net-ssh-open3.rb +59 -31
  4. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6cc5b2173309f5acbd7d9b1dfef343daea71cc6f
4
- data.tar.gz: 769b292fabbab5b0aba1e91c977a85be3362b956
3
+ metadata.gz: f7e8512f0619ba893c15747668049e3b609a8f35
4
+ data.tar.gz: e25fc932b83773b5d02586c89b9a2144e1ed1cbe
5
5
  SHA512:
6
- metadata.gz: df50ddf6685051878ea33fb7bb351fbe23d3d5b34da2c94f9888c0bb258968947ded31eb7ee09af9b2b8a2a82c95f488651a2377dcd4ee690f61ad6f764f400a
7
- data.tar.gz: 5a2a4df652343408c8ab4fbd418129d607f38a0e261677ef16812dd8029aacdc5ae10da81e0761f1ebf27ff9306d6f079a212a1d3326dd1e748bc5e561a59b89
6
+ metadata.gz: 055ca5e6a559a130c04d5795e2a939063da372248e931079f105445d2312ce7df027fd56ce913dd139293f90ae7c8e649cf757538826f7c9017aaf9fa0cc450a
7
+ data.tar.gz: 9fec3e2736acdb0a2f6ef995a3dc3cb14378cb7d8f8ea26c3926c8e3230b9401d26ce0f9eeb5fcae0a84fefd0247844c7232eaebdc0e6dfc68c4c125139109e2
data/README.md CHANGED
@@ -32,3 +32,6 @@ Note: a single SSH session may have several channels, i.e. you may run several O
32
32
 
33
33
  For more information please see documentation inside.
34
34
  Recommended starting point is "Methods included from Net::SSH::Open3" in Net::SSH::Connection::Session.
35
+
36
+ TODO:
37
+ don't require block
data/lib/net-ssh-open3.rb CHANGED
@@ -32,9 +32,10 @@ module Net::SSH
32
32
  # Assigned only if the process has exited normally (i.e. not by a signal).
33
33
  # More information about standard exit codes: http://tldp.org/LDP/abs/html/exitcodes.html
34
34
  attr_reader :exitstatus
35
-
35
+
36
36
  # Process ID of a remote command interpreter or a remote process.
37
37
  # See note on Net::SSH::Process::Status class for more information on how this is fetched.
38
+ # false if PID fetching was disabled.
38
39
  attr_reader :pid
39
40
 
40
41
  # true when process has been killed by a signal and a core dump has been generated for it.
@@ -76,7 +77,7 @@ module Net::SSH
76
77
 
77
78
  # String representation of exit status.
78
79
  def to_s
79
- if @pid
80
+ if @pid != nil
80
81
  "pid #@pid " <<
81
82
  if exited?
82
83
  "exit #@exitstatus"
@@ -122,7 +123,8 @@ module Net::SSH
122
123
  # * +stdin_data+: for +capture*+ only, specifies data to be immediately sent to +stdin+ of a remote process.
123
124
  # stdin is immediately closed then.
124
125
  # * +logger+: an object which responds to +debug/info/warn/error+ and optionally +init/stdin/stdout/stderr+ to log debug information
125
- # and data exchange stream
126
+ # and data exchange stream.
127
+ # * +fetch_pid+: prepend command with 'echo $$' and capture first line of the output as PID. Defaults to true.
126
128
  # * +pty+: true or a +Hash+ of PTY settings to request a pseudo-TTY, see Net::SSH documentation for more information.
127
129
  # A note about sending TERM/QUIT: use modes, e.g.:
128
130
  # Net::SSH.start('localhost', ENV['USER']).capture2e('cat', pty: {
@@ -189,42 +191,48 @@ module Net::SSH
189
191
  # the pipe may overload and ssh loop will get stuck writing to it.
190
192
  def popen3(*args, &block)
191
193
  redirects = extract_open3_options(args)[:redirects]
192
- stdin_inner, stdin_outer = open3_ios_for(:in, redirects)
193
- stdout_outer, stdout_inner = open3_ios_for(:out, redirects)
194
- stderr_outer, stderr_inner = open3_ios_for(:err, redirects)
194
+ local_pipes = []
195
+ stdin_inner, stdin_outer = open3_ios_for(:in, redirects, local_pipes)
196
+ stdout_outer, stdout_inner = open3_ios_for(:out, redirects, local_pipes)
197
+ stderr_outer, stderr_inner = open3_ios_for(:err, redirects, local_pipes)
195
198
 
196
199
  run_popen(*args,
197
200
  stdin: stdin_inner,
198
201
  stdout: stdout_inner,
199
202
  stderr: stderr_inner,
200
203
  block_pipes: [stdin_outer, stdout_outer, stderr_outer],
204
+ local_pipes: local_pipes,
201
205
  &block)
202
206
  end
203
207
 
204
208
  # Yields +stdin+, +stdout-stderr+, +waiter_thread+ into a block.
205
209
  def popen2e(*args, &block)
206
210
  redirects = extract_open3_options(args)[:redirects]
207
- stdin_inner, stdin_outer = open3_ios_for(:in, redirects)
208
- stdout_outer, stdout_inner = open3_ios_for(:out, redirects)
211
+ local_pipes = []
212
+ stdin_inner, stdin_outer = open3_ios_for(:in, redirects, local_pipes)
213
+ stdout_outer, stdout_inner = open3_ios_for(:out, redirects, local_pipes)
209
214
 
210
215
  run_popen(*args,
211
216
  stdin: stdin_inner,
212
217
  stdout: stdout_inner,
213
218
  stderr: stdout_inner,
214
219
  block_pipes: [stdin_outer, stdout_outer],
220
+ local_pipes: local_pipes,
215
221
  &block)
216
222
  end
217
223
 
218
224
  # Yields +stdin+, +stdout+, +waiter_thread+ into a block.
219
225
  def popen2(*args, &block)
220
226
  redirects = extract_open3_options(args)[:redirects]
221
- stdin_inner, stdin_outer = open3_ios_for(:in, redirects)
222
- stdout_outer, stdout_inner = open3_ios_for(:out, redirects)
227
+ local_pipes = []
228
+ stdin_inner, stdin_outer = open3_ios_for(:in, redirects, local_pipes)
229
+ stdout_outer, stdout_inner = open3_ios_for(:out, redirects, local_pipes)
223
230
 
224
231
  run_popen(*args,
225
232
  stdin: stdin_inner,
226
233
  stdout: stdout_inner,
227
234
  block_pipes: [stdin_outer, stdout_outer],
235
+ local_pipes: local_pipes,
228
236
  &block)
229
237
  end
230
238
 
@@ -237,11 +245,11 @@ module Net::SSH
237
245
  end
238
246
  end
239
247
 
240
- def open3_ios_for(name, redirects)
248
+ def open3_ios_for(name, redirects, locals)
241
249
  if redirects and user_supplied_io = redirects[name] and IO === user_supplied_io
242
- name == :in ? user_supplied_io : [nil, user_supplied_io]
250
+ name == :in ? [user_supplied_io, nil] : [nil, user_supplied_io]
243
251
  else
244
- IO.pipe
252
+ IO.pipe.tap { |pipes| locals.concat pipes }
245
253
  end
246
254
  end
247
255
 
@@ -251,9 +259,15 @@ module Net::SSH
251
259
  private_constant :SSH_EXTENDED_DATA_STDERR, :REMOTE_PACKET_THRESHOLD
252
260
 
253
261
  def install_channel_callbacks(channel, options)
254
- logger, stdin, stdout, stderr =
255
- options[:logger], options[:stdin], options[:stdout], options[:stderr]
256
- pid_initialized = false
262
+ logger, stdin, stdout, stderr, local_pipes =
263
+ options[:logger], options[:stdin], options[:stdout], options[:stderr], options[:local_pipes]
264
+
265
+ if options[:fetch_pid]
266
+ pid_initialized = false
267
+ else
268
+ channel.open3_waiter_thread[:status].instance_variable_set(:@pid, false)
269
+ pid_initialized = true
270
+ end
257
271
 
258
272
  channel.on_open_failed do |_channel, code, desc|
259
273
  message = "cannot open channel (error code #{code}): #{desc}"
@@ -306,16 +320,19 @@ module Net::SSH
306
320
 
307
321
  channel.on_eof do
308
322
  logger.debug('server reports EOF') if logger
309
- [stdout, stderr].each { |io| io.close unless io.nil? || io.closed? }
323
+ [stdout, stderr].each { |io| io.close if !io.closed? && local_pipes.include?(io) }
310
324
  end
311
325
 
312
326
  channel.on_close do
313
327
  logger.debug('channel close command received, will enforce EOF afterwards') if logger
314
- if stdin.is_a?(IO)
315
- self.stop_listening_to(stdin)
316
- stdin.close unless stdin.closed?
328
+ begin
329
+ if stdin.is_a?(IO)
330
+ self.stop_listening_to(stdin)
331
+ stdin.close if !stdin.closed? && local_pipes.include?(stdin)
332
+ end
333
+ ensure
334
+ channel.do_eof # Should already be done, but just in case.
317
335
  end
318
- channel.do_eof # Should already be done, but just in case.
319
336
  end
320
337
 
321
338
  if stdin.is_a?(IO)
@@ -361,22 +378,29 @@ module Net::SSH
361
378
  retries, delay = options[:channel_retries]
362
379
  retries ||= 5
363
380
  delay ||= 1
381
+ fetch_pid = options[:fetch_pid] != false
382
+ local_pipes = Array(internal_options[:local_pipes])
364
383
 
365
384
  logger.init(host: self.transport.host_as_string, cmdline: cmdline,
366
385
  env: env, pty: pty_options) if logger.respond_to?(:init)
367
386
 
387
+ cmdline = "echo $$; exec #{cmdline}" if fetch_pid
388
+
368
389
  begin
369
390
  channel = open3_open_channel do |channel|
391
+ channel.open3_signal_open unless fetch_pid
370
392
  channel.request_pty(Hash === pty_options ? pty_options : {}) if pty_options
371
393
  env.each_pair { |var_name, var_value| channel.env(var_name, var_value) }
372
394
 
373
- channel.exec("echo $$; #{cmdline}")
395
+ channel.exec(cmdline)
374
396
 
375
397
  install_channel_callbacks channel,
376
398
  stdin: internal_options[:stdin],
377
399
  stdout: internal_options[:stdout],
378
400
  stderr: internal_options[:stderr],
379
- logger: logger
401
+ logger: logger,
402
+ local_pipes: local_pipes,
403
+ fetch_pid: fetch_pid
380
404
  end.open3_wait_open
381
405
  rescue ChannelOpenFailed
382
406
  logger.warn("channel open failed: #$!, #{retries} retries left") if logger
@@ -393,12 +417,7 @@ module Net::SSH
393
417
  channel.wait
394
418
  end
395
419
  ensure
396
- [
397
- *internal_options[:block_pipes],
398
- internal_options[:stdin],
399
- internal_options[:stdout],
400
- internal_options[:stderr]
401
- ].each { |io| io.close if io.is_a?(IO) && !io.closed? }
420
+ local_pipes.each { |io| io.close unless io.closed? }
402
421
  end
403
422
 
404
423
  def popen_io_name(name)
@@ -423,12 +442,18 @@ module Net::SSH
423
442
 
424
443
  # open3_ping method will pull waiter thread out of select(2) call
425
444
  # to update watched Channels and IOs and process incomes.
426
- pinger_reader, @open3_pinger_writer = IO.pipe
427
- listen_to(pinger_reader) { pinger_reader.readpartial(1) }
445
+ @open3_pinger_reader, @open3_pinger_writer = IO.pipe
446
+ listen_to(@open3_pinger_reader) { @open3_pinger_reader.readpartial(1) }
428
447
 
429
448
  @session_loop = Thread.new { open3_loop }
430
449
  end
431
450
 
451
+ alias_method_once :close_without_open3, :close
452
+ def close(*args, &block)
453
+ @open3_closing = true
454
+ close_without_open3(*args, &block).tap { open3_ping rescue nil }
455
+ end
456
+
432
457
  private
433
458
  def open3_open_channel(type = 'session', *extra, &on_confirm)
434
459
  @open3_channels_mutex.synchronize do
@@ -467,6 +492,7 @@ module Net::SSH
467
492
  w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? }
468
493
  end
469
494
 
495
+ break if @open3_closing
470
496
  readers, writers, = Compat.io_select(r, w, nil, nil)
471
497
  postprocess(readers, writers)
472
498
  end
@@ -480,6 +506,8 @@ module Net::SSH
480
506
  end
481
507
  rescue
482
508
  warn "Caught exception in an Open3 loop: #$!; thread terminating, connections will hang."
509
+ ensure
510
+ [@open3_pinger_reader, @open3_pinger_writer].each(&:close)
483
511
  end
484
512
  end
485
513
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-ssh-open3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Artem Sheremet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-30 00:00:00.000000000 Z
11
+ date: 2013-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-ssh
@@ -52,7 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
52
  version: '0'
53
53
  requirements: []
54
54
  rubyforge_project:
55
- rubygems_version: 2.0.3
55
+ rubygems_version: 2.0.14
56
56
  signing_key:
57
57
  specification_version: 4
58
58
  summary: Thread-safe Open3 for Net::SSH