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.
- checksums.yaml +4 -4
- data/README.md +3 -0
- data/lib/net-ssh-open3.rb +59 -31
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7e8512f0619ba893c15747668049e3b609a8f35
|
4
|
+
data.tar.gz: e25fc932b83773b5d02586c89b9a2144e1ed1cbe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
193
|
-
|
194
|
-
|
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
|
-
|
208
|
-
|
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
|
-
|
222
|
-
|
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
|
-
|
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
|
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
|
-
|
315
|
-
|
316
|
-
|
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(
|
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
|
-
|
427
|
-
listen_to(
|
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.
|
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-
|
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.
|
55
|
+
rubygems_version: 2.0.14
|
56
56
|
signing_key:
|
57
57
|
specification_version: 4
|
58
58
|
summary: Thread-safe Open3 for Net::SSH
|