package Linux::Info; use strict; use warnings; use Carp qw(confess); use POSIX qw(strftime); use UNIVERSAL; use Linux::Info::Compilation; our $VERSION = '2.13'; # VERSION # ABSTRACT: API in Perl to recover information about the running Linux OS sub new { my $class = shift; my $self = bless { obj => {} }, $class; my @options = qw( CpuStats ProcStats MemStats PgSwStats NetStats SockStats DiskStats DiskUsage LoadAVG FileStats Processes ); foreach my $opt (@options) { # backward compatibility $self->{opts}->{$opt} = 0; $self->{maps}->{$opt} = $opt; # new style my $lcopt = lc($opt); $self->{opts}->{$lcopt} = 0; $self->{maps}->{$lcopt} = $opt; } $self->set(@_) if @_; return $self; } sub set { my $self = shift; my $class = ref $self; my $args = ref( $_[0] ) eq 'HASH' ? shift : {@_}; my $opts = $self->{opts}; my $obj = $self->{obj}; my $maps = $self->{maps}; confess 'Linux::Info::SysInfo cannot be instantiated from Linux::Info' if ( exists( $args->{sysinfo} ) ); foreach my $opt ( keys( %{$args} ) ) { confess "invalid delta option '$opt'" unless ( exists( $opts->{$opt} ) ); if ( ref( $args->{$opt} ) ) { $opts->{$opt} = delete $args->{$opt}->{init} || 1; } elsif ( $args->{$opt} !~ qr/^[012]\z/ ) { confess "invalid value for '$opt'"; } else { $opts->{$opt} = $args->{$opt}; } if ( $opts->{$opt} ) { my $package = $class . '::' . $maps->{$opt}; # require module - require know which modules are loaded # and doesn't load a module twice. my $require = $package; $require =~ s/::/\//g; $require .= '.pm'; require $require; if ( !$obj->{$opt} ) { if ( ref( $args->{$opt} ) ) { $obj->{$opt} = $package->new( %{ $args->{$opt} } ); } else { $obj->{$opt} = $package->new(); } } # get initial statistics if the function init() exists # and the option is set to 1 if ( $opts->{$opt} == 1 && UNIVERSAL::can( $package, 'init' ) ) { $obj->{$opt}->init(); } } elsif ( exists $obj->{$opt} ) { delete $obj->{$opt}; } } } sub get { my ( $self, $time ) = @_; sleep $time if $time; my %stat = (); foreach my $opt ( keys %{ $self->{opts} } ) { if ( $self->{opts}->{$opt} ) { $stat{$opt} = $self->{obj}->{$opt}->get(); if ( $opt eq 'netstats' ) { $stat{netinfo} = $self->{obj}->{$opt}->get_raw(); } } } return Linux::Info::Compilation->new( \%stat ); } sub init { my $self = shift; my $class = ref $self; foreach my $opt ( keys %{ $self->{opts} } ) { if ( $self->{opts}->{$opt} > 0 && UNIVERSAL::can( ref( $self->{obj}->{$opt} ), 'init' ) ) { $self->{obj}->{$opt}->init(); } } } sub settime { my $self = shift; my $format = @_ ? shift : '%Y-%m-%d %H:%M:%S'; $self->{timeformat} = $format; } sub gettime { my $self = shift; $self->settime(@_) unless $self->{timeformat}; my $tm = strftime( $self->{timeformat}, localtime ); return wantarray ? split /\s+/, $tm : $tm; } 1; __END__ =pod =encoding UTF-8 =head1 NAME Linux::Info - API in Perl to recover information about the running Linux OS =head1 VERSION version 2.13 =head1 SYNOPSIS use Linux::Info; # you can't use sysinfo like that! my $lxs = Linux::Info->new( cpustats => 1, procstats => 1, memstats => 1, pgswstats => 1, netstats => 1, sockstats => 1, diskstats => 1, diskusage => 1, loadavg => 1, filestats => 1, processes => 1, ); sleep 1; my $stat = $lxs->get; =head1 DESCRIPTION Linux::Info is a fork from L distribution. L is a front-end module and gather different linux system information like processor workload, memory usage, network and disk statistics and a lot more. Refer the documentation of the distribution modules to get more information about all possible statistics. =head1 MOTIVATION L is a great distribution (and I used it a lot), but it was built to recover only Linux statistics when I was also looking for other additional information about the OS. Linux::Info will provide additional information not available in L, as general processor information and hopefully apply patches and suggestions not implemented in the original project. L is also more forgiving regarding compatibility with older perls interpreters, modules version that it depends on and even older OS. If you find that C is not available to your old system, you should try it. =head2 What is different from Sys::Statistics::Linux? Linux::Info has: =over =item * a more modern Perl 5 code; =item * doesn't use C syscall to acquire information; =item * provides additional information about the processors; =item * higher Kwalitee; =back =head1 TECHNICAL NOTE This distribution collects statistics by the virtual F filesystem (procfs) and is developed on the default vanilla kernel. It is tested on x86 hardware with the distributions RHEL, Fedora, Debian, Ubuntu, Asianux, Slackware, Mandriva and openSuSE (SLES on zSeries as well but a long time ago) on kernel versions 2.4 and/or 2.6. It's possible that it doesn't run on all linux distributions if some procfs features are deactivated or too much modified. As example the Linux kernel 2.4 can compiled with the option C what turn on or off block statistics for devices. =head1 VIRTUAL MACHINES Note that if you try to install or run C under virtual machines on guest systems that some statistics are not available, such as C, C and C. The reason is that not all F data are passed to the guests. If the installation fails then try to force the installation with cpan> force install Linux::Info and notice which tests fails, because these statistics maybe not available on the virtual machine - sorry. =head1 DELTAS The statistics for C, C, C, C, C and C are deltas, for this reason it's necessary to initialize the statistics before the data can be prepared by C. These statistics can be initialized with the methods C, C and C. For any option that is set to 1, the statistics will be initialized by the call of C or C. The call of init() re-initialize all statistics that are set to 1 or 2. By the call of C the initial statistics will be updated automatically. Please refer the section L to get more information about the usage of C, C, C and C. Another exigence is to C for a while - at least for one second - before the call of C if you want to get useful statistics. The statistics for C, C, C, C, C and C are no deltas. If you need only one of these information you don't need to sleep before the call of C. The method C prepares all requested statistics and returns the statistics as a L object. The initial statistics will be updated. =head1 MANUAL PROC(5) The Linux Programmer's Manual L If you have questions or don't understand the sense of some statistics then take a look into this awesome documentation. =head1 OPTIONS FOR NEW INSTANCES During the creation of new instances of L, you can pass as parameters to the C method different statistics to collect. The statistics available are those listed on L. 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. In addition you can deactivate statistics with C. The options must be set with one of the following values: 0 - deactivate statistics 1 - activate and init statistics 2 - activate statistics but don't init In addition it's possible to pass a hash reference with options. my $lxs = Linux::Info->new( processes => { init => 1, pids => [ 1, 2, 3 ] }, netstats => { init => 1, initfile => $file, }, ); Option C is useful if you want to store initial statistics on the filesystem. my $lxs = Linux::Info->new( cpustats => { init => 1, initfile => '/tmp/cpustats.yml', }, diskstats => { init => 1, initfile => '/tmp/diskstats.yml', }, netstats => { init => 1, initfile => '/tmp/netstats.yml', }, pgswstats => { init => 1, initfile => '/tmp/pgswstats.yml', }, procstats => { init => 1, initfile => '/tmp/procstats.yml', }, ); Example: use strict; use warnings; use Linux::Info; my $lxs = Linux::Info->new( pgswstats => { init => 1, initfile => '/tmp/pgswstats.yml' } ); $lxs->get(); # without to sleep The initial statistics are stored to the temporary file: #> cat /tmp/pgswstats.yml --- pgfault: 397040955 pgmajfault: 4611 pgpgin: 21531693 pgpgout: 49511043 pswpin: 8 pswpout: 272 time: 1236783534.9328 Every time you call the script the initial statistics are loaded/stored from/to the file. This could be helpful if you doesn't run it as daemon and if you want to calculate the average load of your system since the last call. To get more information about the statistics refer the different modules of the distribution. cpustats - Collect cpu statistics with Linux::Info::CpuStats. procstats - Collect process statistics with Linux::Info::ProcStats. memstats - Collect memory statistics with Linux::Info::MemStats. pgswstats - Collect paging and swapping statistics with Linux::Info::PgSwStats. netstats - Collect net statistics with Linux::Info::NetStats. sockstats - Collect socket statistics with Linux::Info::SockStats. diskstats - Collect disk statistics with Linux::Info::DiskStats. diskusage - Collect the disk usage with Linux::Info::DiskUsage. loadavg - Collect the load average with Linux::Info::LoadAVG. filestats - Collect inode statistics with Linux::Info::FileStats. processes - Collect process statistics with Linux::Info::Processes. The options just described don't apply to L since this module doesn't hold statistics from the OS. If you try to use it C will C with an error message. In order to use L, just create an instance of it directly. See L for information on that. =head1 METHODS =head2 new() Call C to create a new Linux::Info object. You can call C with options. This options would be passed to the method C. Without options my $lxs = Linux::Info->new(); Or with options my $lxs = Linux::Info->new( cpustats => 1 ); Would do nothing my $lxs = Linux::Info->new( cpustats => 0 ); It's possible to call C with a hash reference of options. my %options = ( cpustats => 1, memstats => 1 ); my $lxs = Linux::Info->new(\%options); =head2 set() Call C to activate or deactivate options. The following example would call C and initialize C and delete the object of C. $lxs->set( processes => 0, # deactivate this statistic pgswstats => 1, # activate the statistic and calls new() and init() if necessary netstats => 2, # activate the statistic and call new() if necessary but not init() ); It's possible to call C with a hash reference of options. my %options = ( cpustats => 2, memstats => 2 ); $lxs->set(\%options); =head2 get() Call C to get the collected statistics. C returns a L object. my $lxs = Linux::Info->new(\%options); sleep(1); my $stat = $lxs->get(); Or you can pass the time to sleep with the call of C. my $stat = $lxs->get($time_to_sleep); Now the statistcs are available with $stat->cpustats # or $stat->{cpustats} Take a look to the documentation of L for more information. =head2 init() The call of C initiate all activated statistics that are necessary for deltas. That could be helpful if your script runs in a endless loop with a high sleep interval. Don't forget that if you call C that the statistics are deltas since the last time they were initiated. The following example would calculate average statistics for 30 minutes: # initiate cpustats my $lxs = Linux::Info->new( cpustats => 1 ); while ( 1 ) { sleep(1800); my $stat = $lxs->get; } If you just want a current snapshot of the system each 30 minutes and not the average then the following example would be better for you: # do not initiate cpustats my $lxs = Linux::Info->new( cpustats => 2 ); while ( 1 ) { $lxs->init; # init the statistics my $stat = $lxs->get(1); # get the statistics sleep(1800); # sleep until the next run } If you want to write a simple command line utility that prints the current workload to the screen then you can use something like this: my @order = qw(user system iowait idle nice irq softirq total); printf "%-20s%8s%8s%8s%8s%8s%8s%8s%8s\n", 'time', @order; my $lxs = Linux::Info->new( cpustats => 1 ); while ( 1 ){ my $cpu = $lxs->get(1)->cpustats; my $time = $lxs->gettime; printf "%-20s%8s%8s%8s%8s%8s%8s%8s%8s\n", $time, @{$cpu->{cpu}}{@order}; } =head2 settime() Call C to define a POSIX formatted time stamp, generated with localtime(). $lxs->settime('%Y/%m/%d %H:%M:%S'); To get more information about the formats take a look at C of POSIX.pm or the manpage C. =head2 gettime() C returns a POSIX formatted time stamp, @foo in list and $bar in scalar context. If the time format isn't set then the default format "%Y-%m-%d %H:%M:%S" will be set automatically. You can also set a time format with C. my $date_time = $lxs->gettime; Or my ($date, $time) = $lxs->gettime(); Or my ($date, $time) = $lxs->gettime('%Y/%m/%d %H:%M:%S'); =head1 EXAMPLES A very simple perl script could looks like this: use strict; use warnings; use Linux::Info; my $lxs = Linux::Info->new( cpustats => 1 ); sleep(1); my $stat = $lxs->get; my $cpu = $stat->cpustats->{cpu}; print "Statistics for CpuStats (all)\n"; print " user $cpu->{user}\n"; print " nice $cpu->{nice}\n"; print " system $cpu->{system}\n"; print " idle $cpu->{idle}\n"; print " ioWait $cpu->{iowait}\n"; print " total $cpu->{total}\n"; Set and get a time stamp: use strict; use warnings; use Linux::Info; my $lxs = Linux::Info->new(); $lxs->settime('%Y/%m/%d %H:%M:%S'); print $lxs->gettime, "\n"; If you want to know how the data structure looks like you can use C to check it: use strict; use warnings; use Linux::Info; use Data::Dumper; my $lxs = Linux::Info->new( cpustats => 1 ); sleep(1); my $stat = $lxs->get; print Dumper($stat); How to get the top 5 processes with the highest cpu workload: use strict; use warnings; use Linux::Info; my $lxs = Linux::Info->new( processes => 1 ); sleep(1); my $stat = $lxs->get; my @top5 = $stat->pstop( ttime => 5 ); =head1 EXPORTS Nothing. =head1 SEE ALSO =over =item * The L distribution, which is base of Linux::Info =item * The project website at L. =back =head1 AUTHOR Alceu Rodrigues de Freitas Junior =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Alceu Rodrigues de Freitas Junior. This is free software, licensed under: The GNU General Public License, Version 3, June 2007 =cut