net-ssh-open3 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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