summaryrefslogtreecommitdiff
path: root/extra/syslog.rb
blob: 2af83a041f833fcdd2d28054d7cd37995e2a0e45 (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
#!/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 passing the first argument a number
# To use the default port for syslog (514) you shouldnt pass an argument, but you will need to allow this with:
# sudo setcap 'cap_net_bind_service=+ep' /usr/bin/ruby

# exit if fork   ## TODO: proper daemonize

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

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

    class << self
      def udp port=PORT, listen=0
        port ||= PORT
        io = UDPSocket.new
        io.bind listen, port
        new io, :udp
      end
      def file syslog_file=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
      name = (Resolv.getname ip.to_s rescue ip)
      NAME_MAP.each { |re, sub| name.sub! re, sub }
      name
    end
  end
end

Oxidized::SyslogMonitor.udp ARGV[0]
#Oxidized::SyslogMonitor.file '/var/log/poop'