pass arguments through to Listeners for processing with Getopt::Long
[lwes-perl.git] / lwes-perl-event-listener
blob2cdb267253b7db22241613a09b0c5b7be2ca0d0c
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 Getopt::Long::Configure ("pass_through");
27 GetOptions
29 'help' => \$help_opt,
30 'man' => \$man_opt,
31 'verbose' => \$verbose_opt,
32 'm|addr=s' => \$addr,
33 'p|port=s' => \$port,
34 'r|root=s' => \$rootdir,
35 'pidfile=s' => \$pidfile,
36 ) or pod2usage (2);
38 pod2usage (-exitval => -1, -verbose => 0) if $help_opt;
39 pod2usage (-exitval => -2, -verbose => 2) if $man_opt;
41 my $listener = shift @ARGV;
43 # default listener to one which just prints
44 unless (defined($listener))
46 $listener = "LWES::Listeners::EventPrintingListener";
49 # determine the callback which will process each event
50 my $processEventFunc = getProcessEventFunc ($listener, \@ARGV);
52 unless (defined $rootdir)
54 # default rootdir to current working directory
55 $rootdir = getcwd ();
58 # pidfile means to daemonize
59 if (defined ($pidfile))
61 # place pidfile in cwd as well
62 unless ($pidfile =~ m#^/#)
64 $pidfile = "$rootdir/$pidfile";
67 chdir '/' or die "Can't chdir to /: $!";
68 open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
69 open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!";
70 open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!";
71 defined (my $pid = fork) or die "Can't fork: $!";
72 if ($pid != 0)
74 # parent writes pidfile
75 open PID, ">$pidfile";
76 print PID "$pid";
77 close PID;
78 exit;
80 else
82 # child continues daemonizing
83 POSIX::setsid() or die "Can't start a new session: $!";
84 umask 0;
85 open STDOUT, "> $rootdir/listener.out";
86 open STDERR, "> $rootdir/listener.err";
90 # set up socket either UDP or Multicast
91 my $sock = undef;
92 if ($addr eq "0.0.0.0")
94 $sock = IO::Socket::INET->new(LocalPort=>$port, Proto=>'udp', Reuse=>1);
96 else
98 $sock = IO::Socket::Multicast->new(LocalPort=>$port, Reuse=>1)
99 or die "Can't create socket: $!";
100 # add multicast address
101 $sock->mcast_add($addr) or die "mcast_add: $!";
104 $sock->sockopt(SO_RCVBUF,(16*1024*1024));
106 while (1)
108 my ($message, $peer);
109 die "recv error: $!" unless $peer = recv ($sock, $message, 65535, 0);
110 my ($port, $peeraddr) = sockaddr_in($peer);
112 my $event = bytesToEvent($message);
114 # set up a header similiar to the lwes header
115 my ($seconds, $microseconds) = gettimeofday;
116 my $millis = int($microseconds/1000);
118 $event->{'ReceiptTime'} = $seconds*1000+$millis;
119 $event->{'ReceiptTimeSecs'} = $seconds;
120 $event->{'ReceiptTimeMillis'} = $millis;
121 $event->{'SenderIP'} = inet_ntoa($peeraddr);
122 $event->{'SenderPort'} = $port;
124 # let the listener process the event
125 $processEventFunc->($event);
130 __END__
132 =head1 NAME
134 lwes-perl-event-listener - listens for events on the network
136 =head1 SYNOPSIS
138 lwes-perl-event-listener [options] [<listener> [<args>] | <code>]
140 Options:
141 -m|--addr <ip> Address to listen on
142 (default: 224.0.0.69)
143 -p|--port <port> Port numer to listen on
144 (default: 9191)
145 -r|--root <dir> Directory to write logs/pid files
146 (default: cwd)
147 --pidfile <filepath> If a pidfile is specified this
148 will daemonize itself.
149 -help Brief help message
150 -man Full Documentation
152 <listener> is the name of a perl module which extends the base
153 listener LWES::Listener and provides an initialize and processEvent
154 method. The <args> are passed directly to the listener constructor.
156 code is perl code which is embedded in a function which takes one
157 argument, a reference to a perl hash which contains the contents
158 of the event called $event
160 =head1 OPTIONS
162 =over 8
164 =item B<-m|--addr>
166 The address to listen on, if you are using UDP this should be 0.0.0.0,
167 otherwise it should be a valid multicast address.
169 =item B<-p|--port>
171 The port to listen on.
173 =item B<-r|--root>
175 The directory to write log and pidfiles when daemonizing
177 =item B<--pidfile>
179 The path to a pidfile, it will default to being in the B<root> directory,
180 specifying this option causes the listener to daemonize itself.
182 =item B<-help>
184 Print help information
186 =item B<-man>
188 Print more verbose help information
190 =back
192 =head1 DESCRIPTION
194 The lwes-perl-event-listener is a tool for inspecting LWES events that are
195 flowing to a particular machine. It can listen for either multicast or
196 udp events on an ip and port, then process the events as it receives them.
197 It processes events as they appear, so on a heavy LWES stream may not be
198 able to clear the system network buffer before it overflows.
200 There are several ways to use the tool. To just print out events as they
201 are seen it can be invoked as
203 % lwes-perl-event-listener -m <ip> -p <port>
205 If you wish to create a module for handling events a skeleton is as
206 follows (saved as Foo.pm)
208 package Foo;
210 use strict;
211 use warnings;
212 use Getopt::Long;
214 @Foo::ISA = qw(LWES::Listener);
216 sub initialize
218 my $self = shift;
219 my $args = shift;
220 if (defined ($args))
222 my @ARGV = @{$args};
223 # parse additional options
224 GetOptions ( ... );
228 sub processEvent
230 my $self = shift;
231 my $event = shift;
233 print "Foo : Got event!\n";
238 Which can then be invoked as
240 % lwes-perl-event-listener -m <ip> -p <port> Foo foo_arg
242 This assumes the Foo.pm file is in the directory you invoke the listener
243 from.
245 Alternatively, you can install a listener into the LWES/Listeners/ directory
246 in the perl LWES distribution (you'll have to find it). In that case the
247 file should look like
249 package LWES::Listeners::Bar;
251 use strict;
252 use warnings;
253 use Getopt::Long;
255 @LWES::Listeners::Bar::ISA = qw(LWES::Listener);
257 sub initialize
259 my $self = shift;
260 my $args = shift;
261 if (defined ($args))
263 my @ARGV = @{$args};
264 # parse additional options
265 GetOptions ( ... );
269 sub processEvent
271 my $self = shift;
272 my $event = shift;
274 print "Bar : Got event!\n";
279 And be saved as Bar.pm in the system LWES/Listeners directory. It can then
280 be invoked as
282 % lwes-perl-event-listener -m <ip> -p <port> Bar bar_arg
284 Finally, the listener can be passed code which will be wrapped in a function
285 called for each event. An example of this is
287 % lwes-perl-event-listener -m <ip> -p <port> \
288 'print $event->{"EventType"}."\n";'
290 Internally this is wrapped by a function like
292 sub processEvent {
293 my $event = shift;
294 <code from command line>
297 The $event is a reference to a perl hash. 64-bit values are Math::BigInt
298 objects.
300 =cut