File Coverage

blib/lib/Unix/Lsof.pm
Criterion Covered Total %
statement 26 107 24.3
branch 1 42 2.3
condition 1 12 8.3
subroutine 8 16 50.0
pod 2 2 100.0
total 38 179 21.2


line stmt bran cond sub pod time code
1             package Unix::Lsof;
2              
3 3     3   182949 use 5.008;
  3         13  
  3         106  
4 3     3   2425 use version; our $VERSION = qv('0.1.0');
  3         6671  
  3         18  
5              
6 3     3   1874 use warnings;
  3         14  
  3         81  
7 3     3   15 use strict;
  3         5  
  3         78  
8 3     3   2825 use IPC::Run3;
  3         164802  
  3         199  
9 3     3   2371 use Unix::Lsof::Result;
  3         192  
  3         90  
10              
11 3     3   24 use base qw(Exporter);
  3         5  
  3         4840  
12              
13             our @EXPORT = qw(lsof);
14             our @EXPORT_OK = qw(parse_lsof_output);
15              
16             our %op_field = (
17             a => q(access mode),
18             c => q(command name),
19             C => q(structure share count),
20             d => q(device character code),
21             D => q(major/minor device number),
22             f => q(file descriptor),
23             F => q(structure address),
24             g => q(process group id),
25             G => q(flags),
26             i => q(inode number),
27             k => q(link count),
28             K => q(task id),
29             l => q(lock status),
30             L => q(login name),
31             n => q(file name),
32             N => q(node identifier),
33             o => q(file offset),
34             p => q(process id),
35             P => q(protocol name),
36             r => q(raw device number),
37             R => q(parent pid),
38             s => q(file size),
39             S => q(stream module and device names),
40             t => q(file type),
41             T => q(tcp/tpi info),
42             u => q(user id),
43             z => q(zone name),
44             Z => q(selinux security context),
45            
46             );
47              
48             our %tcptpi_field = (
49             QR => q(read queue size),
50             QS => q(send queue size),
51             SO => q(socket options and values),
52             SS => q(socket states),
53             ST => q(connection state),
54             TF => q(TCP flags and values),
55             WR => q(window read size),
56             WW => q(window write size),
57             );
58              
59             my (%opt,$err);
60              
61             sub lsof {
62 0     0 1 0 my @arg = @_;
63              
64 0         0 $err = undef;
65 0         0 _parse_opt (\@arg);
66              
67             # TODO: split arguments if only one argument is passed, so that a shell
68             # line can be used as-is
69              
70 0   0     0 $opt{binary} ||= _find_binary() || _idie("Cannot find lsof binary");
      0        
71              
72 0 0       0 if ( ! -e $opt{binary} ) {
73 0         0 _idie("Cannot find lsof binary: $!");
74             }
75 0 0 0     0 if ( !-x $opt{binary} || !-f $opt{binary} ) {
76 0         0 _idie("$opt{binary} is not an executable binary: $!");
77             }
78              
79 0         0 my $out="";
80              
81 0         0 eval { run3( [ $opt{binary}, "-F0", @arg ], \undef, \$out, \$err ); };
  0         0  
82              
83 0 0       0 if ($@) {
84 0 0       0 $err = $err ? $@ . $err : $@;
85             }
86              
87 0         0 my $parsed = _parse_lsof_output( $out );
88              
89 0 0       0 if (wantarray) {
90 0         0 return ( $parsed, $err );
91             } else {
92 0         0 return Unix::Lsof::Result->_new( $parsed, $err, $out,\%opt );
93             }
94             }
95              
96             sub _idie {
97 0     0   0 my $message = shift;
98 0 0       0 if ( $opt{suppress_errors} ) {
99 0         0 $err .= $message;
100             } else {
101 0         0 die $message;
102             }
103             }
104              
105             sub _iwarn {
106 0     0   0 my $message = shift;
107 0 0       0 if ( $opt{suppress_errors} ) {
108 0         0 $err .= $message;
109             } else {
110 0         0 warn $message;
111             }
112             }
113              
114             sub _parse_opt {
115 0     0   0 my $arg = shift;
116             # set options to defaults
117 0         0 %opt = (
118             binary => undef,
119             tcp_tpi_parse => "full",
120             suppress_errors => 0,
121             );
122              
123 0 0       0 if ( ref $arg->[-1] eq ref {} ) {
124 0         0 my $manopt = pop @$arg;
125 0         0 for my $k (keys %opt) {
126 0 0       0 if (exists $manopt->{$k}) {
127 0         0 $opt{$k} = $manopt->{$k};
128             }
129             }
130             }
131             }
132              
133             sub _find_binary {
134             # return if (!$ENV{PATH});
135 3     3   479 my @path = split( ":", $ENV{PATH} );
136 3         5 my $bin;
137             PATHLOOP:
138 3         9 for my $p (@path) {
139 21 50 33     407 if ( -f $p . "/lsof" && -x _ ) {
140 0         0 $bin = $p . "/lsof";
141 0         0 last PATHLOOP;
142             }
143             }
144 3         15 return $bin;
145             }
146              
147             # This is a stub for now. Constructing lsof arguments is a little
148             # tricky and will be done conclusively later
149             sub _construct_parameters {
150 0     0     my $options = shift;
151 0           my @cmd_line;
152 0           my %translate = (
153             pid => "-p",
154             file => undef
155             );
156 0           for my $arg ( keys %{$options} ) {
  0            
157 0 0         if ( exists $translate{$arg} ) {
158 0 0         push @cmd_line, $translate{$arg} if ( defined $translate{$arg} );
159             } else {
160 0           push @cmd_line, $options->{$arg};
161             }
162             }
163 0 0         return scalar @cmd_line ? @cmd_line : undef;
164             }
165              
166             sub _parse_lsof_output {
167 0     0     my $out = shift;
168 0           my ( %result, $pid, $previous );
169 0           my @output = split (/\000\012/, $out);
170 0           for my $line (@output) {
171 0           $line =~ s/^[\s\0]*//;
172 0           my @elements = split( "\0", $line );
173 0           my ($ident,$content) = ( $elements[0] =~ m/^(\w)(.*)$/ );
174 0 0         if ( !$ident ) {
    0          
    0          
175 0           _idie("Can't parse line $line, identifier missing");
176             } elsif ($ident eq "p") {
177 0           $pid = $content;
178 0           $result{$pid} = _parseelements( \@elements );
179 0           $previous = $ident;
180             } elsif ( $ident eq "f" ) {
181 0           push @{ $result{$pid}{files} }, _parseelements( \@elements );
  0            
182 0           $previous = $ident;
183             } else {
184 0           _idie("Can't parse line $line, operator field $ident is not valid");
185             }
186              
187             }
188              
189 0           return \%result;
190             }
191              
192             sub parse_lsof_output {
193 0     0 1   my @args = @_;
194 0           $err = undef;
195 0 0         if (ref($args[0]) eq ref([])) {
196 0           my $str = join("\000\012",@{$args[0]});
  0            
197 0           return _parse_lsof_output($str);
198             } else {
199 0           return _parse_lsof_output($args[0]);
200             }
201             }
202              
203             sub _parseelements {
204 0     0     my $elements = shift;
205              
206 0           my %result;
207 0           while ( my $elem = shift @$elements ) {
208 0           my ( $fident, $content ) = ( $elem =~ /^(.)(.*)$/ );
209 0 0         next if !$fident;
210             # Specialised handling of TCP/TPI info, since that field
211             # contains multiple pieces of data
212 0 0         if ($fident eq "T") {
213 0 0         if ($opt{tcp_tpi_parse} eq "array") {
214 0           push @{$result{ $op_field{$fident} } },$content;
  0            
215             } else {
216 0           my ($fi,$fc) = split(/=/,$content);
217 0 0         my $key = $opt{tcp_tpi_parse} eq "part" ? $fi : $tcptpi_field{$fi};
218 0           $result{ $op_field{$fident} }{ $key } = $fc;
219             }
220             } else {
221             # warn $fident. " - ".$op_field{$fident}." - ".$content;
222 0           $result{ $op_field{$fident} } = $content;
223             # exit;
224             }
225             }
226 0           return \%result;
227             }
228              
229             1; # Magic true value required at end of module
230             __END__