File Coverage

blib/lib/Linux/Info.pm
Criterion Covered Total %
statement 62 80 77.5
branch 20 36 55.5
condition 2 8 25.0
subroutine 9 12 75.0
pod 6 6 100.0
total 99 142 69.7


line stmt bran cond sub pod time code
1             package Linux::Info;
2 14     14   849508 use strict;
  14         133  
  14         397  
3 14     14   66 use warnings;
  14         26  
  14         427  
4 14     14   82 use Carp qw(confess);
  14         32  
  14         828  
5 14     14   6833 use POSIX qw(strftime);
  14         85348  
  14         74  
6 14     14   26327 use UNIVERSAL;
  14         179  
  14         54  
7 14     14   6573 use Linux::Info::Compilation;
  14         35  
  14         140  
8              
9             our $VERSION = '1.4'; # VERSION
10              
11             =head1 NAME
12              
13             Linux::Info - API in Perl to recover information about the running Linux OS
14              
15             =head1 SYNOPSIS
16              
17             use Linux::Info;
18              
19             # you can't use sysinfo like that!
20             my $lxs = Linux::Info->new(
21             cpustats => 1,
22             procstats => 1,
23             memstats => 1,
24             pgswstats => 1,
25             netstats => 1,
26             sockstats => 1,
27             diskstats => 1,
28             diskusage => 1,
29             loadavg => 1,
30             filestats => 1,
31             processes => 1,
32             );
33              
34             sleep 1;
35             my $stat = $lxs->get;
36              
37             =head1 DESCRIPTION
38              
39             Linux::Info is a fork from L distribution.
40              
41             L is a front-end module and gather different linux system information
42             like processor workload, memory usage, network and disk statistics and a lot more. Refer the
43             documentation of the distribution modules to get more information about all possible statistics.
44              
45             =head1 MOTIVATION
46              
47             L is a great distribution (and I used it a lot), but it was built to recover
48             only Linux statistics when I was also looking for other additional information about the OS.
49              
50             Linux::Info will provide additional information not available in L, as
51             general processor information and hopefully apply patches and suggestions not implemented in the
52             original project.
53              
54             L is also more forgiving regarding compatibility with older perls interpreters,
55             modules version that it depends on and even older OS. If you find that C is not available to your
56             old system, you should try it.
57              
58             =head2 What is different from Sys::Statistics::Linux?
59              
60             Linux::Info has:
61              
62             =over
63              
64             =item *
65              
66             a more modern Perl 5 code;
67              
68             =item *
69              
70             doesn't use C syscall to acquire information;
71              
72             =item *
73              
74             provides additional information about the processors;
75              
76             =item *
77              
78             higher Kwalitee;
79              
80             =back
81              
82             =head1 TECHNICAL NOTE
83              
84             This distribution collects statistics by the virtual F filesystem (procfs) and is
85             developed on the default vanilla kernel. It is tested on x86 hardware with the distributions
86             RHEL, Fedora, Debian, Ubuntu, Asianux, Slackware, Mandriva and openSuSE (SLES on zSeries as
87             well but a long time ago) on kernel versions 2.4 and/or 2.6. It's possible that it doesn't
88             run on all linux distributions if some procfs features are deactivated or too much modified.
89             As example the Linux kernel 2.4 can compiled with the option C what turn
90             on or off block statistics for devices.
91              
92             =head1 VIRTUAL MACHINES
93              
94             Note that if you try to install or run C under virtual machines
95             on guest systems that some statistics are not available, such as C, C
96             and C. The reason is that not all F data are passed to the guests.
97              
98             If the installation fails then try to force the installation with
99              
100             cpan> force install Linux::Info
101              
102             and notice which tests fails, because these statistics maybe not available on the virtual machine - sorry.
103              
104             =head1 DELTAS
105              
106             The statistics for C, C, C, C, C and C
107             are deltas, for this reason it's necessary to initialize the statistics before the data can be
108             prepared by C. These statistics can be initialized with the methods C, C and
109             C. For any option that is set to 1, the statistics will be initialized by the call of
110             C or C. The call of init() re-initialize all statistics that are set to 1 or 2.
111             By the call of C the initial statistics will be updated automatically. Please refer the
112             section L to get more information about the usage of C, C, C
113             and C.
114              
115             Another exigence is to C for a while - at least for one second - before the call of C
116             if you want to get useful statistics. The statistics for C, C, C,
117             C, C and C are no deltas. If you need only one of these information
118             you don't need to sleep before the call of C.
119              
120             The method C prepares all requested statistics and returns the statistics as a
121             L object. The initial statistics will be updated.
122              
123             =head1 MANUAL PROC(5)
124              
125             The Linux Programmer's Manual
126              
127             L
128              
129             If you have questions or don't understand the sense of some statistics then take a look
130             into this awesome documentation.
131              
132             =head1 OPTIONS FOR NEW INSTANCES
133              
134             During the creation of new instances of L, you can pass as parameters to the C method different statistics to
135             collect. The statistics available are those listed on L.
136              
137             You can use the L by using their respective package names in lowercase. To activate the gathering of statistics you have to set the options by the call of C or C.
138             In addition you can deactivate statistics with C.
139              
140             The options must be set with one of the following values:
141              
142             0 - deactivate statistics
143             1 - activate and init statistics
144             2 - activate statistics but don't init
145              
146             In addition it's possible to pass a hash reference with options.
147              
148             my $lxs = Linux::Info->new(
149             processes => {
150             init => 1,
151             pids => [ 1, 2, 3 ]
152             },
153             netstats => {
154             init => 1,
155             initfile => $file,
156             },
157             );
158              
159             Option C is useful if you want to store initial statistics on the filesystem.
160              
161             my $lxs = Linux::Info->new(
162             cpustats => {
163             init => 1,
164             initfile => '/tmp/cpustats.yml',
165             },
166             diskstats => {
167             init => 1,
168             initfile => '/tmp/diskstats.yml',
169             },
170             netstats => {
171             init => 1,
172             initfile => '/tmp/netstats.yml',
173             },
174             pgswstats => {
175             init => 1,
176             initfile => '/tmp/pgswstats.yml',
177             },
178             procstats => {
179             init => 1,
180             initfile => '/tmp/procstats.yml',
181             },
182             );
183              
184             Example:
185              
186             use strict;
187             use warnings;
188             use Linux::Info;
189              
190             my $lxs = Linux::Info->new(
191             pgswstats => {
192             init => 1,
193             initfile => '/tmp/pgswstats.yml'
194             }
195             );
196              
197             $lxs->get(); # without to sleep
198              
199             The initial statistics are stored to the temporary file:
200              
201             #> cat /tmp/pgswstats.yml
202             ---
203             pgfault: 397040955
204             pgmajfault: 4611
205             pgpgin: 21531693
206             pgpgout: 49511043
207             pswpin: 8
208             pswpout: 272
209             time: 1236783534.9328
210              
211             Every time you call the script the initial statistics are loaded/stored from/to the file.
212             This could be helpful if you doesn't run it as daemon and if you want to calculate the
213             average load of your system since the last call.
214              
215             To get more information about the statistics refer the different modules of the distribution.
216              
217             cpustats - Collect cpu statistics with Linux::Info::CpuStats.
218             procstats - Collect process statistics with Linux::Info::ProcStats.
219             memstats - Collect memory statistics with Linux::Info::MemStats.
220             pgswstats - Collect paging and swapping statistics with Linux::Info::PgSwStats.
221             netstats - Collect net statistics with Linux::Info::NetStats.
222             sockstats - Collect socket statistics with Linux::Info::SockStats.
223             diskstats - Collect disk statistics with Linux::Info::DiskStats.
224             diskusage - Collect the disk usage with Linux::Info::DiskUsage.
225             loadavg - Collect the load average with Linux::Info::LoadAVG.
226             filestats - Collect inode statistics with Linux::Info::FileStats.
227             processes - Collect process statistics with Linux::Info::Processes.
228              
229             The options just described don't apply to L since this module doesn't hold statistics from the OS.
230             If you try to use it C will C with an error message. In order to use L, just
231             create an instance of it directly. See L for information on that.
232              
233             =head1 METHODS
234              
235             =head2 new()
236              
237             Call C to create a new Linux::Info object. You can call C with options.
238             This options would be passed to the method C.
239              
240             Without options
241              
242             my $lxs = Linux::Info->new();
243              
244             Or with options
245              
246             my $lxs = Linux::Info->new( cpustats => 1 );
247              
248             Would do nothing
249              
250             my $lxs = Linux::Info->new( cpustats => 0 );
251              
252             It's possible to call C with a hash reference of options.
253              
254             my %options = (
255             cpustats => 1,
256             memstats => 1
257             );
258              
259             my $lxs = Linux::Info->new(\%options);
260              
261             =head2 set()
262              
263             Call C to activate or deactivate options.
264              
265             The following example would call C and initialize C
266             and delete the object of C.
267              
268             $lxs->set(
269             processes => 0, # deactivate this statistic
270             pgswstats => 1, # activate the statistic and calls new() and init() if necessary
271             netstats => 2, # activate the statistic and call new() if necessary but not init()
272             );
273              
274             It's possible to call C with a hash reference of options.
275              
276             my %options = (
277             cpustats => 2,
278             memstats => 2
279             );
280              
281             $lxs->set(\%options);
282              
283             =head2 get()
284              
285             Call C to get the collected statistics. C returns a L
286             object.
287              
288             my $lxs = Linux::Info->new(\%options);
289             sleep(1);
290             my $stat = $lxs->get();
291              
292             Or you can pass the time to sleep with the call of C.
293              
294             my $stat = $lxs->get($time_to_sleep);
295              
296             Now the statistcs are available with
297              
298             $stat->cpustats
299              
300             # or
301              
302             $stat->{cpustats}
303              
304             Take a look to the documentation of L for more information.
305              
306             =head2 init()
307              
308             The call of C initiate all activated statistics that are necessary for deltas. That could
309             be helpful if your script runs in a endless loop with a high sleep interval. Don't forget that if
310             you call C that the statistics are deltas since the last time they were initiated.
311              
312             The following example would calculate average statistics for 30 minutes:
313              
314             # initiate cpustats
315             my $lxs = Linux::Info->new( cpustats => 1 );
316              
317             while ( 1 ) {
318             sleep(1800);
319             my $stat = $lxs->get;
320             }
321              
322             If you just want a current snapshot of the system each 30 minutes and not the average
323             then the following example would be better for you:
324              
325             # do not initiate cpustats
326             my $lxs = Linux::Info->new( cpustats => 2 );
327              
328             while ( 1 ) {
329             $lxs->init; # init the statistics
330             my $stat = $lxs->get(1); # get the statistics
331             sleep(1800); # sleep until the next run
332             }
333              
334             If you want to write a simple command line utility that prints the current workload
335             to the screen then you can use something like this:
336              
337             my @order = qw(user system iowait idle nice irq softirq total);
338             printf "%-20s%8s%8s%8s%8s%8s%8s%8s%8s\n", 'time', @order;
339              
340             my $lxs = Linux::Info->new( cpustats => 1 );
341              
342             while ( 1 ){
343             my $cpu = $lxs->get(1)->cpustats;
344             my $time = $lxs->gettime;
345             printf "%-20s%8s%8s%8s%8s%8s%8s%8s%8s\n",
346             $time, @{$cpu->{cpu}}{@order};
347             }
348              
349             =head2 settime()
350              
351             Call C to define a POSIX formatted time stamp, generated with localtime().
352              
353             $lxs->settime('%Y/%m/%d %H:%M:%S');
354              
355             To get more information about the formats take a look at C of POSIX.pm
356             or the manpage C.
357              
358             =head2 gettime()
359              
360             C returns a POSIX formatted time stamp, @foo in list and $bar in scalar context.
361             If the time format isn't set then the default format "%Y-%m-%d %H:%M:%S" will be set
362             automatically. You can also set a time format with C.
363              
364             my $date_time = $lxs->gettime;
365              
366             Or
367              
368             my ($date, $time) = $lxs->gettime();
369              
370             Or
371              
372             my ($date, $time) = $lxs->gettime('%Y/%m/%d %H:%M:%S');
373              
374             =head1 EXAMPLES
375              
376             A very simple perl script could looks like this:
377              
378             use strict;
379             use warnings;
380             use Linux::Info;
381              
382             my $lxs = Linux::Info->new( cpustats => 1 );
383             sleep(1);
384             my $stat = $lxs->get;
385             my $cpu = $stat->cpustats->{cpu};
386              
387             print "Statistics for CpuStats (all)\n";
388             print " user $cpu->{user}\n";
389             print " nice $cpu->{nice}\n";
390             print " system $cpu->{system}\n";
391             print " idle $cpu->{idle}\n";
392             print " ioWait $cpu->{iowait}\n";
393             print " total $cpu->{total}\n";
394              
395             Set and get a time stamp:
396              
397             use strict;
398             use warnings;
399             use Linux::Info;
400              
401             my $lxs = Linux::Info->new();
402             $lxs->settime('%Y/%m/%d %H:%M:%S');
403             print $lxs->gettime, "\n";
404              
405             If you want to know how the data structure looks like you can use C to check it:
406              
407             use strict;
408             use warnings;
409             use Linux::Info;
410             use Data::Dumper;
411              
412             my $lxs = Linux::Info->new( cpustats => 1 );
413             sleep(1);
414             my $stat = $lxs->get;
415              
416             print Dumper($stat);
417              
418             How to get the top 5 processes with the highest cpu workload:
419              
420             use strict;
421             use warnings;
422             use Linux::Info;
423              
424             my $lxs = Linux::Info->new( processes => 1 );
425             sleep(1);
426             my $stat = $lxs->get;
427             my @top5 = $stat->pstop( ttime => 5 );
428              
429             =head1 EXPORTS
430              
431             Nothing.
432              
433             =head1 SEE ALSO
434              
435             =over
436              
437             =item *
438              
439             The L distribution, which is base of Linux::Info
440              
441             =item *
442              
443             The project website at L.
444              
445             =back
446              
447             =head1 AUTHOR
448              
449             Alceu Rodrigues de Freitas Junior, Earfreitas@cpan.orgE
450              
451             =head1 COPYRIGHT AND LICENSE
452              
453             This software is copyright (c) 2015 of Alceu Rodrigues de Freitas Junior, Earfreitas@cpan.orgE
454              
455             This file is part of Linux Info project.
456              
457             Linux Info is free software: you can redistribute it and/or modify
458             it under the terms of the GNU General Public License as published by
459             the Free Software Foundation, either version 3 of the License, or
460             (at your option) any later version.
461              
462             Linux Info is distributed in the hope that it will be useful,
463             but WITHOUT ANY WARRANTY; without even the implied warranty of
464             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
465             GNU General Public License for more details.
466              
467             You should have received a copy of the GNU General Public License
468             along with Linux Info. If not, see .
469              
470             =cut
471              
472             sub new {
473 15     15 1 9147 my $class = shift;
474 15         61 my $self = bless { obj => {} }, $class;
475              
476 15         82 my @options = qw(
477             CpuStats ProcStats
478             MemStats PgSwStats NetStats
479             SockStats DiskStats DiskUsage
480             LoadAVG FileStats Processes
481             );
482              
483 15         41 foreach my $opt (@options) {
484             # backward compatibility
485 165         317 $self->{opts}->{$opt} = 0;
486 165         256 $self->{maps}->{$opt} = $opt;
487              
488             # new style
489 165         220 my $lcopt = lc($opt);
490 165         270 $self->{opts}->{$lcopt} = 0;
491 165         341 $self->{maps}->{$lcopt} = $opt;
492             }
493              
494 15 100       63 $self->set(@_) if @_;
495 13         43 return $self;
496             }
497              
498             sub set {
499 16     16 1 2480 my $self = shift;
500 16         35 my $class = ref $self;
501 16 50       82 my $args = ref( $_[0] ) eq 'HASH' ? shift : {@_};
502 16         35 my $opts = $self->{opts};
503 16         31 my $obj = $self->{obj};
504 16         33 my $maps = $self->{maps};
505              
506              
507             confess 'Linux::Info::SysInfo cannot be instantiated from Linux::Info'
508 16 100       64 if ( exists( $args->{sysinfo} ) );
509              
510 15         26 foreach my $opt ( keys( %{$args} ) ) {
  15         57  
511              
512             confess "invalid delta option '$opt'"
513 18 100       70 unless ( exists( $opts->{$opt} ) );
514              
515 17 50       180 if ( ref( $args->{$opt} ) ) {
    50          
516 0   0     0 $opts->{$opt} = delete $args->{$opt}->{init} || 1;
517             }
518             elsif ( $args->{$opt} !~ qr/^[012]\z/ ) {
519 0         0 confess "invalid value for '$opt'";
520             }
521             else {
522 17         46 $opts->{$opt} = $args->{$opt};
523             }
524              
525 17 50       63 if ( $opts->{$opt} ) {
    0          
526 17         65 my $package = $class . '::' . $maps->{$opt};
527              
528             # require module - require know which modules are loaded
529             # and doesn't load a module twice.
530 17         29 my $require = $package;
531 17         74 $require =~ s/::/\//g;
532 17         40 $require .= '.pm';
533 17         7499 require $require;
534              
535 17 100       87 if ( !$obj->{$opt} ) {
536 16 50       61 if ( ref( $args->{$opt} ) ) {
537 0         0 $obj->{$opt} = $package->new( %{ $args->{$opt} } );
  0         0  
538             }
539             else {
540 16         100 $obj->{$opt} = $package->new();
541             }
542             }
543              
544             # get initial statistics if the function init() exists
545             # and the option is set to 1
546 17 100 66     262 if ( $opts->{$opt} == 1 && UNIVERSAL::can( $package, 'init' ) ) {
547 11         42 $obj->{$opt}->init();
548             }
549              
550             }
551             elsif ( exists $obj->{$opt} ) {
552 0         0 delete $obj->{$opt};
553             }
554             }
555             }
556              
557             sub init {
558 0     0 1 0 my $self = shift;
559 0         0 my $class = ref $self;
560              
561 0         0 foreach my $opt ( keys %{ $self->{opts} } ) {
  0         0  
562 0 0 0     0 if ( $self->{opts}->{$opt} > 0
563             && UNIVERSAL::can( ref( $self->{obj}->{$opt} ), 'init' ) )
564             {
565 0         0 $self->{obj}->{$opt}->init();
566             }
567             }
568             }
569              
570             sub get {
571 13     13 1 9002010 my ( $self, $time ) = @_;
572 13 50       116 sleep $time if $time;
573 13         45 my %stat = ();
574              
575 13         40 foreach my $opt ( keys %{ $self->{opts} } ) {
  13         400  
576 286 100       596 if ( $self->{opts}->{$opt} ) {
577 16         134 $stat{$opt} = $self->{obj}->{$opt}->get();
578 16 100       82 if ( $opt eq 'netstats' ) {
579 1         5 $stat{netinfo} = $self->{obj}->{$opt}->get_raw();
580             }
581             }
582             }
583              
584 13         205 return Linux::Info::Compilation->new( \%stat );
585             }
586              
587             sub settime {
588 0     0 1   my $self = shift;
589 0 0         my $format = @_ ? shift : '%Y-%m-%d %H:%M:%S';
590 0           $self->{timeformat} = $format;
591             }
592              
593             sub gettime {
594 0     0 1   my $self = shift;
595 0 0         $self->settime(@_) unless $self->{timeformat};
596 0           my $tm = strftime( $self->{timeformat}, localtime );
597 0 0         return wantarray ? split /\s+/, $tm : $tm;
598             }
599              
600             1;