#!/usr/bin/perl -w
# Perl script to limit cpu usage for all the programs with a certain name
#  - requires a *NIX OS
#  - requires cpulimit
#  - some kludges for our specific case are built in, try to read the code 
#    before executing it :-)
#
#  by Calyx, December 2008.

use strict;

sub limitAllUnlimitedProcesses($$) {
	my ($procName, $limit) = @_;
	chomp(my @procList = `ps aux | awk '{ print \$11, \$2, \$3; }' | grep '^./$procName' | grep -v 0.0 | grep -v grep | awk '{print \$2}'`);

	for my $pid(@procList) {
		cpuLimitForPid($pid, $limit) if (!existsCpuLimitForPid($pid));
	}
}

sub existsCpuLimitForPid($) {
	my ($pid) = @_;

	chomp(my $ret = `ps aux | grep cpulimit | grep '\\-\\-pid $pid' | grep -v grep | wc -l`);
	return $ret;
}

sub cpuLimitForPid($$) {
	my ($pid, $limit) = @_;
	if ($pid != $$) {
		system("cpulimit --pid $pid --limit $limit &");
	}
}

sub killZombieCpuLimits() {
	my @procList = `ps aux | grep cpulimit`;
	for my $procData (@procList) {
		if ($procData =~ m/^\w+\s+(\d+).*?\-\-pid (\d+)/gi) {
			my $pid = $2;
			my $cpuLimitPid = $1;

			killProcess($cpuLimitPid) if ( !processExists($pid));
		}
	}
}

sub killProcess($) {
	my ($pid) = @_;

	if ($pid != $$) {
		`kill -9 $pid 1>&2 2>/dev/null`;
	}
}

sub processExists($) {
	my ($pid) = @_;

	chomp(my $ret = `ps aux | awk '{ print \$2; }' | grep $pid | wc -l` );
	return $ret;
}

sub main() {
	if (@ARGV != 2) {
		print "Usage:\n\t$0 <procFilter> <limit>\n";
		exit(0);
	}

	if ($< != 0) {
		print "Must be root to run this program.\n";
		exit(0);
	}
	
	my $procName = $ARGV[0];
	my $limit = $ARGV[1];

	killZombieCpuLimits();
	limitAllUnlimitedProcesses($procName, $limit);
}

main();
