rye 0.8.19 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +17 -1
- data/README.rdoc +2 -29
- data/bin/try +42 -13
- data/lib/rye/box.rb +318 -224
- data/lib/rye/cmd.rb +156 -78
- data/lib/rye/rap.rb +10 -6
- data/lib/rye.rb +32 -5
- data/rye.gemspec +4 -7
- metadata +68 -49
- data/bin/rye +0 -147
data/lib/rye/box.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
require 'annoy'
|
2
|
+
require 'readline'
|
2
3
|
|
3
4
|
module Rye
|
5
|
+
DEBUG = false unless defined?(Rye::DEBUG)
|
4
6
|
|
5
7
|
# = Rye::Box
|
6
8
|
#
|
@@ -29,12 +31,18 @@ module Rye
|
|
29
31
|
class Box
|
30
32
|
include Rye::Cmd
|
31
33
|
|
34
|
+
attr_accessor :rye_shell
|
35
|
+
attr_accessor :rye_pty
|
36
|
+
|
32
37
|
def host; @rye_host; end
|
33
38
|
def opts; @rye_opts; end
|
34
39
|
def safe; @rye_safe; end
|
35
40
|
def user; @rye_user; end
|
36
41
|
def root?; user.to_s == "root" end
|
37
42
|
|
43
|
+
def templates; @rye_templates; end
|
44
|
+
def templates?; !@rye_templates.nil?; end
|
45
|
+
|
38
46
|
def enable_sudo; @rye_sudo = true; end
|
39
47
|
def disable_sudo; @rye_sudo = false; end
|
40
48
|
def sudo?; @rye_sudo == true end
|
@@ -64,13 +72,14 @@ module Rye
|
|
64
72
|
# The most recent valud for umask (or 0022)
|
65
73
|
def current_umask; @rye_current_umask; end
|
66
74
|
|
67
|
-
def info
|
68
|
-
def debug
|
69
|
-
def error
|
75
|
+
def info?; !@rye_info.nil?; end
|
76
|
+
def debug?; !@rye_debug.nil?; end
|
77
|
+
def error?; !@rye_error.nil?; end
|
70
78
|
|
71
79
|
def ostype=(val); @rye_ostype = val; end
|
72
80
|
def impltype=(val); @rye_impltype = val; end
|
73
81
|
def pre_command_hook=(val); @rye_pre_command_hook = val; end
|
82
|
+
def stdout_hook=(val); @rye_stdout_hook = val; end
|
74
83
|
def post_command_hook=(val); @rye_post_command_hook = val; end
|
75
84
|
# A Hash. The keys are exception classes, the values are Procs to execute
|
76
85
|
def exception_hook=(val); @rye_exception_hook = val; end
|
@@ -89,6 +98,7 @@ module Rye
|
|
89
98
|
# * :error => an IO object to print Rye::Box errors to. Default: STDERR
|
90
99
|
# * :getenv => pre-fetch +host+ environment variables? (default: true)
|
91
100
|
# * :password => the user's password (ignored if there's a valid private key)
|
101
|
+
# * :templates => the template engine to use for uploaded files. One of: :erb (default)
|
92
102
|
# * :sudo => Run all commands via sudo (default: false)
|
93
103
|
#
|
94
104
|
# NOTE: +opts+ can also contain any parameter supported by
|
@@ -109,11 +119,12 @@ module Rye
|
|
109
119
|
@rye_opts = {
|
110
120
|
:safe => true,
|
111
121
|
:port => ssh_opts[:port],
|
112
|
-
:keys =>
|
122
|
+
:keys => Rye.keys,
|
113
123
|
:info => nil,
|
114
124
|
:debug => nil,
|
115
125
|
:error => STDERR,
|
116
126
|
:getenv => true,
|
127
|
+
:templates => :erb,
|
117
128
|
:quiet => false
|
118
129
|
}.merge(opts)
|
119
130
|
|
@@ -128,16 +139,24 @@ module Rye
|
|
128
139
|
@rye_getenv = {} if @rye_opts.delete(:getenv) # Enable getenv with a hash
|
129
140
|
@rye_ostype, @rye_impltype = @rye_opts.delete(:ostype), @rye_opts.delete(:impltype)
|
130
141
|
@rye_quiet, @rye_sudo = @rye_opts.delete(:quiet), @rye_opts.delete(:sudo)
|
142
|
+
@rye_templates = @rye_opts.delete(:templates)
|
131
143
|
|
132
|
-
#
|
133
|
-
@
|
134
|
-
|
135
|
-
|
144
|
+
# Store the state of the terminal
|
145
|
+
@rye_stty_save = `stty -g`.chomp rescue nil
|
146
|
+
|
147
|
+
unless @rye_templates.nil?
|
148
|
+
require @rye_templates.to_s # should be :erb
|
149
|
+
end
|
136
150
|
|
137
151
|
@rye_opts[:logger] = Logger.new(@rye_debug) if @rye_debug # Enable Net::SSH debugging
|
138
152
|
@rye_opts[:paranoid] = true unless @rye_safe == false # See Net::SSH.start
|
139
153
|
@rye_opts[:keys] = [@rye_opts[:keys]].flatten.compact
|
140
154
|
|
155
|
+
# Just in case someone sends a true value rather than IO object
|
156
|
+
@rye_debug = STDERR if @rye_debug == true || DEBUG
|
157
|
+
@rye_error = STDERR if @rye_error == true
|
158
|
+
@rye_info = STDOUT if @rye_info == true
|
159
|
+
|
141
160
|
# Add the given private keys to the keychain that will be used for @rye_host
|
142
161
|
add_keys(@rye_opts[:keys])
|
143
162
|
|
@@ -223,12 +242,23 @@ module Rye
|
|
223
242
|
end
|
224
243
|
|
225
244
|
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
245
|
+
# If STDIN.tty? is true (i.e. if we're connected to a terminal
|
246
|
+
# with a human at the helm), this will open an SSH connection
|
247
|
+
# via the regular SSH command (via a call to system). This
|
248
|
+
# requires the SSH command-line executable (ssh).
|
249
|
+
#
|
250
|
+
# If STDIN.tty? is false or +run+ is false, this will return
|
251
|
+
# the SSH command (a String) that would have been run.
|
252
|
+
#
|
253
|
+
# NOTE: As of Rye 0.9 you can run interactive sessions with
|
254
|
+
# rye by calling any shell method without arguments.
|
255
|
+
#
|
256
|
+
# e.g.
|
257
|
+
#
|
258
|
+
# rbox = Rye::Box.new 'somemachine'
|
259
|
+
# rbox.bash
|
231
260
|
#
|
261
|
+
# TODO: refactor to use net_ssh_exec! in 0.9
|
232
262
|
def interactive_ssh(run=true)
|
233
263
|
debug "interactive_ssh with keys: #{Rye.keys.inspect}"
|
234
264
|
run = false unless STDIN.tty?
|
@@ -249,11 +279,11 @@ module Rye
|
|
249
279
|
additional_keys = [additional_keys].flatten.compact || []
|
250
280
|
return if additional_keys.empty?
|
251
281
|
ret = Rye.add_keys(additional_keys)
|
252
|
-
if ret.is_a?(Rye::Rap)
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
end
|
282
|
+
#if ret.is_a?(Rye::Rap)
|
283
|
+
# debug "ssh-add exit_status: #{ret.exit_status}"
|
284
|
+
# debug "ssh-add stdout: #{ret.stdout}"
|
285
|
+
# debug "ssh-add stderr: #{ret.stderr}"
|
286
|
+
#end
|
257
287
|
self # MUST RETURN self
|
258
288
|
end
|
259
289
|
alias :add_key :add_keys
|
@@ -379,110 +409,6 @@ module Rye
|
|
379
409
|
@rye_guessed_homes[this_user] = "#{user_defaults['HOME']}/#{this_user}"
|
380
410
|
end
|
381
411
|
|
382
|
-
# Copy the local public keys (as specified by Rye.keys) to
|
383
|
-
# this box into ~/.ssh/authorized_keys and ~/.ssh/authorized_keys2.
|
384
|
-
# Returns a Rye::Rap object. The private keys files used to generate
|
385
|
-
# the public keys are contained in stdout.
|
386
|
-
# Raises a Rye::ComandError if the home directory doesn't exit.
|
387
|
-
# NOTE: authorize_keys_remote disables safe-mode for this box while it runs
|
388
|
-
# which will hit you funky style if your using a single instance
|
389
|
-
# of Rye::Box in a multithreaded situation.
|
390
|
-
#
|
391
|
-
def authorize_keys_remote(other_user=nil)
|
392
|
-
if other_user.nil?
|
393
|
-
this_user = opts[:user]
|
394
|
-
homedir = self.quietly { pwd }.first
|
395
|
-
else
|
396
|
-
this_user = other_user
|
397
|
-
# The homedir path is important b/c this is where we're going to
|
398
|
-
# look for the .ssh directory. That's where auth love is stored.
|
399
|
-
homedir = self.guess_user_home(this_user)
|
400
|
-
end
|
401
|
-
|
402
|
-
added_keys = []
|
403
|
-
rap = Rye::Rap.new(self)
|
404
|
-
|
405
|
-
prevdir = self.current_working_directory
|
406
|
-
|
407
|
-
unless self.file_exists?(homedir)
|
408
|
-
rap.add_exit_code(1)
|
409
|
-
rap.add_stderr("Path does not exist: #{homedir}")
|
410
|
-
raise Rye::CommandError.new(rap)
|
411
|
-
end
|
412
|
-
|
413
|
-
# Let's go into the user's home directory that we now know exists.
|
414
|
-
self.cd homedir
|
415
|
-
|
416
|
-
files = ['.ssh/authorized_keys', '.ssh/authorized_keys2', '.ssh/identity']
|
417
|
-
files.each do |akey_path|
|
418
|
-
if self.file_exists?(akey_path)
|
419
|
-
# TODO: Make Rye::Cmd.incremental_backup
|
420
|
-
self.cp(akey_path, "#{akey_path}-previous")
|
421
|
-
authorized_keys = self.file_download("#{homedir}/#{akey_path}")
|
422
|
-
end
|
423
|
-
authorized_keys ||= StringIO.new
|
424
|
-
|
425
|
-
Rye.keys.each do |path|
|
426
|
-
|
427
|
-
info "# Adding public key for #{path}"
|
428
|
-
k = Rye::Key.from_file(path).public_key.to_ssh2
|
429
|
-
authorized_keys.puts k
|
430
|
-
end
|
431
|
-
|
432
|
-
# Remove duplicate authorized keys
|
433
|
-
authorized_keys.rewind
|
434
|
-
uniqlines = authorized_keys.readlines.uniq.join
|
435
|
-
authorized_keys = StringIO.new(uniqlines)
|
436
|
-
# We need to rewind so that all of the StringIO object is uploaded
|
437
|
-
authorized_keys.rewind
|
438
|
-
|
439
|
-
full_path = "#{homedir}/#{akey_path}"
|
440
|
-
temp_path = "/tmp/rye-#{user}-#{File.basename(akey_path)}"
|
441
|
-
|
442
|
-
sudo do
|
443
|
-
mkdir :p, :m, '700', File.dirname(akey_path)
|
444
|
-
file_upload authorized_keys, temp_path
|
445
|
-
mv temp_path, full_path
|
446
|
-
chmod '0600', akey_path
|
447
|
-
chown :R, this_user.to_s, File.dirname(akey_path)
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
# And let's return to the directory we came from.
|
452
|
-
self.cd prevdir
|
453
|
-
|
454
|
-
rap.add_exit_code(0)
|
455
|
-
rap
|
456
|
-
end
|
457
|
-
require 'fileutils'
|
458
|
-
# Authorize the current user to login to the local machine via
|
459
|
-
# SSH without a password. This is the same functionality as
|
460
|
-
# authorize_keys_remote except run with local shell commands.
|
461
|
-
def authorize_keys_local
|
462
|
-
added_keys = []
|
463
|
-
ssh_dir = File.join(Rye.sysinfo.home, '.ssh')
|
464
|
-
Rye.keys.each do |path|
|
465
|
-
debug "# Public key for #{path}"
|
466
|
-
k = Rye::Key.from_file(path).public_key.to_ssh2
|
467
|
-
FileUtils.mkdir ssh_dir unless File.exists? ssh_dir
|
468
|
-
|
469
|
-
authkeys_file = File.join(ssh_dir, 'authorized_keys')
|
470
|
-
|
471
|
-
debug "Writing to #{authkeys_file}"
|
472
|
-
File.open(authkeys_file, 'a') {|f| f.write("#{$/}#{k}") }
|
473
|
-
File.open("#{authkeys_file}2", 'a') {|f| f.write("#{$/}#{k}") }
|
474
|
-
|
475
|
-
unless Rye.sysinfo.os == :windows
|
476
|
-
Rye.shell(:chmod, '700', ssh_dir)
|
477
|
-
Rye.shell(:chmod, '0600', authkeys_file)
|
478
|
-
Rye.shell(:chmod, '0600', "#{authkeys_file}2")
|
479
|
-
end
|
480
|
-
|
481
|
-
added_keys << path
|
482
|
-
end
|
483
|
-
added_keys
|
484
|
-
end
|
485
|
-
|
486
412
|
# A handler for undefined commands.
|
487
413
|
# Raises Rye::CommandNotFound exception.
|
488
414
|
def method_missing(cmd, *args, &block)
|
@@ -518,6 +444,17 @@ module Rye
|
|
518
444
|
@rye_pre_command_hook
|
519
445
|
end
|
520
446
|
|
447
|
+
# Supply a block to be called every time a command receives STDOUT data.
|
448
|
+
#
|
449
|
+
# e.g.
|
450
|
+
# rbox.stdout_hook do |content|
|
451
|
+
# ...
|
452
|
+
# end
|
453
|
+
def stdout_hook(&block)
|
454
|
+
@rye_stdout_hook = block if block
|
455
|
+
@rye_stdout_hook
|
456
|
+
end
|
457
|
+
|
521
458
|
# Supply a block to be called whenever there's an Exception. It's called
|
522
459
|
# with 1 argument: the exception class. If the exception block returns
|
523
460
|
# :retry, the command will be executed again.
|
@@ -559,7 +496,7 @@ module Rye
|
|
559
496
|
# Returns the return value of the block.
|
560
497
|
#
|
561
498
|
def batch(*args, &block)
|
562
|
-
self.instance_exec
|
499
|
+
self.instance_exec(*args, &block)
|
563
500
|
end
|
564
501
|
|
565
502
|
# Like batch, except it disables safe mode before executing the block.
|
@@ -573,6 +510,7 @@ module Rye
|
|
573
510
|
@rye_safe = previous_state
|
574
511
|
ret
|
575
512
|
end
|
513
|
+
alias_method :wildly, :unsafely
|
576
514
|
|
577
515
|
# See unsafely (except in reverse)
|
578
516
|
def safely(*args, &block)
|
@@ -607,7 +545,7 @@ module Rye
|
|
607
545
|
# command.
|
608
546
|
def sudo(*args, &block)
|
609
547
|
if block.nil?
|
610
|
-
|
548
|
+
run_command('sudo', args);
|
611
549
|
else
|
612
550
|
previous_state = @rye_sudo
|
613
551
|
enable_sudo
|
@@ -703,15 +641,19 @@ module Rye
|
|
703
641
|
def disconnect
|
704
642
|
return unless @rye_ssh && !@rye_ssh.closed?
|
705
643
|
begin
|
706
|
-
|
707
|
-
|
644
|
+
if @rye_ssh.busy?;
|
645
|
+
info "Is something still running? (ctrl-C to exit)"
|
646
|
+
Timeout::timeout(10) do
|
647
|
+
@rye_ssh.loop(0.3) { @rye_ssh.busy?; }
|
648
|
+
end
|
708
649
|
end
|
650
|
+
debug "Closing connection to #{@rye_ssh.host}"
|
651
|
+
@rye_ssh.close
|
709
652
|
rescue SystemCallError, Timeout::Error => ex
|
710
|
-
error "Disconnect timeout
|
653
|
+
error "Disconnect timeout"
|
654
|
+
rescue Interrupt
|
655
|
+
debug "Exiting..."
|
711
656
|
end
|
712
|
-
|
713
|
-
debug "Closing connection to #{@rye_ssh.host}"
|
714
|
-
@rye_ssh.close
|
715
657
|
end
|
716
658
|
|
717
659
|
|
@@ -751,11 +693,13 @@ module Rye
|
|
751
693
|
# This method will try to connect to the host automatically
|
752
694
|
# but if it fails it will raise a Rye::NotConnected exception.
|
753
695
|
#
|
754
|
-
def run_command(*args)
|
755
|
-
debug "run_command
|
696
|
+
def run_command(*args, &blk)
|
697
|
+
debug "run_command"
|
756
698
|
|
757
699
|
cmd, args = prep_args(*args)
|
758
700
|
|
701
|
+
#p [:run_command, cmd, blk.nil?]
|
702
|
+
|
759
703
|
connect if !@rye_ssh || @rye_ssh.closed?
|
760
704
|
raise Rye::NotConnected, @rye_host unless @rye_ssh && !@rye_ssh.closed?
|
761
705
|
|
@@ -769,7 +713,7 @@ module Rye
|
|
769
713
|
# The command will otherwise run in the user's home directory.
|
770
714
|
if @rye_current_working_directory
|
771
715
|
cwd = Rye.escape(@rye_safe, 'cd', @rye_current_working_directory)
|
772
|
-
cmd_internal = [cwd, cmd_internal]
|
716
|
+
cmd_internal = '(%s; %s)' % [cwd, cmd_internal]
|
773
717
|
end
|
774
718
|
|
775
719
|
# ditto (same explanation as cwd)
|
@@ -797,22 +741,24 @@ module Rye
|
|
797
741
|
rap = Rye::Rap.new(self)
|
798
742
|
rap.cmd = cmd_clean
|
799
743
|
|
800
|
-
|
744
|
+
channel = net_ssh_exec!(cmd_internal, &blk)
|
801
745
|
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
746
|
+
if channel[:exception]
|
747
|
+
rap = channel[:exception].rap
|
748
|
+
else
|
749
|
+
rap.add_stdout(channel[:stdout].read || '')
|
750
|
+
rap.add_stderr(channel[:stderr].read || '')
|
751
|
+
rap.add_exit_status(channel[:exit_status])
|
752
|
+
rap.exit_signal = channel[:exit_signal]
|
753
|
+
end
|
806
754
|
|
807
|
-
|
808
|
-
#info "stderr: #{rap.stderr}"
|
809
|
-
#info "exit_code: #{rap.exit_code}"
|
755
|
+
debug "RESULT: %s " % [rap.inspect]
|
810
756
|
|
811
757
|
# It seems a convention for various commands to return -1
|
812
|
-
# when something only mildly concerning happens. ls even
|
813
|
-
# returns -1 for apparently no reason sometimes.
|
814
|
-
#
|
815
|
-
raise Rye::
|
758
|
+
# when something only mildly concerning happens. (ls even
|
759
|
+
# returns -1 for apparently no reason sometimes). Anyway,
|
760
|
+
# the real errors are the ones that are greater than zero.
|
761
|
+
raise Rye::Err.new(rap) if rap.exit_status != 0
|
816
762
|
|
817
763
|
rescue Exception => ex
|
818
764
|
return rap if @rye_quiet
|
@@ -826,7 +772,14 @@ module Rye
|
|
826
772
|
retry
|
827
773
|
elsif choice == :skip
|
828
774
|
# do nothing
|
829
|
-
|
775
|
+
elsif choice == :interactive && !@rye_shell
|
776
|
+
@rye_shell = true
|
777
|
+
previous_state = @rye_sudo
|
778
|
+
disable_sudo
|
779
|
+
bash
|
780
|
+
@rye_sudo = previous_state
|
781
|
+
@rye_shell = false
|
782
|
+
elsif !ex.is_a?(Interrupt)
|
830
783
|
raise ex, ex.message
|
831
784
|
end
|
832
785
|
end
|
@@ -858,88 +811,233 @@ module Rye
|
|
858
811
|
[cmd, args]
|
859
812
|
end
|
860
813
|
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
814
|
+
def net_ssh_exec!(cmd, &blk)
|
815
|
+
debug ":net_ssh_exec #{cmd} (has blk: #{!blk.nil?}; pty: #{@rye_pty}; shell: #{@rye_shell})"
|
816
|
+
|
817
|
+
pty_opts = { :term => "xterm",
|
818
|
+
:chars_wide => 80,
|
819
|
+
:chars_high => 24,
|
820
|
+
:pixels_wide => 640,
|
821
|
+
:pixels_high => 480,
|
822
|
+
:modes => {} }
|
823
|
+
|
824
|
+
channel = @rye_ssh.open_channel do |channel|
|
825
|
+
if self.rye_shell && blk.nil?
|
826
|
+
channel.request_pty(pty_opts) do |ch,success|
|
827
|
+
self.rye_pty = success
|
828
|
+
raise Rye::NoPty if !success
|
829
|
+
end
|
830
|
+
end
|
831
|
+
channel.exec(cmd, &create_channel)
|
832
|
+
channel[:state] = :start_session
|
833
|
+
channel[:block] = blk
|
834
|
+
end
|
835
|
+
|
836
|
+
@rye_channels ||= []
|
837
|
+
@rye_channels << channel
|
838
|
+
|
839
|
+
@rye_ssh.loop(0.1) do
|
840
|
+
break if channel.nil? || !channel.active?
|
841
|
+
!channel.eof? # otherwise keep returning true
|
842
|
+
end
|
843
|
+
|
844
|
+
channel
|
845
|
+
end
|
846
|
+
|
847
|
+
|
848
|
+
def state_wait_for_command(channel)
|
849
|
+
debug :wait_for_command
|
850
|
+
end
|
872
851
|
|
873
|
-
|
852
|
+
def state_start_session(channel)
|
853
|
+
debug "#{:start_session} [blk: #{!channel[:block].nil?}] [pty: #{@rye_pty}] [shell: #{@rye_shell}]"
|
854
|
+
channel[:state] = nil
|
855
|
+
channel[:state] = :run_block if channel[:block]
|
856
|
+
channel[:state] = :await_response if @rye_pty
|
857
|
+
end
|
858
|
+
|
859
|
+
def state_await_response(channel)
|
860
|
+
debug :await_response
|
861
|
+
@await_response_counter ||= 0
|
862
|
+
if channel[:stdout].available > 0 || channel[:stderr].available > 0
|
863
|
+
channel[:state] = :read_response
|
864
|
+
elsif @await_response_counter > 50
|
865
|
+
@await_response_counter = 0
|
866
|
+
channel[:state] = :await_input
|
867
|
+
end
|
868
|
+
@await_response_counter += 1
|
869
|
+
end
|
870
|
+
|
871
|
+
def state_read_response(channel)
|
872
|
+
debug :read_response
|
873
|
+
if channel[:stdout].available > 0 || channel[:stderr].available > 0
|
874
874
|
|
875
|
-
channel[:stdout]
|
876
|
-
channel[:stderr]
|
875
|
+
stdout = channel[:stdout].read if channel[:stdout].available > 0
|
876
|
+
stderr = channel[:stderr].read if channel[:stderr].available > 0
|
877
877
|
|
878
|
+
print stdout if stdout
|
879
|
+
print stderr if stderr
|
878
880
|
|
879
|
-
if
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
881
|
+
if channel[:stack].empty?
|
882
|
+
channel[:state] = :await_input
|
883
|
+
elsif channel[:stdout].available > 0 || channel[:stderr].available > 0
|
884
|
+
channel[:state] = :read_response
|
885
|
+
else
|
886
|
+
channel[:state] = :send_data
|
887
|
+
end
|
888
|
+
else
|
889
|
+
channel[:state] = :await_response
|
890
|
+
end
|
891
|
+
|
892
|
+
end
|
893
|
+
|
894
|
+
def state_send_data(channel)
|
895
|
+
debug :send_data
|
896
|
+
cmd = channel[:stack].shift
|
897
|
+
debug "sending #{cmd.inspect}"
|
898
|
+
channel[:state] = :await_response
|
899
|
+
channel.send_data("#{cmd}\n") unless channel.eof?
|
900
|
+
end
|
901
|
+
|
902
|
+
def state_await_input(channel)
|
903
|
+
debug :await_input
|
904
|
+
if channel[:stdout].available > 0
|
905
|
+
channel[:state] = :read_response
|
906
|
+
else
|
907
|
+
ret = nil
|
908
|
+
if channel[:prompt] && (channel[:prompt] =~ /pass/i)
|
909
|
+
ret = Annoy.get_user_input("#{channel[:prompt]} ", echo='*', period=30)
|
910
|
+
channel[:prompt] = nil
|
888
911
|
end
|
912
|
+
begin
|
913
|
+
list = self.commands.sort
|
889
914
|
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
ret =
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
915
|
+
comp = proc { |s|
|
916
|
+
# TODO: Something here for files
|
917
|
+
list.grep( /^#{Regexp.escape(s)}/ )
|
918
|
+
}
|
919
|
+
|
920
|
+
Readline.completion_append_character = " "
|
921
|
+
Readline.completion_proc = comp
|
922
|
+
|
923
|
+
ret = Readline.readline(channel[:prompt] || '', true)
|
924
|
+
#ret = STDIN.gets
|
925
|
+
|
926
|
+
if ret.nil?
|
927
|
+
channel[:state] = :exit
|
928
|
+
else
|
929
|
+
channel[:stack] << ret.chomp
|
930
|
+
channel[:state] = :send_data
|
931
|
+
end
|
932
|
+
rescue Interrupt => e
|
933
|
+
channel[:state] = :exit
|
903
934
|
end
|
935
|
+
channel[:prompt] = nil
|
904
936
|
end
|
905
|
-
|
937
|
+
end
|
938
|
+
|
939
|
+
def state_ignore_response(channel)
|
940
|
+
debug :ignore_response
|
941
|
+
@ignore_response_counter ||= 0
|
942
|
+
if channel[:stdout].available > 0
|
943
|
+
@await_response_counter = 0
|
944
|
+
channel[:stdout].read
|
945
|
+
channel[:state] = :process
|
946
|
+
elsif @ignore_response_counter > 2
|
947
|
+
@await_response_counter = 0
|
948
|
+
channel[:state] = :process
|
906
949
|
end
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
950
|
+
@ignore_response_counter += 1
|
951
|
+
end
|
952
|
+
|
953
|
+
def state_exit(channel)
|
954
|
+
debug :exit_state
|
955
|
+
channel[:state] = nil
|
956
|
+
if rye_shell && (!channel.eof? || !channel.closing?)
|
957
|
+
puts
|
958
|
+
channel.send_data("exit\n")
|
959
|
+
else
|
960
|
+
channel.eof!
|
913
961
|
end
|
914
|
-
|
915
|
-
|
916
|
-
|
962
|
+
end
|
963
|
+
|
964
|
+
# TODO: implement callback in create_channel Proc
|
965
|
+
##def state_handle_error(channel)
|
966
|
+
## debug :handle_error
|
967
|
+
## channel[:state] = nil
|
968
|
+
## if rye_shell && (!channel.eof? || !channel.closing?)
|
969
|
+
## puts
|
970
|
+
## channel.send_data("exit\n")
|
971
|
+
## else
|
972
|
+
## channel.eof!
|
973
|
+
## end
|
974
|
+
##end
|
975
|
+
|
976
|
+
|
977
|
+
def state_run_block(channel)
|
978
|
+
debug :run_block
|
979
|
+
channel[:state] = nil
|
980
|
+
blk = channel[:block]
|
981
|
+
channel[:block] = nil
|
982
|
+
begin
|
983
|
+
instance_eval &blk
|
984
|
+
rescue => ex
|
985
|
+
channel[:exception] = ex
|
917
986
|
end
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
987
|
+
channel[:state] = :exit
|
988
|
+
end
|
989
|
+
|
990
|
+
def create_channel()
|
991
|
+
Proc.new do |channel,success|
|
992
|
+
channel[:stdout ] = Net::SSH::Buffer.new
|
993
|
+
channel[:stderr ] = Net::SSH::Buffer.new
|
994
|
+
channel[:stack] ||= []
|
995
|
+
channel.on_close { |ch|
|
996
|
+
channel[:handler] = ":on_close"
|
997
|
+
}
|
998
|
+
channel.on_data { |ch, data|
|
999
|
+
channel[:handler] = ":on_data"
|
1000
|
+
@rye_stdout_hook.call(data) if !@rye_pty && !@rye_quiet && @rye_stdout_hook.kind_of?(Proc)
|
1001
|
+
if rye_pty && data =~ /password/i
|
1002
|
+
channel[:prompt] = data
|
1003
|
+
channel[:state] = :await_input
|
1004
|
+
else
|
1005
|
+
channel[:stdout].append(data)
|
1006
|
+
end
|
1007
|
+
}
|
1008
|
+
channel.on_extended_data { |ch, type, data|
|
1009
|
+
channel[:handler] = ":on_extended_data"
|
1010
|
+
if rye_pty && data =~ /\Apassword/i
|
1011
|
+
channel[:prompt] = data
|
1012
|
+
channel[:state] = :await_input
|
1013
|
+
else
|
1014
|
+
channel[:stderr].append(data)
|
1015
|
+
end
|
1016
|
+
}
|
1017
|
+
channel.on_request("exit-status") { |ch, data|
|
1018
|
+
channel[:handler] = ":on_request (exit-status)"
|
1019
|
+
channel[:exit_status] = data.read_long
|
1020
|
+
}
|
1021
|
+
channel.on_request("exit-signal") do |ch, data|
|
1022
|
+
channel[:handler] = ":on_request (exit-signal)"
|
1023
|
+
# This should be the POSIX SIGNAL that ended the process
|
1024
|
+
channel[:exit_signal] = data.read_long
|
1025
|
+
end
|
1026
|
+
channel.on_process {
|
1027
|
+
channel[:handler] = :on_process
|
1028
|
+
print channel[:stderr].read if channel[:stderr].available > 0
|
1029
|
+
begin
|
1030
|
+
send("state_#{channel[:state]}", channel) unless channel[:state].nil?
|
1031
|
+
rescue Interrupt
|
1032
|
+
debug :on_process_interrupt
|
1033
|
+
channel[:state] = :exit
|
1034
|
+
end
|
1035
|
+
}
|
922
1036
|
end
|
923
|
-
|
924
|
-
## I'm getting weird behavior with exit codes. Sometimes
|
925
|
-
## a command which usually returns an exit code will not
|
926
|
-
## return one the next time it's run. The following crap
|
927
|
-
## was from the debugging.
|
928
|
-
##Kernel.sleep 5
|
929
|
-
###channel.close
|
930
|
-
#channel.eof!
|
931
|
-
##p [:active, channel.active?]
|
932
|
-
##p [:closing, channel.closing?]
|
933
|
-
##p [:eof, channel.eof?]
|
934
|
-
|
935
|
-
channel[:exit_code] ||= 0
|
936
|
-
channel[:exit_code] &&= channel[:exit_code].to_i
|
937
|
-
channel[:stderr].gsub!(/bash: line \d+:\s+/, '') if channel[:stderr]
|
938
|
-
|
939
|
-
[channel[:stdout], channel[:stderr], channel[:exit_code], channel[:exit_signal]]
|
940
1037
|
end
|
941
1038
|
|
942
1039
|
|
1040
|
+
|
943
1041
|
# * +direction+ is one of :upload, :download
|
944
1042
|
# * +recursive+ should be true for directories and false for files.
|
945
1043
|
# * +files+ is an Array of file paths, the content is direction specific.
|
@@ -978,6 +1076,7 @@ module Rye
|
|
978
1076
|
end
|
979
1077
|
|
980
1078
|
elsif direction == :upload
|
1079
|
+
# p :UPLOAD, @rye_templates
|
981
1080
|
raise "Cannot upload to a StringIO object" if target.is_a?(StringIO)
|
982
1081
|
if files.size == 1
|
983
1082
|
target = self.getenv['HOME'] || guess_user_home
|
@@ -989,7 +1088,9 @@ module Rye
|
|
989
1088
|
# Expand fileglobs (e.g. path/*.rb becomes [path/1.rb, path/2.rb]).
|
990
1089
|
# This should happen after checking files.size to determine the target
|
991
1090
|
unless @rye_safe
|
992
|
-
files.collect! { |file|
|
1091
|
+
files.collect! { |file|
|
1092
|
+
file.is_a?(StringIO) ? file : Dir.glob(File.expand_path(file))
|
1093
|
+
}
|
993
1094
|
files.flatten!
|
994
1095
|
end
|
995
1096
|
end
|
@@ -1004,7 +1105,6 @@ module Rye
|
|
1004
1105
|
end
|
1005
1106
|
end
|
1006
1107
|
|
1007
|
-
info "#{direction.to_s} to: #{target}"
|
1008
1108
|
debug "FILES: " << files.join(', ')
|
1009
1109
|
|
1010
1110
|
# Make sure the target directory exists. We can do this only when
|
@@ -1020,22 +1120,16 @@ module Rye
|
|
1020
1120
|
files.each do |file|
|
1021
1121
|
debug file.to_s
|
1022
1122
|
prev = ""
|
1023
|
-
|
1024
|
-
tmp_file = file.is_a?(StringIO) ? '<string>' : file
|
1025
|
-
tmp_target = target.is_a?(StringIO) ? '<string>' : target
|
1026
|
-
STDOUT.puts "[#{direction}] #{tmp_file} #{tmp_target}"
|
1027
|
-
end
|
1123
|
+
line = nil
|
1028
1124
|
transfers << scp.send(direction, file, target, :recursive => recursive) do |ch, n, s, t|
|
1029
1125
|
line = "%-50s %6d/%-6d bytes" % [n, s, t]
|
1030
1126
|
spaces = (prev.size > line.size) ? ' '*(prev.size - line.size) : ''
|
1031
|
-
pinfo "%s %s
|
1127
|
+
pinfo "[%s] %s %s %s" % [direction, line, spaces, s == t ? "\n" : "\r"] # update line: "file: sent/total"
|
1032
1128
|
@rye_info.flush if @rye_info # make sure every line is printed
|
1033
1129
|
prev = line
|
1034
1130
|
end
|
1035
1131
|
end
|
1036
1132
|
transfers.each { |t| t.wait } # Run file transfers in parallel
|
1037
|
-
pinfo (' '*prev.size) << "\r"
|
1038
|
-
info $/
|
1039
1133
|
end
|
1040
1134
|
|
1041
1135
|
target.is_a?(StringIO) ? target : nil
|