minor fixups
[lwes-perl.git] / lwes-perl-event-listener
blobe47b27192dcc51a942b36576f491f653bb3d9e40
1 #!/usr/bin/env perl
3 use strict;
4 use warnings;
5 use Getopt::Long;
6 use Pod::Usage;
7 use LWES::EventParser;
8 use LWES::Listener;
9 use IO::Socket::INET;
10 use IO::Socket::Multicast;
11 use Time::HiRes qw( gettimeofday );
12 use POSIX;
14 # don't buffer stdout
15 $| = 1;
17 my $port = $ENV{'LWES_PORT'} || 9191;
18 my $addr = $ENV{'LWES_ADDRESS'} || '224.0.0.69';
19 my $pidfile = undef;
20 my $rootdir = undef;
21 my $help_opt = 0;
22 my $man_opt = 0;
23 my $verbose_opt = 0;
25 GetOptions
27 'help' => \$help_opt,
28 'man' => \$man_opt,
29 'verbose' => \$verbose_opt,
30 'm|addr=s' => \$addr,
31 'p|port=s' => \$port,
32 'r|root=s' => \$rootdir,
33 'pidfile=s' => \$pidfile,
34 ) or pod2usage (2);
36 pod2usage (-exitval => -1, -verbose => 0) if $help_opt;
37 pod2usage (-exitval => -2, -verbose => 2) if $man_opt;
39 my $listener = shift @ARGV;
41 # default listener to one which just prints
42 unless (defined($listener))
44 $listener = "LWES::Listeners::EventPrintingListener";
47 # determine the callback which will process each event
48 my $processEventFunc = getProcessEventFunc ($listener, @ARGV);
50 unless (defined $rootdir)
52 # default rootdir to current working directory
53 $rootdir = getcwd ();
56 # pidfile means to daemonize
57 if (defined ($pidfile))
59 # place pidfile in cwd as well
60 unless ($pidfile =~ m#^/#)
62 $pidfile = "$rootdir/$pidfile";
65 chdir '/' or die "Can't chdir to /: $!";
66 open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
67 open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!";
68 open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!";
69 defined (my $pid = fork) or die "Can't fork: $!";
70 if ($pid != 0)
72 # parent writes pidfile
73 open PID, ">$pidfile";
74 print PID "$pid";
75 close PID;
76 exit;
78 else
80 # child continues daemonizing
81 POSIX::setsid() or die "Can't start a new session: $!";
82 umask 0;
83 open STDOUT, "> $rootdir/listener.out";
84 open STDERR, "> $rootdir/listener.err";
88 # set up socket either UDP or Multicast
89 my $sock = undef;
90 if ($addr eq "0.0.0.0")
92 $sock = IO::Socket::INET->new(LocalPort=>$port, Proto=>'udp', Reuse=>1);
94 else
96 $sock = IO::Socket::Multicast->new(LocalPort=>$port, Reuse=>1)
97 or die "Can't create socket: $!";
98 # add multicast address
99 $sock->mcast_add($addr) or die "mcast_add: $!";
102 $sock->sockopt(SO_RCVBUF,(16*1024*1024));
104 while (1)
106 my ($message, $peer);
107 die "recv error: $!" unless $peer = recv ($sock, $message, 65535, 0);
108 my ($port, $peeraddr) = sockaddr_in($peer);
110 my $event = bytesToEvent($message);
112 # set up a header similiar to the lwes header
113 my ($seconds, $microseconds) = gettimeofday;
114 my $millis = int($microseconds/1000);
116 $event->{'ReceiptTime'} = $seconds*1000+$millis;
117 $event->{'ReceiptTimeSecs'} = $seconds;
118 $event->{'ReceiptTimeMillis'} = $millis;
119 $event->{'SenderIP'} = inet_ntoa($peeraddr);
120 $event->{'SenderPort'} = $port;
122 # let the listener process the event
123 $processEventFunc->($event);
128 __END__
130 =head1 NAME
132 lwes-perl-event-listener - listens for events on the network
134 =head1 SYNOPSIS
136 lwes-perl-event-listener [options] [<listener> [<args>] | <code>]
138 Options:
139 -m|--addr <ip> Address to listen on
140 (default: 224.0.0.69)
141 -p|--port <port> Port numer to listen on
142 (default: 9191)
143 -r|--root <dir> Directory to write logs/pid files
144 (default: cwd)
145 --pidfile <filepath> If a pidfile is specified this
146 will daemonize itself.
147 -help Brief help message
148 -man Full Documentation
150 <listener> is the name of a perl module which extends the base
151 listener LWES::Listener and provides an initialize and processEvent
152 method. The <args> are passed directly to the listener constructor.
154 code is perl code which is embedded in a function which takes one
155 argument, a reference to a perl hash which contains the contents
156 of the event called $event
158 =head1 OPTIONS
160 =over 8
162 =item B<-m|--addr>
164 The address to listen on, if you are using UDP this should be 0.0.0.0,
165 otherwise it should be a valid multicast address.
167 =item B<-p|--port>
169 The port to listen on.
171 =item B<-r|--root>
173 The directory to write log and pidfiles when daemonizing
175 =item B<--pidfile>
177 The path to a pidfile, it will default to being in the B<root> directory,
178 specifying this option causes the listener to daemonize itself.
180 =item B<-help>
182 Print help information
184 =item B<-man>
186 Print more verbose help information
188 =back
190 =head1 DESCRIPTION
192 The lwes-perl-event-listener is a tool for inspecting LWES events that are
193 flowing to a particular machine. It can listen for either multicast or
194 udp events on an ip and port, then process the events as it receives them.
195 It processes events as they appear, so on a heavy LWES stream may not be
196 able to clear the system network buffer before it overflows.
198 There are several ways to use the tool. To just print out events as they
199 are seen it can be invoked as
201 % lwes-perl-event-listener -m <ip> -p <port>
203 If you wish to create a module for handling events a skeleton is as
204 follows (saved as Foo.pm)
206 package Foo;
208 use strict;
210 @Foo::ISA = qw(LWES::Listener);
212 sub initialize
214 my $self = shift;
216 print "Bar : initialize with @_\n";
219 sub processEvent
221 my $self = shift;
222 my $event = shift;
224 print "Foo : Got event!\n";
229 Which can then be invoked as
231 % lwes-perl-event-listener -m <ip> -p <port> Foo foo_arg
233 This assumes the Foo.pm file is in the directory you invoke the listener
234 from.
236 Alternatively, you can install a listener into the LWES/Listeners/ directory
237 in the perl LWES distribution (you'll have to find it). In that case the
238 file should look like
240 package LWES::Listeners::Bar;
242 use strict;
244 @LWES::Listeners::Bar::ISA = qw(LWES::Listener);
246 sub initialize
248 my $self = shift;
250 print "Bar : initialize with @_\n";
253 sub processEvent
255 my $self = shift;
256 my $event = shift;
258 print "Bar : Got event!\n";
263 And be saved as Bar.pm in the system LWES/Listeners directory. It can then
264 be invoked as
266 % lwes-perl-event-listener -m <ip> -p <port> Bar bar_arg
268 Finally, the listener can be passed code which will be wrapped in a function
269 called for each event. An example of this is
271 % lwes-perl-event-listener -m <ip> -p <port> \
272 'print $event->{"EventType"}."\n";'
274 Internally this is wrapped by a function like
276 sub processEvent {
277 my $event = shift;
278 <code from command line>
281 The $event is a reference to a perl hash. 64-bit values are Math::BigInt
282 objects.
284 =cut