Module: PryRemoteEm::Server
- Includes:
- Proto
- Defined in:
- lib/pry-remote-em/server.rb,
lib/pry-remote-em/server/shell_cmd.rb
Defined Under Namespace
Modules: ShellCmd
Constant Summary
Constants included
from Proto
Proto::PREAMBLE, Proto::PREAMBLE_LEN, Proto::SEPERATOR, Proto::SEPERATOR_LEN
Class Method Summary
collapse
Instance Method Summary
collapse
Methods included from Proto
#receive_banner, #receive_data, #receive_json, #receive_prompt, #receive_proxy_connection, #receive_register_server, #receive_server_list, #receive_shell_result, #receive_start_tls, #receive_unregister_server, #send_auth, #send_banner, #send_completion, #send_data, #send_heatbeat, #send_json, #send_msg, #send_msg_bcast, #send_prompt, #send_proxy_connection, #send_raw, #send_register_server, #send_server_list, #send_shell_cmd, #send_shell_result, #send_start_tls, #send_unregister_server
Class Method Details
.peers(obj = nil) ⇒ Object
The list of pry-remote-em connections for a given object, or the list of all pry-remote-em connections for this process. The peer list is used when broadcasting messages between connections.
119
120
121
122
|
# File 'lib/pry-remote-em/server.rb', line 119
def peers(obj = nil)
@peers ||= {}
obj.nil? ? @peers.values.flatten : (@peers[obj] ||= [])
end
|
.register(obj, peer) ⇒ Object
Record the association between a given object and a given pry-remote-em connection.
125
126
127
|
# File 'lib/pry-remote-em/server.rb', line 125
def register(obj, peer)
peers(obj).tap { |plist| plist.include?(peer) || plist.push(peer) }
end
|
.run(obj, host = DEFHOST, port = DEFPORT, opts = {:tls => false}) ⇒ Object
Start a pry-remote-em server
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
# File 'lib/pry-remote-em/server.rb', line 77
def run(obj, host = DEFHOST, port = DEFPORT, opts = {:tls => false})
tries = [port, opts[:port_fail]].include?(:auto) ? 100 : 1
port = DEFPORT if :auto == port
raise "root permission required for port below 1024 (#{port})" if port < 1024 && Process.euid != 0
begin
server = EM.start_server(host, port, PryRemoteEm::Server, obj, opts) do |pre|
Fiber.new {
begin
yield pre if block_given?
Pry.start(obj, :input => pre, :output => pre)
ensure
pre.close_connection
end
}.resume
end
rescue => e
if (e.message.include?('port is in use') || e.message.include?('no acceptor')) && tries > 1
tries -= 1
port += 1
retry
end
raise "can't bind to #{host}:#{port} - #{e}"
end
url = "#{opts[:tls] ? 'pryems' : 'pryem'}://#{host}:#{port}/"
name = obj.send(:eval, 'self') rescue "#{obj}"
name = Pry.view_clip(name)
PryRemoteEm.servers[url] = [server, name]
(opts[:logger] || ::Logger.new(STDERR)).info("[pry-remote-em] listening for connections on #{url}")
Broker.run(opts[:broker_host] || ENV['PRYEMBROKER'] || DEF_BROKERHOST, opts[:broker_port] || ENV['PRYEMBROKERPORT'] || DEF_BROKERPORT, opts) do |broker|
broker.register(url, name)
rereg = EM::PeriodicTimer.new(15) do
EM.get_sockname(server) ? broker.register(url, name) : nil end
end url
end
|
.unregister(obj, peer) ⇒ Object
Remove the association between a given object and a given pry-remote-em connection.
130
131
132
|
# File 'lib/pry-remote-em/server.rb', line 130
def unregister(obj, peer)
peers(obj).tap {|plist| true while plist.delete(peer) }
end
|
Instance Method Details
#after_auth(&blk) ⇒ Object
319
320
321
322
|
# File 'lib/pry-remote-em/server.rb', line 319
def after_auth(&blk)
@after_auth.push(blk)
end
|
#auth_attempt {|user, ip| ... } ⇒ Object
Registers a block to call when authentication is attempted.
343
344
345
346
347
348
|
# File 'lib/pry-remote-em/server.rb', line 343
def auth_attempt(*args, &blk)
block_given? ? @auth_attempt_cbs << blk : @auth_attempts.push(args)
while (auth_data = @auth_attempts.shift)
@auth_attempt_cbs.each { |cb| cb.call(*auth_data) }
end
end
|
#auth_fail {|user, ip| ... } ⇒ Object
Registers a block to call when authentication fails.
355
356
357
358
359
360
|
# File 'lib/pry-remote-em/server.rb', line 355
def auth_fail(*args, &blk)
block_given? ? @auth_fail_cbs << blk : @auth_fails.push(args)
while (fail_data = @auth_fails.shift)
@auth_fail_cbs.each { |cb| cb.call(*fail_data) }
end
end
|
#auth_ok {|user, ip| ... } ⇒ Object
Registers a block to call when authentication succeeds.
367
368
369
370
371
372
|
# File 'lib/pry-remote-em/server.rb', line 367
def auth_ok(*args, &blk)
block_given? ? @auth_ok_cbs << blk : @auth_oks.push(args)
while (ok_data = @auth_oks.shift)
@auth_ok_cbs.each { |cb| cb.call(*ok_data) }
end
end
|
#authenticated! ⇒ Object
297
298
299
300
301
|
# File 'lib/pry-remote-em/server.rb', line 297
def authenticated!
while (aa = @after_auth.shift)
aa.call
end
end
|
#completion_proc=(compl) ⇒ Object
398
399
400
|
# File 'lib/pry-remote-em/server.rb', line 398
def completion_proc=(compl)
@compl_proc = compl
end
|
406
407
408
|
# File 'lib/pry-remote-em/server.rb', line 406
def flush
true
end
|
#initialize(obj, opts = {:tls => false}) ⇒ Object
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
# File 'lib/pry-remote-em/server.rb', line 135
def initialize(obj, opts = {:tls => false})
@obj = obj
@opts = opts
@allow_shell_cmds = opts[:allow_shell_cmds]
@log = opts[:logger] || ::Logger.new(STDERR)
@auth_attempt_cbs = []
@auth_attempts = []
@auth_fail_cbs = []
@auth_fails = []
@auth_ok_cbs = []
@auth_oks = []
@auth_tries = 5
@after_auth = []
return unless (a = opts[:auth])
if a.is_a?(Proc)
return fail("auth handler Procs must take two arguments not (#{a.arity})") unless a.arity == 2
@auth = a
elsif a.respond_to?(:call)
return fail("auth handler must take two arguments not (#{a.method(:call).arity})") unless a.method(:call).arity == 2
@auth = a
else
return error("auth handler objects must respond to :call, or :[]") unless a.respond_to?(:[])
@auth = lambda {|u,p| a[u] && a[u] == p }
end
end
|
198
199
200
201
202
203
|
# File 'lib/pry-remote-em/server.rb', line 198
def peer_ip
return @peer_ip if @peer_ip
return "" if get_peername.nil?
@peer_port, @peer_ip = Socket.unpack_sockaddr_in(get_peername)
@peer_ip
end
|
#peer_port ⇒ Object
205
206
207
208
209
210
|
# File 'lib/pry-remote-em/server.rb', line 205
def peer_port
return @peer_port if @peer_port
return "" if get_peername.nil?
@peer_port, @peer_ip = Socket.unpack_sockaddr_in(get_peername)
@peer_port
end
|
#post_init ⇒ Object
175
176
177
178
179
180
181
182
183
184
185
186
|
# File 'lib/pry-remote-em/server.rb', line 175
def post_init
@lines = []
Pry.config., @old_pager = false, Pry.config.
@auth_required = @auth
port, ip = Socket.unpack_sockaddr_in(get_peername)
@log.info("[pry-remote-em] received client connection from #{ip}:#{port}")
send_banner("PryRemoteEm #{VERSION} #{@opts[:tls] ? 'pryems' : 'pryem'}")
@log.info("#{url} PryRemoteEm #{VERSION} #{@opts[:tls] ? 'pryems' : 'pryem'}")
@opts[:tls] ? start_tls : (@auth_required && send_auth(false))
PryRemoteEm::Server.register(@obj, self)
end
|
#print(val) ⇒ Object
Also known as:
write
388
389
390
|
# File 'lib/pry-remote-em/server.rb', line 388
def print(val)
@auth_required ? (after_auth { send_raw(val) }) : send_raw(val)
end
|
#puts(data = "") ⇒ Object
393
394
395
396
|
# File 'lib/pry-remote-em/server.rb', line 393
def puts(data = "")
s = data.to_s
print(s[0] == "\n" ? s : s + "\n")
end
|
#readline(prompt) ⇒ Object
Methods that make Server compatible with Pry
380
381
382
383
384
385
386
|
# File 'lib/pry-remote-em/server.rb', line 380
def readline(prompt)
@last_prompt = prompt
@auth_required ? (after_auth { send_prompt(prompt) }) : send_prompt(prompt)
return @lines.shift unless @lines.empty?
@waiting = Fiber.current
return Fiber.yield
end
|
#receive_auth(user, pass) ⇒ Object
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
# File 'lib/pry-remote-em/server.rb', line 228
def receive_auth(user, pass)
return send_auth(true) if !@auth || !@auth_required
return send_auth('auth data must include a user and pass') if user.nil? || pass.nil?
auth_attempt(user, peer_ip)
unless (@auth_required = !@auth.call(user, pass))
@user = user
auth_ok(user, peer_ip)
authenticated!
else
auth_fail(user, peer_ip)
if @auth_tries <= 0
msg = "max authentication attempts reached"
send_auth(msg)
@log.debug("[pry-remote-em] #{msg} (#{peer_ip}:#{peer_port})")
return close_connection_after_writing
end
@auth_tries -= 1
end
return send_auth(!@auth_required)
end
|
#receive_completion(c) ⇒ Object
223
224
225
226
|
# File 'lib/pry-remote-em/server.rb', line 223
def receive_completion(c)
return if require_auth
send_completion(@compl_proc.call(c))
end
|
#receive_msg(m) ⇒ Object
249
250
251
252
253
|
# File 'lib/pry-remote-em/server.rb', line 249
def receive_msg(m)
return if require_auth
peers.each { |peer| peer.send_message(m, @user) }
send_last_prompt
end
|
#receive_msg_bcast(mb) ⇒ Object
255
256
257
258
259
|
# File 'lib/pry-remote-em/server.rb', line 255
def receive_msg_bcast(mb)
return if require_auth
peers(:all).each { |peer| peer.send_bmessage(mb, @user) }
send_last_prompt
end
|
#receive_raw(d) ⇒ Object
212
213
214
215
216
217
218
219
220
|
# File 'lib/pry-remote-em/server.rb', line 212
def receive_raw(d)
return if require_auth
return send_last_prompt if d.nil? || d.empty?
@lines.push(*d.split("\n"))
if @waiting
f, @waiting = @waiting, nil
f.resume(@lines.shift)
end
end
|
#receive_shell_cmd(cmd) ⇒ Object
261
262
263
264
265
266
267
268
269
270
271
272
|
# File 'lib/pry-remote-em/server.rb', line 261
def receive_shell_cmd(cmd)
return if require_auth
unless @allow_shell_cmds
puts "\033[1mshell commands are not allowed by this server\033[0m"
@log.error("refused to execute shell command '#{cmd}' for #{@user} (#{peer_ip}:#{peer_port})")
send_shell_result(-1)
send_last_prompt
else
@log.warn("executing shell command '#{cmd}' for #{@user} (#{peer_ip}:#{peer_port})")
@shell_cmd = EM.popen3(cmd, ShellCmd, self)
end
end
|
#receive_shell_data(d) ⇒ Object
274
275
276
277
|
# File 'lib/pry-remote-em/server.rb', line 274
def receive_shell_data(d)
return if require_auth
@shell_cmd.send_data(d)
end
|
#receive_shell_sig(type) ⇒ Object
279
280
281
282
|
# File 'lib/pry-remote-em/server.rb', line 279
def receive_shell_sig(type)
return if require_auth
type == :term && @shell_cmd.close_connection
end
|
#receive_unknown(j) ⇒ Object
284
285
286
287
288
289
|
# File 'lib/pry-remote-em/server.rb', line 284
def receive_unknown(j)
return if require_auth
warn "received unexpected data: #{j.inspect}"
send_error("received unexpected data: #{j.inspect}")
send_last_prompt
end
|
#require_auth ⇒ Object
291
292
293
294
295
|
# File 'lib/pry-remote-em/server.rb', line 291
def require_auth
return false if !@auth_required
send_auth(false)
true
end
|
#send_bmessage(msg, from = nil) ⇒ Object
Sends a chat message to the client.
331
332
333
334
|
# File 'lib/pry-remote-em/server.rb', line 331
def send_bmessage(msg, from = nil)
msg = "#{msg} (@#{from})" unless from.nil?
@auth_required ? (after_auth {send_msg_bcast(msg)}) : send_msg_bcast(msg)
end
|
#send_error(msg) ⇒ Object
374
375
376
|
# File 'lib/pry-remote-em/server.rb', line 374
def send_error(msg)
puts "\033[31m#{msg}\033[0m"
end
|
#send_last_prompt ⇒ Object
315
316
317
|
# File 'lib/pry-remote-em/server.rb', line 315
def send_last_prompt
@auth_required ? (after_auth { send_prompt(@last_prompt) }) : send_prompt(@last_prompt)
end
|
#send_message(msg, from = nil) ⇒ Object
Sends a chat message to the client.
325
326
327
328
|
# File 'lib/pry-remote-em/server.rb', line 325
def send_message(msg, from = nil)
msg = "#{msg} (@#{from})" unless from.nil?
@auth_required ? (after_auth {send_msg(msg)}) : send_msg(msg)
end
|
#ssl_handshake_completed ⇒ Object
193
194
195
196
|
# File 'lib/pry-remote-em/server.rb', line 193
def ssl_handshake_completed
@log.info("[pry-remote-em] TLS connection established (#{peer_ip}:#{peer_port})")
send_auth(false) if @auth_required
end
|
#start_tls ⇒ Object
188
189
190
191
|
# File 'lib/pry-remote-em/server.rb', line 188
def start_tls
@log.debug("[pry-remote-em] starting TLS (#{peer_ip}:#{peer_port})")
super(@opts[:tls].is_a?(Hash) ? @opts[:tls] : {})
end
|
#tty? ⇒ Boolean
402
403
404
|
# File 'lib/pry-remote-em/server.rb', line 402
def tty?
true end
|
303
304
305
306
307
|
# File 'lib/pry-remote-em/server.rb', line 303
def unbind
Pry.config. = @old_pager
PryRemoteEm::Server.unregister(@obj, self)
@log.debug("[pry-remote-em] remote session terminated (#{peer_ip}:#{peer_port})")
end
|
170
171
172
173
|
# File 'lib/pry-remote-em/server.rb', line 170
def url
port, host = Socket.unpack_sockaddr_in(get_sockname)
"#{@opts[:tls] ? 'pryems' : 'pryem'}://#{host}:#{port}/"
end
|