Class: EventMachine::Protocols::SimpleTelnet

Inherits:
Connection
  • Object
show all
Defined in:
lib/em-simple_telnet.rb

Overview

Provides the facility to connect to telnet servers using EventMachine. The asynchronity is hidden so you can use this library just like Net::Telnet in a seemingly synchronous manner. See README for an example.

EventMachine.run do

  opts = {
    host: "localhost",
    username: "user",
    password: "secret",
  }

  EM::P::SimpleTelnet.new(opts) do |host|
    # already logged in
    puts host.cmd("ls -la")
  end
end

Because of being event-driven, it performs quite well and can handle a lot of connections concurrently.

Defined Under Namespace

Classes: ConnectionFailed, LoginFailed, TimeoutError

Constant Summary

IAC =

:stopdoc:

255.chr
DONT =
254.chr
DO =
253.chr
WONT =
252.chr
WILL =
251.chr
SB =
250.chr
GA =
249.chr
EL =
248.chr
EC =
247.chr
AYT =
246.chr
AO =
245.chr
IP =
244.chr
BREAK =
243.chr
DM =
242.chr
NOP =
241.chr
SE =
240.chr
EOR =
239.chr
ABORT =
238.chr
SUSP =
237.chr
EOF =
236.chr
SYNCH =

"377" # "xff" # interpret as command "376" # "xfe" # you are not to use option "375" # "xfd" # please, you use option "374" # "xfc" # I won't use option "373" # "xfb" # I will use option "372" # "xfa" # interpret as subnegotiation "371" # "xf9" # you may reverse the line "370" # "xf8" # erase the current line "367" # "xf7" # erase the current character "366" # "xf6" # are you there "365" # "xf5" # abort output--but let prog finish "364" # "xf4" # interrupt process--permanently "363" # "xf3" # break "362" # "xf2" # data mark--for connect. cleaning "361" # "xf1" # nop "360" # "xf0" # end sub negotiation "357" # "xef" # end of record (transparent mode) "356" # "xee" # Abort process "355" # "xed" # Suspend process "354" # "xec" # End of file "362" # "xf2" # for telfunc calls

242.chr
OPT_BINARY =
0.chr
OPT_ECHO =
1.chr
OPT_RCP =
2.chr
OPT_SGA =
3.chr
OPT_NAMS =
4.chr
OPT_STATUS =
5.chr
OPT_TM =
6.chr
OPT_RCTE =
7.chr
OPT_NAOL =
8.chr
OPT_NAOP =
9.chr
OPT_NAOCRD =
10.chr
OPT_NAOHTS =
11.chr
OPT_NAOHTD =
12.chr
OPT_NAOFFD =
13.chr
OPT_NAOVTS =
14.chr
OPT_NAOVTD =
15.chr
OPT_NAOLFD =
16.chr
OPT_XASCII =
17.chr
OPT_LOGOUT =
18.chr
OPT_BM =
19.chr
OPT_DET =
20.chr
OPT_SUPDUP =
21.chr
OPT_SUPDUPOUTPUT =
22.chr
OPT_SNDLOC =
23.chr
OPT_TTYPE =
24.chr
OPT_EOR =
25.chr
OPT_TUID =
26.chr
OPT_OUTMRK =
27.chr
OPT_TTYLOC =
28.chr
OPT_3270REGIME =
29.chr
OPT_X3PAD =
30.chr
OPT_NAWS =
31.chr
OPT_TSPEED =
32.chr
OPT_LFLOW =
33.chr
OPT_LINEMODE =
34.chr
OPT_XDISPLOC =
35.chr
OPT_OLD_ENVIRON =
36.chr
OPT_AUTHENTICATION =
37.chr
OPT_ENCRYPT =
38.chr
OPT_NEW_ENVIRON =
39.chr
OPT_EXOPL =

"000" # "x00" # Binary Transmission "001" # "x01" # Echo "002" # "x02" # Reconnection "003" # "x03" # Suppress Go Ahead "004" # "x04" # Approx Message Size Negotiation "005" # "x05" # Status "006" # "x06" # Timing Mark "a" # "x07" # Remote Controlled Trans and Echo "010" # "x08" # Output Line Width "t" # "x09" # Output Page Size "n" # "x0a" # Output Carriage-Return Disposition "v" # "x0b" # Output Horizontal Tab Stops "f" # "x0c" # Output Horizontal Tab Disposition "r" # "x0d" # Output Formfeed Disposition "016" # "x0e" # Output Vertical Tabstops "017" # "x0f" # Output Vertical Tab Disposition "020" # "x10" # Output Linefeed Disposition "021" # "x11" # Extended ASCII "022" # "x12" # Logout "023" # "x13" # Byte Macro "024" # "x14" # Data Entry Terminal "025" # "x15" # SUPDUP "026" # "x16" # SUPDUP Output "027" # "x17" # Send Location "030" # "x18" # Terminal Type "031" # "x19" # End of Record "032" # "x1a" # TACACS User Identification "e" # "x1b" # Output Marking "034" # "x1c" # Terminal Location Number "035" # "x1d" # Telnet 3270 Regime "036" # "x1e" # X.3 PAD "037" # "x1f" # Negotiate About Window Size " " # "x20" # Terminal Speed "!" # "x21" # Remote Flow Control """ # "x22" # Linemode "#" # "x23" # X Display Location "$" # "x24" # Environment Option "%" # "x25" # Authentication Option "&" # "x26" # Encryption Option "'" # "x27" # New Environment Option "377" # "xff" # Extended-Options-List

255.chr
NULL =
"\000"
CR =
"\015"
LF =
"\012"
EOL =
CR + LF
DefaultOptions =

default options for new connections (used for merging)

{
  host: "localhost",
  port: 23,
  prompt: %r{[$%#>] \z}n,
  connect_timeout: 3,
  timeout: 10,
  wait_time: 0,
  bin_mode: false,
  telnet_mode: true,
  output_log: nil,
  command_log: nil,
  login_prompt: %r{[Ll]ogin[: ]*\z}n,
  password_prompt: %r{[Pp]ass(?:word|phrase)[: ]*\z}n,
  username: nil,
  password: nil,

  # telnet protocol stuff
  SGA: false,
  BINARY: false,
}.freeze
StopWhenEMDone =
lambda do
  stop_ticks += 1
  if stop_ticks >= 100
    stop_ticks = 0
    # stop when everything is done
    if self.connection_count.zero? and EventMachine.defers_finished?
      EventMachine.stop
    else
      EventMachine.next_tick(&StopWhenEMDone)
    end
  else
    EventMachine.next_tick(&StopWhenEMDone)
  end
end
@@_telnet_connection_count =

number of active connections

0

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (SimpleTelnet) initialize(opts)

Initializes the current instance. opts is a Hash of options. The default values are in the constant DefaultOptions. The following keys are recognized:

:host

the hostname or IP address of the host to connect to, as a String. Defaults to "localhost".

:port

the port to connect to. Defaults to 23.

:bin_mode

if false (the default), newline substitution is performed. Outgoing LF is converted to CRLF, and incoming CRLF is converted to LF. If true, this substitution is not performed. This value can also be set with the #bin_mode= method. The outgoing conversion only applies to the #puts and #print methods, not the #write method. The precise nature of the newline conversion is also affected by the telnet options SGA and BIN.

:output_log

the name of the file to write connection status messages and all received traffic to. In the case of a proper Telnet session, this will include the client input as echoed by the host; otherwise, it only includes server responses. Output is appended verbatim to this file. By default, no output log is kept.

:command_log

the name of the file to write the commands executed in this Telnet session. Commands are appended to this file. By default, no command log is kept.

:prompt

a regular expression matching the host's command-line prompt sequence. This is needed by the Telnet class to determine when the output from a command has finished and the host is ready to receive a new command. By default, this regular expression is %r{[$%#>] \z}n.

:login_prompt

a regular expression (or String, see #waitfor) used to wait for the login prompt.

:password_prompt

a regular expression (or String, see #waitfor) used to wait for the password prompt.

:username

the String that is sent to the telnet server after seeing the login prompt. Just leave this value as nil which is the default value if you don't have to log in.

:password

the String that is sent to the telnet server after seeing the password prompt. Just leave this value as nil which is the default value if you don't have to print a password after printing the username.

:telnet_mode

a boolean value, true by default. In telnet mode, traffic received from the host is parsed for special command sequences, and these sequences are escaped in outgoing traffic sent using #puts or #print (but not #write). If you are connecting to a non-telnet service (such as SMTP or POP), this should be set to "false" to prevent undesired data corruption. This value can also be set by the #telnetmode method.

:timeout

the number of seconds (default: 10) to wait before timing out while waiting for the prompt (in #waitfor). Exceeding this timeout causes a TimeoutError to be raised. You can disable the timeout by setting this value to nil.

:connect_timeout

the number of seconds (default: 3) to wait before timing out the initial attempt to connect. You can disable the timeout by setting this value to nil.

:wait_time

the amount of time to wait after seeing what looks like a prompt (that is, received data that matches the Prompt option regular expression) to see if more data arrives. If more data does arrive in this time, it assumes that what it saw was not really a prompt. This is to try to avoid false matches, but it can also lead to missing real prompts (if, for instance, a background process writes to the terminal soon after the prompt is displayed). By default, set to 0, meaning not to wait for more data.

The options are actually merged in connect().



391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/em-simple_telnet.rb', line 391

def initialize opts
  @telnet_options = opts
  @last_command = nil

  @logged_in = nil
  @connection_state = :connecting
  @connection_state_callback = nil
  @input_buffer = ""
  @input_rest = ""
  @wait_time_timer = nil
  @check_input_buffer_timer = nil

  setup_logging
end

Instance Attribute Details

- (Object) command_logger (readonly)

Logger used to log commands



413
414
415
# File 'lib/em-simple_telnet.rb', line 413

def command_logger
  @command_logger
end

- (Object) connection_state_callback

the callback executed after connection established or failed



419
420
421
# File 'lib/em-simple_telnet.rb', line 419

def connection_state_callback
  @connection_state_callback
end

- (Object) last_command (readonly)

Last command that was executed in this telnet session



407
408
409
# File 'lib/em-simple_telnet.rb', line 407

def last_command
  @last_command
end

- (Object) last_prompt (readonly)

last prompt matched



422
423
424
# File 'lib/em-simple_telnet.rb', line 422

def last_prompt
  @last_prompt
end

- (Object) logged_in (readonly)

When the login succeeded for this connection.



500
501
502
# File 'lib/em-simple_telnet.rb', line 500

def logged_in
  @logged_in
end

- (Object) output_logger (readonly)

Logger used to log output



410
411
412
# File 'lib/em-simple_telnet.rb', line 410

def output_logger
  @output_logger
end

- (Object) telnet_options (readonly)

used telnet options Hash



416
417
418
# File 'lib/em-simple_telnet.rb', line 416

def telnet_options
  @telnet_options
end

Class Method Details

+ (Object) connect(opts)

Merges DefaultOptions with opts. Establishes the connection to the :host key using EventMachine.connect, logs in using #login and passes the connection to the block provided. Closes the connection using #close after the block terminates. The connection is then returned.



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/em-simple_telnet.rb', line 251

def connect opts
  opts = DefaultOptions.merge opts

  params = [
    # for EventMachine.connect
    opts[:host],
    opts[:port],
    self,

    # pass the *merged* options to SimpleTelnet#initialize
    opts
  ]

  # start establishing the connection
  connection = EventMachine.connect(*params)

  # set callback to be executed when connection establishing
  # fails/succeeds
  f = Fiber.current
  connection.connection_state_callback = lambda do |obj=nil|
    @connection_state_callback = nil
    f.resume obj
  end

  # block here and get result from establishing connection
  state = Fiber.yield

  # raise if exception (e.g. Telnet::ConnectionFailed)
  raise state if state.is_a? Exception

  # login
  connection.instance_eval {  }

  begin
    yield connection
  ensure
    # Use #close so a subclass can execute some kind of logout command
    # before the connection is closed.
    connection.close
  end

  return connection
end

+ (Object) connection_count

Returns the number of active connections (@@_telnet_connection_count).



299
300
301
# File 'lib/em-simple_telnet.rb', line 299

def connection_count
  @@_telnet_connection_count
end

+ (Object) new(*args, &blk)

Recognizes whether this call was issued by the user program or by EventMachine. If the call was not issued by EventMachine, merges the options provided with the DefaultOptions and creates a Fiber (not started yet). Inside the Fiber SimpleTelnet.connect would be called.

If EventMachine's reactor is already running, just starts the Fiber.

If it's not running yet, starts a new EventMachine reactor and starts the Fiber. The EventMachine block is stopped using the StopWhenEMDone proc (lambda).

The (closed) connection is returned.



213
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
# File 'lib/em-simple_telnet.rb', line 213

def new *args, &blk
  # call super if first argument is a connection signature of
  # EventMachine
  return super(*args, &blk) if args.first.is_a? Integer

  # This method was probably called with a Hash of connection options.

  # create new fiber to connect and execute block
  opts = args[0] || {}
  connection = nil
  fiber = Fiber.new do | callback |
    connection = connect(opts, &blk)
    callback.call if callback
  end

  if EventMachine.reactor_running?
    # Transfer control to the "inner" Fiber and stop the current one.
    # The block will be called after connect() returned to transfer control
    # back to the "outer" Fiber.
    outer_fiber = Fiber.current
    fiber.transfer ->{ outer_fiber.transfer }

  else
    # start EventMachine and stop it when connection is done
    EventMachine.run do
      fiber.resume
      EventMachine.next_tick(&StopWhenEMDone)
    end
  end
  return connection
end

Instance Method Details

- (Object) bin_mode=(bool)

Turn newline conversion on or off for this connection.



450
451
452
# File 'lib/em-simple_telnet.rb', line 450

def bin_mode=(bool)
  @telnet_options[:bin_mode] = bool
end

- (Boolean) bin_mode?

Return current bin mode option of this connection.



443
444
445
# File 'lib/em-simple_telnet.rb', line 443

def bin_mode?
  @telnet_options[:bin_mode]
end

- (Object) check_input_buffer

Checks the input buffer (@input_buffer) for the prompt we're waiting for. Calls the proc in @connection_state_callback if the prompt has been found. Thus, call this method only if @connection_state_callback is set!

If @telnet_options[:wait_time] is set, this amount of seconds is waited (call to @connection_state_callback is scheduled) after seeing what looks like the prompt before firing the @connection_state_callback is fired, so more data can come until the real prompt is reached. This is useful for commands which will cause multiple prompts to be sent.



621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
# File 'lib/em-simple_telnet.rb', line 621

def check_input_buffer
  if md = @input_buffer.match(@telnet_options[:prompt])
    blk = lambda do
      @last_prompt = md.to_s # remember last prompt
      output = md.pre_match + @last_prompt
      @input_buffer = md.post_match
      @connection_state_callback.call(output)
    end

    if s = @telnet_options[:wait_time]
      # fire @connection_state_callback after s seconds
      @wait_time_timer = EventMachine::Timer.new(s, &blk)
    else
      # fire @connection_state_callback now
      blk.call
    end
  end
end

- (Object) close

Tells EventMachine to close the connection after sending what's in the output buffer. Redefine this method to execute some logout command like exit or logout before the connection is closed. Don't forget: The command will probably not return a prompt, so use #puts, which doesn't wait for a prompt.



906
907
908
# File 'lib/em-simple_telnet.rb', line 906

def close
  close_connection_after_writing
end

- (Object) close_logs

Close output and command logs if they're set. IOError is rescued because they could already be closed. #closed? can't be used, because the method is not implemented by Logger, for example.



915
916
917
918
919
920
921
922
# File 'lib/em-simple_telnet.rb', line 915

def close_logs
  begin @output_logger.close
  rescue IOError
  end if @telnet_options[:output_log]
  begin @command_logger.close
  rescue IOError
  end if @telnet_options[:command_log]
end

- (Boolean) closed?

Returns true if the connection is closed.



513
514
515
# File 'lib/em-simple_telnet.rb', line 513

def closed?
  @connection_state == :closed
end

- (Object) cmd(command, opts = {})

Sends a command to the host.

More exactly, the following things are done:

  • stores the command in @last_command

  • logs it using #log_command

  • sends a string to the host (#print or #puts)

  • reads in all received data (using #waitfor)

  • returns the received data as String

opts can be a Hash of options. It is passed to #waitfor as the second parameter. The element in opts with the key :prompt is used as the first parameter in the call to #waitfor. Example usage:

host.cmd "delete user john", prompt: /Are you sure?/
host.cmd "yes"

Note that the received data includes the prompt and in most cases the host's echo of our command.

If opts has the key :hide which evaluates to true, calls #log_command with "<hidden command>" instead of the command itself. This is useful for passwords, so they don't get logged to the command log.

If opts has the key :raw_command which evaluates to true, #print is used to send the command to the host instead of #puts.



764
765
766
767
768
769
770
771
772
773
774
775
776
777
# File 'lib/em-simple_telnet.rb', line 764

def cmd command, opts={}
  command = command.to_s
  @last_command = command

  # log the command
  log_command(opts[:hide] ? "<hidden command>" : command)

  # send the command
  sendcmd = opts[:raw_command] ? :print : :puts
  self.__send__(sendcmd, command)

  # wait for the output
  waitfor(opts[:prompt], opts)
end

- (Object) connection_completed

Called by EventMachine after the connection is successfully established.



894
895
896
897
# File 'lib/em-simple_telnet.rb', line 894

def connection_completed
  @connection_state = :connected
  @connection_state_callback.call if @connection_state_callback
end

- (Boolean) logged_in?

Returns true if the login already succeeded for this connection. Returns false otherwise.



506
507
508
# File 'lib/em-simple_telnet.rb', line 506

def logged_in?
  @logged_in ? true : false
end

- (Object) login(opts = {})

Login to the host with a given username and password.

host. username: "myuser", password: "mypass"

This method looks for the login and password prompt (see implementation) from the host to determine when to send the username and password. If the login sequence does not follow this pattern (for instance, you are connecting to a service other than telnet), you will need to handle login yourself.

If the key :password is omitted (and not set on connection level), the method will not look for a prompt.

The method returns all data received during the login process from the host, including the echoed username but not the password (which the host should not echo anyway).

Don't forget to set @logged_in after the login succeeds when you redefine this method!



800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
# File 'lib/em-simple_telnet.rb', line 800

def  opts={}
  opts = @telnet_options.merge opts

  # don't log in if username is not set
  if opts[:username].nil?
    @logged_in = Time.now
    return
  end

  begin
    output = waitfor opts[:login_prompt]

    if opts[:password]
      # login with username and password
      output << cmd(opts[:username], prompt: opts[:password_prompt])
      output << cmd(opts[:password], hide: true)
    else
      # login with username only
      output << cmd(opts[:username])
    end
  rescue Timeout::Error
    e = LoginFailed.new("Timed out while expecting some kind of prompt.")
    e.set_backtrace $!.backtrace
    raise e
  end

  @logged_in = Time.now
  output
end

- (Object) post_init

Called by EventMachine when the connection is being established (not after the connection is established! see #connection_completed). This occurs directly after the call to #initialize.

Sets the pending_connect_timeout to @telnet_options[:connect_timeout] seconds. This is the duration after which a TCP connection in the connecting state will fail (abort and run #unbind). Increases @@_telnet_connection_count by one after that.

Sets also the comm_inactivity_timeout to @telnet_options[:timeout] seconds. This is the duration after which a TCP connection is automatically closed if no data was sent or received.



846
847
848
849
850
# File 'lib/em-simple_telnet.rb', line 846

def post_init
  self.pending_connect_timeout = @telnet_options[:connect_timeout]
  self.comm_inactivity_timeout = @telnet_options[:timeout]
  @@_telnet_connection_count += 1
end

Sends a string to the host.

This does not automatically append a newline to the string. Embedded newlines may be converted and telnet command sequences escaped depending upon the values of #telnet_mode, #bin_mode, and telnet options set by the host.



703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'lib/em-simple_telnet.rb', line 703

def print(string)
  string = string.gsub(/#{IAC}/no, IAC + IAC) if telnet_mode?

  unless bin_mode?
    string = if @telnet_options[:BINARY] and @telnet_options[:SGA]
      # IAC WILL SGA IAC DO BIN send EOL --> CR
      string.gsub(/\n/n, CR)

    elsif @telnet_options[:SGA]
      # IAC WILL SGA send EOL --> CR+NULL
      string.gsub(/\n/n, CR + NULL)

    else
      # NONE send EOL --> CR+LF
      string.gsub(/\n/n, EOL)
    end
  end

  send_data string
end

- (Object) puts(string)

Sends a string to the host.

Same as #print, but appends a newline to the string unless there's already one.



730
731
732
733
# File 'lib/em-simple_telnet.rb', line 730

def puts(string)
  string += "\n" unless string.end_with? "\n"
  print string
end

- (Object) receive_data(data)

Called by EventMachine when data is received.

The data is processed using #preprocess_telnet and appended to the @input_buffer. The appended data is also logged using #log_output. Then #check_input_buffer is called which checks the input buffer for the prompt.



525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
# File 'lib/em-simple_telnet.rb', line 525

def receive_data data
  if @telnet_options[:telnet_mode]
    c = @input_rest + data
    se_pos = c.rindex(/#{IAC}#{SE}/no) || 0
    sb_pos = c.rindex(/#{IAC}#{SB}/no) || 0
    if se_pos < sb_pos
      buf = preprocess_telnet(c[0 ... sb_pos])
      @input_rest = c[sb_pos .. -1]

    elsif pt_pos = c.rindex(
      /#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) ||
      c.rindex(/\r\z/no)

      buf = preprocess_telnet(c[0 ... pt_pos])
      @input_rest = c[pt_pos .. -1]

    else
      buf = preprocess_telnet(c)
      @input_rest.clear
    end
  else
    # Not Telnetmode.
    #
    # We cannot use #preprocess_telnet on this data, because that
    # method makes some Telnetmode-specific assumptions.
    buf = @input_rest + data
    @input_rest.clear
    unless @telnet_options[:bin_mode]
      if pt_pos = buf.rindex(/\r\z/no)
        buf = buf[0 ... pt_pos]
        @input_rest = buf[pt_pos .. -1]
      end
      buf.gsub!(/#{EOL}/no, "\n")
    end
  end

  # in case only telnet sequences were received
  return if buf.empty?

  # append output from server to input buffer and log it
  @input_buffer << buf
  log_output buf, true

  # cancel the timer for wait_time value because we received more data
  if @wait_time_timer
    @wait_time_timer.cancel
    @wait_time_timer = nil
  end

  # we only need to do something if there's a connection state callback
  return unless @connection_state_callback

  # we ensure there's no timer running to check the input buffer
  if @check_input_buffer_timer
    @check_input_buffer_timer.cancel
    @check_input_buffer_timer = nil
  end

  if @input_buffer.size >= 100_000
    ##
    # if the input buffer is really big
    #

    # We postpone checking the input buffer by one second because the regular
    # expression matches can get quite slow.
    #
    # So as long as data is received (continuously), the input buffer is not
    # checked. It's only checked one second after the whole output has been
    # received.
    @check_input_buffer_timer = EventMachine::Timer.new(1) do
      @check_input_buffer_timer = nil
      check_input_buffer
    end
  else
    ##
    # as long as the input buffer is small
    #

    # check the input buffer now
    check_input_buffer
  end
end

- (Object) telnet_mode=(bool)

Turn telnet command interpretation on or off for this connection. It should be on for true telnet sessions, off if used to connect to a non-telnet service such as SMTP.



436
437
438
# File 'lib/em-simple_telnet.rb', line 436

def telnet_mode=(bool)
  @telnet_options[:telnet_mode] = bool
end

- (Boolean) telnet_mode?

Return current telnet mode option of this connection.



427
428
429
# File 'lib/em-simple_telnet.rb', line 427

def telnet_mode?
  @telnet_options[:telnet_mode]
end

- (Object) timeout(seconds = nil)

If a block is given, sets the timeout to seconds (see #timeout=), executes the block and restores the previous timeout. The block value is returned. This is useful if you want to execute one or more commands with a special timeout.

If no block is given, the current timeout is returned.

Example:

current_timeout = host.timeout

 host.timeout 200 do
   host.cmd "command 1"
   host.cmd "command 2"
 end


480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/em-simple_telnet.rb', line 480

def timeout seconds=nil
  if block_given?
    before = @telnet_options[:timeout]
    self.timeout = seconds
    begin
      yield
    ensure
      self.timeout = before
    end
  else
    if seconds
      warn "Warning: Use EM::P::SimpleTelnet#timeout= to set the timeout."
    end
    @telnet_options[:timeout]
  end
end

- (Object) timeout=(seconds)

Set the activity timeout to seconds for this connection. To disable it, set it to 0 or nil.



458
459
460
461
# File 'lib/em-simple_telnet.rb', line 458

def timeout= seconds
  @telnet_options[:timeout] = seconds
  set_comm_inactivity_timeout( seconds )
end

- (Object) unbind

Called by EventMachine after this connection is closed.

Decreases @@_telnet_connection_count by one and calls #close_logs.

After that and if @connection_state_callback is set, it takes a look on @connection_state. If it was :connecting, calls @connection_state_callback with a new instance of ConnectionFailed. If it was :waiting_for_prompt, calls the callback with a new instance of TimeoutError.

Finally, the @connection_state is set to closed.



865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
# File 'lib/em-simple_telnet.rb', line 865

def unbind
  @@_telnet_connection_count -= 1
  close_logs

  if @connection_state_callback
    # if we were connecting or waiting for a prompt, return an exception to
    # #waitfor
    case @connection_state
    when :connecting
      @connection_state_callback.call(ConnectionFailed.new)
    when :waiting_for_prompt
      error = TimeoutError.new

      # set hostname and command
      if hostname = @telnet_options[:host]
        error.hostname = hostname
      end
      error.command = @last_command if @last_command

      @connection_state_callback.call(error)
    end
  end

  @connection_state = :closed
end

- (Object) waitfor(prompt = nil, opts = {})

Read data from the host until a certain sequence is matched.

All data read will be returned in a single string. Note that the received data includes the matched sequence we were looking for.

prompt can be a Regexp or String. If it's not a Regexp, it's converted to a Regexp (all special characters escaped) assuming it's a String.

opts can be a hash of options. The following options are used and thus can be overridden:

  • :timeout

  • :wait_time (actually used by #check_input_buffer)



655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
# File 'lib/em-simple_telnet.rb', line 655

def waitfor prompt=nil, opts={}
  options_were = @telnet_options
  timeout_was = self.timeout if opts.key?(:timeout)
  opts[:prompt] = prompt if prompt
  @telnet_options = @telnet_options.merge opts

  # convert String prompt into a Regexp
  unless @telnet_options[:prompt].is_a? Regexp
    regex = Regexp.new(Regexp.quote(@telnet_options[:prompt]))
    @telnet_options[:prompt] = regex
  end

  # set custom inactivity timeout, if wanted
  self.timeout = @telnet_options[:timeout] if opts.key?(:timeout)

  # so #unbind knows we were waiting for a prompt (in case that inactivity
  # timeout fires)
  @connection_state = :waiting_for_prompt

  # for the block in @connection_state_callback
  f = Fiber.current

  # will be called by #receive_data to resume at "Fiber.yield" below
  @connection_state_callback = lambda do |output|
    @connection_state_callback = nil
    f.resume(output)
  end

  result = Fiber.yield

  raise result if result.is_a? Exception
  return result
ensure
  @telnet_options = options_were
  self.timeout = timeout_was if opts.key?(:timeout)
  @connection_state = :connected
end