Class: Net::Telnet::RFC2217

Inherits:
Object
  • Object
show all
Defined in:
lib/net/telnet/rfc2217/telnet.rb,
lib/net/telnet/rfc2217/version.rb,
lib/net/telnet/rfc2217.rb

Defined Under Namespace

Modules: TelnetExtensions Classes: WaitReadable

Constant Summary collapse

VERSION =
'1.0.2'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host, port: 23, baud: 115200, data_bits: 8, parity: :none, stop_bits: 1, &block) ⇒ RFC2217

Returns a new instance of RFC2217.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 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

Instance Attribute Details

#baudObject (readonly)

Returns the value of attribute baud.



32
33
34
# File 'lib/net/telnet/rfc2217.rb', line 32

def baud
  @baud
end

#data_bitsObject (readonly)

Returns the value of attribute data_bits.



32
33
34
# File 'lib/net/telnet/rfc2217.rb', line 32

def data_bits
  @data_bits
end

#parityObject (readonly)

Returns the value of attribute parity.



32
33
34
# File 'lib/net/telnet/rfc2217.rb', line 32

def parity
  @parity
end

#stop_bitsObject (readonly)

Returns the value of attribute stop_bits.



32
33
34
# File 'lib/net/telnet/rfc2217.rb', line 32

def stop_bits
  @stop_bits
end

#telnetObject (readonly)

Returns the value of attribute telnet.



14
15
16
# File 'lib/net/telnet/rfc2217.rb', line 14

def telnet
  @telnet
end

Class Method Details

.open(**kwargs) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
# 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

Instance Method Details

#closeObject



174
175
176
# File 'lib/net/telnet/rfc2217.rb', line 174

def close
  telnet.close
end

#flushObject



170
171
172
# File 'lib/net/telnet/rfc2217.rb', line 170

def flush
  sock.flush
end

#read(length, outbuf = '') ⇒ Object



70
71
72
73
74
75
76
# 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 = {}) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# 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

#readbyteObject Also known as: getbyte



78
79
80
# File 'lib/net/telnet/rfc2217.rb', line 78

def readbyte
  read(1)&.[](0)
end

#readpartial(length, outbuf = '') ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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?Boolean

Returns:

  • (Boolean)


148
149
150
151
152
153
154
155
# 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) ⇒ Object

Raises:

  • (ArgumentError)


55
56
57
58
59
60
61
62
63
64
# 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

#sockObject



66
67
68
# File 'lib/net/telnet/rfc2217.rb', line 66

def sock
  @telnet.sock
end

#ungetbyte(b) ⇒ Object



157
158
159
# File 'lib/net/telnet/rfc2217.rb', line 157

def ungetbyte(b)
  @buffer.insert(0, b.chr)
end

#ungetc(c) ⇒ Object



161
162
163
# File 'lib/net/telnet/rfc2217.rb', line 161

def ungetc(c)
  @buffer.insert(0, c)
end

#wait_readable(timeout = nil) ⇒ Object



141
142
143
144
145
146
# 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) ⇒ Object



165
166
167
168
# File 'lib/net/telnet/rfc2217.rb', line 165

def write(string)
  string = string.gsub(/#{IAC}/no, IAC + IAC)
  telnet.write(string)
end