File Coverage

blib/lib/FusionInventory/Agent/Tools/Unix.pm
Criterion Covered Total %
statement 30 168 17.8
branch 0 64 0.0
condition 0 9 0.0
subroutine 10 20 50.0
pod 5 5 100.0
total 45 266 16.9


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Tools::Unix;
2              
3 29     29   5711857 use strict;
  29         152  
  29         1148  
4 29     29   142 use warnings;
  29         65  
  29         918  
5 29     29   171 use base 'Exporter';
  29         62  
  29         2904  
6              
7 29     29   151 use English qw(-no_match_vars);
  29         40  
  29         209  
8 29     29   12681 use File::stat;
  29         9601  
  29         264  
9 29     29   2063 use File::Which;
  29         870  
  29         1265  
10 29     29   788 use Memoize;
  29         1854  
  29         1379  
11 29     29   16908 use Time::Local;
  29         47298  
  29         1901  
12              
13 29     29   571 use FusionInventory::Agent::Tools;
  29         38  
  29         4608  
14 29     29   11611 use FusionInventory::Agent::Tools::Network;
  29         71  
  29         57599  
15              
16             our @EXPORT = qw(
17             getDeviceCapacity
18             getIpDhcp
19             getFilesystemsFromDf
20             getFilesystemsTypesFromMount
21             getProcesses
22             getRoutingTable
23             );
24              
25             memoize('getProcesses');
26              
27             sub getDeviceCapacity {
28 0     0 1   my (%params) = @_;
29              
30 0 0         return unless $params{device};
31              
32             # GNU version requires -p flag
33 0 0         my $command = getFirstLine(command => '/sbin/fdisk -v') =~ '^GNU' ?
34             "/sbin/fdisk -p -s $params{device}" :
35             "/sbin/fdisk -s $params{device}" ;
36              
37 0           my $capacity = getFirstLine(
38             command => $command,
39             logger => $params{logger},
40             );
41              
42 0 0         $capacity = int($capacity / 1000) if $capacity;
43              
44 0           return $capacity;
45             }
46              
47             sub getIpDhcp {
48 0     0 1   my ($logger, $if) = @_;
49              
50 0           my $dhcpLeaseFile = _findDhcpLeaseFile($if);
51              
52 0 0         return unless $dhcpLeaseFile;
53              
54 0           _parseDhcpLeaseFile($logger, $if, $dhcpLeaseFile);
55             }
56              
57             sub _findDhcpLeaseFile {
58 0     0     my ($if) = @_;
59              
60 0           my @directories = qw(
61             /var/db
62             /var/lib/dhcp3
63             /var/lib/dhcp
64             /var/lib/dhclient
65             );
66 0           my @patterns = ("*$if*.lease", "*.lease");
67 0           my @files;
68              
69 0           foreach my $directory (@directories) {
70 0 0         next unless -d $directory;
71 0           foreach my $pattern (@patterns) {
72              
73 0           push @files,
74 0           grep { -s $_ }
75             glob("$directory/$pattern");
76             }
77             }
78              
79 0 0         return unless @files;
80              
81             # sort by creation time
82 0           @files =
83 0           map { $_->[0] }
84 0           sort { $a->[1]->ctime() <=> $b->[1]->ctime() }
85 0           map { [ $_, stat($_) ] }
86             @files;
87              
88             # take the last one
89 0           return $files[-1];
90             }
91              
92             sub _parseDhcpLeaseFile {
93 0     0     my ($logger, $if, $lease_file) = @_;
94              
95              
96 0           my $handle = getFileHandle(file => $lease_file, logger => $logger);
97 0 0         return unless $handle;
98              
99 0           my ($lease, $dhcp, $server_ip, $expiration_time);
100              
101             # find the last lease for the interface with its expire date
102 0           while (my $line = <$handle>) {
103 0 0         if ($line=~ /^lease/i) {
104 0           $lease = 1;
105 0           next;
106             }
107 0 0         if ($line=~ /^}/) {
108 0           $lease = 0;
109 0           next;
110             }
111              
112 0 0         next unless $lease;
113              
114             # inside a lease section
115 0 0         if ($line =~ /interface\s+"([^"]+)"/){
116 0           $dhcp = ($1 eq $if);
117 0           next;
118             }
119              
120 0 0         next unless $dhcp;
121              
122 0 0         if (
    0          
123             $line =~
124             /option \s+ dhcp-server-identifier \s+ (\d{1,3}(?:\.\d{1,3}){3})/x
125             ) {
126             # server IP
127 0           $server_ip = $1;
128             } elsif (
129             $line =~
130             /expire \s+ \d \s+ (\d+)\/(\d+)\/(\d+) \s+ (\d+):(\d+):(\d+)/x
131             ) {
132 0           my ($year, $mon, $day, $hour, $min, $sec)
133             = ($1, $2, $3, $4, $5, $6);
134             # warning, expected ranges is 0-11, not 1-12
135 0           $mon = $mon - 1;
136 0           $expiration_time = timelocal($sec, $min, $hour, $day, $mon, $year);
137             }
138             }
139 0           close $handle;
140              
141 0 0         return unless $expiration_time;
142              
143 0           my $current_time = time();
144              
145 0 0         return $current_time <= $expiration_time ? $server_ip : undef;
146             }
147              
148             sub getFilesystemsFromDf {
149 0     0 1   my (%params) = @_;
150 0           my $handle = getFileHandle(%params);
151              
152 0           my @filesystems;
153              
154             # get headers line first
155 0           my $line = <$handle>;
156 0 0         return unless $line;
157              
158 0           chomp $line;
159 0           my @headers = split(/\s+/, $line);
160              
161 0           while (my $line = <$handle>) {
162 0           chomp $line;
163 0           my @infos = split(/\s+/, $line);
164              
165             # depending on the df implementation, and how it is called
166             # the filesystem type may appear as second colum, or be missing
167             # in the second case, it has to be given by caller
168 0           my ($filesystem, $total, $free, $type);
169 0 0         if ($headers[1] eq 'Type') {
170 0           $filesystem = $infos[1];
171 0           $total = $infos[2];
172 0           $free = $infos[4];
173 0           $type = $infos[6];
174             } else {
175 0           $filesystem = $params{type};
176 0           $total = $infos[1];
177 0           $free = $infos[3];
178 0           $type = $infos[5];
179             }
180              
181             # skip some virtual filesystems
182 0 0 0       next if $total !~ /^\d+$/ || $total == 0;
183 0 0 0       next if $free !~ /^\d+$/ || $free == 0;
184              
185 0           push @filesystems, {
186             VOLUMN => $infos[0],
187             FILESYSTEM => $filesystem,
188             TOTAL => int($total / 1024),
189             FREE => int($free / 1024),
190             TYPE => $type
191             };
192             }
193              
194 0           close $handle;
195              
196 0           return @filesystems;
197             }
198              
199             sub getFilesystemsTypesFromMount {
200 0     0 1   my (%params) = (
201             command => 'mount',
202             @_
203             );
204              
205 0           my $handle = getFileHandle(%params);
206 0 0         return unless $handle;
207              
208 0           my @types;
209 0           while (my $line = <$handle>) {
210             # BSD-style:
211             # /dev/mirror/gm0s1d on / (ufs, local, soft-updates)
212 0 0         if ($line =~ /^\S+ on \S+ \((\w+)/) {
213 0           push @types, $1;
214 0           next;
215             }
216             # Linux style:
217             # /dev/sda2 on / type ext4 (rw,noatime,errors=remount-ro)
218 0 0         if ($line =~ /^\S+ on \S+ type (\w+)/) {
219 0           push @types, $1;
220 0           next;
221             }
222             }
223 0           close $handle;
224              
225             ### raw result: @types
226              
227             return
228 0           uniq
229             @types;
230             }
231              
232             sub getProcesses {
233             my $ps = which('ps');
234             return -l $ps && readlink($ps) eq 'busybox' ? _getProcessesBusybox(@_) :
235             _getProcessesOther(@_) ;
236             }
237              
238             sub _getProcessesBusybox {
239 0     0     my (%params) = (
240             command => 'ps',
241             @_
242             );
243              
244 0           my $handle = getFileHandle(%params);
245              
246             # skip headers
247 0           my $line = <$handle>;
248              
249 0           my @processes;
250              
251 0           while ($line = <$handle>) {
252 0 0         next unless $line =~
253             /^
254             \s* (\S+)
255             \s+ (\S+)
256             \s+ (\S+)
257             \s+ ...
258             \s+ (\S.+)
259             /x;
260 0           my $pid = $1;
261 0           my $user = $2;
262 0           my $vsz = $3;
263 0           my $cmd = $4;
264              
265 0           push @processes, {
266             USER => $user,
267             PID => $pid,
268             VIRTUALMEMORY => $vsz,
269             CMD => $cmd
270             };
271             }
272              
273 0           close $handle;
274              
275 0           return @processes;
276             }
277              
278             sub _getProcessesOther {
279 0 0   0     my (%params) = (
280             command =>
281             'ps -A -o user,pid,pcpu,pmem,vsz,tty,etime' . ',' .
282             ($OSNAME eq 'solaris' ? 'comm' : 'command'),
283             @_
284             );
285              
286 0           my $handle = getFileHandle(%params);
287              
288             # skip headers
289 0           my $line = <$handle>;
290              
291             # get the current timestamp
292 0           my $localtime = time();
293              
294 0           my @processes;
295              
296 0           while ($line = <$handle>) {
297              
298 0 0         next unless $line =~
299             /^ \s*
300             (\S+) \s+
301             (\S+) \s+
302             (\S+) \s+
303             (\S+) \s+
304             (\S+) \s+
305             (\S+) \s+
306             (\S+) \s+
307             (\S.*\S)
308             /x;
309              
310 0           my $user = $1;
311 0           my $pid = $2;
312 0           my $cpu = $3;
313 0           my $mem = $4;
314 0           my $vsz = $5;
315 0           my $tty = $6;
316 0           my $etime = $7;
317 0           my $cmd = $8;
318              
319 0           push @processes, {
320             USER => $user,
321             PID => $pid,
322             CPUUSAGE => $cpu,
323             MEM => $mem,
324             VIRTUALMEMORY => $vsz,
325             TTY => $tty,
326             STARTED => _getProcessStartTime($localtime, $etime),
327             CMD => $cmd
328             };
329             }
330              
331 0           close $handle;
332              
333 0           return @processes;
334             }
335              
336             my %month = (
337             Jan => '01',
338             Feb => '02',
339             Mar => '03',
340             Apr => '04',
341             May => '05',
342             Jun => '06',
343             Jul => '07',
344             Aug => '08',
345             Sep => '09',
346             Oct => '10',
347             Nov => '11',
348             Dec => '12',
349             );
350             my %day = (
351             Mon => '01',
352             Tue => '02',
353             Wed => '03',
354             Thu => '04',
355             Fry => '05',
356             Sat => '06',
357             Sun => '07',
358             );
359             my $monthPattern = join ('|', keys %month);
360              
361             # Computes a consistent process starting time from the process etime value.
362             sub _getProcessStartTime {
363 0     0     my ($localtime, $elapsedtime_string) = @_;
364              
365              
366             # POSIX specifies that ps etime entry looks like [[dd-]hh:]mm:ss
367             # if either day and hour are not present then they will eat
368             # up the minutes and seconds so split on a non digit and reverse it:
369 0           my ($psec, $pmin, $phour, $pday) =
370             reverse(split(/\D/, $elapsedtime_string));
371              
372             ## no critic (ExplicitReturnUndef)
373 0 0 0       return undef unless defined $psec && defined $pmin;
374              
375             # Compute a timestamp from the process etime value
376 0 0         my $elapsedtime = $psec +
    0          
377             $pmin * 60 +
378             ($phour ? $phour * 60 * 60 : 0) +
379             ($pday ? $pday * 24 * 60 * 60 : 0) ;
380              
381             # Substract this timestamp from the current time, creating the date at which
382             # the process was launched
383 0           my (undef, $min, $hour, $day, $month, $year) =
384             localtime($localtime - $elapsedtime);
385              
386             # Output the final date, after completing it (time + UNIX epoch)
387 0           $year = $year + 1900;
388 0           $month = $month + 1;
389 0           return sprintf("%04d-%02d-%02d %02d:%02d", $year, $month, $day, $hour, $min);
390             }
391              
392             sub getRoutingTable {
393 0     0 1   my (%params) = (
394             command => 'netstat -nr -f inet',
395             @_
396             );
397              
398 0           my $handle = getFileHandle(%params);
399 0 0         return unless $handle;
400              
401 0           my $routes;
402              
403             # first, skip all header lines
404 0           while (my $line = <$handle>) {
405 0 0         last if $line =~ /^Destination/;
406             }
407              
408             # second, collect routes
409 0           while (my $line = <$handle>) {
410 0 0         next unless $line =~ /^
411             (
412             $ip_address_pattern
413             |
414             $network_pattern
415             |
416             default
417             )
418             \s+
419             (
420             $ip_address_pattern
421             |
422             $mac_address_pattern
423             |
424             link\#\d+
425             )
426             /x;
427 0           $routes->{$1} = $2;
428             }
429 0           close $handle;
430              
431 0           return $routes;
432             }
433              
434             1;
435             __END__