~ubuntu-branches/ubuntu/intrepid/syslog-ng/intrepid

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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#!/usr/bin/perl
#
# syslog-mc:
# This perl script is to be used in conjuction with syslog-ng and it's
# multicast feature.
#
# All you need to do is run the program, pass an IPv4 or IPv6 multicast
# address as the first parameter and the script will 'tune in' to the syslog
# multicast group and display the syslog lines.
#
# This helps no end if you want to write local trigger scripts or have a live
# view of some daemon without actually having to be logged into the central
# syslog server cluster.
#
# One note to bear in mind is that if you send you logs in the multicast group
# to a port number below 1024 then you will need root privileges to run this
# script.  It's probably a good idea to send your logs to a port number above
# 1024 so that you do not need to run things as root.  Another recommendation
# is to remember to 'seperate' your group addresses by thirty two IP's (for
# IPv4) so that you do not receive any unwanted, although it's harmless and
# filtered, extra traffic.
#
# When using the script, you can run it standalone and it will simply print the
# syslog messages to STDOUT however you might prefer to pipe it into grep or
# some other tool, so to print all the lines containing the word 'cheese':
#
# $ ./syslog-mc 239.194.253.8 5514 | grep cheese
#
# --
# Maintained by  Alexander Clouter <ac56@soas.ac.uk>
# Written/Copyright 2006 by Alexander Clouter <ac56@soas.ac.uk>
#
# This software may be used and distributed according to the terms
# of the version two of the GNU General Public License, incorporated herein by
# reference.
# 
use strict;
use warnings;

my $running = 1;

use sigtrap 'handler' => sub { $running = 0 }, qw( INT );

# under debian all these dependencies can be met with:
# aptitude install libnet-ip-perl libsocket6-perl libio-socket-inet6-perl libparse-syslog-perl
use Net::IP;
use Socket;
use Socket6;
use IO::Socket::INET6;
# we use IO::Select to avoid the nasty blocking nature of Parse::Syslog
use IO::Select;
use Parse::Syslog;
use POSIX;

# enable debugging?
use constant DEBUG => 0;
if ( DEBUG ) {
  use Data::Dumper;
}

my ( $mcGroup, $port ) = ( $ARGV[0], $ARGV[1] );

unless ( $mcGroup ) {
  print <<EOF;
Use: syslog-mc.pl <mcast-addr> [port]
EOF
  exit 0;
}

$mcGroup =~ s/\s*(.+)\s*/$1/;

my $ip = Net::IP->new($mcGroup)
	|| die 'First parameter is not a valid IP address';
if ( $ip->version == 4 ) {
  unless ( Net::IP->new('224.0.0.0/4')->overlaps($ip) != $Net::IP::IP_NO_OVERLAP ) {
    print STDERR "IPv4 address given is not a multicast address\n";
    exit 1;
  }
}
elsif ( $ip->version == 6 ) {
  unless ( Net::IP->new('ff::/120')->overlaps($ip) != $Net::IP::IP_NO_OVERLAP ) {
    print STDERR "IPv6 address given is not a multicast address\n";
    exit 1;
  }
}
else {
  print STDERR "Unknown IP Version\n";
  exit 1;
}

if ( $port ) {
  $port =~ s/\s*(.+)\s*/$1/;

  if ( $port =~ /^\d+$/ ) {
    unless ( $port > 0 && $port < 65536 ) {
      print STDERR "Invalid port number\n";
      exit 1;
    }
  }
}
else {
  $port = 514;

  print STDERR "No port number given, assuming 514\n"
  	if ( DEBUG );
}

my $sock = &subscribe($mcGroup, $port);
unless ( $sock ) {
  print STDERR "Unable to open socket so bombing out\n";
  exit 1;
}

my $parser = Parse::Syslog->new($sock, arrayref => 1);

my $selectPoll = IO::Select->new;
$selectPoll->add($sock);

while ( $running ) {
  my ( $select_set ) = IO::Select->select($selectPoll, undef, undef);

  foreach my $fh ( @$select_set ) {
    if ( $fh == $sock ) {
      if ( $sock->eof ) {
        $running = 0;
	next;
      }

      my $sl = $parser->next;
      
      my $timestamp = POSIX::strftime '%b %e %T', gmtime($sl->[0]);
      my $pid = ( $sl->[3] ) ? $sl->[3] : 'na';

      print "$timestamp " . $sl->[1] . ' ' . $sl->[2] . "[$pid]: " . $sl->[4] . "\n";
    }
  }
}

$selectPoll->remove($sock);

&unsubscribe($sock);

exit 0;

sub subscribe {
  my $mcGroup = shift;
  my $port = shift;

  print STDERR "Trying to join group $mcGroup: "
  	if ( DEBUG );

  my @res = getaddrinfo $mcGroup, $port, AF_UNSPEC, SOCK_DGRAM;
  my $sock = IO::Socket::INET6->new(
		Domain		=> $res[0],
                LocalAddr	=> $mcGroup,
                Proto		=> 'udp',
		Type		=> SOCK_DGRAM,
		LocalPort	=> $port,
		ReuseAddr	=> 1		) || print STDERR "Can't bind : $@\n";
  unless ( $sock && ref $sock eq 'IO::Socket::INET6' ) {
    print STDERR "Unable to open socket! If using a port number below 1024 you need to be root\n";
    return;
  }

  my $joinStatus;
  if ( $sock->sockdomain == AF_INET ) {
    # struct ip_mreq <netinet/in.h>
    my $mreq = pack 'a4 a4', inet_pton(AF_INET, $mcGroup), INADDR_ANY;
    # IP_ADD_MEMBERSHIP = 35
    $joinStatus = setsockopt $sock, IPPROTO_IP, 35, $mreq;
  }
  elsif ( $sock->sockdomain == AF_INET6 ) {
    # struct ipv6_mreq <netinet/in.h>
    my $mreq6 =  pack 'a16 N', inet_pton(AF_INET6, $mcGroup), 0;
    # IPV6_ADD_MEMBERSHIP = 20
    $joinStatus = setsockopt $sock, IPPROTO_IPV6, 20, $mreq6;
  }
  else {
    print STDERR "unknown IP version, "
    	if ( DEBUG );
    $joinStatus = -1;
  }

  if ( $joinStatus == -1 ) {
    print STDERR "unable to ADD_MEMBERSHIP for $mcGroup\n"
    	if ( DEBUG );
    close $sock;
    return;
  }

  print STDERR "successful\n"
  	if ( DEBUG );

  return $sock;
}

sub unsubscribe {
  my $sock = shift;

  my $mcGroup = $sock->sockhost;

  print STDERR "Leaving group $mcGroup: "
  	if ( DEBUG );

  my $leaveStatus;
  if ( $sock->sockdomain == AF_INET ) {
    my $mreq = pack 'a4 a4', inet_pton(AF_INET, $mcGroup), INADDR_ANY;
    # IP_DROP_MEMBERSHIP = 36
    $leaveStatus = setsockopt $sock, IPPROTO_IP, 36, $mreq;
  }
  else {
    my $mreq6 =  pack 'a16 N', inet_pton(AF_INET6, $mcGroup), 0;
    # IPV6_DROP_MEMBERSHIP = 21
    $leaveStatus = setsockopt $sock, IPPROTO_IPV6, 21, $mreq6;
  }

  if ( $leaveStatus == -1 ) {
    print STDERR "warning, unable to DROP_MEMBERSHIP for $mcGroup..."
    	if ( DEBUG );
  }

  close $sock;

  print STDERR "done\n"
  	if ( DEBUG );
}