File Coverage

blib/lib/Sys/Info/Driver/Linux/OS/Distribution.pm
Criterion Covered Total %
statement 147 209 70.3
branch 38 102 37.2
condition 13 53 24.5
subroutine 28 29 96.5
pod 9 9 100.0
total 235 402 58.4


line stmt bran cond sub pod time code
1             package Sys::Info::Driver::Linux::OS::Distribution;
2             $Sys::Info::Driver::Linux::OS::Distribution::VERSION = '0.7911';
3 3     3   327701 use strict;
  3         4  
  3         88  
4 3     3   12 use warnings;
  3         4  
  3         134  
5              
6 3     3   12 use constant STD_RELEASE => 'lsb-release';
  3         7  
  3         185  
7 3     3   10 use constant STD_RELEASE_DIR => 'lsb-release.d';
  3         4  
  3         92  
8 3     3   9 use constant DEBIAN_RELEASE => 'os-release';
  3         4  
  3         126  
9 3     3   11 use constant STD_ETC_DIR => '/etc';
  3         3  
  3         102  
10              
11 3     3   458 use parent qw( Sys::Info::Base );
  3         244  
  3         18  
12 3     3   22777 use Carp qw( croak );
  3         5  
  3         147  
13 3     3   755 use Sys::Info::Driver::Linux;
  3         6  
  3         142  
14 3     3   774 use Sys::Info::Driver::Linux::Constants qw( :all );
  3         5  
  3         450  
15 3     3   1409 use Sys::Info::Driver::Linux::OS::Distribution::Conf;
  3         13  
  3         533  
16 3     3   33 use File::Spec;
  3         7  
  3         8097  
17              
18             # XXX: <REMOVE>
19             my $RELX = sub {
20             my $master = shift;
21             my $t = sub {
22             my($k, $v) = @_;
23             return map { $_ => $v} ref $k ? @{$k} : ($k);
24             };
25             map { $t->($CONF{$_}->{$master}, $_ ) }
26             grep { $CONF{$_}->{$master} }
27             keys %CONF
28             };
29              
30             my %ORIGINAL_RELEASE = $RELX->('release');
31             my %DERIVED_RELEASE = $RELX->('release_derived');
32             #</REMOVE>
33              
34             sub new {
35 2     2 1 250380 my $class = shift;
36 2         5 my %option;
37 2 50       11 if ( @_ ) {
38 0 0       0 die "Parameters must be in name => value format" if @_ % 2;
39 0         0 %option = @_;
40             }
41              
42 2         22 my $self = {
43             DISTRIB_ID => q{},
44             DISTRIB_NAME => q{}, # workround field for new distros
45             DISTRIB_RELEASE => q{},
46             DISTRIB_CODENAME => q{},
47             DISTRIB_DESCRIPTION => q{},
48             release_file => q{},
49             pattern => q{},
50             PROBE => undef,
51             RESULTS => undef,
52             etc_dir => STD_ETC_DIR,
53             %option,
54             };
55              
56 2         13 $self->{etc_dir} =~ s{[/]+$}{}xms;
57              
58 2         5 bless $self, $class;
59 2         30 $self->_initial_probe;
60 2         36 return $self;
61             }
62              
63 3     3 1 10 sub raw_name { return shift->{RESULTS}{raw_name} }
64 6     6 1 37 sub name { return shift->{RESULTS}{name} }
65 4     4 1 13 sub version { return shift->{RESULTS}{version} }
66 1     1 1 2 sub edition { return shift->{RESULTS}{edition} }
67 1     1 1 2 sub kernel { return shift->{PROBE}{kernel} }
68 1     1 1 2 sub build { return shift->{PROBE}{build} }
69 1     1 1 2 sub build_date { return shift->{PROBE}{build_date} }
70             sub manufacturer {
71 1     1 1 1 my $self = shift;
72 1   50     3 my $slot = $CONF{ lc $self->raw_name } || return;
73 1 50       2 return if ! exists $slot->{manufacturer};
74 1         3 return $slot->{manufacturer};
75             }
76              
77             sub _probe {
78 2     2   5 my $self = shift;
79 2 50       11 return $self->{RESULTS} if $self->{RESULTS};
80 2         4 $self->{RESULTS} = {};
81 2         6 $self->{RESULTS}{name} = $self->_probe_name;
82 2         39 $self->{RESULTS}{raw_name} = $self->{RESULTS}{name};
83 2         9 $self->{RESULTS}{version} = $self->_probe_version;
84             # this has to be last, since this also modifies the two above
85 2         9 $self->{RESULTS}{edition} = $self->_probe_edition;
86 2         5 return $self->{RESULTS};
87             }
88              
89             sub _probe_name {
90 2     2   5 my $self = shift;
91 2         7 my $distro = $self->_get_lsb_info;
92 2 50       9 return $distro if $distro;
93 0   0     0 return $self->_probe_release( \%DERIVED_RELEASE )
94             || $self->_probe_release( \%ORIGINAL_RELEASE );
95             }
96              
97             sub _probe_release {
98 0     0   0 my($self, $r) = @_;
99              
100 0         0 foreach my $id ( keys %{ $r } ) {
  0         0  
101 0         0 my $file = File::Spec->catfile( $self->{etc_dir}, $id );
102 0 0 0     0 if ( -f $file && ! -l $file ) {
103 0         0 $self->{DISTRIB_ID} = $r->{ $id };
104 0         0 $self->{release_file} = $id;
105 0         0 return $self->{DISTRIB_ID};
106             }
107             }
108              
109 0         0 return;
110             }
111              
112             sub _probe_version {
113 2     2   5 my $self = shift;
114 2         6 my $release = $self->_get_lsb_info('DISTRIB_RELEASE');
115 2         6 my $dist_id = $self->{DISTRIB_ID};
116              
117 2 0 33     7 if ( ! $dist_id && ! $self->name ) {
118             # centos will return a string, but if couldn't detect the thing, it is
119             # better to return that instead.
120 0 0       0 return $release if $release;
121 0         0 croak 'No version because no distribution';
122             }
123              
124 2         8 my $slot = $CONF{ lc $dist_id };
125              
126             $self->{pattern} = exists $slot->{version_match}
127             ? $slot->{version_match}
128 2 50       10 : q{};
129              
130             my $slot_release = $slot->{release}
131             ? ref $slot->{release} eq 'ARRAY' ? $slot->{release}[0] : $slot->{release}
132             : undef
133 2 0       8 ;
    50          
134              
135 2 50       8 local $self->{release_file} = $slot_release if $slot_release;
136              
137 2         8 my $vrelease = $self->_get_file_info;
138              
139             # Set to the original if we got any, othwerwise try the version
140 2   33     9 $self->{DISTRIB_RELEASE} = $release || $vrelease;
141              
142             # Opposite of above as we want a version number
143             # if we were able locate one
144 2   33     15 return $vrelease || $release;
145             }
146              
147             sub _probe_edition {
148 2     2   5 my $self = shift;
149 2         4 my $p = $self->{PROBE};
150              
151 2 50       8 if ( my $dn = $self->name ) {
152 2   33     9 my $n = $self->{DISTRIB_NAME} || do {
153             my $slot = $CONF{ $dn };
154             exists $slot->{name} ? $slot->{name} : ucfirst $dn;
155             };
156 2         11 $dn = $self->trim( $n );
157 2 50       31 $dn .= ' Linux' if $dn !~ m{Linux}xmsi;
158 2         6 $self->{RESULTS}{name} = $dn;
159             }
160             else {
161 0         0 $self->{RESULTS}{name} = $p->{distro};
162 0         0 $self->{RESULTS}{version} = $p->{kernel};
163             }
164              
165 2         5 my $name = $self->name;
166 2         9 my $raw_name = $self->raw_name;
167 2         8 my $version = $self->version;
168 2   50     7 my $slot = $CONF{$raw_name} || return;
169              
170 2         19 my $int_version = int($version) . '.0';
171 2         24 my $edition;
172              
173 2 50       8 if ( exists $slot->{edition} ) {
174 2         4 my $this_ve = $slot->{edition}{ $version };
175 2 50       10 if ( $this_ve ) {
    50          
176 0         0 $edition = $this_ve;
177             }
178             elsif ( my $this_ie = $slot->{edition}{$int_version} ) {
179 0         0 $edition = $this_ie;
180             }
181             else {
182             # warn?
183             }
184             }
185              
186 2 50       9 if ( ! $edition ) {
187 2 50 33     17 if ( $version && $version !~ m{[0-9]}xms ) {
188 0 0       0 if ( $name =~ m{debian}xmsi ) {
189 0         0 my @buf = split m{/}xms, $version;
190 0 0       0 if ( my $test = $CONF{debian}->{vfix}{ lc $buf[0] } ) {
191             # Debian version comes as the edition name
192 0         0 $edition = $version;
193 0         0 $self->{RESULTS}{version} = $test;
194             }
195             }
196             }
197             else {
198 2 0 33     32 if ( $slot->{use_codename_for_edition}
199             && $self->{DISTRIB_CODENAME}
200             ) {
201 0         0 my $cn = $self->{DISTRIB_CODENAME};
202 0 0       0 $edition = $cn if $cn !~ m{[0-9]}xms;
203             }
204             }
205             }
206              
207 2         8 return $edition;
208             }
209              
210             sub _initial_probe {
211 2     2   4 my $self = shift;
212 2         4 my $version = q{};
213              
214 2 50 33     92 if ( -e proc->{version} && -f _) {
215             $version = $self->trim(
216             $self->slurp(
217             proc->{version},
218 2         31 'I can not open linux version file %s for reading: '
219             )
220             );
221             }
222              
223 2         479 my($str, $build_date) = split /\#/xms, $version;
224 2         6 my($kernel, $distro) = (q{},q{});
225              
226             #$build_date = "1 Fri Jul 23 20:48:29 CDT 2004';";
227             #$build_date = "1 SMP Mon Aug 16 09:25:06 EDT 2004";
228 2 50       9 $build_date = q{} if not $build_date; # running since blah thingie
229              
230 2 50 33     37 if ( $str =~ RE_LINUX_VERSION || $str =~ RE_LINUX_VERSION2 ) {
231 2         7 $kernel = $1;
232 2 50       7 if ( $distro = $self->trim( $2 ) ) {
233 2 50       41 if ( $distro =~ m{ \s\((.+?)\)\) \z }xms ) {
234 0         0 $distro = $1;
235             }
236             }
237             }
238              
239 2 50 33     16 $distro = 'Linux' if ! $distro || $distro =~ m{\(gcc}xms;
240              
241             # kernel build date
242 2 50       16 $build_date = $self->date2time($build_date) if $build_date;
243 2 50       12329 my $build = $build_date ? localtime $build_date : q{};
244              
245             $self->{PROBE} = {
246 2         28 version => $version,
247             kernel => $kernel,
248             build => $build,
249             build_date => $build_date,
250             distro => $distro,
251             };
252              
253 2         10 $self->_probe;
254 2         6 return;
255             }
256              
257             sub _get_lsb_info {
258 4     4   7 my $self = shift;
259 4   100     18 my $field = shift || 'DISTRIB_ID';
260 4         10 my $tmp = $self->{release_file};
261              
262 8         179 my($rfile) = grep { -r $_->[1] }
263             map {
264 4         10 [ $_ => File::Spec->catfile( $self->{etc_dir}, $_ ) ]
  8         150  
265             }
266             STD_RELEASE,
267             DEBIAN_RELEASE
268             ;
269              
270 4 50       17 if ( $rfile ) {
271 4         10 $self->{release_file} = $rfile->[0];
272 4         11 $self->{pattern} = $field . '=(.+)';
273 4         13 my $info = $self->_get_file_info;
274 4 50       22 return $self->{$field} = $info if $info;
275             }
276             else {
277             # CentOS6+? RHEL? Any new distro?
278 0         0 my $dir = File::Spec->catdir( $self->{etc_dir}, STD_RELEASE_DIR );
279 0 0       0 if ( -d $dir ) {
280             my $rv = join q{: },
281 0 0       0 map { m{$dir/(.*)}xms ? $1 : () }
282 0         0 grep { $_ !~ m{ \A [.] }xms }
  0         0  
283             glob "$dir/*";
284 0 0       0 $self->{LSB_VERSION} = $rv if $rv;
285             }
286 0         0 my($release) = do {
287 0 0       0 if ( my @files = glob $self->{etc_dir} . "/*release" ) {
288 0         0 my($real) = sort grep { ! -l } @files;
  0         0  
289 0         0 my %uniq = map { $self->trim( $self->slurp( $_ ) ) => 1 }
  0         0  
290             @files;
291 0 0       0 if ( $real ) {
292 0         0 my $etc = $self->{etc_dir};
293 0         0 ($self->{release_file} = $real) =~ s{$etc/}{}xms;
294 0         0 $self->{pattern} = '(.+)';
295             }
296 0         0 keys %uniq;
297             }
298             };
299              
300 0 0       0 return if ! $release; # huh?
301              
302 0         0 my($rname) = split m{\-}xms, $self->{release_file};
303 0         0 my($distrib_id, @rest) = split m{release}xms, $release, 2;
304 0         0 my($version, $codename) = split m{ \s+ }xms, $self->trim( join ' ', @rest ), 2;
305 0 0       0 $codename =~ s{[()]}{}xmsg if $codename;
306 0         0 $distrib_id = $self->trim( $distrib_id );
307 0         0 $self->{DISTRIB_DESCRIPTION} = $release;
308 0   0     0 $self->{DISTRIB_ID} = $rname || $distrib_id;
309 0         0 $self->{DISTRIB_NAME} = $distrib_id;
310 0         0 $self->{DISTRIB_RELEASE} = $version;
311 0   0     0 $self->{DISTRIB_CODENAME} = $codename || q{};
312              
313             # fix stupidity
314 0 0 0     0 if ( $self->{DISTRIB_ID}
      0        
      0        
315             && $self->{DISTRIB_ID} eq 'redhat'
316             && $self->{DISTRIB_NAME}
317             && index($self->{DISTRIB_NAME}, 'CentOS') != -1
318             ) {
319 0         0 $self->{DISTRIB_ID} = 'centos';
320             }
321              
322 0 0       0 return $self->{ $field } if $self->{ $field };
323             }
324              
325 0         0 $self->{release_file} = $tmp;
326 0         0 $self->{pattern} = q{};
327 0         0 return;
328             }
329              
330             sub _get_file_info {
331 6     6   12 my $self = shift;
332 6         78 my $file = File::Spec->catfile( $self->{etc_dir}, $self->{release_file} );
333 6         45 require IO::File;
334 6         46 my $FH = IO::File->new;
335 6 50       191 $FH->open( $file, '<' ) || croak "Can't open $file: $!";
336 6         494 my @raw = <$FH>;
337 6 50       27 $FH->close || croak "Can't close FH($file): $!";
338             my $new_pattern =
339             $self->{pattern} =~ m{ \A DISTRIB_ID \b }xms ? '^ID=(.+)'
340 6 100       177 : $self->{pattern} =~ m{ \A DISTRIB_RELEASE \b }xms ? '^PRETTY_NAME=(.+)'
    100          
341             : undef;
342 6         11 my $rv;
343 6         15 foreach my $line ( @raw ){
344 14         24 chomp $line;
345 14 50       26 next if ! $line;
346              
347             ## no critic (RequireExtendedFormatting)
348 14         139 my($info) = $line =~ m/$self->{pattern}/ms;
349 14 100       40 if ( $info ) {
    100          
350 4         11 $rv = "\L$info";
351 4         8 last;
352             }
353             elsif ( $new_pattern ) {
354             ## no critic (RequireExtendedFormatting)
355 2         23 my($info2) = $line =~ m/$new_pattern/ms;
356 2 50       8 if ( $info2 ) {
357 0         0 $rv = "\L$info2";
358 0         0 last;
359             }
360             }
361             }
362              
363 6 100       14 if ( $rv ) {
364 4         8 $rv =~ s{ \A ["] }{}xms;
365 4         8 $rv =~ s{ ["] \z }{}xms;
366             }
367              
368 6         35 return $rv;
369             }
370              
371             1;
372              
373             __END__
374              
375             =pod
376              
377             =encoding UTF-8
378              
379             =head1 NAME
380              
381             Sys::Info::Driver::Linux::OS::Distribution
382              
383             =head1 VERSION
384              
385             version 0.7911
386              
387             =head1 SYNOPSIS
388              
389             use Sys::Info::Driver::Linux::OS::Distribution;
390             my $distro = Sys::Info::Driver::Linux::OS::Distribution->new;
391             my $name = $distro->name;
392             if( $name ) {
393             my $version = $distro->version;
394             print "you are running $distro, version $version\n";
395             }
396             else {
397             print "distribution unknown\n";
398             }
399              
400             =head1 DESCRIPTION
401              
402             This is a simple module that tries to guess on what linux distribution
403             we are running by looking for release's files in /etc. It now looks for
404             'lsb-release' first as that should be the most correct and adds ubuntu support.
405             Secondly, it will look for the distro specific files.
406              
407             It currently recognizes slackware, debian, suse, fedora, redhat, turbolinux,
408             yellowdog, knoppix, mandrake, conectiva, immunix, tinysofa, va-linux, trustix,
409             adamantix, yoper, arch-linux, libranet, gentoo, ubuntu and redflag.
410              
411             It has function to get the version for debian, suse, redhat, gentoo, slackware,
412             redflag and ubuntu(lsb). People running unsupported distro's are greatly
413             encouraged to submit patches.
414              
415             =head1 NAME
416              
417             Sys::Info::Driver::Linux::OS::Distribution - Linux distribution probe
418              
419             =head1 METHODS
420              
421             =head2 build
422              
423             =head2 build_date
424              
425             =head2 edition
426              
427             =head2 kernel
428              
429             =head2 manufacturer
430              
431             =head2 name
432              
433             =head2 new
434              
435             =head2 raw_name
436              
437             =head2 version
438              
439             =head1 TODO
440              
441             Add the capability of recognize the version of the distribution for all
442             recognized distributions.
443              
444             =head1 Linux::Distribution AUTHORS
445              
446             Some parts of this module were originally taken from C<Linux::Distribution>
447             and it's authors are:
448              
449             Alberto Re E<lt>alberto@accidia.netE<gt>
450             Judith Lebzelter E<lt>judith@osdl.orgE<gt>
451             Alexandr Ciornii E<lt>alexchorny@gmail.com<gt>
452              
453             =head1 AUTHOR
454              
455             Burak Gursoy
456              
457             =head1 COPYRIGHT AND LICENSE
458              
459             This software is copyright (c) 2006 by Burak Gursoy.
460              
461             This is free software; you can redistribute it and/or modify it under
462             the same terms as the Perl 5 programming language system itself.
463              
464             =cut