File Coverage

blib/lib/Sys/Info/Driver/Linux/OS/Distribution.pm
Criterion Covered Total %
statement 140 202 69.3
branch 34 94 36.1
condition 11 53 20.7
subroutine 28 29 96.5
pod 9 9 100.0
total 222 387 57.3


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