3 # This script is designed to assist the Ganymede server by quickly
4 # scanning through the Ganymede log file and printing out only those
5 # lines which the Ganymede server is seeking to retrieve.
7 # The Ganymede server is not very efficient at doing this, and by
8 # using an external filtering program the Ganymede server can provide
9 # object history from its log file much much more quickly than if the
10 # server had to filter out all the irrelevant history details itself
11 # with the current server code.
18 # robbie@arlut.utexas.edu
21 # jonabbey@arlut.utexas.edu
23 ############################################################
28 ##########################################################################
31 # input: $token, @args
33 # output: the string argument following the single character
34 # $token.. for instance, if $token is 'f', find_arg will return the
35 # string following -f in the @args list, if it can be found.
37 # if the token can't be found following a dash character, an empty string
41 ##########################################################################
44 my ($token, @args) = @_;
46 my ($i, $word, $localword, $tokenregexp);
51 $tokenregexp = $token;
52 $tokenregexp =~ s/(\W)/\\$1/g; # backslash escape any special chars
54 while ($i <= $#args) {
55 if ($args[$i] =~ /^-(.*)$/) {
58 if ($word =~ /^$tokenregexp/) {
59 if (length($word)==1) {
60 $localword = $args[($i + 1)];
62 $word =~ /^$tokenregexp(.*)$/;
73 if ($localword eq "") {
80 ##########################################################################
83 # input: $switchlist, @args
85 # The $switchlist string should be a concatenation of the
86 # permissible single-character command line flags.
88 # output: sets flags in the global %switches hash
90 ##########################################################################
93 my ($switchlist, @args) = @_;
95 my ($i, $word, @switches);
100 while ($i <= $#args && $args[$i] =~ /^-(.*)$/) {
104 # the switches in the following regexp can accept arguments, so
105 # we'll skip past the next token if we see one of them naked.
107 if ($word =~ /^[ies]/) {
108 if (length($word)==1) {
114 if ($word =~ /(^[$switchlist]+)$/) {
115 @switches= split (//, $1);
116 for $switch (@switches) {
117 $switches{$switch}="-$switch";
120 print "\"$word\" is an invalid command entry!\n\n";
126 ##########################################################################
130 # Returns true if FILE is pointing to the first character of a line
131 # (i.e., the character before us is a newline or the start of the file)
133 ##########################################################################
135 sub is_start_of_line
{
146 read(FILE
, $line, 1);
155 ##########################################################################
159 # This subroutine finds the seek point for the start of the last line
160 # prior to $newpos in FILE
162 ##########################################################################
164 sub find_previous_line
{
167 $debug && print "find_previous_line($newpos)\n";
169 my ($backpos, $line, $lastfound, $jump);
175 # step back 64 bytes at a time from $newpos until we find the
176 # last \n before $newpos
178 while ($lastfound == -1 && $backpos > 0) {
179 $backpos = $backpos - 64;
185 seek(FILE
, $backpos, 0);
187 read(FILE
, $line, 64);
193 $jump = index($line, "\n", $jump + 1);
194 } until ($jump == -1);
197 # and return the location we need to go to
199 return $backpos + $lastfound + 1; # will be 0 on degenerate case
202 ##########################################################################
206 # This subroutine does a binary search through FILE looking for the
207 # first event after the specified start date.
209 ##########################################################################
211 sub seek_start_point
{
212 my ($startdate, $lowpos, $highpos) = @_;
214 my ($pos, $line, $linedate);
216 if ($lowpos == $highpos) {
217 $debug && print "Oops, got stuck";
221 $pos = int (($lowpos + $highpos) / 2);
225 if (!is_start_of_line
()) {
226 $pos = find_previous_line
($pos);
232 # read a line for us to check against our target date
236 ($linedate) = split /\|/, $line;
238 # transactions share the same date and we can't use our recursive
239 # algorithm to find the beginning, since we can't differentiate
240 # between lines with the same date. if we have hit on a line that
241 # occurs right on our start time (which is specified with
242 # millisecond accuracy), we'll need to scootch back until we find
243 # the first line that had that date.
245 while ($startdate == $linedate) {
246 $pos = find_previous_line
($pos -1);
252 $debug && print $line;
254 ($linedate) = split /\|/, $line;
256 if (($startdate > $linedate) || ($pos == 0)) {
261 if ($startdate < $linedate) {
262 $debug && print "startdate $startdate <= linedate $linedate\n";
264 seek_start_point
($startdate, $lowpos, find_previous_line
($pos));
267 $debug && print "startdate $startdate > linedate $linedate\n";
269 seek_start_point
($startdate, tell FILE
, $highpos);
274 ##########################################################################
278 ##########################################################################
281 print "Usage: $0 [-a] [-l] [-i invid number] [-s start_date in java timecode] [-e end date in java timecode]\n";
282 print "\t-a: Search for admin invids rather than object invids\n";
283 print "\t-l: Only retrieve login/logout type events\n";
284 print "\t-s: Only retrieve events more recent than the start date, which is provided as a Java timecode\n";
285 print "\t-e: Only retrieve events that occur before recent the end date, which is provided as a Java timecode\n";
289 ################################################################################
291 $file = "<<LOGFILE>>"; # this will be replaced on install
297 # which field in the log lines are we going to look at for the invid
298 # we're filtering on?
303 # let's figure out our command line options
305 read_switches
('al', @ARGV);
307 if (defined $switches{'a'}) {
308 # we're doing an admin invid search, not an object invid search
313 if (defined $switches{'l'}) {
317 $javastartdate = find_arg
('s', @ARGV);
318 $debug && print "javastartdate is $javastartdate\n";
320 $javaenddate = find_arg
('e', @ARGV);
321 $debug && print "javaenddate is $javaenddate\n";
323 $invid = find_arg
('i', @ARGV);
324 $debug && print "invid is $invid\n";
326 if (!defined $invid) {
328 # we didn't get asked for an invid, which may be okay, or it may
329 # mean that we are being called by an earlier version of the
330 # Ganymede server using the old calling sequence, with the invid
331 # being mandatory as the last parameter on our command line.
333 # check for that, using a regexp to recognize what an invid should
336 $invid = $ARGV[$#ARGV];
338 if ($invid !~ /^[0-9]+:[0-9]+$/) {
344 # Okay, we've got all of our arguments, now.
346 # If we've got a start date set, we'll want to do a binary
347 # search through the file to find the start of the region we
351 open FILE
, $file || die "Couldn't open $file.\n";
353 if (defined $javastartdate) {
354 ($size) = (stat($file))[7];
355 seek_start_point
($javastartdate, 0, $size);
360 # if we're looking for a specific invid, let's see if the line has
363 PROCESSLINE
: if (!defined $invid || /$invid/) {
365 @fields = split(/\|/);
367 if (defined $javaenddate && $javaenddate < $fields[0]) {
372 my $token = $fields[2];
374 if ($dologins xor ($token eq "normallogin" ||
375 $token eq "normallogout" ||
376 $token eq "abnormallogout")) {
380 if (!(defined $invid) ||
381 ($fields[$fieldindex] =~ /(^|,)$invid(,|$)/) ||
382 ($fields[$fieldindex] =~ /^$invid$/)) {
386 # we want to show all events in a transaction block, even
387 # the endtransaction events which don't include the object
388 # invids that we are scanning for. So, when we see a
389 # starttransaction, we just print out lines for as long
390 # as we see the transaction id.
392 if (@fields[2] =~ /starttransaction/) {
394 $transaction_label = @fields[5];
399 @fields = split(/\|/);
401 if ($fields[5] eq $transaction_label) {