1
2########################################################
3# Please file all bug reports, patches, and feature
4# requests under:
5#      https://sourceforge.net/p/logwatch/_list/tickets
6# and copy:
7#    Stefan Jakobs <logwatch at localside.net>
8# Help requests and discusion can be filed under:
9#      https://sourceforge.net/p/logwatch/discussion/
10########################################################
11
12###########################################################################
13# This was written and is maintained by:
14#    Stefan Jakobs <logwatch at localside.net>
15#
16# Please send all comments, suggestions, bug reports,
17#    etc, to logwatch at localside.net.
18###########################################################################
19
20###########################################################################
21## Copyright (c) 2008-2013 Stefan Jakobs
22## Covered under the included MIT/X-Consortium License:
23##    http://www.opensource.org/licenses/mit-license.php
24## All modifications and contributions by other persons to
25## this script are assumed to have been donated to the
26## Logwatch project and thus assume the above copyright
27## and licensing terms.  If you want to make contributions
28## under your own copyright or a different license this
29## must be explicitly stated in the contribution an the
30## Logwatch project reserves the right to not accept such
31## contributions.  If you have made significant
32## contributions to this script and want to claim
33## copyright please contact logwatch-devel@lists.sourceforge.net.
34#########################################################
35
36#use warnings;
37use strict;
38
39my $Detail         = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
40my $Version        = "1.4-20130219";
41
42# initialize logwatch variables
43my $ThisLine        = "";
44my %OtherList       = ();
45
46# initialize variables which save the stats
47my ($Starts,$Stops,$Reloads)       = ( 0, 0, 0);
48my ($Perms,$FileOpenErrors)        = ( 0, 0);
49my ($Drops, $WriteErrsSum)         = ( 0, 0);
50my ($ExceedConns)                  = ( 0);
51my (%PermFiles, %OpenFiles)        = ( (), ());
52my (%WriteErrs, %Connections)      = ( (), ());
53my (%Conns, %ConnsSum)             = ();
54my (%Stats_center, %Stats_source, %Stats_dest)   = ( (), (), ());
55my (%Stats_dropped, %Stats_supp, %Stats_global)  = ( (), (), ());
56my (%Stats_program, %Stats_stored, %Stats_net)   = ( (), (), ());
57my (%Stats_dropped_program, %Stats_supp_program) = ( (), ());
58my (%Stats_dropped_net, %Stats_supp_net)         = ( (), ());
59my (%Warnings, %IntErrors)                       = ( (), ());
60
61### Parse the lines ###
62
63while (defined($ThisLine = <STDIN>)) {
64   chomp($ThisLine);
65
66   #TD syslog-ng[2351]: New configuration initialized;
67   if ( ($ThisLine =~ /^[Nn]ew configuration initialized/ ) ||
68        ($ThisLine =~ /^EOF on control channel, closing connection;/ ) ||
69        ($ThisLine =~ /^(?:POLLERR|EOF) occurred while idle;/ )
70   ) {
71      #ignore
72   }
73
74   #TD syslog-ng[9754]: Changing permissions on special file /dev/xconsole
75   elsif ($ThisLine =~ /^Changing permissions on special file ((\/[a-zA-Z0-9_]*)*)$/) {
76      %PermFiles = (%PermFiles, $1 => $PermFiles{$1}+1);
77      $Perms++;
78   }
79
80   #TD syslog-ng[9754]: Cannot open file /tmp/.adir/afile for writing (No such file or directory)
81   elsif ($ThisLine =~ /^Cannot open file ((\/[a-zA-Z0-9_.]*)*) .*/) {
82      # $1 fq file name, $2 only filename
83      %OpenFiles = (%OpenFiles, $1 => $OpenFiles{$1}+1);
84      $FileOpenErrors++;
85   }
86
87   #TD syslog-ng[9754]: SIGHUP received, restarting syslog-ng
88   #TD syslog-ng[4027]: Configuration reload request received, reloading configuration;
89   elsif ($ThisLine =~ /^SIGHUP received, restarting syslog-ng$/ ||
90      $ThisLine =~ /^Configuration reload request received, reloading configuration;/) {
91      $Reloads++;
92   }
93
94   #TD syslog-ng[9754]: syslog-ng version 1.6.2 starting
95   #TD syslog-ng[3956]: syslog-ng starting up; version='2.0.9'
96   elsif ($ThisLine =~ /^syslog-ng version [\d.]+ starting$/ ||
97      $ThisLine =~ /^syslog-ng starting up; version='[\d.]+'$/) {
98      $Starts++;
99   }
100
101   #TD syslog-ng[9754]: syslog-ng version 1.6.2 going down
102   #TD syslog-ng[20043]: syslog-ng shutting down; version='2.0.9'
103   elsif ($ThisLine =~ /^syslog-ng version [\d.]+ going down$/ ||
104      $ThisLine =~ /^syslog-ng shutting down; version='[\d.]+'$/) {
105      $Stops++;
106   }
107
108   #TD syslog-ng[20043]: Termination requested via signal, terminating;
109   elsif ($ThisLine =~ /^Termination requested via signal, terminating;/) {
110      # happens with shutdown, but it's not for extra accounting
111   }
112
113   # syslog-ng v1.X
114   #TD syslog-ng[4833]: STATS: dropped 0
115   elsif ($ThisLine =~ /^STATS: dropped ([0-9]*)$/) {
116      if ($1 != 0) { $Drops = $Drops + $1; }
117   }
118
119   #TD syslog-ng[4833]: Syslog connection closed; fd='45', client='AF_INET(192.168.1.1:40280)', local='AF_INET(192.168.1.10:625)'
120   #TD syslog-ng[4833]: Syslog connection accepted; fd='52', client='AF_INET(192.168.1.1:40280)', local='AF_INET(192.168.1.10:625)'
121   # syslog-ng v3.X
122   #TD Syslog connection broken; fd='63', server='AF_INET(192.169.1.1:514)', time_reopen='60' : 44 Time(s)
123   #TD Syslog connection established; fd='48', server='AF_INET(192.168.1.1:514)', local='AF_INET(0.0.0.0:0)'
124   elsif ($ThisLine =~ /^Syslog connection (\S+); fd='\d+', (server|client)='AF_INET\(([.\d]+):\d+\)', (?:local='AF_INET\(([.\d]+:\d+)\)'|time_reopen='\d+')?$/) {
125      my $loc = defined($4) ? $4 : '0.0.0.0';
126      $Connections{"$1 ($2)"}{$loc}{$3}++;
127   }
128
129   #TD syslog-ng[4833]: Connection broken to AF_INET(XXX.YYY.ZZZ.AAA:BBB), reopening in 60 seconds
130   elsif ($ThisLine =~ /^Connection broken to [A-Z_]*\((([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5})\), reopening in [0-9]* seconds$/) {
131      $Conns{'Connection broken'}{$1}++;
132      $ConnsSum{'Connection broken'}++;
133   }
134
135   # syslog-ng v2.X
136   #TD syslog-ng[4833]: Connection failed; error='Connection timed out (110)', time_reopen='60'
137   #TD syslog-ng[4833]: Connection failed; error='Connection refused (111)', time_reopen='60'
138   #TD syslog-ng[4833]: Connection broken; time_reopen='60'
139   elsif ($ThisLine =~ /^(Connection \w+); (?:error='([^\(']+) \(\d+\)', )?time_reopen='\d+'/ ) {
140      $Conns{$1}{$2}++;
141      $ConnsSum{$1}++;
142   }
143
144   #TD syslog-ng[4869]: io.c: do_write: write() failed (errno 111), Connection refused
145   #TD syslog-ng[4869]: I/O error occurred while writing; fd='66', error='Connection refused (111)'
146   elsif ( ($ThisLine =~ /^io\.c: do_write: write\(\) failed \(errno ([\d]+)\)/) or
147           ($ThisLine =~ /I\/O error occurred while writing; fd='\d+', error='[^'(]+ \((\d+)\)'/)
148   ) {
149      $WriteErrs{$1}++;
150      $WriteErrsSum++;
151   }
152
153   # Log statistics from syslog-ng v2.X
154   #TD syslog-ng[4883]: Log statistics; dropped='program(/path/to/p)=12',
155   #        processed='center(queued)=1717', processed='center(received)=916', ...
156   #        suppressed='program(/path/to/p)=0'
157   # Log statisctics from syslog-ng v3.X
158   #TD syslog-ng[1625]: Log statistics; processed='destination(newsnotice)=0',
159   #     processed='center(queued)=0', processed='src.internal(src#0)=7',
160   #     stamp='src.internal(src#0)=1283808150', processed='global(msg_clones)=0', ...
161   elsif ($ThisLine =~ /^Log statistics; /) {
162      my @processed =
163         $ThisLine =~ /processed='([a-z.]*)\((\S*)\)=([0-9]*)'/g;
164      for (my $i=0; $i<@processed; $i=$i+3)
165      {
166         if ($processed[$i] eq "center") {
167            $Stats_center{$processed[$i+1]} =
168               $Stats_center{$processed[$i+1]} + $processed[$i+2];
169         } elsif ($processed[$i] eq "destination") {
170            $Stats_dest{$processed[$i+1]} =
171               $Stats_dest{$processed[$i+1]} + $processed[$i+2];
172         } elsif ($processed[$i] eq "source" || $processed[$i] eq "src.internal" ||
173                  $processed[$i] eq 'src.none' || $processed[$i] eq 'src.journald') {
174            $Stats_source{$processed[$i+1]} =
175               $Stats_source{$processed[$i+1]} + $processed[$i+2];
176         } elsif ($processed[$i] eq "global") {
177            $Stats_global{$processed[$i+1]} =
178               $Stats_global{$processed[$i+1]} + $processed[$i+2];
179         } elsif ($processed[$i] eq "dst.program") {
180            $Stats_program{$processed[$i+1]} =
181               $Stats_program{$processed[$i+1]} + $processed[$i+2];
182         } elsif ($processed[$i] =~ /(?:dst\.)?(?:udp|tcp)/) {
183            $Stats_net{$processed[$i+1]} =
184               $Stats_net{$processed[$i+1]} + $processed[$i+2];
185         } else { chomp($ThisLine); $OtherList{$ThisLine}++; }
186      }
187      my @dropped =
188              $ThisLine =~ /dropped='([a-z.]*)\((\S*)\)=([0-9]*)'/g;
189      for (my $i=0; $i<@dropped; $i=$i+3)
190      {
191         if ($dropped[$i] eq "program" || $dropped[$i] eq "pipe") {
192            if ($dropped[$i+2] > 0) {
193               $Stats_dropped{$dropped[$i+1]} =
194                  $Stats_dropped{$dropped[$i+1]} + $dropped[$i+2];
195            }
196         } elsif ($dropped[$i] =~ /(?:dst\.)?(?:tcp|udp)/) {
197            if ($dropped[$i+2] > 0) {
198               $Stats_dropped_net{$dropped[$i+1]} =
199                  $Stats_dropped_net{$dropped[$i+1]} + $dropped[$i+2];
200            }
201         } elsif ($dropped[$i] eq "dst.program") {
202            if ($dropped[$i+2] > 0) {
203               $Stats_dropped_program{$dropped[$i+1]} =
204                  $Stats_dropped_program{$dropped[$i+1]} + $dropped[$i+2];
205            }
206         } else { chomp($ThisLine); $OtherList{$ThisLine}++; }
207      }
208      my @suppressed =
209         $ThisLine =~ /suppressed='([a-z.]*)\((\S*)\)=([0-9]*)'/g;
210      for (my $i=0; $i<@suppressed; $i=$i+3)
211      {
212         if ($suppressed[$i] eq "program" || $suppressed[$i] eq "pipe") {
213            if ($suppressed[$i+2] > 0) {
214               $Stats_supp{$suppressed[$i+1]} =
215                  $Stats_supp{$suppressed[$i+1]} + $suppressed[$i+2];
216            }
217         } elsif ($suppressed[$i] =~ /(?:dst\.)?(?:tcp|udp)/) {
218            if ($suppressed[$i+2] > 0) {
219               $Stats_supp_net{$suppressed[$i+1]} =
220                  $Stats_supp_net{$suppressed[$i+1]} + $suppressed[$i+2];
221            }
222         } elsif ($suppressed[$i] eq "dst.program") {
223            if ($suppressed[$i+2] > 0) {
224               $Stats_supp_program{$suppressed[$i+1]} =
225                  $Stats_supp_program{$suppressed[$i+1]} + $suppressed[$i+2];
226            }
227         } else { chomp($ThisLine); $OtherList{$ThisLine}++; }
228      }
229      my @stored =
230          $ThisLine =~ /stored='([a-z.]*)\((\S*)\)=([0-9]*)'/g;
231      for (my $i=0; $i<@stored; $i=$i+3)
232      {
233         if ($stored[$i] =~ "(?:dst\.)?(?:program|tcp|udp)" || $stored[$i] eq "pipe") {
234            if ($stored[$i+2] > 0) {
235               $Stats_stored{$stored[$i+1]} =
236                  $Stats_stored{$stored[$i+1]} + $stored[$i+2];
237            }
238         } else { chomp($ThisLine); $OtherList{$ThisLine}++; }
239      }
240   }
241
242   # syslog-ng v2.X
243   #TD syslog-ng[1796]: Number of allowed concurrent connections exceeded; num='10', max='10'
244   elsif ($ThisLine =~ /^Number of allowed concurrent connections exceeded/) {
245      $ExceedConns++;
246   }
247
248   # syslog-ng v3.X
249   #TD syslog-ng[1601]: WARNING: global: the default value of chain_hostnames is changing to
250   #     'no' in version 3.0, please update your configuration accordingly;
251   #TD syslog-ng[1601]: WARNING: you are using the pipe driver, underlying file is not a
252   #     FIFO, it should be used by file(); filename='/dev/tty10'
253   elsif ($ThisLine =~ /^WARNING: (.*)$/) {
254      $Warnings{$1}++;
255   }
256   # syslog-ng v3.X
257   #TD syslog-ng[1601]: Configuration file has no version number, assuming ...
258   elsif ($ThisLine =~ /(Configuration file has no version number)/) {
259      $Warnings{$1}++;
260   }
261
262   # syslog-ng v2.X
263   #TD syslog-ng[1602]: Error initializing new configuration, reverting to old config;
264   elsif ($ThisLine =~ /^Error initializing new configuration, reverting to old config;/) {
265      $IntErrors{"Error initializing new configuration"}{"reverting to old config"}++;
266   }
267
268   #TD syslog-ng[2550]: Internal error, duplicate configuration elements refer to
269   #   the same persistent config; name='afsocket_sd_connections(dgram,AF_INET(0.0.0.0:514))'
270   elsif ($ThisLine =~ /Internal error, ([^;]*); name='([^']*)'/) {
271      $IntErrors{$1}{$2}++;
272   }
273
274   # syslog-ng v2.X
275   #TD syslog-ng[20709]: Referenced filter rule not found; rule='f_dhcpd'
276   elsif ($ThisLine =~ /(Referenced filter rule not found); (.+)/) {
277      $IntErrors{$1}{$2}++
278   }
279
280   # syslog-ng v2.X
281   #TD syslog-ng[2000]: Error in configuration, unresolved filter reference; filter='f_host_dhcp_dns0'
282   elsif ($ThisLine =~ /^Error in configuration, ([^;]+); (.+)/) {
283      $IntErrors{$1}{$2}++;
284   }
285
286   else {
287      # Report any unmatched entries...
288      chomp($ThisLine);
289      $OtherList{$ThisLine}++;
290   }
291}
292
293### generate the output ###
294
295if ($Starts) {
296    printf "\n%-48s %5i Time(s)", "Syslog-ng started:", $Starts;
297}
298
299if ($Stops) {
300    printf "\n%-48s %5i Time(s)", "Syslog-ng stopped:", $Stops;
301}
302
303if ($Reloads) {
304    printf "\n%-48s %5i Time(s)", "Syslog-ng reloaded:", $Reloads;
305}
306if ($Starts || $Stops || $Reloads) { print "\n"; }
307
308if ($Perms) {
309    if ($Detail >= 5) {
310        print "\nSyslog-ng changed the permission on the file(s):";
311        foreach my $file (keys %PermFiles) {
312             printf "\n\t%-41s %5i Time(s)", $file, $PermFiles{$file};
313        }
314        print "\n";
315    } else {
316        printf "\n$-48s %5i Time(s)\n", "Syslog-ng changed permission on file(s):", $Perms;
317    }
318}
319
320if ($FileOpenErrors) {
321    if ($Detail >= 5) {
322        print "\nSyslog-ng could not open the file(s):";
323        foreach my $file (keys %OpenFiles) {
324            printf "\n\t%-41s %5i Time(s)", $file, $OpenFiles{$file};
325        }
326        print "\n";
327    } else {
328        printf "\n%-48s %5i Time(s)\n", "Syslog-ng could not open file:", $FileOpenErrors;
329    }
330}
331
332if (keys %Conns) {
333    foreach my $cat (keys %Conns) {
334        if ($Detail >= 5) {
335            print "\n$cat:";
336            foreach my $IP (keys %{$Conns{$cat}}) {
337                printf "\n\t%-41s %5i Time(s)", $IP, $Conns{$cat}{$IP};
338            }
339            print "\n";
340        } else {
341            printf "\n%-48s %5i Time(s)\n", "$cat:", $ConnsSum{$cat};
342        }
343    }
344}
345
346if (keys %WriteErrs) {
347    if ($Detail >= 5) {
348        print "\nWrite Error(s):";
349        foreach my $err (keys %WriteErrs) {
350            printf "\n\t%-41s %5i Time(s)", "Error Number $err:", $WriteErrs{$err};
351        }
352        print "\n";
353    } else {
354        printf "\n%-48s %5i Time(s)\n", "Write Error(s):", $WriteErrsSum;
355    }
356}
357
358if ($ExceedConns && $Detail >= 5) {
359    printf "\n%-48s %5i Time(s)\n", "Concurrent Connections Exceeded:", $ExceedConns;
360}
361
362if (keys %Stats_center || keys %Stats_dest || keys %Stats_source ||
363    keys %Stats_dropped || keys %Stats_supp || keys %Stats_global ||
364    keys %Stats_stored || keys %Stats_program || keys %Stats_net) {
365   my ($lost_rcvd, $lost_dest) = ( 0, 0);
366
367   if ($Stats_center{received} && %Stats_source) {
368      $lost_rcvd = 0 - $Stats_center{received};
369      map {
370         # skip 'src#X' as this seams to be aggregated into 'src'
371         # skip 'journal' as this is not counted.
372         $lost_rcvd = $lost_rcvd + $Stats_source{$_} unless ($_ =~ /(?:src#\d+|journal)/);
373      } keys %Stats_source;
374   }
375   if ($Stats_center{queued} && %Stats_dest) {
376      $lost_dest = $Stats_center{queued};
377      map { $lost_dest = $lost_dest - $Stats_dest{$_} } keys %Stats_dest;
378   }
379
380   if ($Detail >= 6) { print "\nLog Statistics:"; }
381   if ($lost_rcvd != 0 || $lost_dest != 0) {
382      if ($lost_rcvd != 0) {
383         if ($Detail >= 5) {
384            print "\n- Failed to receive $lost_rcvd message(s)!";
385         }
386      }
387      if ($lost_dest != 0) {
388         if ($Detail >= 5 ) {
389            print "\n- Failed to save $lost_dest message(s) in logfile(s)!";
390         } else { $Drops = $Drops +  $lost_dest; }
391      }
392      if ($Detail >= 5) { print "\n"; }
393   }
394
395   if ($Detail >= 6) {
396      if (keys %Stats_center) {
397         print "\nCenter:";
398         foreach my $center (sort {$a cmp $b} keys %Stats_center) {
399            printf "\n\t%-34s %12i", $center, $Stats_center{$center};
400         }
401      }
402      if (keys %Stats_dest) {
403         print "\nDestination:";
404         foreach my $dest (sort {$a cmp $b} keys %Stats_dest) {
405            printf "\n\t%-34s %12i", $dest, $Stats_dest{$dest};
406         }
407      }
408      if (keys %Stats_source) {
409         print "\nSource:";
410         foreach my $source (sort {$a cmp $b} keys %Stats_source) {
411            printf "\n\t%-34s %12i", $source, $Stats_source{$source};
412         }
413      }
414      if (keys %Stats_net) {
415         print "\nNetwork:";
416         foreach my $source (sort {$a cmp $b} keys %Stats_net) {
417            (my $short_source) = ($source =~ /,?([^,]*)/);
418            printf "\n\t%-34s %12i", $short_source, $Stats_net{$source};
419         }
420      }
421      if (keys %Stats_program) {
422         print "\nProgram:";
423         foreach my $source (sort {$a cmp $b} keys %Stats_program) {
424            (my $short_source) = ($source =~ /,?([^,]*)/);
425            printf "\n\t%-34s %12i", $short_source, $Stats_program{$source};
426         }
427      }
428      if (keys %Stats_supp) {
429         print "\nSuppressed:";
430         foreach my $source (sort {$a cmp $b} keys %Stats_supp) {
431            printf "\n\t%-34s %12i", $source, $Stats_supp{$source};
432         }
433      }
434      if (keys %Stats_supp_net) {
435         print "\nSuppressed(net):";
436         foreach my $source (sort {$a cmp $b} keys %Stats_supp_net) {
437            printf "\n\t%-34s %12i", $source, $Stats_supp_net{$source};
438         }
439      }
440      if (keys %Stats_supp_program) {
441         print "\nSuppressed(program):";
442         foreach my $source (sort {$a cmp $b} keys %Stats_supp_program) {
443            printf "\n\t%-34s %12i", $source, $Stats_supp_program{$source};
444         }
445      }
446      if (keys %Stats_stored) {
447         print "\nStored:";
448         foreach my $source (sort {$a cmp $b} keys %Stats_stored) {
449            (my $short_source) = ($source =~ /,?([^,]*)/);
450            printf "\n\t%-34s %12i", $short_source, $Stats_stored{$source};
451         }
452      }
453      if (keys %Stats_global) {
454         print "\nGlobal:";
455         foreach my $source (sort {$a cmp $b} keys %Stats_global) {
456            printf "\n\t%-34s %12i", $source, $Stats_global{$source};
457         }
458      }
459   }
460   if (keys %Stats_dropped) {
461      print "\nDropped:";
462      foreach my $source (sort {$a cmp $b} keys %Stats_dropped) {
463         printf "\n\t%-34s %12i", $source, $Stats_dropped{$source};
464      }
465   }
466   if (keys %Stats_dropped_net) {
467   print "\nDropped(net):";
468   foreach my $source (sort {$a cmp $b} keys %Stats_dropped_net) {
469         printf "\n\t%-34s %12i", $source, $Stats_dropped_net{$source};
470      }
471   }
472   if (keys %Stats_dropped_program) {
473   print "\nDropped(program):";
474   foreach my $source (sort {$a cmp $b} keys %Stats_dropped_program) {
475         printf "\n\t%-34s %12i", $source, $Stats_dropped_program{$source};
476      }
477   }
478   if (keys %Stats_center or keys %Stats_dropped or keys %Stats_dropped_net or
479       keys %Stats_dropped_program) {
480      print "\n";
481   }
482}
483
484if (keys %Connections) {
485   foreach my $state (sort {$a cmp $b} keys %Connections) {
486      my $sum = 0;
487      print "\nConnections $state: ";
488      print "\n" if $Detail > 2;
489      foreach my $localip (sort {$a cmp $b} keys %{$Connections{$state}}) {
490         print "    $localip: " if $Detail > 2;
491         print "\n" if $Detail > 5;
492         my $sum_pro_ip = 0;
493         foreach my $extip (sort {$a cmp $b} keys %{$Connections{$state}{$localip}}) {
494            print "\t$extip : $Connections{$state}{$localip}{$extip} Time(s)\n" if $Detail > 5;
495            $sum = $sum + $Connections{$state}{$localip}{$extip};
496            $sum_pro_ip = $sum_pro_ip + $Connections{$state}{$localip}{$extip};
497         }
498         print "$sum_pro_ip\n" if $Detail > 2 and $Detail <= 5;
499      }
500      print "$sum\n" if $Detail <= 2;
501   }
502}
503
504if ($Drops) {
505    print "\nSyslog-ng dropped " . $Drops ." line(s)\n";
506}
507
508if (keys %IntErrors) {
509   print "\nInternal Errors:";
510   foreach my $class (sort {$a cmp $b} keys %IntErrors) {
511      print "\n    $class";
512      foreach my $error (sort {$a cmp $b} keys %{$IntErrors{$class}}) {
513         printf "\n\t%-41s %5i Time(s)", "$error:", $IntErrors{$class}{$error};
514      }
515   }
516   print "\n";
517}
518
519if (keys %Warnings) {
520   print "\nWarnings:";
521   foreach my $warning (keys %Warnings) {
522      printf "\n\t%-41s %5i Time(s)", "$warning:", $Warnings{$warning};
523   }
524   print "\n";
525}
526
527if (keys %OtherList) {
528   print "\n**** Unmatched entries ****\n";
529   foreach my $Error (keys %OtherList) {
530      print "    $Error : $OtherList{$Error} Time(s)\n";
531   }
532}
533
534### return without a failure ###
535exit(0);
536
537# vi: shiftwidth=3 tabstop=3 syntax=perl et
538
539