File Coverage

blib/lib/System/Info/Linux.pm
Criterion Covered Total %
statement 177 204 86.7
branch 88 128 68.7
condition 44 74 59.4
subroutine 15 16 93.7
pod 11 11 100.0
total 335 433 77.3


line stmt bran cond sub pod time code
1             package System::Info::Linux;
2              
3 3     3   108267 use strict;
  3         16  
  3         87  
4 3     3   15 use warnings;
  3         5  
  3         75  
5              
6 3     3   16 use base "System::Info::Base";
  3         19  
  3         7697  
7              
8             our $VERSION = "0.052";
9              
10             =head1 NAME
11              
12             System::Info::Linux - Object for specific Linux info.
13              
14             =head1 DESCRIPTION
15              
16             =head2 $si->prepare_sysinfo
17              
18             Use os-specific tools to find out more about the system.
19              
20             =cut
21              
22             sub prepare_sysinfo {
23 114     114 1 163 my $self = shift;
24 114         359 $self->SUPER::prepare_sysinfo;
25 114         370 $self->prepare_os;
26 114 50       460 $self->prepare_proc_cpuinfo or return;
27              
28 114         599 for ($self->get_cpu_type) {
29 114 100       516 m/arm/ and do { $self->linux_arm; last };
  3         39  
  3         7  
30 111 50       246 m/aarch64/ and do { $self->linux_arm; last };
  0         0  
  0         0  
31 111 100       220 m/ppc/ and do { $self->linux_ppc; last };
  1         28  
  1         4  
32 110 50       213 m/sparc/ and do { $self->linux_sparc; last };
  0         0  
  0         0  
33 110 100       237 m/s390x/ and do { $self->linux_s390x; last };
  2         31  
  2         5  
34             # default
35 108         272 $self->linux_generic;
36             }
37 114         353 return $self;
38             } # prepare_sysinfo
39              
40             =head2 $si->prepare_os
41              
42             Use os-specific tools to find out more about the operating system.
43              
44             =cut
45              
46             sub _file_info {
47 458     458   955 my ($file, $os) = @_;
48 458 50       15175 open my $fh, "<", $file or return;
49 458         15442 while (<$fh>) {
50 1911 100       5852 m/^\s*[;#]/ and next;
51 1899         2840 chomp;
52 1899 100       5364 m/\S/ or next;
53 1740         3524 s/^\s+//;
54 1740         3154 s/\s+$//;
55 1740 100       8142 if (my ($k, $v) = (m/^(.*\S)\s*=\s*(\S.*)$/)) {
56             # Having a value prevails over being defined
57 1327 100       2973 defined $os->{$k} and next;
58 1319         4920 $v =~ s/^"\s*(.*?)\s*"$/$1/;
59 1319 100       3090 $v =~ m{^["(]?undef(?:ined)?[")]$}i and $v = "undefined";
60 1319         3118 $os->{$k} = $v;
61 1319         4923 next;
62             }
63 413 100       1001 m/^[12][0-9]{3}(?:,\s*[12][0-9]{3})*$/ and next; # Copyright years
64 411 100       3507 exists $os->{$_} or $os->{$_} = undef;
65             }
66 458         5691 close $fh;
67             } # _file_info
68              
69             sub _lsb_release {
70 114     114   196 my $os = shift;
71              
72 114 100       354 $ENV{SMOKE_USE_ETC} and return;
73              
74             $os->{DISTRIB_ID} || $os->{DISTRIB_RELEASE} || $os->{DISTRIB_CODENAME}
75 15 0 33     37 or return;
      0        
76              
77             #use DP;die DDumper $os;
78 15 50       9800 open my $ch, "lsb_release -a 2>&1 |" or return;
79 0         0 my %map = (
80             "LSB Version" => "don't care",
81             "Distributor ID" => "DISTRIB_ID",
82             "Description" => "DISTRIB_DESCRIPTION",
83             "Release" => "DISTRIB_RELEASE",
84             "Code" => "DISTRIB_CODENAME",
85             );
86 0         0 while (<$ch>) {
87 0         0 chomp;
88 0 0       0 m/^\s*(\S.*?)\s*:\s*(.*?)\s*$/ or next;
89 0 0 0     0 $os->{$map{$1} || $1} ||= $2 unless $2 eq "n/a";
      0        
90             }
91             } # _lsb_release
92              
93             sub prepare_os {
94 114     114 1 187 my $self = shift;
95              
96 114   100     357 my $etc = $ENV{SMOKE_USE_ETC} || "/etc";
97 950 100       11340 my @dist_file = grep { -f $_ && -s _ } map {
98 114 100       14835 -d $_ ? glob ("$_/*") : ($_)
  823         15565  
99             } glob ("$etc/*[-_][rRvV][eE][lLrR]*"), "$etc/issue",
100             "$etc.defaults/VERSION", "$etc/VERSION", "$etc/release";
101              
102 114         968 my $os = $self->_os;
103 114         244 my %os;
104             my $distro;
105 114         250 foreach my $df (@dist_file) {
106             # use "debian" out of /etc/debian-release
107 458 100 100     1962 unless (defined $distro or $df =~ m/\blsb-/) {
108 114         2241 ($distro = $df) =~ s{^$etc(?:\.defaults)?/}{}i;
109 114         716 $distro =~ s{[-_]?(?:release|version)\b}{}i;
110             }
111 458         1104 _file_info ($df, \%os);
112             }
113 114         498 _lsb_release (\%os);
114              
115 114 50       29459 keys %os or return;
116              
117 114         1133 foreach my $key (keys %os) {
118 1645         2568 my $KEY = uc $key;
119 1645 100       2836 defined $os{$key} or next;
120 1319 100       2614 exists $os{$KEY} or $os{$KEY} = $os{$key};
121             }
122              
123 114 100 66     566 if ($os{DISTRIB_DESCRIPTION}) {
    100 66        
    50 66        
    100 66        
    100          
    100          
124 26         163 $distro = $os{DISTRIB_DESCRIPTION};
125 26 100 100     868 $os{DISTRIB_CODENAME} && $distro !~ m{\b$os{DISTRIB_CODENAME}\b}i and
126             $distro .= " ($os{DISTRIB_CODENAME})";
127 26 100 100     832 if ($os{VERSION_ID} && $distro !~ m{\b$os{VERSION_ID}\b}i) {
    50 33        
128 1         5 $distro .= " $os{VERSION_ID}";
129             }
130             elsif ($os{DISTRIB_RELEASE} && $distro !~ m{\b$os{DISTRIB_RELEASE}\b}i) {
131 0         0 $distro .= " $os{DISTRIB_RELEASE}";
132             }
133             }
134             elsif ($os{PRETTY_NAME}) {
135 58         103 $distro = $os{PRETTY_NAME}; # "openSUSE 12.1 (Asparagus) (x86_64)"
136 58 100       128 if (my $vid = $os{VERSION_ID}) { # wheezy 7 => 7.2
137 57         83 my @rv;
138 57 100       1561 if (@rv = grep m{^$vid\.} => sort keys %os) {
139             # from /etc/debian_version
140 10 50       147 $rv[0] =~ m/^[0-9]+\.\w+$/ and
141             $distro =~ s/\b$vid\b/$rv[0]/;
142             }
143 57 100 66     2306 if (!@rv && defined $os{NAME} and # CentOS Linux 7 = CentOS Linux 7.1.1503
      100        
144             @rv = grep m{^$os{NAME} (?:(?:release|version)\s+)?$vid\.} => sort keys %os) {
145 13 50       217 if ($rv[0] =~ m/\s($vid\.[-.\w]+)/) {
146 13         38 my $vr = $1;
147 13         103 $distro =~ s/\s$vid\b/ $vr/;
148             }
149             }
150             }
151 58         194 $distro =~ s{\s*[-:/,]\s*Version\s*:?\s*}{ };
152 58         121 $distro =~ s/\)\s+\(\w+\)\s*$/)/; # remove architectural part
153 58         214 $distro =~ s/\s+\(?(?:i\d86|x86_64)\)?\s*$//; # i386 i486 i586 x86_64
154 58 100 100     636 $os{VERSION_ID} && $distro !~ m{\b$os{VERSION_ID}\b}i and
155             $distro .= " $os{VERSION_ID}";
156             }
157             elsif ($os{VERSION} && $os{NAME}) {
158 0         0 $distro = qq{$os{NAME} $os{VERSION}};
159             }
160             elsif ($os{VERSION} && $os{CODENAME}) {
161 1 50       41 if (my @welcome = grep s{^\s*Welcome\s+to\s+(\S*$distro\S*)\b.*}{$1}i => keys %os) {
162 1         5 $distro = $welcome[0];
163             }
164 1         5 $distro .= qq{ $os{VERSION}};
165 1 50       15 $distro =~ m/\b$os{CODENAME}\b/ or
166             $distro .= qq{ ($os{CODENAME})};
167             }
168             elsif ($os{MAJORVERSION} && defined $os{MINORVERSION}) {
169 10 50 33     316 -d "/usr/syno" || "@dist_file" =~ m{^\S*/VERSION$} and $distro .= "DSM";
170 10         34 $distro .= qq{ $os{MAJORVERSION}.$os{MINORVERSION}};
171 10 50       31 $os{BUILDNUMBER} and $distro .= qq{-$os{BUILDNUMBER}};
172 10 100       25 $os{SMALLFIXNUMBER} and $distro .= qq{-$os{SMALLFIXNUMBER}};
173             }
174             elsif ($os{DISTRIBVER} && exists $os{NETBSDSRCDIR}) {
175 2         9 (my $dv = $os{DISTRIBVER}) =~ tr{ ''"";}{}d;
176 2         7 $distro .= qq{ NetBSD $dv};
177             }
178             else {
179             # /etc/issue:
180             # Welcome to SUSE LINUX 10.0 "Prague" (i586) - Kernel \r (\l).
181             # Welcome to openSUSE 10.1 "Agama Lizard" (i586) - Kernel \r (\l).
182             # Welcome to openSUSE 10.2 (i586) - Kernel \r (\l).
183             # Welcome to openSUSE 10.2 "Basilisk Lizard" (X86-64) - Kernel \r (\l).
184             # Welcome to openSUSE 10.3 (i586) - Kernel \r (\l).
185             # Welcome to openSUSE 10.3 (X86-64) - Kernel \r (\l).
186             # Welcome to openSUSE 11.1 - Kernel \r (\l).
187             # Welcome to openSUSE 11.2 "Emerald" - Kernel \r (\l).
188             # Welcome to openSUSE 11.3 "Teal" - Kernel \r (\l).
189             # Welcome to openSUSE 11.4 "Celadon" - Kernel \r (\l).
190             # Welcome to openSUSE 12.1 "Asparagus" - Kernel \r (\l).
191             # Welcome to openSUSE 12.2 "Mantis" - Kernel \r (\l).
192             # Welcome to openSUSE 12.3 "Dartmouth" - Kernel \r (\l).
193             # Welcome to openSUSE 13.1 "Bottle" - Kernel \r (\l).
194             # Welcome to openSUSE 13.2 "Harlequin" - Kernel \r (\l).
195             # Welcome to openSUSE Leap 42.1 - Kernel \r (\l).
196             # Welcome to openSUSE 20151218 "Tumbleweed" - Kernel \r (\l).
197             # Welcome to SUSE Linux Enterprise Server 11 SP1 for VMware (x86_64) - Kernel \r (\l).
198             # Ubuntu 10.04.4 LTS \n \l
199             # Debian GNU/Linux wheezy/sid \n \l
200             # Debian GNU/Linux 6.0 \n \l
201             # CentOS release 6.4 (Final)
202             # /etc/redhat-release:
203             # CentOS release 5.7 (Final)
204             # CentOS release 6.4 (Final)
205             # Red Hat Enterprise Linux ES release 4 (Nahant Update 2)
206             # /etc/debian_version:
207             # 6.0.4
208             # wheezy/sid
209             # squeeze/sid
210              
211 17         83 my @key = sort keys %os;
212 17         143 s/\s*\\[rln].*// for @key;
213              
214 17         105 my @vsn = grep m/^[0-9.]+$/ => @key;
215             #$self->{__X__} = { os => \%os, key => \@key, vsn => \@vsn };
216              
217 17 100 0     209 if (my @welcome = grep s{^\s*Welcome\s+to\s+}{}i => @key) {
    100          
    50          
    0          
218 3         21 ($distro = $welcome[0]) =~ s/"([^"]+)"/($1)/;
219             }
220             elsif (my @rel = grep m{\brelease\b}i => @key) {
221 12 50 66     54 @rel > 1 && $rel[0] =~ m/^Enterprise Linux Enterprise/
      66        
222             && $rel[1] =~ m/^Oracle Linux/ and shift @rel;
223 12         25 $distro = $rel[0];
224 12         67 $distro =~ s/ *release//;
225 12         38 $distro =~ s/Red Hat Enterprise Linux/RHEL/; # Too long for subject
226             # RHEL ES 4 (Nahant Update 2) => RHEL Server 4.2 (Nahant)
227 12         35 $distro =~ s/^RHEL ES (\d+)\s+(.*)\s+Update\s+(\d+)/RHEL Server $1.$3 $2/;
228             }
229             elsif ( my @lnx = grep m{\bLinux\b}i => @key ) {
230 2         6 $distro = $lnx[0];
231             }
232             elsif ( $distro && @vsn ) {
233 0         0 $distro .= "-$vsn[0]";
234             }
235             else {
236 0         0 $distro = $key[0];
237             }
238 17         52 $distro =~ s/\s+-\s+Kernel.*//i;
239             }
240 114 50       1014 if ($distro =~ s/^\s*(.*\S)\s*$/$1/) {
241 114         589 $self->{__distro} = $distro;
242 114         441 $os .= " [$distro]";
243             }
244 114         274 $self->{__release_info} = \%os;
245 114         545 $self->{__os} = $os;
246             } # prepare_os
247              
248             =head2 $si->linux_generic
249              
250             Check C for these keys:
251              
252             =over
253              
254             =item "processor" (count occurrence for __cpu_count)
255              
256             =item "model name" (part of __cpu)
257              
258             =item "vendor_id" (part of __cpu)
259              
260             =item "cpu mhz" (part of __cpu)
261              
262             =item "cpu cores" (add values to add to __cpu_count)
263              
264             =back
265              
266             =cut
267              
268             sub linux_generic {
269 108     108 1 197 my $self = shift;
270              
271 108   100     665 my $n_phys_id = $self->count_unique_in_cpuinfo (qr/^physical id\s+:/) || 0;
272 108   100     490 my $n_core_id = $self->count_unique_in_cpuinfo (qr/^core id\s+:/) || 0;
273 108   50     436 my $n_processor = $self->count_unique_in_cpuinfo (qr/^processor\s+:/) || 0;
274 108   66     332 my $n_cpu = $n_phys_id || $n_core_id || $n_processor;
275              
276             # ::diag"Np: $n_phys_id, NC: $n_core_id, NP: $n_processor, NC: $n_cpu";
277 108         326 $self->{__cpu_count} = $n_cpu;
278              
279 108         296 my @parts = ("model name", "vendor_id", "cpu mhz");
280 108         180 my %info = map { ($_ => $self->from_cpuinfo ($_)) } @parts;
  324         725  
281 108         307 $self->{__cpu} = sprintf "%s (%s %.0fMHz)", map { $info{$_} } @parts;
  324         1362  
282              
283 108 100       339 if ($n_phys_id) {
284             $n_processor > $n_phys_id and
285 107 100       265 $self->{__cpu_count} .= " [$n_processor cores]";
286 107         419 return;
287             }
288 1 50       4 if ($n_core_id) {
289             $n_processor > $n_core_id and
290 0 0       0 $self->{__cpu_count} .= " [$n_processor cores]";
291 0         0 return;
292             }
293              
294 1         4 my $n_cores = 0;
295 1         13 my $core_id = 0;
296 1         2 my %cores;
297 1         7 for my $cores (grep m/(cpu cores|core id)\s*:\s*\d+/ => $self->_proc_cpuinfo) {
298 0 0       0 my ($tag, $count) = $cores =~ m/^(.*\S)\s*:\s*(\d+)/ or next;
299 0 0       0 if ($tag eq "core id") {
300 0         0 $core_id = $count;
301             }
302             else {
303 0         0 $cores{$core_id} = $count;
304             }
305             }
306 1         8 $n_cores += $cores{$_} for keys %cores;
307              
308 1 50       7 $n_cores > $n_cpu and $self->{__cpu_count} .= " [$n_cores cores]";
309             } # _linux_generic
310              
311             =head2 $si->linux_arm
312              
313             Check C for these keys:
314              
315             =over
316              
317             =item "processor" (count occurrence for __cpu_count)
318              
319             =item "Processor" (part of __cpu)
320              
321             =item "BogoMIPS" (part of __cpu)
322              
323             =back
324              
325             =cut
326              
327             sub linux_arm {
328 3     3 1 7 my $self = shift;
329              
330 3         54 $self->{__cpu_count} = $self->count_in_cpuinfo (qr/^processor\s+:\s+/i);
331              
332 3   66     23 my $cpu = $self->from_cpuinfo ("Processor") ||
333             $self->from_cpuinfo ("Model[_ ]name");
334 3         17 my $bogo = $self->from_cpuinfo ("BogoMIPS");
335 3         52 my $mhz = 100 * int (($bogo + 50) / 100);
336 3         32 $cpu =~ s/\s+/ /g;
337 3 50       31 $mhz and $cpu .= " ($mhz MHz)";
338 3         11 $self->{__cpu} = $cpu;
339             } # _linux_arm
340              
341             =head2 $si->linux_ppc
342              
343             Check C for these keys:
344              
345             =over
346              
347             =item "processor" (count occurrence for __cpu_count)
348              
349             =item "cpu" (part of __cpu)
350              
351             =item "machine" (part of __cpu)
352              
353             =item "clock" (part of __cpu)
354              
355             =item "detected" (alters machine if present)
356              
357             =back
358              
359             =cut
360              
361             sub linux_ppc {
362 1     1 1 12 my $self = shift;
363              
364 1         29 $self->{__cpu_count} = $self->count_in_cpuinfo (qr/^processor\s+:\s+/);
365              
366 1         16 my @parts = qw( cpu machine clock );
367 1         4 my %info = map { ($_ => $self->from_cpuinfo ($_)) } @parts;
  3         11  
368 1 50       17 if ($info{detected} = $self->from_cpuinfo ("detected as")){
369 1         22 $info{detected} =~ s/.*(\b.+Mac G\d).*/$1/;
370 1         6 $info{machine} = $info{detected};
371             }
372              
373 1         11 $self->{__cpu} = sprintf "%s %s (%s)", map { $info{$_} } @parts;
  3         24  
374             } # linux_ppc
375              
376             =head2 $si->linux_sparc
377              
378             Check C for these keys:
379              
380             =over
381              
382             =item "processor" (count occurrence for __cpu_count)
383              
384             =item "cpu" (part of __cpu)
385              
386             =item "Cpu0ClkTck" (part of __cpu)
387              
388             =back
389              
390             =cut
391              
392             sub linux_sparc {
393 0     0 1 0 my $self = shift;
394              
395 0         0 $self->{__cpu_count} = $self->from_cpuinfo ("ncpus active");
396              
397 0         0 my @parts = qw( cpu Cpu0ClkTck );
398 0         0 my %info = map { ($_ => $self->from_cpuinfo ($_)) } @parts;
  0         0  
399 0         0 my $cpu = $info{cpu};
400             $info{Cpu0ClkTck} and
401 0 0       0 $cpu .= sprintf " (%.0fMHz)", hex ($info{Cpu0ClkTck}) / 1_000_000;
402 0         0 $self->{__cpu} = $cpu;
403             } # linux_sparc
404              
405             =head2 $si->linux_s390x
406              
407             Check C for these keys:
408              
409             =over
410              
411             =item "processor" (count occurrence for __cpu_count)
412              
413             =item "Processor" (part of __cpu)
414              
415             =item "BogoMIPS" (part of __cpu)
416              
417             =back
418              
419             =cut
420              
421             sub linux_s390x {
422 2     2 1 6 my $self = shift;
423              
424 2         31 $self->{__cpu_count} = $self->count_in_cpuinfo (qr/^processor\s+\d+:\s+/i);
425              
426 2   33     16 my $cpu = $self->from_cpuinfo ("vendor_id") ||
427             $self->from_cpuinfo ("Processor") ||
428             $self->from_cpuinfo ("Model[_ ]name");
429 2         15 my $bogo = $self->from_cpuinfo (qr{BogoMIPS(?:\s*per[ _]CPU)?}i);
430 2         28 my $mhz = 100 * int (($bogo + 50) / 100);
431 2         9 $cpu =~ s/\s+/ /g;
432 2 50       14 $mhz and $cpu .= " ($mhz MHz)";
433 2         11 $self->{__cpu} = $cpu;
434             } # _linux_s390x
435              
436             =head2 $si->prepare_proc_cpuinfo
437              
438             Read the complete C<< /proc/cpuinfo >>.
439              
440             =cut
441              
442             sub prepare_proc_cpuinfo {
443 114     114 1 200 my $self = shift;
444              
445 114 50       3838 if (open my $pci, "<", "/proc/cpuinfo") {
446 114         42896 chomp (my @pci = <$pci>);
447 114         119553 s/[\s\xa0]+/ /g for @pci;
448 114         8777 s/ $// for @pci;
449 114         335 $self->{__proc_cpuinfo} = \@pci;
450 114         1395 close $pci;
451 114         870 return 1;
452             }
453             } # prepare_proc_cpuinfo
454              
455             =head2 $si->count_in_cpuinfo ($regex)
456              
457             Returns the number of lines $regex matches for.
458              
459             =cut
460              
461             sub count_in_cpuinfo {
462 6     6 1 45 my ($self, $regex) = @_;
463              
464 6         482 return scalar grep /$regex/, $self->_proc_cpuinfo;
465             } # count_in_cpuinfo
466              
467             =head2 $si->count_unique_in_cpuinfo ($regex)
468              
469             Returns the number of lines $regex matches for.
470              
471             =cut
472              
473             sub count_unique_in_cpuinfo {
474 324     324 1 760 my ($self, $regex) = @_;
475              
476 324         1431 my %match = map { $_ => 1 } grep /$regex/ => $self->_proc_cpuinfo;
  5059         9143  
477 324         6684 return scalar keys %match;
478             } # count_unique_in_cpuinfo
479              
480             =head2 $si->from_cpuinfo ($key)
481              
482             Returns the first value of that key in C<< /proc/cpuinfo >>.
483              
484             =cut
485              
486             sub from_cpuinfo {
487 339     339 1 727 my ($self, $key) = @_;
488              
489 339         1581 my ($first) = grep m/^\s*$key\s*[:=]\s*/i => $self->_proc_cpuinfo;
490 339 50       6156 defined $first or $first = "";
491 339         8481 $first =~ s/^\s*$key\s*[:=]\s*//i;
492 339         1964 return $first;
493             } # from_cpuinfo
494              
495             1;
496              
497             __END__