File Coverage

blib/lib/File/Tail/FAM.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             ###########################################
2             package File::Tail::FAM;
3             ###########################################
4 1     1   154631 use SGI::FAM;
  0            
  0            
5             use Log::Log4perl qw(:easy);
6             use strict;
7             use warnings;
8              
9             our $VERSION = "0.02";
10              
11             ###########################################
12             sub new {
13             ###########################################
14             my($class, %options) = @_;
15              
16             my $self = {
17             fam => SGI::FAM->new(),
18             %options,
19             };
20              
21             LOGDIE "Mandatory parameter missing: file" unless
22             exists $self->{file};
23              
24             LOGDIE "File $self->{file} doesn't exist" unless
25             -f $self->{file};
26              
27             LOGDIE "File $self->{file} isn't readable" unless
28             -r $self->{file};
29              
30             $self->{fam}->monitor($self->{file}) or
31             LOGDIE "Monitoring $self->{file} failed";
32              
33             # Block until we get the 'exist' event to make
34             # sure the monitor is in place
35             my $e = $self->{fam}->next_event();
36              
37             bless $self, $class;
38              
39             $self->file_open();
40             $self->checkpoint(2);
41              
42             return $self;
43             }
44              
45             ###########################################
46             sub read_nonblock {
47             ###########################################
48             my($self) = @_;
49              
50             return $self->read(1);
51             }
52              
53             ###########################################
54             sub poll_pending {
55             # The test suite uses this to avoid race conditions
56             ###########################################
57             my($self) = @_;
58              
59             while(! $self->{fam}->pending()) {
60             select undef, undef, undef, 0.1;
61             }
62             }
63              
64             ###########################################
65             sub checkpoint {
66             ###########################################
67             my($self, $whence) = @_;
68              
69             DEBUG "Checkpoint on file $self->{file}";
70              
71             if(defined $self->{offset}) {
72             my $new_size = -s "$self->{file}";
73              
74             if($new_size < $self->{offset}) {
75             # File truncated, re-read
76             DEBUG "Assuming truncated file";
77             $self->file_close();
78             $self->file_open(0);
79             }
80             }
81              
82             # Seek to $whence
83             seek $self->{fh}, 0, $whence;
84             $self->{offset} = tell $self->{fh};
85              
86             DEBUG "Offset on $self->{file} is $self->{offset}";
87             }
88              
89             ###########################################
90             sub read {
91             ###########################################
92             my($self, $nonblock) = @_;
93              
94             while(1) {
95              
96             if($nonblock) {
97             unless($self->{fam}->pending()) {
98             DEBUG "No events pending in non-blocking read";
99             return undef;
100             }
101             }
102              
103             DEBUG "Blocking for next event";
104             my $e = $self->{fam}->next_event();
105             DEBUG "Got event: ", $e->type();
106              
107             my $data;
108              
109             if($e->type() eq "create") {
110             $self->checkpoint(0);
111             $data = $self->read_more();
112             redo unless defined $data;
113             return $data;
114             } elsif( $e->type() eq "change" ) {
115             $data = $self->read_more();
116             redo unless defined $data;
117             return $data;
118             }
119             }
120             }
121              
122             ###########################################
123             sub read_more {
124             ###########################################
125             my($self) = @_;
126              
127             # Lift EOF
128             seek $self->{fh}, 0, 1;
129              
130             local $/;
131             $/ = undef;
132            
133             my $fh = $self->{fh};
134             my $data = <$fh>;
135              
136             if(defined $data) {
137             DEBUG "Found data: '$data'";
138             } else {
139             # This can happen if we get several change events
140             # in a row, no problem
141             $self->checkpoint(2);
142             return undef;
143             }
144              
145             $self->{offset} = tell $self->{fh};
146              
147             return $data;
148             }
149              
150             ##################################################
151             sub file_close {
152             ##################################################
153             my($self) = @_;
154              
155             DEBUG "Closing file $self->{file}";
156              
157             undef $self->{fh};
158             }
159              
160             ##################################################
161             sub file_open {
162             ##################################################
163             my($self, $whence) = @_;
164              
165             $whence = 2 unless defined $whence;
166              
167             DEBUG "Opening file $self->{file}";
168              
169             my $fh = do { local *FH; *FH; };
170              
171             open $fh, "$self->{file}" or
172             LOGDIE "Can't open $self->{file} ($!)";
173              
174             $self->{fh} = $fh;
175              
176             # Seek to EOF
177             seek $self->{fh}, 0, $whence;
178             $self->{offset} = tell $self->{fh};
179              
180             DEBUG "Setting offset to $self->{offset}";
181             }
182              
183             1;
184              
185             __END__