rye 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,278 @@
1
+ require 'net/ssh'
2
+
3
+ #
4
+ # For channel requests, see SSH_MSG_CHANNEL_REQUEST messages
5
+ # in http://www.snailbook.com/docs/connection.txt
6
+ DEBUG = true
7
+
8
+ module Rye
9
+ class Box
10
+ def debug(state, msg='')
11
+ return unless DEBUG
12
+ puts " ------ %s: %s" % [state, msg]
13
+ end
14
+
15
+ def start_session_state(channel)
16
+ debug :start_session, channel[:handler]
17
+ channel.send_data("unset PS1; stty -echo\n")
18
+ channel[:state] = :ignore_response
19
+ end
20
+
21
+ def ignore_response_state(channel)
22
+ debug :ignore_response, channel[:handler]
23
+ @ignore_response_counter ||= 0
24
+ if channel[:buffer].available > 0
25
+ @await_response_counter = 0
26
+ channel[:buffer].read
27
+ channel[:state] = :process
28
+ elsif @ignore_response_counter > 2
29
+ @await_response_counter = 0
30
+ channel[:state] = :process
31
+ end
32
+ @ignore_response_counter += 1
33
+ end
34
+
35
+ def process_state(channel)
36
+ debug :process, channel[:handler]
37
+ if channel[:block]
38
+ channel[:state] = :run_block
39
+ else
40
+ channel[:state] = :await_input
41
+ end
42
+ end
43
+
44
+ def send_data_state(channel)
45
+ debug :send_data, channel[:handler]
46
+ #if channel[:stack].empty?
47
+ # channel[:state] = :await_input
48
+ #else
49
+ cmd = channel[:stack].shift
50
+ #return if cmd.strip.empty?
51
+ debug :send_data, "calling #{cmd.inspect}"
52
+ channel[:state] = :await_response
53
+ channel.send_data("#{cmd}\n") unless channel.eof?
54
+ #channel.exec("#{cmd}\n", &create_channel)
55
+ #end
56
+ end
57
+
58
+ def await_input_state(channel)
59
+ debug :await_input, channel[:handler]
60
+
61
+ if channel[:buffer].available > 0
62
+ channel[:state] = :read_input
63
+ else
64
+ ret = STDIN.gets
65
+ if ret.nil?
66
+ channel.eof!
67
+ channel[:state] = :exit
68
+ else
69
+ channel[:stack] << ret.chomp
70
+ channel[:state] = :send_data
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ def check_interactive_state(channel)
77
+ debug :read_input, channel[:handler]
78
+ channel.send_data("x")
79
+ end
80
+
81
+ def read_input_state(channel)
82
+ debug :read_input, channel[:handler]
83
+ if channel[:buffer].available > 0
84
+ print channel[:buffer].read
85
+
86
+ if channel[:stack].empty?
87
+ channel[:state] = :await_input
88
+ elsif channel[:buffer].available > 0
89
+ channel[:state] = :read_input
90
+ else
91
+ channel[:state] = :send_data
92
+ end
93
+ else
94
+ channel[:state] = :await_response
95
+ end
96
+ end
97
+
98
+ def exit_state(channel)
99
+ debug :exit_state, channel[:exit_status]
100
+ puts
101
+ channel.eof!
102
+ end
103
+
104
+ def handle_error_state(channel)
105
+ debug :handle_error, channel[:handler]
106
+ channel.eof!
107
+ end
108
+
109
+
110
+ def await_response_state(channel)
111
+ debug :await_response, channel[:handler]
112
+ @await_response_counter ||= 0
113
+ if channel[:buffer].available > 0
114
+ channel[:state] = :read_input
115
+ elsif @await_response_counter > 20
116
+ @await_response_counter = 0
117
+ channel[:state] = :await_input
118
+ end
119
+ @await_response_counter += 1
120
+ end
121
+
122
+ def run_block_state(channel)
123
+ debug :run_block, channel[:handler]
124
+ channel[:state] = nil
125
+ blk = channel[:block]
126
+ channel[:block] = nil
127
+ instance_eval &blk
128
+ channel[:state] = :exit
129
+ end
130
+
131
+ def command(name,*args, &blk)
132
+ debug :command, channel[:handler]
133
+ return if @channel.eof?
134
+ cmd = "%s %s" % [name, args.join(' ')]
135
+ debug :command, "Running: #{cmd}"
136
+ if self.pty && channel[:buffer].available
137
+ prompt = channel[:buffer].read
138
+ end
139
+ channel.send_data("#{cmd}\n")
140
+ channel.connection.loop do
141
+ break if channel[:buffer].available > 0
142
+ p :loop
143
+ channel.active?
144
+ end
145
+ ret = channel[:buffer].read
146
+ ret
147
+
148
+ end
149
+
150
+ def wait_for_command_state(channel)
151
+ debug :wait_for_command, channel[:handler]
152
+ end
153
+
154
+ def ls(*args) command(:ls, *args) end
155
+ def cat(*args) command(:cat, *args) end
156
+ def echo(*args) command(:echo, *args) end
157
+ def sudo(*args) command(:sudo, *args) end
158
+ def date(*args) command(:date, *args) end
159
+ def uname(*args) command(:uname, *args) end
160
+ def chroot(*args) command(:chroot, *args) end
161
+ def bash(*args, &blk) command(:bash, *args, &blk) end
162
+ def exit(*args, &blk) command(:exit, *args, &blk) end
163
+
164
+ attr_reader :session, :channel
165
+ attr_accessor :running, :pty
166
+
167
+ def connect(host, user, opts={})
168
+ opts = {
169
+ :auth_methods => %w[keyboard-interactive]
170
+ }.merge(opts)
171
+ opts[:auth_methods].unshift 'publickey' unless opts[:keys].nil?
172
+ opts[:auth_methods].unshift 'password' unless opts[:password].nil?
173
+ @sessions ||= []
174
+ @session = Net::SSH.start(host, user, opts)
175
+ end
176
+
177
+ def run(shell, &blk)
178
+
179
+ puts "Running #{shell}"
180
+ @channel = @session.open_channel do |channel|
181
+ #if blk.nil?
182
+ channel.request_pty do |ch,success|
183
+ self.pty = success
184
+ raise "pty request denied" unless success
185
+ end
186
+ #end
187
+ channel.exec shell, &create_channel
188
+ end
189
+
190
+ channel[:block] = blk
191
+
192
+ @session.loop(0.5) do
193
+ break if !@channel.active?
194
+ !@channel.eof? # otherwise keep returning true
195
+ end
196
+
197
+ end
198
+
199
+ def stop
200
+ @control.join if @control
201
+ @ssh.running = false
202
+ @ssh.close
203
+ end
204
+
205
+ private
206
+ def busy_proc
207
+ Proc.new { |s| !s.busy? }
208
+ end
209
+
210
+ def create_channel
211
+ Proc.new do |channel,success|
212
+ channel[:callback] = Proc.new { p :callback }
213
+ channel[:buffer ] = Net::SSH::Buffer.new
214
+ #channel[:batch ] = blk
215
+ channel[:stderr ] = Net::SSH::Buffer.new
216
+ channel[:state ] = "start_session"
217
+ @channel[:stack] ||= []
218
+ channel.on_close { |ch|
219
+ channel[:handler] = ":on_close"
220
+ }
221
+ channel.on_data { |ch, data|
222
+ channel[:handler] = ":on_data"
223
+ channel[:buffer].append(data)
224
+ }
225
+ channel.on_extended_data { |ch, type, data|
226
+ channel[:handler] = ":on_extended_data"
227
+ channel[:stderr].append(data)
228
+ channel[:state] = :handle_error
229
+ }
230
+ channel.on_request("exit-status") { |ch, data|
231
+ channel[:handler] = ":on_request (exit-status)"
232
+ channel[:exit] = data.read_long
233
+ }
234
+ channel.on_request("exit-signal") do |ch, data|
235
+ channel[:handler] = ":on_request (exit-signal)"
236
+ # This should be the POSIX SIGNAL that ended the process
237
+ channel[:exit_signal] = data.read_long
238
+ end
239
+ channel.on_process {
240
+ channel[:handler] = :on_process
241
+ print channel[:stderr].read if channel[:stderr].available > 0
242
+ begin
243
+ send("#{channel[:state]}_state", channel) unless channel[:state].nil?
244
+ rescue Interrupt
245
+ debug :await_input_interrupt
246
+ channel[:state] = :exit
247
+ end
248
+ }
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ begin
255
+ puts $$
256
+ rbox = Rye::Box.new
257
+ rbox.connect 'ec2-184-72-169-231.compute-1.amazonaws.com', 'ubuntu', :verbose => :fatal, :keys => ['~/.ssh/key-us-east-1b-build-arch']
258
+ #rbox.run 'bash'
259
+ rbox.run 'bash' do
260
+ puts command("date")
261
+ p cat("/etc/issue")
262
+ command("SUDO_PS1=''")
263
+ puts sudo( 'chroot', '/mnt/archlinux-x86_64')
264
+ command("unset PS1;")
265
+ p cat("/etc/issue")
266
+ end
267
+ puts rbox.channel[:stderr] if rbox.channel[:stderr]
268
+ end
269
+
270
+
271
+ __END__
272
+ http://tldp.org/LDP/abs/html/intandnonint.html
273
+ case $- in
274
+ *i*) # interactive shell
275
+ ;;
276
+ *) # non-interactive shell
277
+ ;;
278
+ # (Courtesy of "UNIX F.A.Q.," 1993)
@@ -0,0 +1,280 @@
1
+ require 'net/ssh'
2
+
3
+ #
4
+ # For channel requests, see SSH_MSG_CHANNEL_REQUEST messages
5
+ # in http://www.snailbook.com/docs/connection.txt
6
+ DEBUG = true
7
+
8
+ module Rye
9
+ class Box
10
+ def debug(state, msg='')
11
+ return unless DEBUG
12
+ puts " ------ %s: %s" % [state, msg]
13
+ end
14
+
15
+ def start_session_state(channel)
16
+ debug :start_session, channel[:handler]
17
+ #channel.send_data("stty -echo\n")
18
+ #channel[:state] = :ignore_response
19
+ channel[:state] = :await_response
20
+ end
21
+
22
+ def await_response_state(channel)
23
+ debug :await_response, channel[:handler]
24
+ @await_response_counter ||= 0
25
+ if channel[:buffer].available > 0
26
+ channel[:state] = :read_input
27
+ elsif @await_response_counter > 10
28
+ @await_response_counter = 0
29
+ channel[:state] = :await_input
30
+ end
31
+ @await_response_counter += 1
32
+ end
33
+
34
+ def read_input_state(channel)
35
+ debug :read_input, channel[:handler]
36
+ if channel[:buffer].available > 0
37
+ print channel[:buffer].read
38
+
39
+ if channel[:stack].empty?
40
+ channel[:state] = :await_input
41
+ elsif channel[:buffer].available > 0
42
+ channel[:state] = :read_input
43
+ else
44
+ channel[:state] = :send_data
45
+ end
46
+ else
47
+ channel[:state] = :await_response
48
+ end
49
+ end
50
+
51
+ def send_data_state(channel)
52
+ debug :send_data, channel[:handler]
53
+ #if channel[:stack].empty?
54
+ # channel[:state] = :await_input
55
+ #else
56
+ cmd = channel[:stack].shift
57
+ #return if cmd.strip.empty?
58
+ debug :send_data, "calling #{cmd.inspect}"
59
+ channel[:state] = :await_response
60
+ channel.send_data("#{cmd}\n") unless channel.eof?
61
+ #channel.exec("#{cmd}\n", &create_channel)
62
+ #end
63
+ end
64
+
65
+ def await_input_state(channel)
66
+ debug :await_input, channel[:handler]
67
+
68
+ if channel[:buffer].available > 0
69
+ channel[:state] = :read_input
70
+ else
71
+ ret = STDIN.gets
72
+ if ret.nil?
73
+ channel.eof!
74
+ channel[:state] = :exit
75
+ else
76
+ channel[:stack] << ret.chomp
77
+ channel[:state] = :send_data
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ def check_interactive_state(channel)
84
+ debug :read_input, channel[:handler]
85
+ channel.send_data("x")
86
+ end
87
+
88
+ def exit_state(channel)
89
+ debug :exit_state, channel[:exit_status]
90
+ puts
91
+ channel.eof!
92
+ end
93
+
94
+ def handle_error_state(channel)
95
+ debug :handle_error, channel[:handler]
96
+ channel.eof!
97
+ end
98
+
99
+ def ignore_response_state(channel)
100
+ debug :ignore_response, channel[:handler]
101
+ @ignore_response_counter ||= 0
102
+ if channel[:buffer].available > 0
103
+ @await_response_counter = 0
104
+ channel[:buffer].read
105
+ channel[:state] = :await_input
106
+ elsif @ignore_response_counter > 2
107
+ @await_response_counter = 0
108
+ channel[:state] = :await_input
109
+ end
110
+ @ignore_response_counter += 1
111
+ end
112
+
113
+
114
+ def run_block_state(channel)
115
+ instance_eval &channel[:block]
116
+ channel[:block] = nil
117
+ channel[:state] = :await_response
118
+ end
119
+
120
+ def command(name,*args, &blk)
121
+ return if @channel.eof?
122
+ channel[:block] = blk
123
+ cmd = "%s %s" % [name, args.join(' ')]
124
+ channel.send_data("#{cmd}\n")
125
+ #channel.wait
126
+ channel[:state] = :await_response
127
+
128
+ #@channel[:buffer]
129
+ # channel.exec "ls -l /home" do |ch, success|
130
+ # if success
131
+ # puts "command has begun executing..."
132
+ # # this is a good place to hang callbacks like #on_data...
133
+ # else
134
+ # puts "alas! the command could not be invoked!"
135
+ # end
136
+ # end
137
+ end
138
+
139
+ def ls(*args) command(:ls, *args) end
140
+ def date(*args) command(:date, *args) end
141
+ def bash(*args, &blk) command(:bash, *args, &blk) end
142
+ def exit(*args, &blk) command(:exit, *args, &blk) end
143
+
144
+ attr_reader :session, :channel
145
+ attr_accessor :running
146
+
147
+ def connect(host, user, opts={})
148
+ opts = {
149
+ :auth_methods => %w[keyboard-interactive]
150
+ }.merge(opts)
151
+ opts[:auth_methods].unshift 'publickey' unless opts[:keys].nil?
152
+ opts[:auth_methods].unshift 'password' unless opts[:password].nil?
153
+ @sessions ||= []
154
+ @session = Net::SSH.start(host, user, opts)
155
+ end
156
+
157
+ def run(shell, &blk)
158
+
159
+ #@session.process(0.1, &busy_proc)
160
+
161
+ #result = nil
162
+ #@channel = @session.open_channel do |ch|
163
+ # ch.exec("irb") do |c, success|
164
+ # ch.on_data { |c, data| puts data }
165
+ # ch.on_extended_data { |c, type, data| puts data }
166
+ # ch.on_close { |c| c.close }
167
+ # end
168
+ #end
169
+
170
+ @channel = @session.open_channel do |channel|
171
+ channel.request_pty do |ch,success|
172
+ if success
173
+
174
+ else
175
+ raise "pty request denied"
176
+ end
177
+ end
178
+ channel.exec shell, &create_channel
179
+ end
180
+
181
+
182
+ @channel[:stack] ||= []
183
+ #@channel[:stack] << "require 'gibbler'"
184
+ #@channel[:stack] << "{}.gibbler"
185
+
186
+ #trap("INT") {
187
+ # p [:INT, @session.closed?, @channel.eof?]
188
+ # #if channel[:state] == :await_input
189
+ # @channel.eof!
190
+ # @session.close unless @session.closed?
191
+ #}
192
+ #trap("INT") {
193
+ #@channel.eof! unless @channel.eof?
194
+ #@session.close unless @session.closed?
195
+ # @channel[:state] = :exit
196
+ #}
197
+
198
+ @session.loop(0.1) do
199
+ break if !@channel.active?
200
+ !@channel.eof? # otherwise keep returning true
201
+ end
202
+
203
+ #puts @channel[:stderr], @channel[:exit_status]
204
+
205
+ end
206
+
207
+ def stop
208
+ @control.join if @control
209
+ @ssh.running = false
210
+ @ssh.close
211
+ end
212
+
213
+ private
214
+ def busy_proc
215
+ Proc.new { |s| !s.busy? }
216
+ end
217
+
218
+ def create_channel
219
+ Proc.new do |channel,success|
220
+ channel[:callback] = Proc.new { p :callback }
221
+ channel[:buffer ] = Net::SSH::Buffer.new
222
+ #channel[:batch ] = blk
223
+ channel[:stderr ] = Net::SSH::Buffer.new
224
+ channel[:state ] = "start_session"
225
+ @channel[:stack] ||= []
226
+ channel.on_close { |ch|
227
+ channel[:handler] = ":on_close"
228
+ }
229
+ channel.on_data { |ch, data|
230
+ channel[:handler] = ":on_data"
231
+ channel[:buffer].append(data)
232
+ }
233
+ channel.on_extended_data { |ch, type, data|
234
+ channel[:handler] = ":on_extended_data"
235
+ channel[:stderr].append(data)
236
+ channel[:state] = :handle_error
237
+ }
238
+ channel.on_request("exit-status") { |ch, data|
239
+ channel[:handler] = ":on_request (exit-status)"
240
+ channel[:exit] = data.read_long
241
+ }
242
+ channel.on_request("exit-signal") do |ch, data|
243
+ channel[:handler] = ":on_request (exit-signal)"
244
+ # This should be the POSIX SIGNAL that ended the process
245
+ channel[:exit_signal] = data.read_long
246
+ end
247
+ channel.on_process {
248
+ channel[:handler] = :on_process
249
+ print channel[:stderr].read if channel[:stderr].available > 0
250
+ begin
251
+ send("#{channel[:state]}_state", channel)
252
+ rescue Interrupt
253
+ debug :await_input_interrupt
254
+ channel[:state] = :exit
255
+ end
256
+ }
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ begin
263
+ puts $$
264
+ rbox = Rye::Box.new
265
+ rbox.connect 'localhost', 'delano', :keys => []
266
+ rbox.run 'bash'
267
+
268
+ #p rbox.channel[:exit], rbox.channel[:exit_signal]
269
+
270
+ end
271
+
272
+
273
+ __END__
274
+ http://tldp.org/LDP/abs/html/intandnonint.html
275
+ case $- in
276
+ *i*) # interactive shell
277
+ ;;
278
+ *) # non-interactive shell
279
+ ;;
280
+ # (Courtesy of "UNIX F.A.Q.," 1993)