File: //proc/3233645/root/opt/sys-snap/sys-snap.pl
#!/usr/bin/perl
# Copyright (C) 2015
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
# Author Bryan Christensen
use warnings;
use strict;
use Getopt::Long;
use Fcntl qw(:DEFAULT :flock);
my %opt = (
'start' => 0,
'stop' => 0,
'check' => 0,
'print' => 0,
'loadavg' => 0,
'help' => 0,
'network' => 0,
'io' => 0,
'print_cpu' => 1,
'print_memory' => 1,
'interval' => 10,
'loadavg' => 0,
'dir' => '/opt/sys-snap/system-snapshot',
'verbose' => '0',
'max-lines' => '20',
'line_length' => '145',
'pidfile' => '/var/run/sys-snap.pid',
);
GetOptions(
\%opt,
'help|h+',
'print',
'network',
'start',
'stop',
'check|c',
'loadavg',
'io',
'cpu!' => \$opt{'print_cpu'},
'mem!' => \$opt{'print_memory'},
'interval|i=i' => \$opt{'interval'},
'dir|d=s' => \$opt{'dir'},
'verbose|v!' => \$opt{'verbose'},
'max-lines|ml=i' => \$opt{'max-lines'},
) or usage();
########################################################
# start of parameters that don't need time
########################################################
if ( $opt{'help'} ) {
usage();
exit;
}
elsif ( $opt{'start'} ) {
run_install();
exit;
}
elsif ( $opt{'stop'} ) {
stop_syssnap();
exit;
}
elsif ( $opt{'check'} ) {
check_status();
exit;
}
########################################################
# start of parameters that need time
########################################################
# two extra parameters are expected if you are using options that need time
if ( @ARGV < 2 ) {
usage();
#print "No time range specified\n";
exit();
}
elsif ( @ARGV > 3 ) {
print "Too many unknown parameters\n";
exit;
}
elsif ( @ARGV == 2 ) {
$opt{'time1'} = $ARGV[0];
$opt{'time2'} = $ARGV[1];
}
if ( $opt{'loadavg'} ) {
loadavg( \%opt );
exit;
}
if ( $opt{'io'} ) {
snap_io( \%opt );
exit;
}
if ( $opt{'print'} ) {
snap_print_range( \%opt );
exit;
}
if ( $opt{'network'} ) {
snap_network( \%opt );
exit;
}
# I don't think the logic flow should ever hit this, but just in case
usage();
exit;
sub snap_network {
my %opt = %{ shift @_ };
my $time1 = $opt{'time1'};
my $time2 = $opt{'time2'};
my $snapshot_dir = $opt{'dir'};
my $detail_level = $opt{'verbose'};
my $max_lines = $opt{'max-lines'};
my $print_cpu = $opt{'print_cpu'};
my $print_memory = $opt{'print_memory'};
# old school 80 is standard, but 145 works well with 1366 width monitor
my $line_length = $opt{'line_length'};
#root_dir is legacy param, will remove later
my $root_dir = "";
# the default formatting where the process ID is added needs 16 lines
# subtracting 16 here will make the specified width more "true"
$line_length = $line_length - 16;
module_sanity_check();
eval("use Time::Piece;");
if ($@) {
print "***\nCould not install Time::Piece - try manually installing.\n***\n";
exit;
}
my ( $time1_hour, $time1_minute, $time2_hour, $time2_minute ) = &parse_check_time( $time1, $time2 );
my @snap_log_files = &get_range( $root_dir, $snapshot_dir, $time1_hour, $time1_minute, $time2_hour, $time2_minute );
my %ip_connections;
my ( %localip, %foreignip );
#print "Time\t1min-avg\t5min-avg\t15min-avg\n";
foreach my $file_name (@snap_log_files) {
open( my $FILE, "<", $file_name ) or next; #die "Couldn't open file: $!";
my $string = join( "", <$FILE> );
close($FILE);
my @lines;
# reading line by line to split the sections might be faster
my $matchme = "^Active Internet connections [^\n]+\n";
#my $matchme = "^Process List:\n\nUSER[^\n]+COMMAND\n";
if ( $string =~ /$matchme(.*)\nActive UNIX domain sockets \(servers and established\)/sm ) {
my $baseString = $1;
@lines = split( /\n/, $baseString );
}
# could add ports in the future and connection state
# should skip listen and time_wait entries
foreach my $line (@lines) {
if ( $line =~ /[a-z]{3}\s+\d+\s+\d+\s+(\d+\.\d+\.\d+\.\d+):\d+\s+(\d+\.\d+\.\d+\.\d+):\d+\s+(?!TIME_WAIT)/ ) {
if ( $ip_connections{$1}{$2} ) {
$ip_connections{$1}{$2} += 1;
}
else {
$ip_connections{$1}{$2} = 1;
}
}
}
}
foreach my $localip ( keys %ip_connections ) {
my @sorted_ip = sort { $ip_connections{$localip}{$b} <=> $ip_connections{$localip}{$a} } keys %{ $ip_connections{$localip} };
print "$localip: \n";
for (@sorted_ip) {
printf "\t%-15s %-8d\n", $_, $ip_connections{$localip}{$_};
}
print "\n";
}
}
sub usage {
my $text = <<"ENDTXT";
USAGE:
./sys-snap.pl [options]
--start : Creates, disowns, and drops 'sys-snap.pl --start' process into the background
--stop : stops sys-snap after confirming PID info
--check : Checks if sys-snap is running
--print <start-time end-time> : Where time HH:MM, prints basic usage by default
--network <start-time end-time> : Prints IP connections durring time range
--v | v : verbose output from --print
--max-lines : max number of processes printed per mem/cpu section, default is 20
--ll : line length, default is 145
--no-cpu | --nc : skips CPU output
--no-mem | --nm : skips memory output
--loadavg <start-time end-time>: Where time HH:MM, prints load average for time period - default 10 min interval
--dir : specifies a different sys-snap folder for --print and --loadavg
ENDTXT
print $text;
exit;
}
sub snap_io {
eval("use Time::Piece;");
my %opt = %{ shift @_ };
my $time1 = $opt{'time1'};
my $time2 = $opt{'time2'};
my $interval = $opt{'interval'};
my $snapshot_dir = $opt{'dir'};
#root_dir is legacy param, will remove later
my $root_dir = "";
if ( $interval > 60 || $interval < 0 ) {
$interval = 10;
}
my ( $time1_hour, $time1_minute, $time2_hour, $time2_minute ) = &parse_check_time( $time1, $time2 );
my @snap_log_files = &get_range( $root_dir, $snapshot_dir, $time1_hour, $time1_minute, $time2_hour, $time2_minute );
print "avg-cpu:\t%user\t%nice\t%system\t%iowait\t%steal\t%idle\n";
foreach my $file_name (@snap_log_files) {
# load information is currently printed to the first line
# only need to read first line
open( my $FILE, "<", $file_name ) or next; #die "Couldn't open file: $!";
#my $string = <$FILE>;
my $string = join( "", <$FILE> );
my ($min) = $string =~ m{^\d+\s+\d+\s+(\d+)\s+Load Average:}g;
#print "$min\n";
close($FILE);
my @lines;
if ( $string =~ /^IO wait:\n(.*)\nMYSQL Processes:$/sm ) {
my $baseString = $1;
@lines = split( /\n/, $baseString );
}
foreach my $line (@lines) {
my ( $io_user, $nice, $io_system, $io_wait, $steal, $idle );
( $io_user, $nice, $io_system, $io_wait, $steal, $idle ) = $line =~ m{^\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)};
if ( defined $io_user && ( $min % $interval == 0 ) ) {
print "\t\t$io_user\t$nice\t$io_system\t$io_wait\t$steal\t$idle\n";
}
}
}
return;
}
sub loadavg {
eval("use Time::Piece;");
my %opt = %{ shift @_ };
my $time1 = $opt{'time1'};
my $time2 = $opt{'time2'};
my $interval = $opt{'interval'};
my $snapshot_dir = $opt{'dir'};
#root_dir is legacy param, will remove later
my $root_dir = "";
if ( $interval > 60 || $interval < 0 ) {
$interval = 10;
}
my ( $time1_hour, $time1_minute, $time2_hour, $time2_minute ) = &parse_check_time( $time1, $time2 );
my @snap_log_files = &get_range( $root_dir, $snapshot_dir, $time1_hour, $time1_minute, $time2_hour, $time2_minute );
print "Time\t1min-avg\t5min-avg\t15min-avg\n";
foreach my $file_name (@snap_log_files) {
# load information is currently printed to the first line
# only need to read first line
open( my $FILE, "<", $file_name ) or next; #die "Couldn't open file: $!";
my $string = <$FILE>;
close($FILE);
my ( $avg1min, $avg5min, $avg15min, $hour, $min );
( $hour, $min, $avg1min, $avg5min, $avg15min ) = $string =~ m{^\d+\s+(\d+)\s+(\d+)\s+Load Average: (\d+\.\d+)\s(\d+\.\d+)\s(\d+\.\d+)\s.*$};
if ( defined $hour && defined $min & defined $avg1min && defined $avg5min && defined $avg15min && ( $min % $interval == 0 ) ) {
$min = "0" . $min if ( $min =~ m{^\d$} );
print "$hour:$min\t$avg1min\t\t$avg5min\t\t$avg15min\n";
}
}
return;
}
sub stop_syssnap {
if ( my $pid = check_status() ) {
print "Stop this process (y/n)?:";
#my $choice = "0";
#$choice = <STDIN>;
#while ( $choice !~ /[yn]/i ) {
# print "Stop this process (y/n)?:";
# $choice = <STDIN>;
# chomp($choice);
#}
#if ( $choice =~ /[y]/i ) {
# print "Stopping $pid\n";
# kill 9, $pid;
# unlink $opt{'pidfile'};
# exit;
#}
#else { print "Exiting...\n"; exit; }
kill 9, $pid;
unlink $opt{'pidfile'};
print "Exiting...\n";
exit;
}
return;
}
sub check_status {
my $pid;
my $pidfh;
my $pidfile = $opt{'pidfile'};
my $status = 0;
sysopen( $pidfh, $pidfile, O_RDWR | O_CREAT )
or die "Could not open $pidfile: $!\n";
if ( flock( $pidfh, LOCK_NB | LOCK_EX ) ) {
print "Sys-snap not currently running.\n";
flock( $pidfh, LOCK_UN )
or die "Problem releasing lock on '$pidfile': $!\n";
$status = 0;
}
else {
print "Sys-snap is running, PID: ";
while ( $pid = <$pidfh> ) {
print "'$pid'\n";
$status = $pid;
}
}
return $status;
}
sub parse_check_time {
my $time1 = shift;
my $time2 = shift;
if ( !defined $time1 || !defined $time2 ) { print "Need 2 parameters, \"./snap-print start-time end-time\"\n"; exit; }
my ( $time1_hour, $time1_minute, $time2_hour, $time2_minute );
if ( ( $time1_hour, $time1_minute ) = $time1 =~ m{^(\d{1,2}):(\d{2})$} ) {
if ( $time1_hour >= 0 && $time1_hour <= 23 && $time1_minute >= 0 && $time1_minute <= 59 ) {
#print "$time1_hour $time1_minute\n";
}
else { print "Fail: Fictitious time.\n"; exit; }
}
else { print "Fail: Could not parse start time\n"; exit; }
if ( ( $time2_hour, $time2_minute ) = $time2 =~ m{(\d{1,2}):(\d{2})} ) {
if ( $time2_hour >= 0 && $time2_hour <= 23 && $time2_minute >= 0 && $time2_minute <= 59 ) {
#print $time2_hour $time2_minute\n";
}
else { print "Fail: Fictitious time.\n"; exit; }
}
else { print "Fail: Could not parse end time\n"; exit; }
if ( defined $time1_hour && defined $time2_hour ) { return ( $time1_hour, $time1_minute, $time2_hour, $time2_minute ); }
return 0;
}
sub snap_print_range {
my %opt = %{ shift @_ };
my $time1 = $opt{'time1'};
my $time2 = $opt{'time2'};
my $snapshot_dir = $opt{'dir'};
my $detail_level = $opt{'verbose'};
my $max_lines = $opt{'max-lines'};
my $print_cpu = $opt{'print_cpu'};
my $print_memory = $opt{'print_memory'};
# old school 80 is standard, but 145 works well with 1366 width monitor
my $line_length = $opt{'line_length'};
#root_dir is legacy param, will remove later
my $root_dir = "";
# the default formatting where the process ID is added needs 16 lines
# subtracting 16 here will make the specified width more "true"
$line_length = $line_length - 16;
if ( !defined $time1 || !defined $time2 ) { print "Need 2 parameters, \"./snap-print start-time end-time\"\n"; exit; }
module_sanity_check();
eval("use Time::Piece;");
if ($@) {
print "***\nCould not install Time::Piece - try manually installing.\n***\n";
exit;
}
use Time::Seconds;
# not using this yet, but if we parse a range of data that crosses this file the resulting data is noncontigous
# and might be misleading. printing a warning might be apropriate in this scenario or having some other flag
# to indicate this has happened
#my $newest_file = qx(ls -la ${root_dir}/system-snapshot/current);
my ( $time1_hour, $time1_minute, $time2_hour, $time2_minute ) = &parse_check_time( $time1, $time2 );
# get the files we want to read
my @snap_log_files = &get_range( $root_dir, $snapshot_dir, $time1_hour, $time1_minute, $time2_hour, $time2_minute );
my ( $tmp1, $tmp2 ) = &read_logs( \@snap_log_files );
# users cumulative CPU and Mem score
my %basic_usage = %$tmp1;
#raw data from logs
my %process_list_data = %$tmp2;
# weighted process & memory
my %users_wcpu_process;
my %users_wmemory_process;
if ( $detail_level == 0 ) { &run_basic( \%basic_usage, $print_cpu, $print_memory ); exit }
# adding up memory and CPU usage per user's process
foreach my $user ( sort keys %process_list_data ) {
foreach my $process ( sort keys %{ $process_list_data{$user} } ) {
$users_wcpu_process{$user}{$process} += $process_list_data{$user}{$process}{'cpu'};
$users_wmemory_process{$user}{$process} += $process_list_data{$user}{$process}{'memory'};
}
}
my $sort_param;
if ($print_cpu) { $sort_param = "cpu"; }
else { $sort_param = "memory"; }
foreach my $user ( sort { $basic_usage{$b}->{$sort_param} <=> $basic_usage{$a}->{$sort_param} } keys %basic_usage ) {
printf "user: %-15s", $user;
my $num_lines = 0;
if ($print_cpu) {
my @sorted_cpu = sort { $users_wcpu_process{$user}{$b} <=> $users_wcpu_process{$user}{$a} } keys %{ $users_wcpu_process{$user} };
printf "\n\tcpu-score: %-10.2f\n", $basic_usage{$user}{'cpu'};
for (@sorted_cpu) {
printf "\t\tC: %4.2f proc: ", $users_wcpu_process{$user}{$_};
print substr( $_, 0, $line_length ) . "\n";
if ( $num_lines >= $max_lines - 1 ) { last; }
else { $num_lines += 1; }
}
}
$num_lines = 0;
if ($print_memory) {
my @sorted_mem = sort { $users_wmemory_process{$user}{$b} <=> $users_wmemory_process{$user}{$a} } keys %{ $users_wmemory_process{$user} };
printf "\n\tmemory-score: %-11.2f\n", $basic_usage{$user}{'memory'};
for (@sorted_mem) {
printf "\t\tM: %4.2f proc: ", $users_wmemory_process{$user}{$_};
print substr( $_, 0, $line_length ) . "\n";
if ( $num_lines >= $max_lines - 1 ) { last; }
else { $num_lines += 1; }
}
}
print "\n";
}
exit;
}
## should be rewritten to take parameters of log subsections to be read
# returns hash of hashes
sub read_logs {
my $tmp = shift;
my @snap_log_files = @$tmp;
my %process_list_data;
my %basic_usage;
foreach my $file_name (@snap_log_files) {
my @lines;
open( my $FILE, "<", $file_name ) or next; #die "Couldn't open file: $!";
my $string = join( "", <$FILE> );
close($FILE);
# reading line by line to split the sections might be faster
my $matchme = "^Process List:\n\nUSER[^\n]+COMMAND\n";
if ( $string =~ /^$matchme(.*)\nNetwork Connections\:$/sm ) {
my $baseString = $1;
@lines = split( /\n/, $baseString );
}
foreach my $l (@lines) {
my ( $user, $cpu, $memory, $command );
( $user, $cpu, $memory, $command ) = $l =~ m{^(\w+)\s+\d+\s+(\d{1,2}\.\d)\s+(\d{1,2}\.\d).*\d{1,2}:\d{2}\s+(.*)$};
if ( defined $user && defined $cpu && defined $memory && defined $command ) {
if ( $user !~ m/[a-zA-Z0-9_\.\-]+/ ) { next; }
if ( $cpu !~ m/[0-9\.]+/ && $memory !~ m/[0-9\.]+/ ) { next; }
$basic_usage{$user}{'memory'} += $memory;
$basic_usage{$user}{'cpu'} += $cpu;
# agrigate hash? of commands - roll object
# if the process is the same, accumulate it, if not create it
# assuming if we have a memory value for a command, we should have a cpu value - nothing can ever go wrong here :smiley face:
if ( defined $process_list_data{$user}{$command}{'memory'} ) {
$process_list_data{$user}{$command}{'memory'} += $memory;
$process_list_data{$user}{$command}{'cpu'} += $cpu;
}
else {
$process_list_data{$user}{$command}{'cpu'} = $cpu;
$process_list_data{$user}{$command}{'memory'} = $memory;
}
}
}
}
return ( \%basic_usage, \%process_list_data );
}
# returns ordered array of stings that represent file location
# could create $accuracy variable to run modulo integers for faster processing at expense of accuracy
sub get_range {
my $root_dir = shift;
my $snapshot_dir = shift;
my $time1_hour = shift;
my $time1_minute = shift;
my $time2_hour = shift;
my $time2_minute = shift;
my $time1 = "$time1_hour:$time1_minute";
my $time2 = "$time2_hour:$time2_minute";
my @snap_log_files;
my ( $file_hour, $file_minute );
# Even if we want to ignore the date, Time::Piece will create one. This is probably easier than rolling a custom time cycle for over night periods such as 23:57 0:45,
# and should make modification easier if longer date ranges are added too.
# Mind the date format 'DAY MONTH YEAR(XXXX)'
my $start_time = Time::Piece->strptime( "2-2-1993 $time1", "%d-%m-%Y %H:%M" );
my $end_time;
if ( $time1_hour < $time2_hour || ( $time1_hour == $time2_hour && $time1_minute < $time2_minute ) ) {
$end_time = Time::Piece->strptime( "2-2-1993 $time2", "%d-%m-%Y %H:%M" );
}
else {
$end_time = Time::Piece->strptime( "3-2-1993 $time2", "%d-%m-%Y %H:%M" );
}
while ( $start_time <= $end_time ) {
#print $start_time->strftime('%H:%M') . "\n";
( $file_hour, $file_minute ) = split( /:/, $start_time->strftime('%H:%M') );
#sys-snap not currently appending 0's to the front of files
$file_minute =~ s/^0(\d)$/$1/;
$file_hour =~ s/^0(\d)$/$1/;
#print "$root_dir$snapshot_dir/$file_hour/$file_minute.log\n";
push @snap_log_files, "$root_dir$snapshot_dir/$file_hour/$file_minute.log";
$start_time += 60;
}
return @snap_log_files;
}
# since mem and cpu info gets printed to the same line, we already have the data at this point,
# and even sorting a large number of users by usage is relativly inexpensive, just going to mute unwanted output
sub run_basic {
my $tmp = shift;
my $print_cpu = shift;
my $print_memory = shift;
my %basic_usage;
%basic_usage = %$tmp;
my $sortby = 'cpu';
if ( $print_cpu != 1 ) { $sortby = 'memory'; }
foreach my $key (
sort { $basic_usage{$b}->{$sortby} <=> $basic_usage{$a}->{$sortby} }
keys %basic_usage
) {
my $value = $basic_usage{$key};
#printf( "user: %-15s\n\tcpu-score: %-12.2f \n\tmemory-score: %-12.2f\n\n", $key, $value->{cpu}, $value->{memory} );
printf( "user: %-15s\n", $key );
printf( "\tcpu-score: %-12.2f\n", $value->{cpu} ) if $print_cpu;
printf( "\tmemory-score: %-12.2f\n", $value->{memory} ) if $print_memory;
}
print "\n";
exit;
}
sub run_install {
if ( not check_status ) {
print "Start sys-snap logging to '/opt/sys-snap/system-snapshot/' (y/n)?:";
my $choice = "0";
#$choice = <STDIN>;
#while ( $choice !~ /[yn]/i ) {
# print "Start sys-snap logging to '/opt/sys-snap/system-snapshot/' (y/n)?:";
# $choice = <STDIN>;
# chomp($choice);
#}
#if ( $choice =~ /[y]/i ) {
# print "Starting...\n";
#}
#else { print "Exiting...\n"; exit; }
print "Starting...\n";
}
else {
print "Unable to start, only one sys-snap process should be active at a time\n";
exit;
}
use File::Path qw(rmtree);
use POSIX qw(setsid);
###############
# Set Options #
###############
# Set the time between snapshots in seconds
my $sleep_time = 60;
# The base directory under which to build the directory where snapshots are stored.
my $root_dir = '/opt/sys-snap';
# Sometimes you won't have mysql and/or you won't have the root password to put in a .my.cnf file
# if that's the case, set this to 0
my $mysql = 1;
# If the server has lighttpd or some other webserver, set this to 0
# cPanel is autodetected later, so this setting is not used if running cPanel.
my $apache = 1;
# If you want extended data, set this to 1
my $max_data = 0;
# Get the date, hour, and min for various tasks
my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time);
$year += 1900; # Format year correctly
$mon++; # Format month correctly
$mon = 0 . $mon if $mon < 10;
$mday = 0 . $mday if $mday < 10;
my $date = $year . $mon . $mday;
# Ensure target directory exists and is writable
if ( !-d $root_dir ) {
die "$root_dir is not a directory\n";
}
elsif ( !-w $root_dir ) {
die "$root_dir is not writable\n";
}
if ( -d "$root_dir/system-snapshot" ) {
system 'tar', 'czf', "${root_dir}/system-snapshot.${date}.${hour}${min}.tar.gz", "${root_dir}/system-snapshot";
rmtree("$root_dir/system-snapshot");
}
if ( !-d "$root_dir/system-snapshot" ) {
mkdir "$root_dir/system-snapshot";
}
# try to split process into background
chdir '/' or die "Can't chdir to /: $!";
open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
defined( my $pid = fork ) or die "Can't fork: $!";
exit if $pid;
print "$pid\n";
setsid or die "Can't start a new session: $!";
open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
# Create a PID file for the new child process
my $childpid = $$;
my $pidfh;
my $pidfile = $opt{'pidfile'};
sysopen( $pidfh, $pidfile, O_RDWR | O_CREAT )
or die "Could not open $pidfile: $!\n";
print $pidfh "$childpid";
flock( $pidfh, LOCK_NB | LOCK_EX )
or die "Could not get lock on $pidfile: $!\n";
##########
# Main() #
##########
while (1) {
# Ensure we have a current date/time
( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time);
$year += 1900; # Format year correctly
$mon++; # Format month correctly
$mon = 0 . $mon if $mon < 10;
$mday = 0 . $mday if $mday < 10;
$date = $year . $mon . $mday;
# go to the next log file
mkdir "$root_dir/system-snapshot/$hour";
my $current_interval = "$hour/$min";
my $logfile = "$root_dir/system-snapshot/$current_interval.log";
open( my $LOG, '>', $logfile )
or die "Could not open log file $logfile, $!\n";
# start actually logging #
my $load = qx(cat /proc/loadavg);
#print $LOG "Load Average:\n\n"; # without this line, you can get historical loads with head -n1 *
print $LOG "$date $hour $min Load Average: $load\n";
print $LOG qx(uptime | sed 's/^\\s//g'; cat /proc/uptime), "\n";
print $LOG "Memory Usage:\n\n";
print $LOG qx(cat /proc/meminfo), "\n";
print $LOG "Virtual Memory Stats:\n\n";
print $LOG qx(vmstat 1 10), "\n";
print $LOG "Process List:\n\n";
print $LOG qx(ps awwxf -o user:20,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,args), "\n";
print $LOG "Network Connections:\n\n";
print $LOG qx(netstat -anp), "\n";
print $LOG "IO wait:\n\n";
print $LOG qx(iostat), "\n";
# optional logging
if ($mysql) {
print $LOG "MYSQL Processes:\n\n";
print $LOG qx(mysqladmin proc), "\n";
}
print $LOG "Apache Processes:\n\n";
if ( -f '/usr/local/cpanel/cpanel' ) {
print $LOG qx(/app/bin/webstatus.sh), "\n";
}
elsif ($apache) {
print $LOG qx#lynx -width=1024 -dump http://localhost/server-status | egrep '(Client.+Request|GET|POST|HEAD)'#, "\n";
}
if ($max_data) {
print $LOG "Process List for user Nobody:\n\n";
my @process_list = qx(ps aux | grep [n]obody | awk '{print \$2}');
foreach my $process (@process_list) {
print $LOG qx(ls -al /proc/$process | grep cwd | grep home);
}
print $LOG "List of Open Files:\n\n";
print $LOG qx(lsof), "\n";
}
close $LOG;
# rotate the "current" pointer
rmtree("$root_dir/system-snapshot/current");
symlink "${current_interval}.log", "$root_dir/system-snapshot/current";
sleep($sleep_time);
}
}
sub module_sanity_check {
eval("use Time::Piece;");
if ($@) {
print "WARNING: Perl Module Time::Piece.pm not installed!\n";
print "Would you like sys-snap to attempt to install this moduel(y/n):";
my $choice = <STDIN>;
if ( $choice =~ /yes|y/i ) {
print "Installing now - Please stand by.\n";
system("cpan -i Time::Piece");
}
else {
exit;
}
}
return;
}