File Coverage

blib/lib/FusionInventory/Agent/Tools/Linux.pm
Criterion Covered Total %
statement 24 205 11.7
branch 0 132 0.0
condition 0 9 0.0
subroutine 8 18 44.4
pod 6 6 100.0
total 38 370 10.2


line stmt bran cond sub pod time code
1             package FusionInventory::Agent::Tools::Linux;
2              
3 15     15   8943143 use strict;
  15         29  
  15         624  
4 15     15   81 use warnings;
  15         23  
  15         570  
5 15     15   84 use base 'Exporter';
  15         51  
  15         1648  
6              
7 15     15   80 use English qw(-no_match_vars);
  15         23  
  15         146  
8 15     15   9061 use Memoize;
  15         10740  
  15         934  
9              
10 15     15   1478 use FusionInventory::Agent::Tools;
  15         24  
  15         2304  
11 15     15   6842 use FusionInventory::Agent::Tools::Unix;
  15         37  
  15         1369  
12 15     15   102 use FusionInventory::Agent::Tools::Network;
  15         19  
  15         47140  
13              
14             our @EXPORT = qw(
15             getDevicesFromUdev
16             getDevicesFromHal
17             getDevicesFromProc
18             getCPUsFromProc
19             getInfoFromSmartctl
20             getInterfacesFromIfconfig
21             getInterfacesFromIp
22             );
23              
24             memoize('getDevicesFromUdev');
25              
26             sub getDevicesFromUdev {
27             my (%params) = @_;
28              
29             my @devices;
30              
31             foreach my $file (glob ("/dev/.udev/db/*")) {
32             my $device = getFirstMatch(
33             file => $file,
34             pattern => qr/^N:(\S+)/
35             );
36             next unless $device;
37             next unless $device =~ /([hsv]d[a-z]|sr\d+)$/;
38             push (@devices, _parseUdevEntry(
39             logger => $params{logger}, file => $file, device => $device
40             ));
41             }
42              
43             foreach my $device (@devices) {
44             next if $device->{TYPE} && $device->{TYPE} eq 'cd';
45             $device->{DISKSIZE} = getDeviceCapacity(device => '/dev/' . $device->{NAME})
46             }
47              
48             return @devices;
49             }
50              
51             sub _parseUdevEntry {
52 0     0     my (%params) = @_;
53              
54 0           my $handle = getFileHandle(%params);
55 0 0         return unless $handle;
56              
57 0           my ($result, $serial);
58 0           while (my $line = <$handle>) {
59 0 0         if ($line =~ /^S:.*-scsi-(\d+):(\d+):(\d+):(\d+)/) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
60 0           $result->{SCSI_COID} = $1;
61 0           $result->{SCSI_CHID} = $2;
62 0           $result->{SCSI_UNID} = $3;
63 0           $result->{SCSI_LUN} = $4;
64             } elsif ($line =~ /^E:ID_VENDOR=(.*)/) {
65 0           $result->{MANUFACTURER} = $1;
66             } elsif ($line =~ /^E:ID_MODEL=(.*)/) {
67 0           $result->{MODEL} = $1;
68             } elsif ($line =~ /^E:ID_REVISION=(.*)/) {
69 0           $result->{FIRMWARE} = $1;
70             } elsif ($line =~ /^E:ID_SERIAL=(.*)/) {
71 0           $serial = $1;
72             } elsif ($line =~ /^E:ID_SERIAL_SHORT=(.*)/) {
73 0           $result->{SERIALNUMBER} = $1;
74             } elsif ($line =~ /^E:ID_TYPE=(.*)/) {
75 0           $result->{TYPE} = $1;
76             } elsif ($line =~ /^E:ID_BUS=(.*)/) {
77 0           $result->{DESCRIPTION} = $1;
78             }
79             }
80 0           close $handle;
81              
82 0 0         if (!$result->{SERIALNUMBER}) {
83 0           $result->{SERIALNUMBER} = $serial;
84             }
85              
86 0           $result->{NAME} = $params{device};
87              
88 0           return $result;
89             }
90              
91             sub getCPUsFromProc {
92 0     0 1   my (%params) = (
93             file => '/proc/cpuinfo',
94             @_
95             );
96              
97 0           my $handle = getFileHandle(%params);
98              
99 0           my (@cpus, $cpu);
100              
101 0           while (my $line = <$handle>) {
102 0 0         if ($line =~ /^([^:]+\S) \s* : \s (.+)/x) {
    0          
103 0           $cpu->{lc($1)} = trimWhitespace($2);
104             } elsif ($line =~ /^$/) {
105             # an empty line marks the end of a cpu section
106             # push to the list, but only if it is a valid cpu
107 0 0 0       push @cpus, $cpu if $cpu && _isValidCPU($cpu);
108 0           undef $cpu;
109             }
110             }
111 0           close $handle;
112              
113             # push remaining cpu to the list, if it is valid cpu
114 0 0 0       push @cpus, $cpu if $cpu && _isValidCPU($cpu);
115              
116 0           return @cpus;
117             }
118              
119             sub _isValidCPU {
120 0     0     my ($cpu) = @_;
121              
122 0   0       return exists $cpu->{processor} || exists $cpu->{cpu};
123             }
124              
125              
126             sub getDevicesFromHal {
127 0     0 1   my (%params) = (
128             command => '/usr/bin/lshal',
129             @_
130             );
131 0           my $handle = getFileHandle(%params);
132              
133 0           my (@devices, $device);
134              
135 0           while (my $line = <$handle>) {
136 0           chomp $line;
137 0 0         if ($line =~ m{^udi = '/org/freedesktop/Hal/devices/(storage|legacy_floppy|block)}) {
138 0           $device = {};
139 0           next;
140             }
141              
142 0 0         next unless defined $device;
143              
144 0 0         if ($line =~ /^$/) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
145 0           push(@devices, $device);
146 0           undef $device;
147             } elsif ($line =~ /^\s+ storage.serial \s = \s '([^']+)'/x) {
148 0           $device->{SERIALNUMBER} = $1;
149             } elsif ($line =~ /^\s+ storage.firmware_version \s = \s '([^']+)'/x) {
150 0           $device->{FIRMWARE} = $1;
151             } elsif ($line =~ /^\s+ block.device \s = \s '([^']+)'/x) {
152 0           my $value = $1;
153 0           ($device->{NAME}) = $value =~ m{/dev/(\S+)};
154             } elsif ($line =~ /^\s+ info.vendor \s = \s '([^']+)'/x) {
155 0           $device->{MANUFACTURER} = $1;
156             } elsif ($line =~ /^\s+ storage.model \s = \s '([^']+)'/x) {
157 0           $device->{MODEL} = $1;
158             } elsif ($line =~ /^\s+ storage.drive_type \s = \s '([^']+)'/x) {
159 0           $device->{TYPE} = $1;
160             } elsif ($line =~ /^\s+ storage.size \s = \s (\S+)/x) {
161 0           my $value = $1;
162 0           $device->{DISKSIZE} = int($value/(1024*1024) + 0.5);
163             }
164             }
165 0           close $handle;
166              
167 0           return @devices;
168             }
169              
170             sub getDevicesFromProc {
171 0     0 1   my (%params) = @_;
172              
173 0           my $logger = $params{logger};
174              
175             # compute list of devices
176 0           my @names;
177              
178 0           foreach my $file (glob ("/sys/block/*")) {
179 0 0         next unless $file =~ /([shv]d[a-z]|fd\d)$/;
180 0           push @names, $1;
181             }
182              
183 0 0         my $command = getFirstLine(command => '/sbin/fdisk -v') =~ '^GNU' ?
184             "/sbin/fdisk -p -l" :
185             "/sbin/fdisk -l" ;
186              
187 0           my $handle = getFileHandle(
188             command => $command,
189             logger => $logger
190             );
191              
192 0 0         return unless $handle;
193              
194 0           while (my $line = <$handle>) {
195 0 0         next unless $line =~ m{^/dev/([shv]d[a-z])};
196 0           push @names, $1;
197             }
198 0           close $handle;
199              
200             # filter duplicates
201 0           my %seen;
202 0           @names = grep { !$seen{$_}++ } @names;
  0            
203              
204             # extract information
205 0           my @devices;
206 0           foreach my $name (@names) {
207 0 0         my $device = {
208             NAME => $name,
209             MANUFACTURER => _getValueFromSysProc($logger, $name, 'vendor'),
210             MODEL => _getValueFromSysProc($logger, $name, 'model'),
211             FIRMWARE => _getValueFromSysProc($logger, $name, 'rev'),
212             SERIALNUMBER => _getValueFromSysProc($logger, $name, 'serial'),
213             TYPE =>
214             _getValueFromSysProc($logger, $name, 'removable') ?
215             'removable' : 'disk'
216             };
217 0           push @devices, $device;
218             }
219              
220 0           return @devices;
221             }
222              
223             sub _getValueFromSysProc {
224 0     0     my ($logger, $device, $key) = @_;
225              
226             ## no critic (ExplicitReturnUndef)
227              
228 0 0         my $file =
    0          
229             -f "/sys/block/$device/device/$key" ? "/sys/block/$device/device/$key" :
230             -f "/proc/ide/$device/$key" ? "/proc/ide/$device/$key" :
231             undef;
232              
233 0 0         return undef unless $file;
234              
235 0           my $handle = getFileHandle(file => $file, logger => $logger);
236 0 0         return undef unless $handle;
237              
238 0           my $value = <$handle>;
239 0           close $handle;
240              
241 0           chomp $value;
242 0           $value =~ s/^(\w+)\W*/$1/;
243              
244 0           return $value;
245             }
246              
247             sub getInfoFromSmartctl {
248 0     0 1   my (%params) = @_;
249              
250 0 0         my $handle = getFileHandle(
251             %params,
252             command => $params{device} ? "smartctl -i $params{device}" : undef,
253             );
254 0 0         return unless $handle;
255              
256 0           my $info = {
257             TYPE => 'disk',
258             DESCRIPTION => 'SATA',
259             };
260              
261 0           while (my $line = <$handle>) {
262 0 0         if ($line =~ /^Vendor: +(\S+)/i) {
263 0           $info->{MANUFACTURER} = getCanonicalManufacturer($1);
264 0           next;
265             }
266              
267 0 0         if ($line =~ /^Product: +(\S+)/i) {
268 0           $info->{MODEL} = $1;
269 0           next;
270             }
271              
272 0 0         if ($line =~ /^Revision: +(\S+)/i) {
273 0           $info->{FIRMWARE} = $1;
274 0           next;
275             }
276              
277 0 0         if ($line =~ /^User Capacity: +(\S.+\S)/i) {
278 0           $info->{DISKSIZE} = getCanonicalSize($1, 1024);
279 0           next;
280             }
281              
282 0 0         if ($line =~ /^Transport protocol: +(\S+)/i) {
283 0           $info->{DESCRIPTION} = $1;
284 0           next;
285             }
286              
287 0 0         if ($line =~ /^Device type: +(\S+)/i) {
288 0           $info->{TYPE} = $1;
289 0           next;
290             }
291              
292 0 0         if ($line =~ /^Serial number: +(\S+)/i) {
293 0           $info->{SERIALNUMBER} = $1;
294 0           next;
295             }
296             }
297 0           close $handle;
298              
299 0           return $info;
300             }
301              
302             sub getInterfacesFromIfconfig {
303 0     0 1   my (%params) = (
304             command => '/sbin/ifconfig -a',
305             @_
306             );
307 0           my $handle = getFileHandle(%params);
308 0 0         return unless $handle;
309              
310 0           my @interfaces;
311             my $interface;
312              
313 0           my %types = (
314             Ethernet => 'ethernet',
315             );
316              
317 0           while (my $line = <$handle>) {
318 0 0         if ($line =~ /^$/) {
319             # end of interface section
320 0 0         push @interfaces, $interface if $interface;
321 0           next;
322             }
323              
324 0 0         if ($line =~ /^([\w\d.]+)/) {
325             # new interface
326              
327 0           $interface = {
328             STATUS => 'Down',
329             DESCRIPTION => $1
330             }
331              
332             }
333 0 0         if ($line =~ /
334             inet \s ($ip_address_pattern) \s+
335             netmask \s ($ip_address_pattern) \s+
336             broadcast \s $ip_address_pattern
337             /x) {
338 0           $interface->{IPADDRESS} = $1;
339 0           $interface->{IPMASK} = $2;
340             }
341              
342 0 0         if ($line =~ /
343             ether \s ($mac_address_pattern)
344             .+
345             \( Ethernet \)
346             /x) {
347 0           $interface->{MACADDR} = $1;
348 0           $interface->{TYPE} = 'ethernet';
349             }
350              
351 0 0         if ($line =~ /inet6 \s (\S+)/x) {
352 0           $interface->{IPADDRESS6} = $1;
353             }
354              
355 0 0         if ($line =~ /inet addr:($ip_address_pattern)/i) {
356 0           $interface->{IPADDRESS} = $1;
357             }
358              
359 0 0         if ($line =~ /Mask:($ip_address_pattern)/) {
360 0           $interface->{IPMASK} = $1;
361             }
362              
363 0 0         if ($line =~ /inet6 addr: (\S+)/i) {
364 0           $interface->{IPADDRESS6} = $1;
365             }
366              
367 0 0         if ($line =~ /hwadd?r\s+($mac_address_pattern)/i) {
368 0           $interface->{MACADDR} = $1;
369             }
370              
371 0 0         if ($line =~ /^\s+UP\s/) {
372 0           $interface->{STATUS} = 'Up';
373             }
374              
375 0 0         if ($line =~ /flags=.*[<,]UP[>,]/) {
376 0           $interface->{STATUS} = 'Up';
377             }
378              
379 0 0         if ($line =~ /Link encap:(\S+)/) {
380 0           $interface->{TYPE} = $types{$1};
381             }
382              
383             }
384 0           close $handle;
385              
386 0           return @interfaces;
387             }
388             sub getInterfacesFromIp {
389 0     0 1   my (%params) = (
390             command => '/sbin/ip addr show',
391             @_
392             );
393              
394 0           my $handle = getFileHandle(%params);
395 0 0         return unless $handle;
396              
397 0           my (@interfaces, @addresses, $interface);
398              
399 0           while (my $line = <$handle>) {
400 0 0         if ($line =~ /^\d+:\s+(\S+): <([^>]+)>/) {
    0          
    0          
    0          
401              
402 0 0         if (@addresses) {
    0          
403 0           push @interfaces, @addresses;
404 0           undef @addresses;
405             } elsif ($interface) {
406 0           push @interfaces, $interface;
407             }
408              
409 0           my ($name, $flags) = ($1, $2);
410             my $status =
411 0 0   0     (any { $_ eq 'UP' } split(/,/, $flags)) ? 'Up' : 'Down';
  0            
412              
413 0           $interface = {
414             DESCRIPTION => $name,
415             STATUS => $status
416             };
417             } elsif ($line =~ /link\/\S+ ($any_mac_address_pattern)?/) {
418 0           $interface->{MACADDR} = $1;
419             } elsif ($line =~ /inet6 (\S+)\/(\d{1,2})/) {
420 0           my $address = $1;
421 0           my $mask = getNetworkMaskIPv6($2);
422 0           my $subnet = getSubnetAddressIPv6($address, $mask);
423              
424 0           push @addresses, {
425             IPADDRESS6 => $address,
426             IPMASK6 => $mask,
427             IPSUBNET6 => $subnet,
428             STATUS => $interface->{STATUS},
429             DESCRIPTION => $interface->{DESCRIPTION},
430             MACADDR => $interface->{MACADDR}
431             };
432             } elsif ($line =~ /
433             inet \s
434             ($ip_address_pattern)(?:\/(\d{1,3}))? \s
435             .* \s
436             (\S+)$
437             /x) {
438 0           my $address = $1;
439 0           my $mask = getNetworkMask($2);
440 0           my $subnet = getSubnetAddress($address, $mask);
441 0           my $name = $3;
442              
443             # the name associated with the address differs from the current
444             # interface if the address is actually attached to an alias
445 0           push @addresses, {
446             IPADDRESS => $address,
447             IPMASK => $mask,
448             IPSUBNET => $subnet,
449             STATUS => $interface->{STATUS},
450             DESCRIPTION => $name,
451             MACADDR => $interface->{MACADDR}
452             };
453             }
454             }
455              
456 0 0         if (@addresses) {
    0          
457 0           push @interfaces, @addresses;
458 0           undef @addresses;
459             } elsif ($interface) {
460 0           push @interfaces, $interface;
461             }
462              
463 0           return @interfaces;
464             }
465              
466             1;
467             __END__