File Coverage

blib/lib/Linux/Fuser.pm
Criterion Covered Total %
statement 96 102 94.1
branch 14 22 63.6
condition 3 6 50.0
subroutine 19 19 100.0
pod 2 2 100.0
total 134 151 88.7


line stmt bran cond sub pod time code
1             #*****************************************************************************
2             #* *
3             #* Gellyfish Software *
4             #* *
5             #* *
6             #*****************************************************************************
7             #* *
8             #* PROGRAM : Linux::Fuser *
9             #* *
10             #* AUTHOR : JNS *
11             #* *
12             #* DESCRIPTION : Provide an 'fuser' like facility in Perl *
13             #* *
14             #* *
15             #*****************************************************************************
16             #* *
17             #* $Id$
18             #* *
19             #*****************************************************************************
20              
21             package Linux::Fuser;
22              
23             =head1 NAME
24              
25             Linux::Fuser - Determine which processes have a file open
26              
27             =head1 SYNOPSIS
28              
29             use Linux::Fuser;
30              
31             my $fuser = Linux::Fuser->new();
32              
33             my @procs = $fuser->fuser('foo');
34              
35             foreach my $proc ( @procs )
36             {
37             print $proc->pid(),"\t", $proc->user(),"\n",@{$proc->cmd()},"\n";
38             }
39              
40             =head1 DESCRIPTION
41              
42             This module provides information similar to the Unix command 'fuser' about
43             which processes have a particular file open. The way that this works is
44             highly unlikely to work on any other OS other than Linux and even then it
45             may not work on other than 2.2.* kernels. Some features may not work
46             correctly on kernel versions older than 2.6.22
47              
48             It should also be borne in mind that this may not produce entirely accurate
49             results unless you are running the program as the Superuser as the module
50             will require access to files in /proc that may only be readable by their
51             owner.
52              
53             =head2 METHODS
54              
55             =over 4
56              
57             =cut
58              
59 1     1   1019 use strict;
  1         3  
  1         65  
60              
61 1         534 use vars qw(
62             $VERSION
63             @ISA
64 1     1   6 );
  1         2  
65              
66             $VERSION = '1.6';
67              
68             =item new
69              
70             The constructor of the object. It takes no arguments and returns a blessed
71             reference suitable for calling the methods on.
72              
73             =cut
74              
75             sub new
76             {
77 2     2 1 858 my ( $proto, @args ) = @_;
78              
79 2   33     22 my $class = ref($proto) || $proto;
80              
81 2         5 my $self = {};
82              
83 2         6 bless $self, $class;
84              
85 2         6 return $self;
86              
87             }
88              
89             =item fuser SCALAR $file
90              
91             Given the name of a file it will return a list of Linux::Fuser::Procinfo
92             objects, one for each process that has the file open - this will be the
93             empty list if no processes have the file open or undef if the file doesnt
94             exist.
95              
96             =cut
97              
98             sub fuser
99             {
100 2     2 1 154 my ( $self, $file, @args ) = @_;
101              
102 2 100       33 return () unless -f $file;
103              
104 1         3 my @procinfo = ();
105              
106 1         23 my ( $dev, $ino, @ostuff ) = stat($file);
107              
108 1 50       46 opendir PROC, '/proc' or die "Can't access /proc - $!\n";
109              
110 1         124 my @procs = grep /^\d+$/, readdir PROC;
111              
112 1         17 closedir PROC;
113              
114 1         3 foreach my $proc (@procs)
115             {
116 10 50       201 opendir FD, "/proc/$proc/fd" or next;
117              
118 10         282 my @fds = map { ["/proc/$proc/fd/$_",$_] } grep /^\d+$/, readdir FD;
  48         127  
119              
120 10         106 closedir FD;
121              
122 10         19 foreach my $fd_data (@fds)
123             {
124 48         72 my $fd = $fd_data->[0];
125 48         58 my $fd_no = $fd_data->[1];
126              
127 48 100       962 if ( my @statinfo = stat $fd )
128             {
129 7 100 66     59 if ( ( $dev == $statinfo[0] ) && ( $ino == $statinfo[1] ) )
130             {
131 1         12 push @procinfo,Linux::Fuser::Procinfo->new($proc, $fd_data);
132             }
133             }
134             }
135             }
136 1         10 return @procinfo;
137             }
138              
139             1;
140              
141             package Linux::Fuser::Procinfo;
142              
143             =back
144              
145             =head2 PER PROCESS METHODS
146              
147             The fuser() method will return a list of objects of type Linux::Fuser::Procinfo
148             which itself has methods to return information about the process.
149              
150             =over 2
151              
152             =item user
153              
154             The login name of the user that started this process ( or more precisely
155             that owns the file descriptor that the file is open on ).
156              
157             =item pid
158              
159             The process id of the process that has the file open.
160              
161             =item cmd
162              
163             The command line of the program that opened the file. This actually returns
164             a reference to an array containing the individual elements of the command
165             line.
166              
167             =item filedes
168              
169             A Linux::Fuser::FileDescriptor object that has details of the file as
170             the process has it opened - see below.
171              
172             =back
173              
174              
175             =cut
176              
177 1     1   7 use strict;
  1         2  
  1         33  
178 1     1   5 use Carp;
  1         2  
  1         94  
179              
180 1     1   6 use vars qw($AUTOLOAD);
  1         1  
  1         229  
181              
182             sub new
183             {
184 1     1   3 my ( $class, $pid, $fd_data ) = @_;
185              
186 1         2 my $fd = $fd_data->[0];
187 1         2 my $fd_no = $fd_data->[1];
188              
189 1         1136 my $user = getpwuid( ( lstat($fd) )[4] );
190              
191 1         7 my @cmd = ('');
192              
193 1 50       49 if ( open CMD, "/proc/$pid/cmdline" )
194             {
195 1         42 chomp( @cmd = );
196             }
197              
198 1         11 my $filedes = Linux::Fuser::FileDescriptor->new($pid, $fd_no);
199              
200 1         8 my $procinfo = {
201             pid => $pid,
202             user => $user,
203             cmd => \@cmd,
204             filedes => $filedes
205             };
206              
207 1         3 bless $procinfo, $class;
208              
209 1         7 return $procinfo;
210              
211             }
212              
213             sub AUTOLOAD
214             {
215 3     3   2471 my ( $self, @args ) = @_;
216              
217 1     1   7 no strict 'refs';
  1         2  
  1         302  
218              
219 3         17 ( my $method = $AUTOLOAD ) =~ s/.*://;
220              
221 3 50       9 return if $method eq 'DESTROY';
222              
223 3 50       12 if ( exists $self->{$method} )
224             {
225 3         14 *{$AUTOLOAD} = sub {
226 3     3   6 my ( $self, @args ) = @_;
227 3         18 return $self->{$method};
228 3         13 };
229             }
230             else
231             {
232 0         0 my $pack = ref($self);
233 0         0 croak "Can't find method $method via package $self";
234             }
235              
236 3         6 goto &{$AUTOLOAD};
  3         13  
237              
238             }
239              
240             1;
241              
242             package Linux::Fuser::FileDescriptor;
243              
244             =head2 Linux::Fuser::FileDescriptor
245              
246             This is returned by the filedes method of the Linux::Fuser::Procinfo and
247             contains the information about the file descriptor that the process has the
248             file open under.
249              
250             The information which this is based on is only available from Linux Kernel
251             version 2.6.22 onwards so will not be available on earlier kernels (except
252             the 'fd'.)
253              
254             It has the following methods (though future versions of the Linux Kernel may
255             provide different or fuller information via /proc/$pid/fdinfo):
256              
257             =over 2
258              
259             =item fd
260              
261             The file descriptor that this file is opened under - this will be unique
262             within a process (if a file is opened more than once by a process) but not
263             within the system.
264              
265             =item flags
266              
267             The flags with which the file was opened (by open or creat) as a long integer.
268              
269             =item pos
270              
271             The location (in bytes) of the file pointer within the file.
272              
273             =back
274              
275             =cut
276              
277 1     1   5 use strict;
  1         3  
  1         27  
278 1     1   4 use warnings;
  1         4  
  1         42  
279 1     1   5 use Carp;
  1         2  
  1         64  
280              
281 1     1   5 use vars qw($AUTOLOAD);
  1         1  
  1         218  
282              
283             sub new
284             {
285 1     1   3 my ( $class, $pid, $fd_no ) = @_;
286              
287              
288 1         4 my $self = {
289             fd => $fd_no
290             };
291              
292 1 50       48 if ( open FDINFO,'<',"/proc/$pid/fdinfo/$fd_no" )
293             {
294 1         35 while(my $fd_info = )
295             {
296 2         5 chomp($fd_info);
297 2         13 my ($key, $value ) = split /:\s+/, $fd_info;
298 2         17 $self->{$key} = $value;
299             }
300             }
301             else
302             {
303 0         0 $self->{'pos'} = undef;
304 0         0 $self->{'flags'} = undef;
305             }
306              
307 1         6 return bless $self, $class;
308             }
309              
310             sub AUTOLOAD
311             {
312 1     1   511 my ( $self, @args ) = @_;
313              
314 1     1   5 no strict 'refs';
  1         3  
  1         200  
315              
316 1         9 ( my $method = $AUTOLOAD ) =~ s/.*://;
317              
318 1 50       6 return if $method eq 'DESTROY';
319              
320 1 50       8 if ( exists $self->{$method} )
321             {
322 1         5 *{$AUTOLOAD} = sub {
323 1     1   3 my ( $self, @args ) = @_;
324 1         675 return $self->{$method};
325 1         6 };
326             }
327             else
328             {
329 0         0 my $pack = ref($self);
330 0         0 croak "Can\'t find method $method via package $self";
331             }
332              
333 1         3 goto &{$AUTOLOAD};
  1         5  
334              
335             }
336              
337             1;
338             __END__