Blame | Last modification | View Log | RSS feed
#! /usr/bin/perl -w
#
# This is part of the Sysinfo client package which collects information from a system
# and reports it back to a centralized server.
# this library is for reporting on Linux systems
#
# Author: R. W. Rodolico
#
# v0.1 20090101 RWR
# Created and entered into Sysinfo system
#
# v0.2 20090508 RWR
# lscpi causing some problems. Rewrote routine to solve issues where lspci is inconsistent on its reporting
package SysinfoLinux;
# gets information on disks and partitions. Returns them as a hash
# uses standard df -kT and parses the output
# skips anything that does not have a device with /dev in it
# assumes the structure of the output is:
# device fstype size usedSpace availableSpace percentSpace mountPoint
# however, the output can sometimes wrap to multi lines, especially if the
# device is long. So, we will replace all newlines with spaces, then
# remove space duplications, then split on spaces and process each item in
# turn.
# NOTE: this will totally break if you have spaces in your mount point
sub getDiskInfo {
my @directoriesToWatch = @_; # optional directories to check
my %returnValue; # the has we sill store the results in
# process physical partitions
my $temp = qx/df -kT/; # execute the system command df, returned values in kilobytes, showing file system types
@temp = split("\n", $temp ); # get array of lines
shift @temp; # get rid of the first line . . . it is nothing but header info
$temp = join( ' ', @temp ); # rejoin everything back with spaces
$temp =~ s/ +/ /gi; # remove all duplicate spaces
@temp = split( ' ', $temp ); # turn it back into an array of space separated values
while (@temp) {
my $device = shift @temp; # get the device name
my %thisDisk;
$thisDisk{'fstype'} = shift @temp; # next is fs type
$thisDisk{'size'} = shift @temp; # total partition size, in k
$thisDisk{'used'} = shift @temp; # disk usage, in k
shift @temp; # $available, not recorded
shift @temp; # $percent, not recorded
$thisDisk{'mount'} = shift @temp; # mount point
# now, if it is a /dev device, we add it to the diskInfo hash
$returnValue{$device} = \%thisDisk if $device =~ m/\/dev/;
}
# process any directories to watch
while ( my $path = shift @directoriesToWatch ) {
my @results = qx/du -sk $path/;
chomp @results;
while ( my $thisValue = shift @results ) {
my ($used,$mount) = split( "\t", $thisValue );
$returnValue{$mount}{'used'} = $used;
}
}
return \%returnValue;
}
sub getNetwork {
# eth1 Link encap:Ethernet HWaddr 00:16:3E:1F:EF:4F
my $regexHWADDR = 'hwaddr[^0-9a-f:]+([0-9a-f:]+)[^0-9a-f:]';
# inet addr:10.110.110.2 Bcast:10.110.110.255 Mask:255.255.255.0
my $regexINET = 'inet addr:\s*([0-9.]+)[^0-9].*mask:([0-9.]+)';
# inet6 addr: fe80::216:3eff:fe1f:ef4f/64 Scope:Link
my $regexINET6 = 'inet6 addr: ([0-9a-f:]+)\/([0-9]+)[^0-9]';
# UP LOOPBACK RUNNING MTU:16436 Metric:1
my $regexMTU = 'mtu:([0-9]+)[^0-9]';
my $temp = qx/ifconfig/;
my @temp = split( "\n", $temp );
my %returnValue;
my $currentIF;
while ( @temp ) {
$line = shift @temp;
next unless $line;
if ( $line =~ m/^([^ ]+) /) { # if the first character is not a space, starting new entry
$currentIF = $1;
if ( $line =~ m/$regexHWADDR/i ) {
$returnValue{$currentIF}{'mac'} = $1;
} else {
$returnValue{$currentIF}{'mac'} = 'unknown';
}
} elsif ( $line =~ m/$regexINET/i ) {
$returnValue{$currentIF}{'address'} = $1;
$returnValue{$currentIF}{'netmask'} = $2;
} elsif ( $line =~ m/$regexINET6/i ) {
$returnValue{$currentIF}{'ip6address'} = $1;
$returnValue{$currentIF}{'ip6networkbits'} = $2;
} elsif ( $line =~ m/$regexMTU/i ) {
$returnValue{$currentIF}{'mtu'} = $1;
}
}
return \%returnValue;
}
sub getSoftware {
my $CUSTOM_PACKAGE_FINDER = shift; # used if there is an additional routine to call for software
my %returnValue;
# We currently handle dpkg and rpm
if ( `which dpkg 2>/dev/null` ) { # system uses dpkg, ie debian or derivitive
my @packageList = split( "\n", qx(dpkg -l | grep ^i));
chomp @packageList;
for ( $i = 0; $i < @packageList; $i++ ) {
my ($status,$package, $version, @description) = split ' ', $packageList[$i];
$returnValue{$package}{'version'} = $version;
$returnValue{$package}{'description'} = join(" ", @description);
}
} elsif ( `which rpm 2>/dev/null` ) { # system uses rpm, ie most RedHat derivitives
my @packageList = split( "\n", qx( rpm -qa --qf "%{NAME}\\t%{VERSION}\\t%{DISTRIBUTION}\\n" ) );
chomp @packageList;
for ( $i = 0; $i < @packageList; $i++ ) {
my ($package, $version, $distribution) = split( "\t", $packageList[$i]);
$returnValue{$package}{'version'} = $version;
$returnValue{$package}{'description'} = join(" ", @description);
}
} else {
die 'Unknown package manager, please modify the script';
}
# ok, we have the packages found by "normal" means, not do the package manager if it exists
# it is assumed to return a tab delimited list of package, version and description
if ( $CUSTOM_PACKAGE_FINDER ) {
my $customPackages = qx/$CUSTOM_PACKAGE_FINDER $APPLICATION_ROOT/ if ( -e $CUSTOM_PACKAGE_FINDER );
my @packageList = split( "\n", $customPackages);
foreach my $thisPackage ( @packageList ) {
my ($package,$version,$description) = split( "\t", $thisPackage );
$returnValue{$package}{'version'} = $version;
$returnValue{$package}{'description'} = $description;
}
}
return \%returnValue;
}
sub cleanUp {
my ($delimiter, $text) = @_;
$text =~ m/[^$delimiter]*$delimiter\s*(.*)/;
return $1;
}
sub chompHash {
my $hash = shift;
foreach my $key ( keys %$hash ) {
chomp $$hash{$key} if $$hash{$key};
}
return $hash;
}
# Just get various OS parameters; name, description, release, etc...
sub getOperatingSystem {
my %returnValue;
$returnValue{'distribution'} = &cleanUp(':', qx(lsb_release -i));
$returnValue{'description'} = &cleanUp(':', qx(lsb_release -d)) . "\n";
$returnValue{'release'} = &cleanUp(':', qx(lsb_release -r)) . "\n";
$returnValue{'codename'} = &cleanUp(':', qx(lsb_release -c)) . "\n";
$returnValue{'os_name'} = qx(uname -s);
$returnValue{'os_version'} = qx(cat /proc/version);
$returnValue{'kernel'} = qx(uname -r);
return &chompHash(\%returnValue);
}
sub getRebootInfo {
# get uptime from /proc/uptime
my $uptime = qx(cat /proc/uptime);
$uptime =~ m/(\d+)/;
$uptime = int($1); # uptime now has the up time in seconds
return time - $uptime;
# following has been commented out as process_sysinfo knows how to read the information in unix time
# Now, calculate the last boot time. This may be off by a second, but that is close
# enough for these purposes. this used to be done by capturing the output of
# procinfo | grep Bootup | sed 's/Bootup: //g' | cut -f1-6 -d' '
# but, I wanted to return a database ready date, and Windows systems will be off more
# than this anyway.
#my $lastBoot = time - $uptime;
#my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
# localtime($lastBoot);
#$year += 1900;
#$mon++;
#return (sprintf( "%04d%02d%02d%02d%02d%02d", $year, $mon, $mday, $hour, $min, $sec ),$uptime);
}
sub getSystemInformation {
my $hostname = shift;
my %returnValue;
$returnValue{'hostname'} = ( $hostname ? $hostname : qx(hostname -f) );
$returnValue{'memory'} = qx(free | grep Mem | awk '{print \$2}');
$returnValue{'num_cpu'} = qx(cat /proc/cpuinfo | grep processor | wc -l | awk '{print \$1}');
$returnValue{'cpu_speed'} = qx(cat /proc/cpuinfo | grep MHz | tail -n1 | awk '{print \$4}');
$returnValue{'cpu_type'} = qx(cat /proc/cpuinfo | grep vendor_id | tail -n 1 | awk '{print \$3}');
$returnValue{'cpu_sub'} = qx(uname -m);
$returnValue{'last_boot'} = &getRebootInfo;
return &chompHash(\%returnValue);
}
sub getPCI {
my %returnValue;
my $pciInfo = qx(lspci -Dvmm);
# this is a regular expression to "find" the slot number, if one exists
# Different versions of lspci use different keys for the name and the slot
# in some cases, the key Device: is used for both the device name and the slot (Debian Etch lspci version 2.2.4-pre4)
# so I have to use this kludge. I may rewrite it to just search the sys directory tree later.
my $SLOT_REGEX = '^[0-9a-z]+[:.][0-9a-z]+';
my @pciInfo = split ("\n\n", $pciInfo);
my $i = 0;
while (my $test = shift (@pciInfo)) {
foreach my $thisLine (sort split("\n", $test)) {
if ($thisLine =~ m/([a-z]+):\s*(\S.*)/i) {
my ($key, $value) = (lc $1,$2);
# remove any leading whitespace
$key =~ s/^\s*//g;
$value =~ s/^\s*//g;
while (defined($returnValue{$i}{$key})) { # dup key, so give it a unique value
$key .= '0'; # just add some 0's at the end
}
$returnValue{$i}{$key} = $value;
}
}
unless (defined $returnValue{$i}{'slot'}) { # no slot number, so see if we have one
$returnValue{$i}{'slot'} = 'Unknown';
for my $thisKey ( keys %{$returnValue{$i}} ) {
if ($returnValue{$i}{$thisKey} =~ m/$SLOT_REGEX/i) {
$returnValue{$i}{'slot'} = $returnValue{$i}{$thisKey}; # this puts it in two places, so remove the original
delete $returnValue{$i}{$thisKey};
last;
}
}
}
if (defined ($returnValue{$i}{'name'})) { # we need to not have this; it messes up the xml package
$returnValue{$i}{'device name'} = $returnValue{$i}{'name'};
delete $returnValue{$i}{'name'}
}
unless (defined ($returnValue{$i}{'name'})) { # no name, so see if we have one
$returnValue{$i}{'name'} = 'Unknown';
foreach my $thisKey ( 'slot', 'device', 'device0', 'sdevice', 'class', 'vendor', 'svendor' ) {
if (defined($returnValue{$i}{$thisKey}) && ($returnValue{$i} ne 'Unknown') ) {
$returnValue{$i}{'name'} = $returnValue{$i}{$thisKey};
last;
}
}
}
$i++;
}
return \%returnValue;
}
1;
Generated by GNU Enscript 1.6.5.90.