#!/usr/bin/env perl
# run as www-data (same as /run/hilis4/*.pid owner)
# perl script/kidreaper.pl <service> <MB>
# adapted from http://www.catalystframework.org/calendar/2007/18:
# * changed kB to MB
# * used IPC::System::Simple to capture ppid from service-name
# can be run as JUST_TESTING by omitting max_mem (ARGV[1])
#==============================================================================
my $JUST_TESTING = 0; # reports to command-line, doesn't kill processes
#==============================================================================
use lib '/home/raj/perl5/lib/perl5';
use IPC::System::Simple qw(capture);
use DateTime::Format::Strptime;
use Data::Printer;
use Modern::Perl;
my $service_names = $ARGV[0] or
die "Usage: $0 <comma_delimited_service_names> [<ram_limit_in_MB>]\n";
my $max_mb = $ARGV[1]; # optional - if empty, switches to JUST_TESTING and sets default 1000
#==============================================================================
$JUST_TESTING = 1 if ! $max_mb; # warn $JUST_TESTING;
#==============================================================================
$max_mb ||= 1000; # warn $max_mb; warn $service_names;
my @services = split ',', $service_names; # p @services;
my $formatter = DateTime::Format::Strptime->new(pattern => '%F %T');
for my $service (@services) {
my $ppid = capture("cat /run/hilis4/$service.pid") or die $!; # warn $ppid; exit;
my $cmd = sprintf '/bin/ps -o pid=,vsz= --ppid %s|', $ppid;
if ( open (my $kids, $cmd) ) {
my @goners = ();
while (<$kids>) {
chomp;
my ($pid, $mem) = split; # p $mem;
# ps shows KB. we want MBytes.
$mem /= 1024;
if ($mem >= $max_mb) {
push @goners, { name => $service, pid => $pid, mem => $mem };
if ($JUST_TESTING) {
say sprintf "process %s mem [%s MB] > permitted [%s MB]",
$pid, $mem, $max_mb;
}
}
}
close($kids);
if (@goners and not $JUST_TESTING) {
# kill them slowly, so that they don't all suddenly die at once:
foreach my $victim (@goners) { # p $victim;
kill 'HUP', $victim->{pid}; # send Signal HangUp
_log($victim);
sleep 10;
}
}
}
else {
my $time = _get_time();
say "$time | can't get process list for $service [$ppid]: $!";
}
}
sub _log { # kidreaper.log:
my $data = shift;
say sprintf '%s | %s process %s mem %sMB > permitted %sMB',
_get_time(), uc $data->{name}, $data->{pid}, int $data->{mem}, $max_mb;
}
sub _get_time {
my $time = DateTime->now;
$time->set_formatter($formatter); # warn $time;
return $time;
}