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.
- 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
|