#!/usr/bin/perl
################################################################################
#    Copyright  Afrihost.com (PTY) Ltd 2004
#    Original file by: Ed Pascoe <ed@pascoe.co.za>
#    $Id: apachelimit,v 1.2 2004/03/08 07:09:27 elp Exp $
################################################################################


my @procps=();
my %psps=();

#{{{ sub psmem($)
sub psmem($){
   my ($pid)=@_;
   if (! open(PFILE,"</proc/$pid/status") ){
      #print STDERR "Unable to read /proc/$pid/status\n";
      return 0;
   }
   $mem=0;
   LOOP: while(<PFILE>){
      if (/^VmRSS:\s+(\d+)\skb/i){
	 $mem=$1;
	 last LOOP;
      }
   }
   close PFILE;
   return $mem;
}
#}}} ---------------------------------------------------------------------------
#{{{ sub psinfo($)
sub psinfo($){
    my ($pid) = @_;
    my ($env,$cmdline);
    $filename="/proc/$pid/cmdline";
    open(PINFO,"<$filename");
    sysread(PINFO,$cmdline,10240);
    close PINFO;
    $cmdline=~ tr/\000/ /;
    $cmdline=readlink("/proc/$pid/exe") ." " . $cmdline;
    return $cmdline;
}
#}}}
#{{{ sub isparent($)
sub isparent($){
   my ($pid)=@_;
   if (! open(PFILE,"</proc/$pid/status") ){
      #print STDERR "Unable to read /proc/$pid/status\n";
      return 0;
   }
   my $pspid=undef;
   LOOP: while(<PFILE>){
      if (/^PPid:\s+(\d+)/i){
	 $pspid=$1;
	 last LOOP;
      }
   }
   close PFILE;
   return 1 if ($pspid eq "1");
   return undef;
}
#}}} ---------------------------------------------------------------------------
#{{{ sub cmdline($) Reads the command line of the given process
sub cmdline($){ 
   my ($pid)= @_;
   open(CMDFILE,"</proc/$pid/cmdline") || die("Error: $!");
   $cmdline=<CMDFILE>;
   chomp($cmdline);
   close CMDFILE;
   return $cmdline;
}
#}}} ---------------------------------------------------------------------------
#{{{ sub apachepid() #create a list of all process IDs
sub apachepid($){ #create a list of all process IDs
   my ($pid) =@_;
   open(PIPE,"lynx -dump -width=200 http://127.0.0.1/whm-server-status | grep $pid |") || return "";
   my $cmd=<PIPE>;
   chomp($cmd);
   close PIPE;
   return $cmd;
}
#}}}
#{{{ sub memcheck() #create a list of all process IDs
sub memcheck($$){
   my ($limit,$majorlimit)=@_;
   opendir(DIR,"/proc") || die("Error: $!");
LOOP:   while(my $d=readdir(DIR)){
      next unless ($d =~ /\d+/);
      $mem=psmem($d);
      if ($mem >= $majorlimit){ #Over the hard limit, panic and kill it now
            print "". localtime()." majorlimit $mem >= $majorlimit:  $d " . psinfo($d)."\n";
            system("kill $d > /dev/null 2> /dev/null");
            system("kill -9 $d > /dev/null 2> /dev/null");
            system("kill -9 $d > /dev/null 2> /dev/null");
            system("kill -9 $d > /dev/null 2> /dev/null");
      }
      if ($mem > $limit){
	 $cmd=cmdline($d);
	 next LOOP if (! ($cmd =~ /httpd/)); #Skip non apache stuff
	 chomp($cmd);
	     $apache=apachepid($d);
	     next LOOP if ($apache =~ /whm-server-status/); #Skip our own status lookups
	     print "" . localtime()." $d ($mem kb) $apache\n";
	     system("/etc/rc.d/init.d/httpd stop");
	     sleep 2;
             system("kill -9 httpd");
	     sleep 1;
             #We are running at -10, so use 14 to get to +4
	     system("nice -14 /etc/rc.d/init.d/httpd start");
      }
   }
   closedir(DIR);

}
#}}}

$|=1;
$0="apachelimit";
my $limit= $ARGV[0] || 39000;
my $majorlimit= $ARGV[1] || 120000;
system("renice -10 $$");
print "Starting limiter at " . localtime() ."\n";
while(1){
   memcheck($limit,$majorlimit);
   sleep 3; 
}
#    vim600: sw=3 fdm=marker sm
