File Coverage

blib/lib/FusionInventory/Agent/Tools/Linux.pm
Criterion Covered Total %
statement 166 205 80.9
branch 102 132 77.2
condition 8 9 88.8
subroutine 16 18 88.8
pod 6 6 100.0
total 298 370 80.5


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