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.0'

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.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/net/telnet/rfc2217.rb', line 35

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.



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

def baud
  @baud
end

#data_bitsObject (readonly)

Returns the value of attribute data_bits.



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

def data_bits
  @data_bits
end

#parityObject (readonly)

Returns the value of attribute parity.



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

def parity
  @parity
end

#stop_bitsObject (readonly)

Returns the value of attribute stop_bits.



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

def stop_bits
  @stop_bits
end

#telnetObject (readonly)

Returns the value of attribute telnet.



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

def telnet
  @telnet
end

Class Method Details

.open(**kwargs) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/net/telnet/rfc2217.rb', line 18

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



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

def close
  telnet.close
end

#flushObject



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

def flush
  sock.flush
end

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



71
72
73
74
75
76
77
# File 'lib/net/telnet/rfc2217.rb', line 71

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



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/net/telnet/rfc2217.rb', line 123

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



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

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

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



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
121
# File 'lib/net/telnet/rfc2217.rb', line 84

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)


149
150
151
152
153
154
155
156
# File 'lib/net/telnet/rfc2217.rb', line 149

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)


56
57
58
59
60
61
62
63
64
65
# File 'lib/net/telnet/rfc2217.rb', line 56

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



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

def sock
  @telnet.sock
end

#ungetbyte(b) ⇒ Object



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

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

#ungetc(c) ⇒ Object



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

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

#wait_readable(timeout = nil) ⇒ Object



142
143
144
145
146
147
# File 'lib/net/telnet/rfc2217.rb', line 142

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



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

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