File Coverage

blib/lib/SyslogScan/SyslogEntry.pm
Criterion Covered Total %
statement 1 4 25.0
branch n/a
condition n/a
subroutine 1 2 50.0
pod n/a
total 2 6 33.3


line stmt bran cond sub pod time code
1             # SyslogEntry: generic line in a syslog program.
2              
3             package SyslogScan;
4              
5             $VERSION = 0.31;
6 0     0     sub Version { $VERSION };
7              
8              
9             package SyslogScan::SyslogEntry;
10              
11 6     6   3453 use SyslogScan::ParseDate;
  0            
  0            
12              
13             $VERSION = 0.31;
14             sub Version { $VERSION };
15              
16             use SyslogScan::UnsupportedEntry;
17             use Carp;
18             use strict;
19              
20             # to handle 'last message repeated n times' lines
21             my %gLastLineByHost;
22             my $gLineToRepeat;
23             my $gFinalMonth;
24             my $gFinalDay;
25             my $gFinalTime;
26             my $gRepeatCount = 0;
27              
28             my %gTable =
29             (
30             # examples:
31             # 'cli' => 'SyslogScan::AnnexEntry',
32             # 'slip' => 'SyslogScan::AnnexEntry',
33             # 'telnet_cmd' => 'SyslogScan::AnnexEntry',
34             # 'ppp' => 'SyslogScan::AnnexEntry',
35             # 'rlogin_rdr' => 'SyslogScan::AnnexEntry',
36             );
37              
38             my $pIsSubclass = sub {
39             my($superclass,$possibleSubclass) = @_;
40             my(@superclassList);
41            
42             die "illegal subclass (has whitespace)" if
43             $possibleSubclass =~ /\s/;
44             @superclassList = eval '@' . "$possibleSubclass" . "::ISA";
45             return 't' if (grep (($superclass eq $_), @superclassList));
46             '';
47             };
48              
49             sub new
50             {
51             my $staticType = shift;
52             my $SYSLOG = shift;
53              
54             defined $SYSLOG or croak("syslog not defined");
55              
56             my ($self, $className, $line);
57              
58             # check if we are repeating ourselves
59             if ($gRepeatCount)
60             {
61             $line = $gLineToRepeat;
62             }
63             else
64             {
65             # read the next syslog line
66             no strict 'refs';
67             defined($line = <$SYSLOG>) || return undef; # at EOF
68             use strict 'refs';
69             if (chop($line) ne "\n")
70             {
71             warn "Discarding final line which was not newline-terminated.\n";
72             print STDERR " (consider using 'tail -f syslog')\n";
73             return undef;
74             }
75             }
76            
77             # parse a line like: 'Jun 13 02:32:27 satellife mydaemon[25994]: foo'
78             my ($month,$day,$time,$machine,$rest) =
79             split ' ', $line, 5;
80              
81             # check for 'last line repeated n times' message
82             if ($rest =~ /^last message repeated (\d+) time/)
83             {
84             $gRepeatCount and
85             die "repetition of 'last message repeated' line!?";
86             $gRepeatCount = $1;
87             $gLineToRepeat = $gLastLineByHost{$machine};
88             ($gFinalMonth, $gFinalDay, $gFinalTime) = ($month, $day, $time);
89             $gRepeatCount ||
90             die "repetition of length 0!?";
91             return SyslogScan::SyslogEntry -> new($SYSLOG);
92             }
93              
94             if ($gRepeatCount)
95             {
96             if ($gRepeatCount == 1) # on last repetition
97             {
98             ($month, $day, $time) = ($gFinalMonth, $gFinalDay, $gFinalTime);
99             }
100             else
101             {
102             ($month, $day, $time) = (); # cannot precisely know time
103             }
104             $gRepeatCount--;
105             }
106              
107             $gLastLineByHost{$machine} = $line;
108            
109             my ($executable,$tag,$content) =
110             $rest =~ /^([^\:\[\]]+)(\[\d+\])?\: (.*)/;
111             $tag =~ s/\[(.+)\]/$1/ if defined $tag;
112              
113             if (! defined $executable)
114             {
115             $rest and
116             print STDERR "executable not defined in line: $line\n"
117             unless $::gbQuiet;
118             }
119              
120             # fill in my 'self' array
121             $self = {
122             "content" => $content,
123             "month" => $month,
124             "day" => $day,
125             "time" => $time,
126             "machine" => $machine,
127             "executable" => $executable,
128             "tag" => $tag,
129             "raw" => $line
130             };
131              
132             if (defined $time)
133             {
134             my $date = "$month $day $time";
135             $self->{"unix_time"} = SyslogScan::ParseDate::parseDate($date);
136             }
137              
138             # check for possible i/o error
139             if ($line =~ m^I/O error^ and $` !~ /\bstat=/)
140             {
141             print STDERR "may be syslog I/O error in line:\n $line\n"
142             unless $::gbQuiet;
143             $$self{suspectIOError} = 1;
144             }
145              
146             # Make first letter of program capital, and change . to _,
147             # so the module to handle 'in.identd' is named "In_identdLine.pm"
148              
149             my $oldChar = substr($executable,0,1);
150             substr($executable,0,1) =~ tr/a-z/A-Z/;
151             my $handlerClass = "SyslogScan::" . $executable . "Line";
152             $handlerClass =~ s/[\. ]/_/g;
153             substr($executable,0,1) = $oldChar;
154              
155             # If the module to handle this program has been "use"'d,
156             # then subclass our object and call its parseContent() method.
157             if (&$pIsSubclass("SyslogScan::SyslogEntry",$handlerClass))
158             {
159             bless($self,$handlerClass);
160             }
161             elsif (defined ($gTable{$executable}))
162             {
163             bless($self,$gTable{$executable});
164             }
165             else
166             {
167             # this line is not supported by a handler class
168             bless($self,"SyslogScan::UnsupportedEntry");
169             }
170              
171             # TODO: get rid of 'type' in favor of checking ref
172             eval
173             {
174             $self -> parseContent;
175             };
176              
177             if ($@ ne "")
178             {
179             # provide "escape hatches" so a module can halt the
180             # entire program execution if it really needs to
181             if (($@ =~ /SYSLOGMODULEFATAL/) ||
182             defined $$self{"ERRORS ARE FATAL"})
183             {
184             die "fatal module error: $@" ;
185             }
186            
187             # catch non-fatal errors so flawed module does not break others
188             my ($brokenHandler) = ref $self;
189             bless ($self, "SyslogScan::BotchedEntry");
190             $$self{"brokenHandler"} = $brokenHandler;
191             $$self{"errorString"} = $@;
192             print STDERR "SyslogEntry.pm caught $brokenHandler module error: \n" .
193             " $@\n" .
194             " returning BotchedEntry object\n";
195             }
196              
197             $self;
198             }
199              
200             sub parseContent
201             {
202             my ($self) = @_;
203             die "class ", ref($self), " did not override parseContent!\n";
204             }
205              
206             # access methods
207              
208             sub content { return ( (my $self = shift)->{"content"});}
209             sub raw { return ( (my $self = shift)->{"raw"});}
210             sub month { return ( (my $self = shift)->{"month"});}
211             sub day { return ( (my $self = shift)->{"day"});}
212             sub time { return ( (my $self = shift)->{"time"});}
213             sub machine { return ( (my $self = shift)->{"machine"});}
214             sub executable { return ( (my $self = shift)->{"executable"});}
215             sub tag { return ( (my $self = shift)->{"tag"});}
216             sub unix_time { return ( (my $self = shift)->{"unix_time"});}
217              
218             1;
219              
220             __END__