em-simple_telnet 0.0.1 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|