summaryrefslogtreecommitdiff
path: root/lib/oxidized/input/telnet.rb
blob: 17fad533889e5ba32caefb9ba8bbec0ad26e031e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
module Oxidized
  require 'net/telnet'
  require 'oxidized/input/cli'
  class Telnet < Input
    RescueFail = {}
    include Input::CLI
    attr_reader :telnet

    def connect node
      @node    = node
      @timeout = Oxidized.config.timeout
      @node.model.cfg['telnet'].each { |cb| instance_exec(&cb) }
      @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-telnet", 'w') if Oxidized.config.input.debug?
      port = vars(:telnet_port) || 23

      telnet_opts = { 'Host'    => @node.ip,
                      'Port'    => port.to_i,
                      'Timeout' => @timeout,
                      'Model'   => @node.model,
                      'Log'     => @log }

      @telnet = Net::Telnet.new telnet_opts
      begin
        login
      rescue Timeout::Error
        raise PromptUndetect, ['unable to detect prompt:', @node.prompt].join(' ')
      end
      connected?
    end

    def connected?
      @telnet and not @telnet.sock.closed?
    end

    def cmd cmd_str, expect = @node.prompt
      return send(cmd_str + "\n") unless expect

      Oxidized.logger.debug "Telnet: #{cmd_str} @#{@node.name}"
      args = { 'String'  => cmd_str,
               'Match'   => expect,
               'Timeout' => @timeout }
      @telnet.cmd args
    end

    def send data
      @telnet.write data
    end

    def output
      @telnet.output
    end

    private

    def expect re
      @telnet.oxidized_expect expect: re, timeout: @timeout
    end

    def disconnect
      begin
        disconnect_cli
        @telnet.close
      rescue Errno::ECONNRESET
      ensure
        @log.close if Oxidized.config.input.debug?
        (@telnet.close rescue true) unless @telnet.sock.closed?
      end
    end
  end
end

class Net::Telnet
  ## how to do this, without redefining the whole damn thing
  ## FIXME: we also need output (not sure I'm going to support this)
  attr_reader :output
  def oxidized_expect(options) # :yield: recvdata
    model    = @options["Model"]
    @log     = @options["Log"]

    expects  = [options[:expect]].flatten
    time_out = options[:timeout] || @options["Timeout"] || Oxidized.config.timeout?

    Timeout::timeout(time_out) do
      line = ""
      rest = ""
      buf  = ""
      loop do
        c = @sock.readpartial(1024 * 1024)
        @output = c
        c = rest + c

        if Integer(c.rindex(/#{IAC}#{SE}/no) || 0) <
           Integer(c.rindex(/#{IAC}#{SB}/no) || 0)
          buf = preprocess(c[0...c.rindex(/#{IAC}#{SB}/no)])
          rest = c[c.rindex(/#{IAC}#{SB}/no)..-1]
        elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) ||
                   c.rindex(/\r\z/no)
          buf = preprocess(c[0...pt])
          rest = c[pt..-1]
        else
          buf = preprocess(c)
          rest = ''
        end
        if Oxidized.config.input.debug?
          @log.print buf
          @log.flush
        end
        line += buf
        line = model.expects line
        match = expects.find { |re| line.match re }
        return match if match
      end
    end
  end
end