File Coverage

blib/lib/Sys/Facter.pm
Criterion Covered Total %
statement 12 64 18.7
branch 0 18 0.0
condition n/a
subroutine 4 10 40.0
pod 4 4 100.0
total 20 96 20.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =head1 NAME
4              
5             Sys::Facter - collect facts about operating system
6              
7             =head1 SYNOPSIS
8              
9             use Sys::Facter;
10             use Data::Dumper;
11              
12             my $facter = new Sys::Facter(modules => ["/some/where"]);
13              
14             # load some facts manually and print them
15             $facter->load("kernel", "lsbrelease", "lsbid");
16             print Dumper $facter->facts;
17              
18             # print some facts (they'll be loaded automatically)
19             print $facter->hostname;
20             print $facter->get("memorytotal");
21              
22             =head1 DESCRIPTION
23              
24             This module is a wrapper over Pfacter. Pfacter is a Facter
25             (L) port to Perl.
26              
27             The idea is to have a set of modules that detect various things about the host
28             operating system, but also to easily extend this set by new, possibly
29             user-written, modules. This is achieved through defining an API for additional
30             plugins.
31              
32             Pfacter specifies some API for plugins, but completely lacks documentation,
33             and usage in Perl code is troublesome. This module simplifies Pfacter usage
34             while preserving its API (somebody could already have some plugins written).
35              
36             You can find a plugin API specification in this document, in
37             L section.
38              
39             =cut
40              
41             #-----------------------------------------------------------------------------
42              
43             package Sys::Facter;
44              
45 1     1   79809 use warnings;
  1         3  
  1         70  
46 1     1   7 use strict;
  1         3  
  1         160  
47              
48 1     1   1229 use POSIX qw{uname};
  1         35506  
  1         9  
49 1     1   1493 use Carp;
  1         3  
  1         1029  
50              
51             #-----------------------------------------------------------------------------
52              
53             our $VERSION = '1.01';
54              
55             #-----------------------------------------------------------------------------
56              
57             =head1 METHODS
58              
59             Following methods are available:
60              
61             =over
62              
63             =cut
64              
65             #-----------------------------------------------------------------------------
66              
67             =item C
68              
69             Constructor.
70              
71             Following options are honoured:
72              
73             =over
74              
75             =item C<< modules => [...] >> (optional)
76              
77             List of directories to be searched additionally to C<@INC> for Pfacter
78             modules.
79              
80             These directories have the precedence over C<@INC> and are searched in the
81             order of appearance.
82              
83             Plugins in these directories should be placed under F subdirectory,
84             as it would be for C<@INC> directories.
85              
86             =back
87              
88             =cut
89              
90             sub new {
91 0     0 1   my ($class, %opts) = @_;
92              
93 0 0         my @modules = @{ $opts{modules} || [] };
  0            
94              
95 0           my ($hostname, $domainname) = split /\./, (uname)[1], 2;
96              
97 0           my $self = bless {
98             pfact => {
99             # the first and only pfact detected here
100             kernel => (uname)[0],
101             hostname => $hostname,
102             },
103             var => {
104             modules => \@modules,
105             loaded => {
106             kernel => 1,
107             hostname => 1,
108             },
109             },
110             }, $class;
111              
112 0 0         if (defined $domainname) {
113 0           $self->{pfact}{domain} = $domainname;
114 0           $self->{var}{loaded}{domain} = 1;
115             }
116              
117 0           $self->load(qw{operatingsystem domain});
118              
119 0           return $self;
120             }
121              
122             #-----------------------------------------------------------------------------
123              
124             =item C
125              
126             Load and cache specified facts.
127              
128             If you don't specify any facts at all, Sys::Facter will load all of them.
129              
130             =cut
131              
132             sub load {
133 0     0 1   my ($self, @facts) = @_;
134              
135             # XXX: yes, this is lower case
136 0           my @invalid = grep { not m{^[a-z0-9_]+$} } @facts;
  0            
137 0 0         if (@invalid) {
138 0           croak "Invalid fact names: @invalid\n";
139             }
140              
141 0 0         if (not @facts) {
142 0 0         @facts = map { (split m{[/.]})[-2] }
  0            
143 0           grep { m{/[a-z0-9_]+\.pm$} && -f $_ }
144 0           map { glob "$_/Pfacter/*.pm" }
145 0           @{ $self->{var}{modules} }, @INC;
146             }
147              
148 0           for my $fact (grep { not $self->{var}{loaded}{$_} } @facts) {
  0            
149 0           my $module = "Pfacter::$fact";
150 0           my ($file) = grep { -f $_ }
  0            
151 0           map { "$_/Pfacter/$fact.pm" }
152 0           @{ $self->{var}{modules} }, @INC;
153              
154 0 0         if (not defined $file) {
155 0           carp "Couldn't load fact `$fact'";
156 0           next;
157             }
158              
159 0           my $result = eval { require $file; $module->pfact($self) };
  0            
  0            
160 0 0         die $@ if $@;
161              
162 0           $self->{var}{loaded}{$fact} = 1;
163              
164 0 0         if ($result) {
165 0           chomp $result;
166 0           $self->{pfact}{$fact} = $result;
167             }
168             }
169             }
170              
171             #-----------------------------------------------------------------------------
172              
173             =item C
174              
175             Return currently loaded facts as a %hashmap.
176              
177             =cut
178              
179             sub facts {
180 0     0 1   my ($self) = @_;
181              
182 0           return $self->{pfact};
183             }
184              
185             #-----------------------------------------------------------------------------
186              
187             =item C
188              
189             Return the value of specified fact, loading it if necessary.
190              
191             =cut
192              
193             sub get {
194 0     0 1   my ($self, $fact) = @_;
195              
196 0           $self->load($fact);
197 0           return $self->{pfact}{$fact};
198             }
199              
200             #-----------------------------------------------------------------------------
201              
202             =item C<${fact_name}()>
203              
204             For convenience, facts can be accessed with methods named by their names. For
205             example, C<< $facter->get("kernel") >> is equivalent to
206             C<< $facter->kernel >>.
207              
208             Of course, facts called "get", "new", "facts" and "load" can't be fetched this
209             way, but from these only "load" could be useful name.
210              
211             =back
212              
213             =cut
214              
215             #-----------------------------------------------------------------------------
216              
217             sub AUTOLOAD {
218 0     0     my ($self) = @_;
219              
220 0           our $AUTOLOAD;
221 0           my $fact = (split /::/, $AUTOLOAD)[-1];
222              
223             # it's a proper fact name
224 0 0         if ($fact =~ m{^[a-z0-9_]+$}) {
225 0           return $self->get($fact);
226             }
227              
228 0           croak "Unknown method $AUTOLOAD for object @{[ref $self]}";
  0            
229             }
230              
231             sub DESTROY {
232 0     0     my ($self) = @_;
233              
234             # nuffin();
235             }
236              
237             #-----------------------------------------------------------------------------
238              
239             =head1 FACT PLUGIN API
240              
241             Pfacter doesn't provide an API documentation, so this is a short reference.
242              
243             Pfacter plugin is a separate Perl module of name C.
244             C<${plugin_name}> is all-lowercase with numbers and underscore (it should
245             match regexp C).
246              
247             The module needs to have C function defined. This function has two
248             arguments provided: package name and a %hash-like object that contains
249             "pfact" key with facts hashmap. This can be used to determine way of
250             collecting facts about the system.
251              
252             C function is expected to return a single-line string. If it returns
253             non-TRUE value, the fact is considered to be loaded but non-applicable to this
254             system and will not be listed in C<< $facter->facts() >>.
255              
256             Example module:
257              
258             package Pfacter::example;
259              
260             sub pfact {
261             my ($pkg, $facter) = @_;
262             my $facts = $facter->{pfact};
263              
264             if ($facts->{kernel} eq "Linux") {
265             return "single-line";
266             } else {
267             return undef;
268             }
269             }
270              
271             # remember to return TRUE
272             1;
273              
274             Note that while C<$facter> in code above will be C reference, the
275             plugin should not use anything except C<< $facter->{pfact} >> field. This is
276             to keep compatibility with original F command line tool.
277              
278             Modules may assume that following facts are pre-loaded:
279              
280             =over
281              
282             =item - C
283              
284             Under Linux it will be "Linux"
285              
286             =item - C
287              
288             Under Linux it could be "Debian", "RedHat", "Gentoo", "SuSE" or similar.
289              
290             =item - C
291              
292             Host name, up to (but not including) first dot, if any.
293              
294             =item - C
295              
296             Domain name. If output of C contains dots, everything after first
297             dot. Otherwise, autodetected.
298              
299             =back
300              
301             =cut
302              
303             #-----------------------------------------------------------------------------
304              
305             =head1 AUTHOR
306              
307             Stanislaw Klekot, C<< >>
308              
309             =head1 BUGS
310              
311             Please report any bugs or feature requests to C, or
312             through the web interface at
313             L. I will be
314             notified, and then you'll automatically be notified of progress on your bug as
315             I make changes.
316              
317             =head1 LICENSE AND COPYRIGHT
318              
319             Copyright 2012 Stanislaw Klekot.
320              
321             This program is free software; you can redistribute it and/or modify it
322             under the terms of either: the GNU General Public License as published
323             by the Free Software Foundation; or the Artistic License.
324              
325             See http://dev.perl.org/licenses/ for more information.
326              
327             =head1 SEE ALSO
328              
329             L, L
330              
331             =cut
332              
333             #-----------------------------------------------------------------------------
334             1;
335             # vim:ft=perl