rye 0.8.19 → 0.9.0
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.
- 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
|