mirror of https://github.com/captn3m0/Scripts.git
Installed parallel and other utils from the package
This commit is contained in:
parent
b652cbcab3
commit
569c4a7715
|
@ -0,0 +1,882 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
# Copyright (C) 2004,2005,2006,2006,2008,2009,2010 Ole Tange,
|
||||
# http://ole.tange.dk
|
||||
#
|
||||
# Copyright (C) 2010,2011,2012,2013,2014,2015 Ole Tange,
|
||||
# http://ole.tange.dk and Free Software Foundation, Inc.
|
||||
#
|
||||
# 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, see <http://www.gnu.org/licenses/>
|
||||
# or write to the Free Software Foundation, Inc., 51 Franklin St,
|
||||
# Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
$Global::progname="niceload";
|
||||
$Global::version = 20150522;
|
||||
Getopt::Long::Configure("bundling","require_order");
|
||||
get_options_from_array(\@ARGV) || die_usage();
|
||||
if($opt::version) {
|
||||
version();
|
||||
exit 0;
|
||||
}
|
||||
if($opt::help) {
|
||||
help();
|
||||
exit 0;
|
||||
}
|
||||
if($opt::factor and $opt::suspend) {
|
||||
# You cannot have --suspend and --factor
|
||||
help();
|
||||
exit;
|
||||
}
|
||||
|
||||
if(not (defined $opt::start_io or defined $opt::run_io
|
||||
or defined $opt::start_load or defined $opt::run_load
|
||||
or defined $opt::start_mem or defined $opt::run_mem
|
||||
or defined $opt::start_noswap or defined $opt::run_noswap
|
||||
or defined $opt::io or defined $opt::load
|
||||
or defined $opt::mem or defined $opt::noswap)) {
|
||||
# Default is --runload=1
|
||||
$opt::run_load = 1;
|
||||
}
|
||||
|
||||
if(not defined $opt::start_io) { $opt::start_io = $opt::io; }
|
||||
if(not defined $opt::run_io) { $opt::run_io = $opt::io; }
|
||||
if(not defined $opt::start_load) { $opt::start_load = $opt::load; }
|
||||
if(not defined $opt::run_load) { $opt::run_load = $opt::load; }
|
||||
if(not defined $opt::start_mem) { $opt::start_mem = $opt::mem; }
|
||||
if(not defined $opt::run_mem) { $opt::run_mem = $opt::mem; }
|
||||
if(not defined $opt::start_noswap) { $opt::start_noswap = $opt::noswap; }
|
||||
if(not defined $opt::run_noswap) { $opt::run_noswap = $opt::noswap; }
|
||||
|
||||
if(defined $opt::load) { multiply_binary_prefix($opt::load); }
|
||||
|
||||
my $limit = Limit->new();
|
||||
my $process = Process->new($opt::nice,@ARGV);
|
||||
$::exitstatus = 0;
|
||||
if(@opt::prg) {
|
||||
# Find all pids of prg
|
||||
my $out = `pidof -x @opt::prg`;
|
||||
$process->set_pid(split /\s+/,$out);
|
||||
$::resume_process = $process;
|
||||
$SIG{TERM} = $SIG{INT} = \&resume;
|
||||
} elsif(@opt::pid) {
|
||||
$process->set_pid(@opt::pid);
|
||||
$::resume_process = $process;
|
||||
$SIG{TERM} = $SIG{INT} = \&resume;
|
||||
} elsif (@ARGV) {
|
||||
# Wait until limit is below start_limit and run_limit
|
||||
while($limit->over_start_limit()
|
||||
or
|
||||
($limit->hard() and $limit->over_run_limit())) {
|
||||
$limit->sleep_for_recheck();
|
||||
}
|
||||
$process->start();
|
||||
}
|
||||
|
||||
while($process->is_alive()) {
|
||||
if($limit->over_run_limit()) {
|
||||
$process->suspend();
|
||||
$limit->sleep_for_recheck();
|
||||
if(not $limit->hard()) {
|
||||
$process->resume();
|
||||
$limit->sleep_while_running();
|
||||
}
|
||||
} else {
|
||||
$process->resume();
|
||||
$limit->sleep_while_running();
|
||||
}
|
||||
}
|
||||
|
||||
exit($::exitstatus);
|
||||
|
||||
sub resume {
|
||||
$::resume_process->resume();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
sub uniq {
|
||||
# Remove duplicates and return unique values
|
||||
return keys %{{ map { $_ => 1 } @_ }};
|
||||
}
|
||||
|
||||
sub multiply_binary_prefix {
|
||||
# Evalualte numbers with binary prefix
|
||||
# k=10^3, m=10^6, g=10^9, t=10^12, p=10^15, e=10^18, z=10^21, y=10^24
|
||||
# K=2^10, M=2^20, G=2^30, T=2^40, P=2^50, E=2^70, Z=2^80, Y=2^80
|
||||
# Ki=2^10, Mi=2^20, Gi=2^30, Ti=2^40, Pi=2^50, Ei=2^70, Zi=2^80, Yi=2^80
|
||||
# ki=2^10, mi=2^20, gi=2^30, ti=2^40, pi=2^50, ei=2^70, zi=2^80, yi=2^80
|
||||
# 13G = 13*1024*1024*1024 = 13958643712
|
||||
my $s = shift;
|
||||
$s =~ s/k/*1000/g;
|
||||
$s =~ s/M/*1000*1000/g;
|
||||
$s =~ s/G/*1000*1000*1000/g;
|
||||
$s =~ s/T/*1000*1000*1000*1000/g;
|
||||
$s =~ s/P/*1000*1000*1000*1000*1000/g;
|
||||
$s =~ s/E/*1000*1000*1000*1000*1000*1000/g;
|
||||
$s =~ s/Z/*1000*1000*1000*1000*1000*1000*1000/g;
|
||||
$s =~ s/Y/*1000*1000*1000*1000*1000*1000*1000*1000/g;
|
||||
$s =~ s/X/*1000*1000*1000*1000*1000*1000*1000*1000*1000/g;
|
||||
|
||||
$s =~ s/Ki?/*1024/gi;
|
||||
$s =~ s/Mi?/*1024*1024/gi;
|
||||
$s =~ s/Gi?/*1024*1024*1024/gi;
|
||||
$s =~ s/Ti?/*1024*1024*1024*1024/gi;
|
||||
$s =~ s/Pi?/*1024*1024*1024*1024*1024/gi;
|
||||
$s =~ s/Ei?/*1024*1024*1024*1024*1024*1024/gi;
|
||||
$s =~ s/Zi?/*1024*1024*1024*1024*1024*1024*1024/gi;
|
||||
$s =~ s/Yi?/*1024*1024*1024*1024*1024*1024*1024*1024/gi;
|
||||
$s =~ s/Xi?/*1024*1024*1024*1024*1024*1024*1024*1024*1024/gi;
|
||||
$s = eval $s;
|
||||
return $s;
|
||||
}
|
||||
|
||||
sub get_options_from_array {
|
||||
# Run GetOptions on @array
|
||||
# Returns:
|
||||
# true if parsing worked
|
||||
# false if parsing failed
|
||||
# @array is changed
|
||||
my $array_ref = shift;
|
||||
# A bit of shuffling of @ARGV needed as GetOptionsFromArray is not
|
||||
# supported everywhere
|
||||
my @save_argv;
|
||||
my $this_is_ARGV = (\@::ARGV == $array_ref);
|
||||
if(not $this_is_ARGV) {
|
||||
@save_argv = @::ARGV;
|
||||
@::ARGV = @{$array_ref};
|
||||
}
|
||||
my @retval = GetOptions
|
||||
("debug|D" => \$opt::debug,
|
||||
"factor|f=s" => \$opt::factor,
|
||||
"hard|H" => \$opt::hard,
|
||||
"soft|S" => \$opt::soft,
|
||||
"sensor=s" => \$opt::sensor,
|
||||
|
||||
"si|sio|startio|start-io=s" => \$opt::start_io,
|
||||
"ri|rio|runio|run-io=s" => \$opt::run_io,
|
||||
"io|I=s" => \$opt::io,
|
||||
|
||||
"sl|startload|start-load=s" => \$opt::start_load,
|
||||
"rl|runload|run-load=s" => \$opt::run_load,
|
||||
"load|L|l=s" => \$opt::load,
|
||||
|
||||
"sm|startmem|start-mem=s" => \$opt::start_mem,
|
||||
"rm|runmem|run-mem=s" => \$opt::run_mem,
|
||||
"mem|M=s" => \$opt::mem,
|
||||
|
||||
"sn|startnoswap|start-noswap|start-no-swap" => \$opt::start_noswap,
|
||||
"rn|runnoswap|run-noswap|run-no-swap" => \$opt::run_noswap,
|
||||
"noswap|N" => \$opt::noswap,
|
||||
|
||||
# niceload -l -1 --sensor 'cat /sys/class/power_supply/BAT0/status /proc/acpi/battery/BAT0/state 2>/dev/null |grep -i -q discharging; echo $?'
|
||||
"battery|B" => \$opt::battery,
|
||||
|
||||
"nice|n=i" => \$opt::nice,
|
||||
"program|prg=s" => \@opt::prg,
|
||||
"process|pid|p=s" => \@opt::pid,
|
||||
"suspend|s=s" => \$opt::suspend,
|
||||
"recheck|t=s" => \$opt::recheck,
|
||||
"quote|q" => \$opt::quote,
|
||||
"help|h" => \$opt::help,
|
||||
"verbose|v" => \$opt::verbose,
|
||||
"version|V" => \$opt::version,
|
||||
);
|
||||
if(not $this_is_ARGV) {
|
||||
@{$array_ref} = @::ARGV;
|
||||
@::ARGV = @save_argv;
|
||||
}
|
||||
return @retval;
|
||||
}
|
||||
|
||||
|
||||
sub die_usage {
|
||||
help();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
|
||||
sub help {
|
||||
print q{
|
||||
Usage:
|
||||
niceload [-v] [-n niceness] [-L loadavg] [-I io] [-N] [-M mem]
|
||||
[-s suspend_sec|-f factor] [-H] [-S]
|
||||
command or -p pid
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
sub die_bug {
|
||||
my $bugid = shift;
|
||||
print STDERR
|
||||
("$Global::progname: This should not happen. You have found a bug.\n",
|
||||
"Please contact <parallel\@gnu.org> and include:\n",
|
||||
"* The version number: $Global::version\n",
|
||||
"* The bugid: $bugid\n",
|
||||
"* The command line being run\n",
|
||||
"* The files being read (put the files on a webserver if they are big)\n",
|
||||
"\n",
|
||||
"If you get the error on smaller/fewer files, please include those instead.\n");
|
||||
exit(255);
|
||||
}
|
||||
|
||||
|
||||
sub usleep {
|
||||
# Sleep this many milliseconds.
|
||||
my $secs = shift;
|
||||
::debug("Sleeping ",$secs," millisecs\n");
|
||||
select(undef, undef, undef, $secs/1000);
|
||||
}
|
||||
|
||||
|
||||
sub debug {
|
||||
if($opt::debug) {
|
||||
print STDERR @_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub my_dump {
|
||||
# Returns:
|
||||
# ascii expression of object if Data::Dump(er) is installed
|
||||
# error code otherwise
|
||||
my @dump_this = (@_);
|
||||
eval "use Data::Dump qw(dump);";
|
||||
if ($@) {
|
||||
# Data::Dump not installed
|
||||
eval "use Data::Dumper;";
|
||||
if ($@) {
|
||||
my $err = "Neither Data::Dump nor Data::Dumper is installed\n".
|
||||
"Not dumping output\n";
|
||||
print STDERR $err;
|
||||
return $err;
|
||||
} else {
|
||||
return Dumper(@dump_this);
|
||||
}
|
||||
} else {
|
||||
eval "use Data::Dump qw(dump);";
|
||||
return (Data::Dump::dump(@dump_this));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub version {
|
||||
# Returns: N/A
|
||||
print join("\n",
|
||||
"GNU $Global::progname $Global::version",
|
||||
"Copyright (C) 2004,2005,2006,2007,2008,2009 Ole Tange",
|
||||
"Copyright (C) 2010,2011 Ole Tange and Free Software Foundation, Inc.",
|
||||
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>",
|
||||
"This is free software: you are free to change and redistribute it.",
|
||||
"GNU $Global::progname comes with no warranty.",
|
||||
"",
|
||||
"Web site: http://www.gnu.org/software/parallel\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
sub max {
|
||||
# Returns:
|
||||
# Maximum value of array
|
||||
my $max;
|
||||
for (@_) {
|
||||
# Skip undefs
|
||||
defined $_ or next;
|
||||
defined $max or do { $max = $_; next; }; # Set $_ to the first non-undef
|
||||
$max = ($max > $_) ? $max : $_;
|
||||
}
|
||||
return $max;
|
||||
}
|
||||
|
||||
sub min {
|
||||
# Returns:
|
||||
# Minimum value of array
|
||||
my $min;
|
||||
for (@_) {
|
||||
# Skip undefs
|
||||
defined $_ or next;
|
||||
defined $min or do { $min = $_; next; }; # Set $_ to the first non-undef
|
||||
$min = ($min < $_) ? $min : $_;
|
||||
}
|
||||
return $min;
|
||||
}
|
||||
|
||||
|
||||
package Process;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $nice = shift;
|
||||
my @ARGV = @_;
|
||||
if($nice) {
|
||||
unshift(@ARGV, "nice", "-n", $nice);
|
||||
}
|
||||
return bless {
|
||||
'running' => 0, # Is the process running now?
|
||||
'command' => [@ARGV],
|
||||
}, ref($class) || $class;
|
||||
}
|
||||
|
||||
sub pgrp {
|
||||
my $self = shift;
|
||||
my @pgrp;
|
||||
if(not $self->{'pgrp'}) {
|
||||
for(@{$self->{'pids'}}) {
|
||||
push @pgrp,-getpgrp($_);
|
||||
}
|
||||
@pgrp = ::uniq(@pgrp);
|
||||
@{$self->{'pgrp'}} = @pgrp;
|
||||
}
|
||||
return @{$self->{'pgrp'}};
|
||||
}
|
||||
|
||||
sub set_pid {
|
||||
my $self = shift;
|
||||
push(@{$self->{'pids'}},@_);
|
||||
$self->{'running'} = 1;
|
||||
$::exitstatus = 0;
|
||||
}
|
||||
|
||||
sub start {
|
||||
# Start the program
|
||||
my $self = shift;
|
||||
::debug("Starting @{$self->{'command'}}\n");
|
||||
$self->{'running'} = 1;
|
||||
if($self->{'pid'} = fork) {
|
||||
# set signal handler to kill children if parent is killed
|
||||
push @{$self->{'pids'}}, $self->{'pid'};
|
||||
$Global::process = $self;
|
||||
$SIG{CHLD} = \&REAPER;
|
||||
$SIG{INT}=\&kill_child_INT;
|
||||
$SIG{TSTP}=\&kill_child_TSTP;
|
||||
$SIG{CONT}=\&kill_child_CONT;
|
||||
sleep 1; # Give child time to setpgrp(0,0);
|
||||
} else {
|
||||
setpgrp(0,0);
|
||||
::debug("Child pid: $$, pgrp: ",getpgrp $$,"\n");
|
||||
::debug("@{$self->{'command'}}\n");
|
||||
if($opt::quote) {
|
||||
system(@{$self->{'command'}});
|
||||
} else {
|
||||
system("@{$self->{'command'}}");
|
||||
}
|
||||
$::exitstatus = $? >> 8;
|
||||
$::exitsignal = $? & 127;
|
||||
::debug("Child exit $::exitstatus\n");
|
||||
exit($::exitstatus);
|
||||
}
|
||||
}
|
||||
|
||||
use POSIX ":sys_wait_h";
|
||||
use POSIX qw(:sys_wait_h);
|
||||
|
||||
sub REAPER {
|
||||
my $stiff;
|
||||
while (($stiff = waitpid(-1, &WNOHANG)) > 0) {
|
||||
# do something with $stiff if you want
|
||||
$::exitstatus = $? >> 8;
|
||||
$::exitsignal = $? & 127;
|
||||
}
|
||||
$SIG{CHLD} = \&REAPER; # install *after* calling waitpid
|
||||
}
|
||||
|
||||
|
||||
sub kill_child_CONT {
|
||||
my $self = $Global::process;
|
||||
::debug("SIGCONT received. Killing @{$self->{'pgrp'}}\n");
|
||||
kill CONT => $self->pgrp();
|
||||
}
|
||||
|
||||
|
||||
sub kill_child_TSTP {
|
||||
my $self = $Global::process;
|
||||
::debug("SIGTSTP received. Killing $self->{'pid'} and self ($$)\n");
|
||||
kill TSTP => $self->pgrp();
|
||||
kill STOP => -$$;
|
||||
kill STOP => $$;
|
||||
}
|
||||
|
||||
|
||||
sub kill_child_INT {
|
||||
my $self = $Global::process;
|
||||
::debug("SIGINT received.\n");
|
||||
if(not @opt::pid) {
|
||||
::debug("Killing $self->{'pid'} Exit\n");
|
||||
kill INT => $self->pgrp();
|
||||
} else {
|
||||
::debug("Continue pids $self->{'pid'} Exit\n");
|
||||
kill CONT => $self->pgrp();
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
sub resume {
|
||||
my $self = shift;
|
||||
::debug("Resume @{$self->{'pids'}}\n");
|
||||
if(not $self->{'running'}) {
|
||||
# - = PID group
|
||||
map { kill "CONT", -$_ } @{$self->{'pids'}};
|
||||
# If using -p it is not in a group
|
||||
map { kill "CONT", $_ } @{$self->{'pids'}};
|
||||
$self->{'running'} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub suspend {
|
||||
my $self = shift;
|
||||
::debug("Suspend @{$self->{'pids'}}\n");
|
||||
if($self->{'running'}) {
|
||||
# - = PID group
|
||||
map { kill "STOP", -$_ } @{$self->{'pids'}};
|
||||
# If using -p it is not in a group
|
||||
map { kill "STOP", $_ } @{$self->{'pids'}};
|
||||
$self->{'running'} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub is_alive {
|
||||
# The process is dead if none of the pids exist
|
||||
my $self = shift;
|
||||
my ($exists) = 0;
|
||||
for my $pid (@{$self->{'pids'}}) {
|
||||
if(kill 0 => $pid) { $exists++ }
|
||||
}
|
||||
::debug("is_alive: $exists\n");
|
||||
return $exists;
|
||||
}
|
||||
|
||||
|
||||
package Limit;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my %limits = @_;
|
||||
my $hard = $opt::soft ? 0 : $opt::hard;
|
||||
my $runio = $opt::run_io ? ::multiply_binary_prefix($opt::run_io) : 0;
|
||||
my $startio = $opt::start_io ? ::multiply_binary_prefix($opt::start_io) : 0;
|
||||
my $runload = $opt::run_load ? ::multiply_binary_prefix($opt::run_load) : 0;
|
||||
my $startload = $opt::start_load ? ::multiply_binary_prefix($opt::start_load) : 0;
|
||||
my $runmem = $opt::run_mem ? ::multiply_binary_prefix($opt::run_mem) : 0;
|
||||
my $startmem = $opt::start_mem ? ::multiply_binary_prefix($opt::start_mem) : 0;
|
||||
my $runnoswap = $opt::run_noswap ? ::multiply_binary_prefix($opt::run_noswap) : 0;
|
||||
my $startnoswap = $opt::start_noswap ? ::multiply_binary_prefix($opt::start_noswap) : 0;
|
||||
my $recheck = $opt::recheck ? ::multiply_binary_prefix($opt::recheck) : 1; # Default
|
||||
my $runtime = $opt::suspend ? ::multiply_binary_prefix($opt::suspend) : 1; # Default
|
||||
|
||||
return bless {
|
||||
'hard' => $hard,
|
||||
'recheck' => $recheck,
|
||||
'runio' => $runio,
|
||||
'startio' => $startio,
|
||||
'runload' => $runload,
|
||||
'startload' => $startload,
|
||||
'runmem' => $runmem,
|
||||
'startmem' => $startmem,
|
||||
'runnoswap' => $runnoswap,
|
||||
'startnoswap' => $startnoswap,
|
||||
'factor' => $opt::factor || 1,
|
||||
'recheck' => $recheck,
|
||||
'runtime' => $runtime,
|
||||
'over_run_limit' => 1,
|
||||
'over_start_limit' => 1,
|
||||
'verbose' => $opt::verbose,
|
||||
}, ref($class) || $class;
|
||||
}
|
||||
|
||||
|
||||
sub over_run_limit {
|
||||
my $self = shift;
|
||||
my $status = 0;
|
||||
if($self->{'runmem'}) {
|
||||
# mem should be between 0-10ish
|
||||
# 100% available => 0 (1-1)
|
||||
# 50% available => 1 (2-1)
|
||||
# 10% available => 9 (10-1)
|
||||
my $mem = $self->mem_status();
|
||||
::debug("Run memory: $self->{'runmem'}/$mem\n");
|
||||
$status += (::max(1,$self->{'runmem'}/$mem)-1);
|
||||
}
|
||||
if($self->{'runload'}) {
|
||||
# If used with other limits load should be between 0-10ish
|
||||
no warnings 'numeric';
|
||||
my $load = $self->load_status();
|
||||
if($self->{'runload'} > 0) {
|
||||
# Stop if the load is above the limit
|
||||
$status += ::max(0,$load - $self->{'runload'});
|
||||
} else {
|
||||
# Stop if the load is below the limit (for sensor)
|
||||
$status += ::max(0,-$load - $self->{'runload'});
|
||||
}
|
||||
}
|
||||
if($self->{'runnoswap'}) {
|
||||
# swap should be between 0-10ish
|
||||
# swap in or swap out or no swap = 0
|
||||
# else log(swapin*swapout)
|
||||
my $swap = $self->swap_status();
|
||||
$status += log(::max(1, $swap - $self->{'runnoswap'}));
|
||||
}
|
||||
if($self->{'runio'}) {
|
||||
my $io = $self->io_status();
|
||||
$status += ::max(0,$io - $self->{'runio'});
|
||||
}
|
||||
$self->{'over_run_limit'} = $status;
|
||||
if(not $opt::recheck) {
|
||||
$self->{'recheck'} = $self->{'factor'} * $self->{'over_run_limit'};
|
||||
}
|
||||
::debug("over_run_limit: $status\n");
|
||||
return $self->{'over_run_limit'};
|
||||
}
|
||||
|
||||
sub over_start_limit {
|
||||
my $self = shift;
|
||||
my $status = 0;
|
||||
if($self->{'startmem'}) {
|
||||
# mem should be between 0-10ish
|
||||
# 100% available => 0 (1-1)
|
||||
# 50% available => 1 (2-1)
|
||||
# 10% available => 9 (10-1)
|
||||
my $mem = $self->mem_status();
|
||||
::debug("Start memory: $self->{'startmem'}/$mem\n");
|
||||
$status += (::max(1,$self->{'startmem'}/$mem)-1);
|
||||
}
|
||||
if($self->{'startload'}) {
|
||||
# load should be between 0-10ish
|
||||
# 0 load => 0
|
||||
no warnings 'numeric';
|
||||
my $load = $self->load_status();
|
||||
if($self->{'startload'} > 0) {
|
||||
# Stop if the load is above the limit
|
||||
$status += ::max(0,$load - $self->{'startload'});
|
||||
} else {
|
||||
# Stop if the load is below the limit (for sensor)
|
||||
$status += ::max(0,-$load - $self->{'startload'});
|
||||
}
|
||||
}
|
||||
if($self->{'startnoswap'}) {
|
||||
# swap should be between 0-10ish
|
||||
# swap in or swap out or no swap = 0
|
||||
# else log(swapin*swapout)
|
||||
my $swap = $self->swap_status();
|
||||
$status += log(::max(1, $swap - $self->{'startnoswap'}));
|
||||
}
|
||||
if($self->{'startio'}) {
|
||||
my $io = $self->io_status();
|
||||
$status += ::max(0,$io - $self->{'startio'});
|
||||
}
|
||||
$self->{'over_start_limit'} = $status;
|
||||
if(not $opt::recheck) {
|
||||
$self->{'recheck'} = $self->{'factor'} * $self->{'over_start_limit'};
|
||||
}
|
||||
::debug("over_start_limit: $status\n");
|
||||
return $self->{'over_start_limit'};
|
||||
}
|
||||
|
||||
|
||||
sub hard {
|
||||
my $self = shift;
|
||||
return $self->{'hard'};
|
||||
}
|
||||
|
||||
|
||||
sub verbose {
|
||||
my $self = shift;
|
||||
return $self->{'verbose'};
|
||||
}
|
||||
|
||||
|
||||
sub sleep_for_recheck {
|
||||
my $self = shift;
|
||||
if($self->{'recheck'} < 0.5) {
|
||||
# Never sleep less than 0.5 sec
|
||||
$self->{'recheck'} = 0.5;
|
||||
}
|
||||
if($self->verbose()) {
|
||||
$self->{'recheck'} = int($self->{'recheck'}*100)/100;
|
||||
print STDERR "Sleeping $self->{'recheck'}s\n";
|
||||
}
|
||||
::debug("recheck in $self->{'recheck'}s\n");
|
||||
::usleep(1000*$self->{'recheck'});
|
||||
}
|
||||
|
||||
|
||||
sub sleep_while_running {
|
||||
my $self = shift;
|
||||
::debug("check in $self->{'runtime'}s\n");
|
||||
if($self->verbose()) {
|
||||
$self->{'runtime'} = int($self->{'runtime'}*100)/100;
|
||||
print STDERR "Running $self->{'runtime'}s\n";
|
||||
}
|
||||
::usleep(1000*$self->{'runtime'});
|
||||
}
|
||||
|
||||
|
||||
sub nonblockGetLines {
|
||||
# An non-blocking filehandle read that returns an array of lines read
|
||||
# Returns: ($eof,@lines)
|
||||
# Example: --sensor 'vmstat 1 | perl -ane '\''$|=1; 4..0 and print $F[8],"\n"'\'
|
||||
my ($fh,$timeout) = @_;
|
||||
|
||||
$timeout = 0 unless defined $timeout;
|
||||
my $rfd = '';
|
||||
$::nonblockGetLines_last{$fh} = ''
|
||||
unless defined $::nonblockGetLines_last{$fh};
|
||||
|
||||
vec($rfd,fileno($fh),1) = 1;
|
||||
return unless select($rfd, undef, undef, $timeout)>=0;
|
||||
# I'm not sure the following is necessary?
|
||||
return unless vec($rfd,fileno($fh),1);
|
||||
my $buf = '';
|
||||
my $n = sysread($fh,$buf,1024*1024);
|
||||
|
||||
my $eof = eof($fh);
|
||||
# If we're done, make sure to send the last unfinished line
|
||||
return ($eof,$::nonblockGetLines_last{$fh}) unless $n;
|
||||
# Prepend the last unfinished line
|
||||
$buf = $::nonblockGetLines_last{$fh}.$buf;
|
||||
# And save any newly unfinished lines
|
||||
$::nonblockGetLines_last{$fh} =
|
||||
(substr($buf,-1) !~ /[\r\n]/ && $buf =~ s/([^\r\n]*)$//)
|
||||
? $1 : '';
|
||||
$buf ? ($eof,split(/\n/,$buf)) : ($eof);
|
||||
}
|
||||
|
||||
sub read_sensor {
|
||||
my $self = shift;
|
||||
::debug("read_sensor");
|
||||
my $fh = $self->{'sensor_fh'};
|
||||
if(not $fh) {
|
||||
# Start the sensor
|
||||
open($fh, "-|", $opt::sensor) || ::die_bug("Cannot open: $opt::sensor");
|
||||
$self->{'sensor_fh'} = $fh;
|
||||
}
|
||||
# Read as much as we can (non_block)
|
||||
my ($eof,@lines) = nonblockGetLines($fh);
|
||||
|
||||
# new load = last full line
|
||||
foreach my $line (@lines) {
|
||||
if(defined $line) {
|
||||
::debug("Pipe saw: $eof [$line]\n");
|
||||
$Global::last_sensor_reading = $line;
|
||||
}
|
||||
}
|
||||
if($eof) {
|
||||
# End of file => Restart the sensor
|
||||
close $fh;
|
||||
open($fh, "-|", $opt::sensor) || ::die_bug("Cannot open: $opt::sensor");
|
||||
$self->{'sensor_fh'} = $fh;
|
||||
}
|
||||
|
||||
return $Global::last_sensor_reading;
|
||||
}
|
||||
|
||||
sub load_status {
|
||||
# Returns:
|
||||
# loadavg or sensor measurement
|
||||
my $self = shift;
|
||||
|
||||
if($opt::sensor) {
|
||||
if(not defined $self->{'load_status'} or
|
||||
$self->{'load_status_cache_time'} + $self->{'recheck'} < time) {
|
||||
$self->{'load_status'} = $self->read_sensor();
|
||||
while (not defined $self->{'load_status'}) {
|
||||
sleep 1;
|
||||
$self->{'load_status'} = $self->read_sensor();
|
||||
}
|
||||
$self->{'load_status_cache_time'} = time - 0.001;
|
||||
}
|
||||
} else {
|
||||
# Normal load avg
|
||||
# Cache for some seconds
|
||||
if(not defined $self->{'load_status'} or
|
||||
$self->{'load_status_cache_time'} + $self->{'recheck'} < time) {
|
||||
$self->{'load_status'} = load_status_linux() if $^O ne 'darwin';
|
||||
$self->{'load_status'} = load_status_darwin() if $^O eq 'darwin';
|
||||
$self->{'load_status_cache_time'} = time;
|
||||
}
|
||||
}
|
||||
::debug("load_status: ".$self->{'load_status'}."\n");
|
||||
return $self->{'load_status'};
|
||||
}
|
||||
|
||||
sub undef_as_zero {
|
||||
my $a = shift;
|
||||
return $a ? $a : 0;
|
||||
}
|
||||
|
||||
|
||||
sub load_status_linux {
|
||||
my ($loadavg);
|
||||
if(open(IN,"/proc/loadavg")) {
|
||||
# Linux specific (but fast)
|
||||
my $upString = <IN>;
|
||||
if($upString =~ m/^(\d+\.\d+)/) {
|
||||
$loadavg = $1;
|
||||
} else {
|
||||
::die_bug("proc_loadavg");
|
||||
}
|
||||
close IN;
|
||||
} elsif (open(IN,"uptime|")) {
|
||||
my $upString = <IN>;
|
||||
if($upString =~ m/averages?.\s*(\d+\.\d+)/) {
|
||||
$loadavg = $1;
|
||||
} else {
|
||||
::die_bug("uptime");
|
||||
}
|
||||
close IN;
|
||||
}
|
||||
return $loadavg;
|
||||
}
|
||||
|
||||
sub load_status_darwin {
|
||||
my $loadavg = `sysctl vm.loadavg`;
|
||||
if($loadavg =~ /vm\.loadavg: { ([0-9.]+) ([0-9.]+) ([0-9.]+) }/) {
|
||||
$loadavg = $1;
|
||||
} elsif (open(IN,"LANG=C uptime|")) {
|
||||
my $upString = <IN>;
|
||||
if($upString =~ m/averages?.\s*(\d+\.\d+)/) {
|
||||
$loadavg = $1;
|
||||
} else {
|
||||
::die_bug("uptime");
|
||||
}
|
||||
close IN;
|
||||
}
|
||||
return $loadavg;
|
||||
}
|
||||
|
||||
|
||||
sub swap_status {
|
||||
# Returns:
|
||||
# (swap in)*(swap out) kb
|
||||
my $self = shift;
|
||||
my $status;
|
||||
# Cache for some seconds
|
||||
if(not defined $self->{'swap_status'} or
|
||||
$self->{'swap_status_cache_time'}+$self->{'recheck'} < time) {
|
||||
$status = swap_status_linux() if $^O ne 'darwin';
|
||||
$status = swap_status_darwin() if $^O eq 'darwin';
|
||||
$self->{'swap_status'} = ::max($status,0);
|
||||
$self->{'swap_status_cache_time'} = time;
|
||||
}
|
||||
::debug("swap_status: $self->{'swap_status'}\n");
|
||||
return $self->{'swap_status'};
|
||||
}
|
||||
|
||||
|
||||
sub swap_status_linux {
|
||||
my $swap_activity;
|
||||
$swap_activity = "vmstat 1 2 | tail -n1 | awk '{print \$7*\$8}'";
|
||||
# Run swap_activity measuring.
|
||||
return qx{ $swap_activity };
|
||||
}
|
||||
|
||||
sub swap_status_darwin {
|
||||
# Mach Virtual Memory Statistics: (page size of 4096 bytes, cache hits 0%)
|
||||
# free active spec inactive wire faults copy 0fill reactive pageins pageout
|
||||
# 298987 251463 162637 69437 265724 29730558 299022 2308237 1 110058 0
|
||||
# 298991 251479 162637 69437 265726 43 4 16 0 0 0
|
||||
my ($pagesize, $pageins, $pageouts);
|
||||
my @vm_stat = `vm_stat 1 | head -n4`;
|
||||
$pagesize = $1 if $vm_stat[0] =~ m/page size of (\d+) bytes/;
|
||||
$pageins = (split(/\s+/,$vm_stat[3]))[9];
|
||||
$pageouts = (split(/\s+/,$vm_stat[3]))[10];
|
||||
return ($pageins*$pageouts*$pagesize)/1024;
|
||||
}
|
||||
|
||||
|
||||
sub mem_status {
|
||||
# Returns:
|
||||
# number of bytes (free+cache)
|
||||
my $self = shift;
|
||||
# Cache for one second
|
||||
if(not defined $self->{'mem_status'} or
|
||||
$self->{'mem_status_cache_time'}+$self->{'recheck'} < time) {
|
||||
$self->{'mem_status'} = mem_status_linux() if $^O ne 'darwin';
|
||||
$self->{'mem_status'} = mem_status_darwin() if $^O eq 'darwin';
|
||||
$self->{'mem_status_cache_time'} = time;
|
||||
}
|
||||
::debug("mem_status: $self->{'mem_status'}\n");
|
||||
return $self->{'mem_status'};
|
||||
}
|
||||
|
||||
|
||||
sub mem_status_linux {
|
||||
# total used free shared buffers cached
|
||||
# Mem: 3366496 2901664 464832 0 179228 1850692
|
||||
# -/+ buffers/cache: 871744 2494752
|
||||
# Swap: 6445476 1396860 5048616
|
||||
my @free = `free`;
|
||||
my $free = (split(/\s+/,$free[2]))[3];
|
||||
return $free*1024;
|
||||
}
|
||||
|
||||
sub mem_status_darwin {
|
||||
# Mach Virtual Memory Statistics: (page size of 4096 bytes, cache hits 0%)
|
||||
# free active spec inactive wire faults copy 0fill reactive pageins pageout
|
||||
# 298987 251463 162637 69437 265724 29730558 299022 2308237 1 110058 0
|
||||
# 298991 251479 162637 69437 265726 43 4 16 0 0 0
|
||||
my ($pagesize, $pages_free, $pages_speculative);
|
||||
my @vm_stat = `vm_stat 1 | head -n4`;
|
||||
$pagesize = $1 if $vm_stat[0] =~ m/page size of (\d+) bytes/;
|
||||
$pages_free = (split(/\s+/,$vm_stat[3]))[0];
|
||||
$pages_speculative = (split(/\s+/,$vm_stat[3]))[2];
|
||||
return ($pages_free+$pages_speculative)*$pagesize;
|
||||
}
|
||||
|
||||
|
||||
sub io_status {
|
||||
# Returns:
|
||||
# max percent for all devices
|
||||
my $self = shift;
|
||||
# Cache for one second
|
||||
if(not defined $self->{'io_status'} or
|
||||
$self->{'io_status_cache_time'}+$self->{'recheck'} < time) {
|
||||
$self->{'io_status'} = io_status_linux() if $^O ne 'darwin';
|
||||
$self->{'io_status'} = io_status_darwin() if $^O eq 'darwin';
|
||||
$self->{'io_status_cache_time'} = time;
|
||||
}
|
||||
::debug("io_status: $self->{'io_status'}\n");
|
||||
return $self->{'io_status'};
|
||||
}
|
||||
|
||||
|
||||
sub io_status_linux {
|
||||
# Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
|
||||
# sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
|
||||
my @iostat_out = `LANG=C iostat -x 1 2`;
|
||||
# throw away all execpt the last Device:-section
|
||||
my @iostat;
|
||||
for(reverse @iostat_out) {
|
||||
/Device:/ and last;
|
||||
push @iostat, (split(/\s+/,$_))[13];
|
||||
}
|
||||
my $io = ::max(@iostat);
|
||||
return undef_as_zero($io)/10;
|
||||
}
|
||||
|
||||
sub io_status_darwin {
|
||||
# disk0 disk1 disk2
|
||||
# KB/t tps MB/s KB/t tps MB/s KB/t tps MB/s
|
||||
# 14.95 15 0.22 11.18 35 0.38 2.00 0 0.00
|
||||
# 0.00 0 0.00 0.00 0 0.00 0.00 0 0.00
|
||||
my @iostat_out = `LANG=C iostat -d -w 1 -c 2`;
|
||||
# return the MB/s of the last second (not the %util)
|
||||
my @iostat = split(/\s+/, $iostat_out[3]);
|
||||
my $io = $iostat[3] + $iostat[6] + $iostat[9];
|
||||
return ::min($io, 10);
|
||||
}
|
||||
|
||||
$::exitsignal = $::exitstatus = $opt::battery = 0; # Dummy
|
Loading…
Reference in New Issue