em-simple_telnet 0.0.1 → 0.0.6
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/README.rdoc +47 -0
- data/lib/em-simple_telnet.rb +83 -82
- metadata +22 -5
data/README.rdoc
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
== EventMachine::Protocols::SimpleTelnet
|
2
|
+
|
3
|
+
Provides telnet client functionality.
|
4
|
+
|
5
|
+
This class derived from the Net::Telnet class in Ruby's standard library.
|
6
|
+
It was developed with simplicity in mind. It tries to hide the complexity
|
7
|
+
that normally comes with asynchronous programming using the Fiber class
|
8
|
+
found in Ruby 1.9.
|
9
|
+
|
10
|
+
== Overview
|
11
|
+
|
12
|
+
The telnet protocol allows a client to login remotely to a user account on a
|
13
|
+
server and execute commands via a shell. The equivalent is done by creating
|
14
|
+
a EventMachine::Protocols::SimpleTelnet instance with the <tt>:host</tt>
|
15
|
+
option set to your host along with a block which defines the task to be done
|
16
|
+
on the host. Inside the task you can issue one or more #cmd calls. After the
|
17
|
+
block is executed, #close is automatically called.
|
18
|
+
|
19
|
+
This class can also be used to connect to non-telnet services, such as SMTP
|
20
|
+
or HTTP. In this case, you normally want to provide the <tt>:port</tt>
|
21
|
+
option to specify the port to connect to, and set the <tt>:telnet_mode</tt>
|
22
|
+
option to +false+ to prevent the client from attempting to interpret telnet
|
23
|
+
command sequences. Generally, #login will not work with other protocols,
|
24
|
+
and you have to handle authentication yourself.
|
25
|
+
|
26
|
+
== Examples
|
27
|
+
|
28
|
+
EventMachine.run do
|
29
|
+
|
30
|
+
opts = {
|
31
|
+
host: "localhost",
|
32
|
+
username: "user",
|
33
|
+
password: "secret",
|
34
|
+
}
|
35
|
+
|
36
|
+
EM::P::SimpleTelnet.new(opts) do |host|
|
37
|
+
# already logged in
|
38
|
+
puts host.cmd("ls -la")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
== References
|
43
|
+
|
44
|
+
There are a large number of RFCs relevant to the Telnet protocol.
|
45
|
+
RFCs 854-861 define the base protocol. For a complete listing
|
46
|
+
of relevant RFCs, see
|
47
|
+
http://www.omnifarious.org/~hopper/technical/telnet-rfc.html
|
data/lib/em-simple_telnet.rb
CHANGED
@@ -15,27 +15,18 @@ require "eventmachine"
|
|
15
15
|
# still working on a deferred action or not.
|
16
16
|
#
|
17
17
|
module EventMachine # :nodoc:
|
18
|
-
|
19
|
-
|
20
|
-
(not defined? @resultqueue or (rq=@resultqueue).nil? or rq.empty? ) and
|
21
|
-
(not defined? @threadpool or (tp=@threadpool).nil? or tp.none? {|t|t[:working]})
|
22
|
-
end
|
18
|
+
# ensure they're always defined
|
19
|
+
@threadpool = @threadqueue = @resultqueue = nil
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@resultqueue << [result, cback]
|
34
|
-
EventMachine.signal_loopbreak
|
35
|
-
end
|
36
|
-
end
|
37
|
-
@threadpool << thread
|
38
|
-
end
|
21
|
+
##
|
22
|
+
# Returns +true+ if all deferred actions are done executing and their
|
23
|
+
# callbacks have been fired.
|
24
|
+
#
|
25
|
+
def self.defers_finished?
|
26
|
+
return false if @threadqueue and not @threadqueue.empty?
|
27
|
+
return false if @resultqueue and not @resultqueue.empty?
|
28
|
+
return false if @threadpool and @threadqueue.num_waiting != @threadpool.size
|
29
|
+
return true
|
39
30
|
end
|
40
31
|
end
|
41
32
|
|
@@ -261,32 +252,39 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
261
252
|
opts
|
262
253
|
]
|
263
254
|
|
264
|
-
|
265
|
-
|
255
|
+
begin
|
256
|
+
# start establishing the connection
|
257
|
+
connection = EventMachine.connect(*params)
|
266
258
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
@connection_state_callback = nil
|
272
|
-
f.resume obj
|
273
|
-
end
|
259
|
+
# set callback to be executed when connection establishing
|
260
|
+
# fails/succeeds
|
261
|
+
f = Fiber.current
|
262
|
+
connection.connection_state_callback = ->(o){ f.resume(o) }
|
274
263
|
|
275
|
-
|
276
|
-
|
264
|
+
# block here and get result from establishing connection
|
265
|
+
state = Fiber.yield
|
277
266
|
|
278
|
-
|
279
|
-
|
267
|
+
# raise if exception (e.g. Telnet::ConnectionFailed)
|
268
|
+
raise state if state.is_a? Exception
|
280
269
|
|
281
|
-
|
282
|
-
|
270
|
+
# login
|
271
|
+
connection.instance_eval { login }
|
283
272
|
|
284
|
-
|
285
|
-
|
273
|
+
begin
|
274
|
+
yield connection
|
275
|
+
ensure
|
276
|
+
# Use #close so a subclass can execute some kind of logout command
|
277
|
+
# before the connection is closed.
|
278
|
+
connection.close
|
279
|
+
end
|
286
280
|
ensure
|
287
|
-
#
|
288
|
-
|
289
|
-
|
281
|
+
# close the connection in any case
|
282
|
+
if connection
|
283
|
+
connection.close_connection_after_writing
|
284
|
+
|
285
|
+
# give some time to send the remaining data, which should be nothing
|
286
|
+
EventMachine.add_timer(2) { connection.close_connection }
|
287
|
+
end
|
290
288
|
end
|
291
289
|
|
292
290
|
return connection
|
@@ -574,7 +572,7 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
574
572
|
# we only need to do something if there's a connection state callback
|
575
573
|
return unless @connection_state_callback
|
576
574
|
|
577
|
-
# we ensure there's no timer running
|
575
|
+
# we ensure there's no timer running for checking the input buffer
|
578
576
|
if @check_input_buffer_timer
|
579
577
|
@check_input_buffer_timer.cancel
|
580
578
|
@check_input_buffer_timer = nil
|
@@ -588,9 +586,9 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
588
586
|
# We postpone checking the input buffer by one second because the regular
|
589
587
|
# expression matches can get quite slow.
|
590
588
|
#
|
591
|
-
# So as long as data is received (continuously), the input buffer
|
592
|
-
# checked. It's only checked one second after the whole output
|
593
|
-
# received.
|
589
|
+
# So as long as data is being received (continuously), the input buffer
|
590
|
+
# is not checked. It's only checked one second after the whole output
|
591
|
+
# has been received.
|
594
592
|
@check_input_buffer_timer = EventMachine::Timer.new(1) do
|
595
593
|
@check_input_buffer_timer = nil
|
596
594
|
check_input_buffer
|
@@ -607,33 +605,32 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
607
605
|
|
608
606
|
##
|
609
607
|
# Checks the input buffer (<tt>@input_buffer</tt>) for the prompt we're
|
610
|
-
# waiting for. Calls
|
608
|
+
# waiting for. Calls #call_connection_state_callback with the output if the
|
611
609
|
# prompt has been found. Thus, call this method *only* if
|
612
610
|
# <tt>@connection_state_callback</tt> is set!
|
613
611
|
#
|
614
|
-
# If <tt>@telnet_options[:wait_time]</tt> is set,
|
615
|
-
#
|
616
|
-
#
|
617
|
-
#
|
618
|
-
#
|
619
|
-
# multiple prompts to be sent.
|
612
|
+
# If <tt>@telnet_options[:wait_time]</tt> is set, it will wait this amount
|
613
|
+
# of seconds after seeing what looks like the prompt before calling
|
614
|
+
# #call_connection_state_callback. This way, more data can be received
|
615
|
+
# until the real prompt is received. This is useful for commands that send
|
616
|
+
# multiple prompts.
|
620
617
|
#
|
621
618
|
def check_input_buffer
|
622
|
-
|
623
|
-
blk = lambda do
|
624
|
-
@last_prompt = md.to_s # remember last prompt
|
625
|
-
output = md.pre_match + @last_prompt
|
626
|
-
@input_buffer = md.post_match
|
627
|
-
@connection_state_callback.call(output)
|
628
|
-
end
|
619
|
+
return unless md = @input_buffer.match(@telnet_options[:prompt])
|
629
620
|
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
621
|
+
blk = lambda do
|
622
|
+
@last_prompt = md.to_s # remember last prompt
|
623
|
+
output = md.pre_match + @last_prompt
|
624
|
+
@input_buffer = md.post_match
|
625
|
+
call_connection_state_callback(output)
|
626
|
+
end
|
627
|
+
|
628
|
+
if s = @telnet_options[:wait_time] and s > 0
|
629
|
+
# fire @connection_state_callback after s seconds
|
630
|
+
@wait_time_timer = EventMachine::Timer.new(s, &blk)
|
631
|
+
else
|
632
|
+
# fire @connection_state_callback now
|
633
|
+
blk.call
|
637
634
|
end
|
638
635
|
end
|
639
636
|
|
@@ -675,10 +672,7 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
675
672
|
f = Fiber.current
|
676
673
|
|
677
674
|
# will be called by #receive_data to resume at "Fiber.yield" below
|
678
|
-
@connection_state_callback =
|
679
|
-
@connection_state_callback = nil
|
680
|
-
f.resume(output)
|
681
|
-
end
|
675
|
+
@connection_state_callback = ->(output){ f.resume(output) }
|
682
676
|
|
683
677
|
result = Fiber.yield
|
684
678
|
|
@@ -769,8 +763,7 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
769
763
|
log_command(opts[:hide] ? "<hidden command>" : command)
|
770
764
|
|
771
765
|
# send the command
|
772
|
-
|
773
|
-
self.__send__(sendcmd, command)
|
766
|
+
opts[:raw_command] ? print(command) : puts(command)
|
774
767
|
|
775
768
|
# wait for the output
|
776
769
|
waitfor(opts[:prompt], opts)
|
@@ -855,10 +848,10 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
855
848
|
# Decreases <tt>@@_telnet_connection_count</tt> by one and calls #close_logs.
|
856
849
|
#
|
857
850
|
# After that and if <tt>@connection_state_callback</tt> is set, it takes a
|
858
|
-
# look
|
859
|
-
#
|
860
|
-
#
|
861
|
-
#
|
851
|
+
# look at <tt>@connection_state</tt>. If it was <tt>:connecting</tt>, calls
|
852
|
+
# #call_connection_state_callback with a new instance of ConnectionFailed.
|
853
|
+
# If it was <tt>:waiting_for_prompt</tt>, calls the same method with a new
|
854
|
+
# instance of TimeoutError.
|
862
855
|
#
|
863
856
|
# Finally, the <tt>@connection_state</tt> is set to +closed+.
|
864
857
|
#
|
@@ -888,23 +881,31 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
|
|
888
881
|
@connection_state = :closed
|
889
882
|
end
|
890
883
|
|
884
|
+
##
|
885
|
+
# Calls the @connection_state_callback with _obj_ and sets it to +nil+.
|
886
|
+
#
|
887
|
+
# Call this method *only* if <tt>@connection_state_callback</tt> is set!
|
888
|
+
#
|
889
|
+
def call_connection_state_callback(obj=nil)
|
890
|
+
callback, @connection_state_callback = @connection_state_callback, nil
|
891
|
+
callback.call(obj)
|
892
|
+
end
|
893
|
+
|
891
894
|
##
|
892
895
|
# Called by EventMachine after the connection is successfully established.
|
893
896
|
#
|
894
897
|
def connection_completed
|
895
898
|
@connection_state = :connected
|
896
|
-
|
899
|
+
call_connection_state_callback if @connection_state_callback
|
897
900
|
end
|
898
901
|
|
899
902
|
##
|
900
|
-
#
|
901
|
-
#
|
902
|
-
#
|
903
|
-
#
|
904
|
-
# wait for a prompt.
|
903
|
+
# Redefine this method to execute some logout command like +exit+ or
|
904
|
+
# +logout+ before the connection is closed. Don't forget: The command will
|
905
|
+
# probably not return a prompt, so use #puts, which doesn't wait for a
|
906
|
+
# prompt.
|
905
907
|
#
|
906
908
|
def close
|
907
|
-
close_connection_after_writing
|
908
909
|
end
|
909
910
|
|
910
911
|
##
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,16 +10,34 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
date: 2011-11-21 00:00:00.000000000 Z
|
13
|
-
dependencies:
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: eventmachine
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0.beta.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.0.beta.2
|
14
30
|
description: This library provides a very simple way to connect to telnet servers
|
15
31
|
using EventMachine in a seemingly synchronous manner.
|
16
32
|
email:
|
17
33
|
- paddor@gmail.com
|
18
34
|
executables: []
|
19
35
|
extensions: []
|
20
|
-
extra_rdoc_files:
|
36
|
+
extra_rdoc_files:
|
37
|
+
- README.rdoc
|
21
38
|
files:
|
22
39
|
- lib/em-simple_telnet.rb
|
40
|
+
- README.rdoc
|
23
41
|
homepage: http://github.com/paddor/em-simple_telnet
|
24
42
|
licenses: []
|
25
43
|
post_install_message:
|
@@ -40,9 +58,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
58
|
version: '0'
|
41
59
|
requirements: []
|
42
60
|
rubyforge_project:
|
43
|
-
rubygems_version: 1.8.
|
61
|
+
rubygems_version: 1.8.23
|
44
62
|
signing_key:
|
45
63
|
specification_version: 3
|
46
64
|
summary: Simple telnet client on EventMachine
|
47
65
|
test_files: []
|
48
|
-
has_rdoc:
|