em-simple_telnet 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/em-simple_telnet.rb +204 -102
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78218823aba97e9250159f56bbb7c666a52178f8
|
4
|
+
data.tar.gz: 3035ce24632a5f23788253e8ff9c022175711965
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d7c0638b72d5c0b1fe0cee058b9391d965edf491af743a6ab19572656d6e1cb717aecf1f05304a4b5441e0c57875dc209b1e48cd6c875b6a532292559ebd937
|
7
|
+
data.tar.gz: 3f0bb2ee015a659e426afce49fd3148fde96bee0e501cce2e8e909763c47e89046a79513b769eceab498ffda37c10decab1ebe35095c0ab0bd13774653a8e21a
|
data/lib/em-simple_telnet.rb
CHANGED
@@ -187,6 +187,7 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
187
187
|
fiber = Fiber.new do | callback |
|
188
188
|
connection = connect(opts, &blk)
|
189
189
|
callback.call if callback
|
190
|
+
@@_fiber_statuses.delete opts[:hostname]
|
190
191
|
end
|
191
192
|
|
192
193
|
if EventMachine.reactor_running?
|
@@ -229,19 +230,11 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
229
230
|
# start establishing the connection
|
230
231
|
connection = EventMachine.connect(*params)
|
231
232
|
|
232
|
-
#
|
233
|
-
|
234
|
-
f = Fiber.current
|
235
|
-
connection.connection_state_callback = ->(o){ f.resume(o) }
|
236
|
-
|
237
|
-
# block here and get result from establishing connection
|
238
|
-
state = Fiber.yield
|
239
|
-
|
240
|
-
# raise if exception (e.g. Telnet::ConnectionFailed)
|
241
|
-
raise state if state.is_a? Exception
|
233
|
+
# will be resumed by #connection_completed or #unbind
|
234
|
+
connection.pause_and_wait_for_result
|
242
235
|
|
243
236
|
# login
|
244
|
-
connection.
|
237
|
+
connection.__send__(:login)
|
245
238
|
|
246
239
|
begin
|
247
240
|
yield connection
|
@@ -365,11 +358,13 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
365
358
|
|
366
359
|
@logged_in = nil
|
367
360
|
@connection_state = :connecting
|
368
|
-
|
361
|
+
f = Fiber.current
|
362
|
+
@fiber_resumer = ->(result = nil){ f.resume(result) }
|
369
363
|
@input_buffer = ""
|
370
364
|
@input_rest = ""
|
371
365
|
@wait_time_timer = nil
|
372
366
|
@check_input_buffer_timer = nil
|
367
|
+
@recently_received_data = "" if $DEBUG
|
373
368
|
|
374
369
|
setup_logging
|
375
370
|
end
|
@@ -386,8 +381,13 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
386
381
|
# used telnet options Hash
|
387
382
|
attr_reader :telnet_options
|
388
383
|
|
389
|
-
# the callback executed
|
390
|
-
attr_accessor :
|
384
|
+
# the callback executed again and again to resume this connection's Fiber
|
385
|
+
attr_accessor :fiber_resumer
|
386
|
+
|
387
|
+
def connection_state_callback
|
388
|
+
warn "#connection_state_callback deprecated. use #fiber_resumer instead"
|
389
|
+
fiber_resumer
|
390
|
+
end
|
391
391
|
|
392
392
|
# last prompt matched
|
393
393
|
attr_reader :last_prompt
|
@@ -428,6 +428,7 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
428
428
|
#
|
429
429
|
def timeout= seconds
|
430
430
|
@telnet_options[:timeout] = seconds
|
431
|
+
# warn "#{node}: Setting comm_inactivity_timeout to #{seconds} ...".yellow
|
431
432
|
set_comm_inactivity_timeout( seconds )
|
432
433
|
end
|
433
434
|
|
@@ -492,6 +493,8 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
492
493
|
# #log_output. Then calls #process_payload.
|
493
494
|
#
|
494
495
|
def receive_data data
|
496
|
+
@last_received_data = Time.now
|
497
|
+
@recently_received_data << data if $DEBUG
|
495
498
|
if @telnet_options[:telnet_mode]
|
496
499
|
c = @input_rest + data
|
497
500
|
se_pos = c.rindex(/#{IAC}#{SE}/no) || 0
|
@@ -536,8 +539,8 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
536
539
|
|
537
540
|
##
|
538
541
|
# Appends _buf_ to the <tt>@input_buffer</tt>.
|
539
|
-
# Then cancels the @wait_time_timer
|
540
|
-
#
|
542
|
+
# Then cancels the @wait_time_timer and @check_input_buffer_timer if they're
|
543
|
+
# set.
|
541
544
|
#
|
542
545
|
# Does some performance optimizations in case the input buffer is becoming
|
543
546
|
# huge and finally calls #check_input_buffer.
|
@@ -546,77 +549,95 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
546
549
|
# append output from server to input buffer and log it
|
547
550
|
@input_buffer << buf
|
548
551
|
|
549
|
-
|
550
|
-
|
551
|
-
@wait_time_timer.cancel
|
552
|
-
@wait_time_timer = nil
|
553
|
-
end
|
552
|
+
case @connection_state
|
553
|
+
when :waiting_for_prompt
|
554
554
|
|
555
|
-
|
556
|
-
|
555
|
+
# cancel the timer for wait_time value because we received more data
|
556
|
+
if @wait_time_timer
|
557
|
+
@wait_time_timer.cancel
|
558
|
+
@wait_time_timer = nil
|
559
|
+
end
|
557
560
|
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
561
|
+
# we ensure there's no timer running for checking the input buffer
|
562
|
+
if @check_input_buffer_timer
|
563
|
+
@check_input_buffer_timer.cancel
|
564
|
+
@check_input_buffer_timer = nil
|
565
|
+
end
|
563
566
|
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
567
|
+
if @input_buffer.size >= 100_000
|
568
|
+
##
|
569
|
+
# if the input buffer is really big
|
570
|
+
#
|
571
|
+
|
572
|
+
# We postpone checking the input buffer by one second because the regular
|
573
|
+
# expression matches can get quite slow.
|
574
|
+
#
|
575
|
+
# So as long as data is being received (continuously), the input buffer
|
576
|
+
# is not checked. It's only checked one second after the whole output
|
577
|
+
# has been received.
|
578
|
+
@check_input_buffer_timer = EventMachine::Timer.new(1) do
|
579
|
+
@check_input_buffer_timer = nil
|
580
|
+
check_input_buffer
|
581
|
+
end
|
582
|
+
else
|
583
|
+
##
|
584
|
+
# as long as the input buffer is small
|
585
|
+
#
|
568
586
|
|
569
|
-
|
570
|
-
# expression matches can get quite slow.
|
571
|
-
#
|
572
|
-
# So as long as data is being received (continuously), the input buffer
|
573
|
-
# is not checked. It's only checked one second after the whole output
|
574
|
-
# has been received.
|
575
|
-
@check_input_buffer_timer = EventMachine::Timer.new(1) do
|
576
|
-
@check_input_buffer_timer = nil
|
587
|
+
# check the input buffer now
|
577
588
|
check_input_buffer
|
578
589
|
end
|
590
|
+
when :listening
|
591
|
+
@fiber_resumer.(buf)
|
592
|
+
when :connected, :sleeping
|
593
|
+
if $VERBOSE
|
594
|
+
warn "#{node}: Discarding data that was received while not waiting " +
|
595
|
+
"for data (state = #{@connection_state.inspect}): #{buf.inspect}"
|
596
|
+
end
|
579
597
|
else
|
580
|
-
|
581
|
-
|
582
|
-
#
|
583
|
-
|
584
|
-
# check the input buffer now
|
585
|
-
check_input_buffer
|
598
|
+
raise "Don't know what to do with received data while being in " +
|
599
|
+
"connection state #{@connection_state.inspect}"
|
586
600
|
end
|
587
601
|
end
|
588
602
|
|
589
603
|
##
|
590
604
|
# Checks the input buffer (<tt>@input_buffer</tt>) for the prompt we're
|
591
|
-
# waiting for. Calls
|
592
|
-
# prompt has been found.
|
593
|
-
# <tt>@connection_state_callback</tt> is set!
|
605
|
+
# waiting for. Calls @fiber_resumer with the output if the
|
606
|
+
# prompt has been found.
|
594
607
|
#
|
595
608
|
# If <tt>@telnet_options[:wait_time]</tt> is set, it will wait this amount
|
596
609
|
# of seconds after seeing what looks like the prompt before calling
|
597
|
-
#
|
610
|
+
# @fiber_resumer. This way, more data can be received
|
598
611
|
# until the real prompt is received. This is useful for commands that send
|
599
612
|
# multiple prompts.
|
600
613
|
#
|
601
614
|
def check_input_buffer
|
602
615
|
return unless md = @input_buffer.match(@telnet_options[:prompt])
|
603
616
|
|
604
|
-
blk = lambda do
|
605
|
-
@last_prompt = md.to_s # remember last prompt
|
606
|
-
output = md.pre_match + @last_prompt
|
607
|
-
@input_buffer = md.post_match
|
608
|
-
call_connection_state_callback(output)
|
609
|
-
end
|
610
|
-
|
611
617
|
if s = @telnet_options[:wait_time] and s > 0
|
612
|
-
#
|
613
|
-
@wait_time_timer = EventMachine::Timer.new(s
|
618
|
+
# resume Fiber after s seconds
|
619
|
+
@wait_time_timer = EventMachine::Timer.new(s) { process_match_data(md) }
|
614
620
|
else
|
615
|
-
#
|
616
|
-
|
621
|
+
# resume Fiber now
|
622
|
+
process_match_data(md)
|
617
623
|
end
|
618
624
|
end
|
619
625
|
|
626
|
+
# Takes out the @last_prompt from _md_ (MatchData) and remembers it.
|
627
|
+
# Resumes the fiber (using @fiber_resumer) with the output (which
|
628
|
+
# includes the prompt and everything before).
|
629
|
+
def process_match_data(md)
|
630
|
+
@last_prompt = md.to_s # remember the prompt matched
|
631
|
+
output = md.pre_match + @last_prompt
|
632
|
+
@input_buffer = md.post_match
|
633
|
+
@fiber_resumer.(output)
|
634
|
+
end
|
635
|
+
|
636
|
+
@@_fiber_statuses = {}
|
637
|
+
def self._fiber_statuses
|
638
|
+
@@_fiber_statuses
|
639
|
+
end
|
640
|
+
|
620
641
|
##
|
621
642
|
# Read data from the host until a certain sequence is matched.
|
622
643
|
#
|
@@ -633,6 +654,9 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
633
654
|
# * +:wait_time+ (actually used by #check_input_buffer)
|
634
655
|
#
|
635
656
|
def waitfor prompt=nil, opts={}
|
657
|
+
if closed?
|
658
|
+
abort "Can't wait for anything when connection is already closed!"
|
659
|
+
end
|
636
660
|
options_were = @telnet_options
|
637
661
|
timeout_was = self.timeout if opts.key?(:timeout)
|
638
662
|
opts[:prompt] = prompt if prompt
|
@@ -649,25 +673,78 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
649
673
|
|
650
674
|
# so #unbind knows we were waiting for a prompt (in case that inactivity
|
651
675
|
# timeout fires)
|
652
|
-
|
676
|
+
self.connection_state = :waiting_for_prompt
|
653
677
|
|
654
|
-
|
655
|
-
|
678
|
+
pause_and_wait_for_result
|
679
|
+
ensure
|
680
|
+
@telnet_options = options_were
|
681
|
+
self.timeout = timeout_was if opts.key?(:timeout)
|
682
|
+
|
683
|
+
# #unbind could have been called in the meantime
|
684
|
+
self.connection_state = :connected if !closed?
|
685
|
+
end
|
656
686
|
|
657
|
-
|
658
|
-
|
687
|
+
# Pauses the current Fiber. When resumed, returns the value passed. If the
|
688
|
+
# value passed is an Exeption, it's raised.
|
689
|
+
def pause_and_wait_for_result
|
690
|
+
@@_fiber_statuses[node] = :paused
|
691
|
+
result = nil
|
692
|
+
while result == nil
|
693
|
+
# measure how long Fiber is paused
|
694
|
+
if $DEBUG
|
695
|
+
before_pause = Time.now
|
696
|
+
result = Fiber.yield
|
697
|
+
pause_duration = Time.now - before_pause
|
698
|
+
|
699
|
+
m = "#{node}: Fiber was paused for #{pause_duration * 1000}ms and " +
|
700
|
+
"is resumed with: #{result.inspect}"
|
701
|
+
result.nil? ? warn(m.red) : warn(m)
|
702
|
+
else
|
703
|
+
result = Fiber.yield
|
704
|
+
end
|
705
|
+
end
|
659
706
|
|
660
|
-
|
707
|
+
@@_fiber_statuses[node] = :running
|
661
708
|
|
662
709
|
raise result if result.is_a? Exception
|
663
710
|
return result
|
711
|
+
end
|
712
|
+
|
713
|
+
# Identifier for this connection. Like an IP address or hostname. In this
|
714
|
+
# case, it's <tt>@telnet_options[:host]</tt>.
|
715
|
+
def node
|
716
|
+
@telnet_options[:host]
|
717
|
+
end
|
718
|
+
|
719
|
+
# Listen for anything that's received from the node. Each received chunk
|
720
|
+
# will be yielded to the block passed. To make it stop listening, the block
|
721
|
+
# should +return+ or +raise+ something.
|
722
|
+
#
|
723
|
+
# The default timeout during listening is 90 seconds. Use the option
|
724
|
+
# +:timeout+ to change this.
|
725
|
+
def listen(opts = {}, &blk)
|
726
|
+
self.connection_state = :listening
|
727
|
+
timeout(opts.fetch(:timeout, 90)) do
|
728
|
+
yield pause_and_wait_for_result while true
|
729
|
+
end
|
664
730
|
ensure
|
665
|
-
|
666
|
-
|
667
|
-
|
731
|
+
self.connection_state = :connected
|
732
|
+
end
|
733
|
+
|
734
|
+
# Passes argument to #send_data.
|
735
|
+
def write(s)
|
736
|
+
send_data(s)
|
668
737
|
end
|
669
738
|
|
670
|
-
|
739
|
+
# Raises Errno::ENOTCONN in case the connection is closed (#unbind has been
|
740
|
+
# called before). Also contains some debugging stuff depending on $DEBUG.
|
741
|
+
def send_data(s)
|
742
|
+
raise Errno::ENOTCONN, "Connection is already closed." if closed?
|
743
|
+
@last_sent_data = Time.now
|
744
|
+
print_recently_received_data if $DEBUG
|
745
|
+
warn "#{node}: Sending #{s.inspect}" if $DEBUG
|
746
|
+
super
|
747
|
+
end
|
671
748
|
|
672
749
|
##
|
673
750
|
# Sends a string to the host.
|
@@ -830,56 +907,74 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
830
907
|
#
|
831
908
|
# Decreases <tt>@@_telnet_connection_count</tt> by one and calls #close_logs.
|
832
909
|
#
|
833
|
-
# After that
|
910
|
+
# After that is set, it takes a
|
834
911
|
# look at <tt>@connection_state</tt>. If it was <tt>:connecting</tt>, calls
|
835
|
-
#
|
912
|
+
# @fiber_resumer with a new instance of ConnectionFailed.
|
836
913
|
# If it was <tt>:waiting_for_prompt</tt>, calls the same method with a new
|
837
914
|
# instance of TimeoutError.
|
838
915
|
#
|
839
916
|
# Finally, the <tt>@connection_state</tt> is set to +closed+.
|
840
917
|
#
|
841
|
-
def unbind
|
918
|
+
def unbind(reason)
|
919
|
+
@unbound_at = Time.now
|
920
|
+
prev_conn_state = @connection_state
|
921
|
+
self.connection_state = :closed
|
922
|
+
if $DEBUG
|
923
|
+
warn "#{node}: unbinding because of: " + reason.inspect.red.bold
|
924
|
+
end
|
925
|
+
@@unbound_at[node] = [ Time.now, prev_conn_state ]
|
842
926
|
@@_telnet_connection_count -= 1
|
843
927
|
close_logs
|
844
928
|
|
845
|
-
if
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
when :waiting_for_prompt
|
852
|
-
error = TimeoutError.new
|
853
|
-
|
854
|
-
# set hostname and command
|
855
|
-
if hostname = @telnet_options[:host]
|
856
|
-
error.hostname = hostname
|
857
|
-
end
|
858
|
-
error.command = @last_command if @last_command
|
929
|
+
# if we were connecting or waiting for a prompt, return an exception to
|
930
|
+
# #waitfor
|
931
|
+
case prev_conn_state
|
932
|
+
when :waiting_for_prompt, :listening
|
933
|
+
# NOTE: reason should be Errno::ETIMEDOUT in these cases.
|
934
|
+
error = TimeoutError.new
|
859
935
|
|
860
|
-
|
936
|
+
# set hostname and command
|
937
|
+
if hostname = @telnet_options[:host]
|
938
|
+
error.hostname = hostname
|
861
939
|
end
|
862
|
-
|
940
|
+
error.command = @last_command if @last_command
|
863
941
|
|
864
|
-
|
865
|
-
|
942
|
+
@fiber_resumer.(error)
|
943
|
+
when :sleeping, :connected
|
866
944
|
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
945
|
+
when :connecting
|
946
|
+
@fiber_resumer.(ConnectionFailed.new)
|
947
|
+
else
|
948
|
+
m = "#{node}: bad connection state #{prev_conn_state.inspect} " +
|
949
|
+
"while unbinding"
|
950
|
+
warn m.red
|
951
|
+
debugger
|
952
|
+
end
|
875
953
|
end
|
876
954
|
|
955
|
+
@@unbound_at = {}
|
956
|
+
|
957
|
+
def self.unbound_at() @@unbound_at end
|
958
|
+
|
877
959
|
##
|
878
960
|
# Called by EventMachine after the connection is successfully established.
|
879
961
|
#
|
880
962
|
def connection_completed
|
881
|
-
|
882
|
-
|
963
|
+
self.connection_state = :connected
|
964
|
+
@fiber_resumer.(:connection_completed)
|
965
|
+
|
966
|
+
# print received data in a more readable way
|
967
|
+
if $DEBUG
|
968
|
+
EventMachine.add_periodic_timer(0.5) { print_recently_received_data }
|
969
|
+
end
|
970
|
+
end
|
971
|
+
|
972
|
+
# Prints recently received data (@recently_received), if there's any, and
|
973
|
+
# empties that buffer afterwards.
|
974
|
+
def print_recently_received_data
|
975
|
+
return if @recently_received_data.empty?
|
976
|
+
warn "#{node}: Received: #{@recently_received_data.inspect}".cyan
|
977
|
+
@recently_received_data = ""
|
883
978
|
end
|
884
979
|
|
885
980
|
##
|
@@ -907,6 +1002,13 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
907
1002
|
|
908
1003
|
private
|
909
1004
|
|
1005
|
+
# Sets the @connection_state to _new_state_. Raises if current (old) state is
|
1006
|
+
# :closed, because that can't be changed.
|
1007
|
+
def connection_state=(new_state)
|
1008
|
+
raise Errno::ENOTCONN, "Connection is already closed." if closed?
|
1009
|
+
@connection_state = new_state
|
1010
|
+
end
|
1011
|
+
|
910
1012
|
##
|
911
1013
|
# Sets up output and command logging.
|
912
1014
|
#
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-simple_telnet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patrik Wenger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
@@ -54,9 +54,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
54
|
version: '0'
|
55
55
|
requirements: []
|
56
56
|
rubyforge_project:
|
57
|
-
rubygems_version: 2.0.
|
57
|
+
rubygems_version: 2.0.2
|
58
58
|
signing_key:
|
59
59
|
specification_version: 4
|
60
60
|
summary: Simple telnet client on EventMachine
|
61
61
|
test_files: []
|
62
|
-
has_rdoc: true
|