summaryrefslogtreecommitdiff
path: root/extra/syslog.rb
blob: 87227bbf3ab96886bef84b4f745f43fc8df7b2c3 (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env ruby

# IOS:
# logging discriminator CFG mnemonics includes CONFIG_I
# logging host SERVER discriminator CFG

# JunOS:
# set system syslog host SERVER interactive-commands notice
# set system syslog host SERVER match "^mgd\[[0-9]+\]: UI_COMMIT: .*"

# Ports < 1024 need extra privileges, use a port higher than this by setting the port option in your oxidized config file.
# To use the default port for syslog (514) you shouldn't pass an argument, but you will need to allow this with:
# sudo setcap 'cap_net_bind_service=+ep' /usr/bin/ruby

# Config options are:
# syslogd
#  port (Default = 514)
#  file (Default = messages)
#  resolve (Default = true)

# To stop the resolution of IP's to PTR you can set resolve to false

# exit if fork   ## TODO: proper daemonize

require 'socket'
require 'resolv'
require_relative 'rest_client'

module Oxidized
  
  require 'asetus'
  class Config
    Root      = File.join ENV['HOME'], '.config', 'oxidized'
  end

  CFGS = Asetus.new :name=>'oxidized', :load=>false, :key_to_s=>true
  CFGS.default.syslogd.port        = 514
  CFGS.default.syslogd.file        = 'messages'
  CFGS.default.syslogd.resolve     = true

  begin
    CFGS.load
  rescue => error
    raise InvalidConfig, "Error loading config: #{error.message}"
  ensure
    CFG = CFGS.cfg  # convenienence, instead of Config.cfg.password, CFG.password
  end

  class SyslogMonitor
    NAME_MAP = {
      /(.*)\.ip\.tdc\.net/ => '\1',
      /(.*)\.ip\.fi/       => '\1',
    }
    MSG = {
      :ios   => /%SYS-(SW[0-9]+-)?5-CONFIG_I:/,
      :junos => 'UI_COMMIT:',
    }

    class << self
      def udp port=Oxidized::CFG.syslogd.port, listen=0
        io = UDPSocket.new
        io.bind listen, port
        new io, :udp
      end
      def file syslog_file=Oxidized::CFG.syslogd.file
        io = open syslog_file, 'r'
        io.seek 0, IO::SEEK_END
        new io, :file
      end
    end

    private

    def initialize io, mode=:udp
      @mode = mode
      run io
    end

    def rest opt
      Oxidized::RestClient.next opt
    end

    def ios ip, log, i
      # TODO: we need to fetch 'ip/name' in mode == :file here
      user = log[i+5]
      from = log[-1][1..-2]
      rest( :user => user, :from => from, :model => 'ios', :ip => ip,
            :name => getname(ip) )
    end

    def jnpr ip, log, i
      # TODO: we need to fetch 'ip/name' in mode == :file here
      user = log[i+2][1..-2]
      msg  = log[(i+6)..-1].join(' ')[10..-2]
      msg  = nil if msg == 'none'
      rest( :user => user, :msg => msg, :model => 'jnpr', :ip => ip,
            :name => getname(ip) )
    end

    def handle_log log, ip
      log = log.to_s.split ' '
      if i = log.find_index { |e| e.match( MSG[:ios] ) }
        ios ip, log,  i
      elsif i = log.index(MSG[:junos])
        jnpr ip, log, i
      end
    end

    def run io
      while true
        log = select [io]
        log, ip = log.first.first, nil
        if @mode == :udp
          log, ip = log.recvfrom_nonblock 2000
          ip = ip.last
        else
          begin
            log = log.read_nonblock 2000
          rescue EOFError
            sleep 1
            retry
          end
        end
        handle_log log, ip
      end
    end

    def getname ip
      if Oxidized::CFG.syslogd.resolve == false
        ip
      else
        name = (Resolv.getname ip.to_s rescue ip)
        NAME_MAP.each { |re, sub| name.sub! re, sub }
        name
      end
    end
  end
end

Oxidized::SyslogMonitor.udp
#Oxidized::SyslogMonitor.file '/var/log/poop'