aboutsummaryrefslogtreecommitdiff
path: root/lib/mauve/sender.rb
blob: f27efe5e4961de159163066b944fd2730045dada (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
# encoding: UTF-8
require 'ipaddr'
require 'resolv'
require 'socket'
require 'mauve/mauve_time'

module Mauve 
  class Sender
    DEFAULT_PORT = 32741
    include Resolv::DNS::Resource::IN
    
    def initialize(*destinations)
      destinations = destinations.flatten
      
      destinations = begin
        File.read("/etc/mauvealert/mauvesend.destination").split(/\s+/)
      rescue Errno::ENOENT => notfound
        []
      end if destinations.empty?
      
      if !destinations || destinations.empty?
        raise ArgumentError.new("No destinations specified, and could not read any destinations from /etc/mauvealert/mauvesend.destination")
      end
    
      @destinations = destinations.map do |spec|
        next_spec = begin
          # FIXME: for IPv6
          port = spec.split(":")[1] || DEFAULT_PORT
          IPAddr.new(spec.split(":")[0])
          ["#{spec}:#{port}"]
        rescue ArgumentError => not_an_ip_address
          Resolv::DNS.open do |dns|
            srv_spec = spec[0] == ?_ ? spec : "_mauvealert._udp.#{spec}"
            list = dns.getresources(srv_spec, SRV).map do |srv|
              srv.target.to_s + ":#{srv.port}"
            end
            list = [spec] if list.empty?
            list.map do |spec2|
              spec2_addr, spec2_port = spec2.split(":")
              spec2_port ||= DEFAULT_PORT
              dns.getresources(spec2_addr, A).map do |a|
                "#{a.address}:#{spec2_port}"
              end
            end
          end
        end.flatten

        error "Can't resolve destination #{spec}" if next_spec.empty?

        next_spec
      end.
      flatten.
      uniq
    end
    
    def send(update, verbose=0)

      #
      # Must have a source, so default to hostname if user doesn't care 
      update.source ||= `hostname -f`.chomp
      
      #
      # Make sure all alerts default to "-r now"
      #
      update.alert.each do |alert|
        next if alert.raise_time || alert.clear_time
        alert.raise_time = MauveTime.now.to_i
      end
     
      #
      # Make sure we set the transmission time
      #
      update.transmission_time = MauveTime.now.to_i 

      data = update.serialize_to_string


      if verbose == 1
        print "#{update.transmission_id}\n"
      elsif verbose >= 2
        print "Sending #{update.inspect.chomp} to #{@destinations.join(", ")}\n"
      end

      @destinations.each do |spec|
        UDPSocket.open do |sock|
          ip = spec.split(":")[0]
          port = spec.split(":")[1].to_i
          sock.send(data, 0, ip, port)
        end
      end
    end
  end
end