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
Class Method Summary
collapse
Instance Method Summary
collapse
Methods included from Proto
#receive_banner, #receive_data, #receive_object, #receive_prompt, #receive_proxy_connection, #receive_register_server, #receive_server_list, #receive_server_reload_list, #receive_shell_result, #receive_start_tls, #receive_unregister_server, #send_auth, #send_banner, #send_clear_buffer, #send_completion, #send_heatbeat, #send_msg, #send_msg_bcast, #send_object, #send_prompt, #send_proxy_connection, #send_raw, #send_register_server, #send_server_list, #send_server_reload_list, #send_shell_cmd, #send_shell_data, #send_shell_result, #send_shell_sig, #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.
114
115
116
117
|
# File 'lib/pry-remote-em/server.rb', line 114
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.
120
121
122
|
# File 'lib/pry-remote-em/server.rb', line 120
def register(obj, peer)
peers(obj).tap { |plist| plist.include?(peer) || plist.push(peer) }
end
|
.run(options = {}, &block) ⇒ Object
Start a pry-remote-em server
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
# File 'lib/pry-remote-em/server.rb', line 91
def run(options = {}, &block)
description = options.dup
description[:target] ||= PryRemoteEm::Sandbox.new
description[:host] ||= ENV['PRYEMHOST'].nil? || ENV['PRYEMHOST'].empty? ? DEFAULT_SERVER_HOST : ENV['PRYEMHOST']
determine_port_and_tries(description)
determine_name(description)
description[:id] ||= SecureRandom.uuid
description[:logger] ||= ::Logger.new(STDERR)
description[:external_url] ||= ENV['PRYEMURL'] || "#{description[:tls] ? 'pryems' : 'pryem'}://#{description[:host]}:#{description[:port]}/"
description[:details] ||= {}
description[:allow_shell_cmds] = true if description[:allow_shell_cmds].nil?
description[:heartbeat_interval] ||= ENV['PRYEMHBSEND'].nil? || ENV['PRYEMHBSEND'].empty? ? HEARTBEAT_SEND_INTERVAL : ENV['PRYEMHBSEND']
description[:urls] = expand_url(description[:external_url])
description[:server] = start_server(description, &block)
description[:logger].info("[pry-remote-em] listening for connections on #{description[:external_url]}")
PryRemoteEm.servers[description[:id]] = description
register_in_broker(description)
return description
end
|
.unregister(obj, peer) ⇒ Object
Remove 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 unregister(obj, peer)
peers(obj).tap {|plist| true while plist.delete(peer) }
end
|
Instance Method Details
#after_auth(&blk) ⇒ Object
420
421
422
423
|
# File 'lib/pry-remote-em/server.rb', line 420
def after_auth(&blk)
@after_auth.push(blk)
end
|
#auth_attempt {|user, ip| ... } ⇒ Object
Registers a block to call when authentication is attempted.
444
445
446
447
448
449
|
# File 'lib/pry-remote-em/server.rb', line 444
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.
456
457
458
459
460
461
|
# File 'lib/pry-remote-em/server.rb', line 456
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.
468
469
470
471
472
473
|
# File 'lib/pry-remote-em/server.rb', line 468
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
399
400
401
402
403
|
# File 'lib/pry-remote-em/server.rb', line 399
def authenticated!
while (aa = @after_auth.shift)
aa.call
end
end
|
#completion_proc=(compl) ⇒ Object
499
500
501
|
# File 'lib/pry-remote-em/server.rb', line 499
def completion_proc=(compl)
@compl_proc = compl
end
|
507
508
509
|
# File 'lib/pry-remote-em/server.rb', line 507
def flush
true
end
|
#initialize(opts = {}) ⇒ Object
214
215
216
217
218
219
220
221
222
223
224
225
226
227
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 214
def initialize(opts = {})
@obj = opts[:target]
@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
|
286
287
288
289
290
291
|
# File 'lib/pry-remote-em/server.rb', line 286
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
293
294
295
296
297
298
|
# File 'lib/pry-remote-em/server.rb', line 293
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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
|
# File 'lib/pry-remote-em/server.rb', line 254
def post_init
@lines = []
@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'}")
need_new_line = false
if @opts[:details].any?
send_raw("\nServer details:\n#{@opts[:details].map { |key, value| " #{key}: #{value}" } * "\n"}\n")
need_new_line = true
end
if PryRemoteEm::Metrics.any?
send_raw("\nServer metrics:\n#{PryRemoteEm::Metrics.list.map { |key, value| " #{key}: #{value}" } * "\n"}\n")
need_new_line = true
end
send_raw("\n") if need_new_line
@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
489
490
491
|
# File 'lib/pry-remote-em/server.rb', line 489
def print(val)
@auth_required ? (after_auth { send_raw(val) }) : send_raw(val)
end
|
#puts(data = '') ⇒ Object
494
495
496
497
|
# File 'lib/pry-remote-em/server.rb', line 494
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
481
482
483
484
485
486
487
|
# File 'lib/pry-remote-em/server.rb', line 481
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
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
|
# File 'lib/pry-remote-em/server.rb', line 330
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_clear_buffer ⇒ Object
300
301
302
303
304
|
# File 'lib/pry-remote-em/server.rb', line 300
def receive_clear_buffer
@opts[:pry].eval_string.replace('')
@last_prompt = @opts[:pry].select_prompt
send_last_prompt
end
|
#receive_completion(c) ⇒ Object
325
326
327
328
|
# File 'lib/pry-remote-em/server.rb', line 325
def receive_completion(c)
return if require_auth
send_completion(@compl_proc.call(c))
end
|
#receive_msg(m) ⇒ Object
351
352
353
354
355
|
# File 'lib/pry-remote-em/server.rb', line 351
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
357
358
359
360
361
|
# File 'lib/pry-remote-em/server.rb', line 357
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
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
|
# File 'lib/pry-remote-em/server.rb', line 306
def receive_raw(d)
return if require_auth
return send_last_prompt if d.nil?
if d.empty?
@lines.push('')
else
lines = d.split("\n")
@lines.push(*lines)
end
if @waiting
f, @waiting = @waiting, nil
f.resume(@lines.shift)
end
end
|
#receive_shell_cmd(cmd) ⇒ Object
363
364
365
366
367
368
369
370
371
372
373
374
|
# File 'lib/pry-remote-em/server.rb', line 363
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
376
377
378
379
|
# File 'lib/pry-remote-em/server.rb', line 376
def receive_shell_data(d)
return if require_auth
@shell_cmd.send_data(d)
end
|
#receive_shell_sig(type) ⇒ Object
381
382
383
384
|
# File 'lib/pry-remote-em/server.rb', line 381
def receive_shell_sig(type)
return if require_auth
@shell_cmd.close_connection if type == :int
end
|
#receive_unknown(j) ⇒ Object
386
387
388
389
390
391
|
# File 'lib/pry-remote-em/server.rb', line 386
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
393
394
395
396
397
|
# File 'lib/pry-remote-em/server.rb', line 393
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.
432
433
434
435
|
# File 'lib/pry-remote-em/server.rb', line 432
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
475
476
477
|
# File 'lib/pry-remote-em/server.rb', line 475
def send_error(msg)
puts "\033[31m#{msg}\033[0m"
end
|
#send_last_prompt ⇒ Object
416
417
418
|
# File 'lib/pry-remote-em/server.rb', line 416
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.
426
427
428
429
|
# File 'lib/pry-remote-em/server.rb', line 426
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
281
282
283
284
|
# File 'lib/pry-remote-em/server.rb', line 281
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
276
277
278
279
|
# File 'lib/pry-remote-em/server.rb', line 276
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
503
504
505
|
# File 'lib/pry-remote-em/server.rb', line 503
def tty?
true end
|
405
406
407
408
|
# File 'lib/pry-remote-em/server.rb', line 405
def unbind
PryRemoteEm::Server.unregister(@obj, self)
@log.debug("[pry-remote-em] remote session terminated (#{@ip}:#{@port})")
end
|
249
250
251
252
|
# File 'lib/pry-remote-em/server.rb', line 249
def url
port, host = Socket.unpack_sockaddr_in(get_sockname)
"#{@opts[:tls] ? 'pryems' : 'pryem'}://#{host}:#{port}/"
end
|