class Net::Telnet::RFC2217

Constants

COM_PORT_OPTION
EVEN
FLOWCONTROL_SUSPEND
FLOWCONTROL_SUSPEND_RESPONSE
MARK
NONE
NOTIFY_LINESTATE
NOTIFY_LINESTATE_RESPONSE
NOTIFY_MODEMSTATE
NOTIFY_MODEMSTATE_RESPONSE
ODD
PURGE_DATA
PURGE_DATA_RESPONSE
SET_BAUDRATE
SET_BAUDRATE_RESPONSE
SET_CONTROL
SET_CONTROL_RESPONSE
SET_DATASIZE
SET_DATASIZE_RESPONSE
SET_LINESTATE_MASK
SET_LINESTATE_MASK_RESPONSE
SET_MODEMSTATE_MASK
SET_MODEMSTATE_MASK_RESPONSE
SET_PARITY
SET_PARITY_RESPONSE
SET_STOPSIZE
SET_STOPSIZE_RESPONSE
SPACE
VERSION

Attributes

baud[R]
data_bits[R]
parity[R]
stop_bits[R]
telnet[R]

Public Class Methods

new(host, port: 23, baud: 115200, data_bits: 8, parity: :none, stop_bits: 1, &block) click to toggle source
# File lib/net/telnet/rfc2217.rb, line 34
def initialize(host, port: 23, baud: 115200, data_bits: 8, parity: :none, stop_bits: 1, &block)
  set_modem_params(baud: baud, data_bits: data_bits, parity: parity, stop_bits: stop_bits)

  options = {
    'Host' => host,
    'Port' => port,
  }
  options['Binmode'] = true
  options['Telnetmode'] = false
  @telnet = Telnet.new(options, &block)
  telnet.write(IAC + WILL + COM_PORT_OPTION)
  sock.flush
  @buffer = ''
  start = Time.now.to_f
  loop do
    raise "could not negotiate serial port in time" if Time.now.to_f - start > 5
    break if @negotiated
    readpartial(0)
  end
end
open(**kwargs) { |sp| ... } click to toggle source
# File lib/net/telnet/rfc2217.rb, line 17
def open(**kwargs)
  sp = new(opt**kwargs)
  if block_given?
    begin
      yield sp
    ensure
      sp.close
    end
    nil
  else
    nil
  end
end

Public Instance Methods

close() click to toggle source
# File lib/net/telnet/rfc2217.rb, line 174
def close
  telnet.close
end
flush() click to toggle source
# File lib/net/telnet/rfc2217.rb, line 170
def flush
  sock.flush
end
getbyte()
Alias for: readbyte
read(length, outbuf = '') click to toggle source
# File lib/net/telnet/rfc2217.rb, line 70
def read(length, outbuf = '')
  readpartial(length, outbuf)
  while outbuf.length < length
    outbuf.concat(readpartial(length - outbuf.length))
  end
  outbuf
end
read_nonblock(length, outbuf = '', options = {}) click to toggle source
# File lib/net/telnet/rfc2217.rb, line 122
def read_nonblock(length, outbuf = '', options = {})
  if outbuf == ({ exception: false })
    options = outbuf
    outbuf = ''
  end
  loop do
    result = wait_readable(0)
    if result == nil
      raise WaitReadable unless options[:exception] == false
      return :wait_readable
    end
    # we have to try to consume control characters first
    readpartial(0)
    # and then only do a real read if we have something
    return readpartial(length, outbuf) unless @buffer.empty?
    # otherwise loop and see if there's more there
  end
end
readbyte() click to toggle source
# File lib/net/telnet/rfc2217.rb, line 78
def readbyte
  read(1)&.[](0)
end
Also aliased as: getbyte
readpartial(length, outbuf = '') click to toggle source
# File lib/net/telnet/rfc2217.rb, line 83
def readpartial(length, outbuf = '')
  loop do
    # 0 is special and means "just see if there's data to read"
    break if length != 0 && @buffer.length != 0
    raise "could not negotiate serial port in first 1MB of data" if @buffer.length >= 1024 * 1024

    data = sock.sysread([length - @buffer.length, 64 * 1024].max)

    # avoid getting caught in the middle of a control sequence
    while (data[-1] == IAC && sock.wait_readable(0))
      data.concat(sock.sysread(16))
    end

    data = @telnet.preprocess(data) do |control|
      if DO[0] == control[0] && COM_PORT_OPTION == control[1]
        # start negotiation
        write_modem_params
        @negotiated = true
        true
      elsif DONT[0] == control[0] && COM_PORT_OPTION == control[1]
        raise "Serial port control not supported"
      elsif (WILL[0] == control[0] || DONT[0] == control[0]) && OPT_ECHO == control[1]
        # just ignore echo requests
        true
      else
        false
      end
    end
    @buffer.concat(data)

    break if length == 0
  end

  length = [length, @buffer.length].min
  outbuf.replace(@buffer[0...length])
  @buffer = @buffer[length..-1]
  outbuf
end
ready?() click to toggle source
# File lib/net/telnet/rfc2217.rb, line 148
def ready?
  loop do
    return true unless @buffer.empty?
    return false if sock.wait_readable(0).nil?
    # consume control characters first
    readpartial(0)
  end
end
set_modem_params(baud: nil, data_bits: nil, parity: nil, stop_bits: nil) click to toggle source
# File lib/net/telnet/rfc2217.rb, line 55
def set_modem_params(baud: nil, data_bits: nil, parity: nil, stop_bits: nil)
  raise ArgumentError, "Parity must be :none, :even, :odd, :mark, or :space" unless parity.nil? || %i{none even odd mark space}.include?(parity)

  @baud ||= baud || 115200
  @data_bits ||= data_bits || 8
  @parity ||= parity || :none
  @stop_bits ||= stop_bits || 1

  write_modem_params if telnet
end
sock() click to toggle source
# File lib/net/telnet/rfc2217.rb, line 66
def sock
  @telnet.sock
end
ungetbyte(b) click to toggle source
# File lib/net/telnet/rfc2217.rb, line 157
def ungetbyte(b)
  @buffer.insert(0, b.chr)
end
ungetc(c) click to toggle source
# File lib/net/telnet/rfc2217.rb, line 161
def ungetc(c)
  @buffer.insert(0, c)
end
wait_readable(timeout = nil) click to toggle source
# File lib/net/telnet/rfc2217.rb, line 141
def wait_readable(timeout = nil)
  return true unless @buffer.empty?
  result = sock.wait_readable(timeout)
  result = self if result == sock
  result
end
write(string) click to toggle source
# File lib/net/telnet/rfc2217.rb, line 165
def write(string)
  string = string.gsub(/#{IAC}/no, IAC + IAC)
  telnet.write(string)
end

Private Instance Methods

write_modem_params() click to toggle source
# File lib/net/telnet/rfc2217.rb, line 214
def write_modem_params
  telnet.write(
    IAC + SB + COM_PORT_OPTION + SET_BAUDRATE + [baud].pack("N") + IAC + SE +
    IAC + SB + COM_PORT_OPTION + SET_DATASIZE + data_bits.chr + IAC + SE +
    IAC + SB + COM_PORT_OPTION + SET_STOPSIZE + stop_bits.chr + IAC + SE +
    IAC + SB + COM_PORT_OPTION + SET_PARITY + const_get(parity.upcase, false).chr + IAC + SE)
  sock.flush
end