Re: Determine if a proc spawned under screen's control

Thomas Glanzmann <[email protected]>
Newsgroups gmane.comp.gnu.screen
Message-ID <[email protected]>
Hi Travis,

> Can anyone suggest a fast way that will solidly report whether the
> process is currently under the control of an active screen session?

I use the following script which works just perfect under Solaris and
Linux. Get sure that you have a recent version of Proc::ProcessTable
installed (with two bugfixes from me) 0.35 or newer.

	Thomas

 

Yahoo! Groups Links

To visit your group on the web, go to:
 http://groups.yahoo.com/group/gnu-screen/

To unsubscribe from this group, send an email to:
 [email protected]

Your use of Yahoo! Groups is subject to:
 http://docs.yahoo.com/info/terms/
process_checker (text/plain, 12.8 KB)
#!/local/perl-5.8/bin/perl -w

use strict;
use integer;
use Proc::ProcessTable;
use Mail::Mailer; 

chomp (my $hostname = `hostname`); # set hostname
chomp (my $uname    = `uname`   ); # set uname
chomp (my $uptime   = `uptime`  ); # set uptime

#                                  load average: 0.00, 0.01, 0.12
$uptime                        =~ /load average:\s+(\d+)\.(\d+),\s+(\d+)\.(\d+),\s+(\d+)\.(\d+)$/;

my $load_1                     = ($1 * 100) + $2 +0; # 100 * load last  1 minute 
my $load_5                     = ($3 * 100) + $4 +0; # 100 * load last  5 minutes
my $load_15                    = ($4 * 100) + $5 +0; # 100 * load last 15 minutes

my $ttynum_notty               = 0;  # on solaris -1 on linux 0
my $verschiekonstante          = 0;  # time is on linux in microseconds and on sunos in seconds
my $lsof_binary                = ""; # Where to find my lsof?

if      ($uname eq 'SunOS') {
	$verschiekonstante = 60;
	$ttynum_notty = -1;
	$lsof_binary = '/local/++GENERIC++/.arch.os.sun4.sos5/bin/lsof-5.9';

} elsif ($uname eq 'Linux') {
	$verschiekonstante = 1000 * 60;
	$ttynum_notty =  0;
	$lsof_binary = '/usr/sbin/lsof';

} else {
	die("unsupported operating system: $!");
}

my $time_between_term_and_kill = 2;   # in seconds
my $max_minutes                = 60;  # how many minutes a user process can stay on cpu?
my $max_minutes_trouble_maker  = 5;   # how many cpu minutes can have a trouble maker?
my $max_inetsocks              = 10;  # how many open inet sockets?
my $pctcpu_limit               = 90;  # more than 90 % CPU?
my $cpu_limit                  = 10;  # cpu time limit in minutes
my $troublemaker_cpu           = 29;  # more than 29 % CPU
my $rss_limit                  = 100; # more than Mbytes of RSS RAM ... grep for special rules ...
my @hosts_with_2_cpus          = qw( faui04a faui04b faui04c faui04d faui00 ); # machines with 2 cpus
my @hosts_with_4_cpus          = qw( faui07 faui02 faui06 );  # machines with 4 cpus
my $min_uid                    = 103; # uids under or equal min_uid are ignored
my @allow_users                = qw( sitowert@faui00i sistsigw@faui00h www@faui02 oracle@faui02 sijoange@faui00i sichkell@faui00i simadraf@faui00i sidosche@faui00i ); # allowed users
my @allow_groups               = qw( root icipguru ); # allowed groups
my @allow_daemons              = qw( screen-3.9.11 screen sshd ); # allowed processes without tty eg. daemons
my @process_names_2_kill       = qw( ping eggdrop john seti dnetc sockd mldonkey mldonkey_gui donkey donkey_s ed2k_gui rd jftpgw gtk-gnutella limewire ); # processnames to kill
my @trouble_makers             = qw( netscape kdeinit scm vim xemacs ark kvt ); # troublemaker to kill
my @allowed_games              = qw( /local/et /local/quake /local/rune /usr/games /proj/i9lib/packages/maya-4.5 ); # works only under Linux: Specify the start of the beginning path
my @never_ever_kill            = qw( /usr/sbin/rpc.rusersd );
my @servers                    = qw( faui0cons faui07 faui06 faui05 mephisto faui03 faui01 ); # I do _not_ run on servers
my $from                       = 'Process checker <[email protected]>';
my $cc                         = 'Process checker <[email protected]>';

my $to_sithglan                = ""; # gather listens and connections and report them to sithglan

my @allow_uids                 = resolve_allowed_uids(\@allow_users, \@allow_groups); # turn allowed users and groups into uids

my %lsof_listens               = (); # key=pid values=array with open LISTENS
my %lsof_connections           = (); # key=pid values=array with open network connections
my %killed_uids                = (); # key=uid values=array with killed pids
my %killed_reason              = (); # key=pid value=reason
my %killed_method              = (); # key=pid values=TERM|KILL|FAIL
my %killed_cmndline            = (); # key=pid values=commandline

if (grep("$hostname" eq "$_", @hosts_with_2_cpus)) {
	$pctcpu_limit     /= 2;
	$troublemaker_cpu /= 2;
}

if (grep("$hostname" eq "$_", @hosts_with_4_cpus)) {
	$pctcpu_limit     /= 4;
	$troublemaker_cpu /= 4;
}

if ($hostname =~ /^faui0[05][a-z]$/) {
	$rss_limit *= 5;
}

if ($hostname =~ /^faui0[678][a-z]$/) {
	$rss_limit *= 4;
}

# I don't run on servers
if (grep("$hostname" eq "$_", @servers)) {
	exit;
}

# Gather lsof information
# open(LSOF, "$lsof_binary -u^root,^daemon,^smmsp -i |") || die "Can't start $lsof_binary: $!";

# FIXME LSOF is 64 bit on solaris

#while (0 && <LSOF>) {
#	if ($. == 1) {
#		next;
#	}
#
#	chomp;
#
#	# Match Solaris listens
#	if      (/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(TCP|UDP)\s+([^>]*)$/) {
#		$to_sithglan .= "List: <$1> <$2> <$3> <$4> <$5> <$6> <$7> <$8> <$9>\n";
#		push(@{$lsof_connections{$2}}, $9);
#
#	# Match Solaris connections
#	} elsif (/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(TCP|UDP)\s+(.*)$/) {
#		$to_sithglan .= "Conn: <$1> <$2> <$3> <$4> <$5> <$6> <$7> <$8> <$9>\n";
#		push(@{$lsof_listens{$2}}, $9);
#
#	# Match Linux listens
#	} elsif (/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(TCP|UDP)\s+([^>]*)$/) {
#		$to_sithglan .= "List: <$1> <$2> <$3> <$4> <$5> <$6> <$7> <$8>\n";
#		push(@{$lsof_connections{$2}}, $8);
#
#	# Match Linux connections
#	} elsif (/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(TCP|UDP)\s+(.*)$/) {
#		$to_sithglan .= "Conn: <$1> <$2> <$3> <$4> <$5> <$6> <$7> <$8>\n";
#		push(@{$lsof_listens{$2}}, $8);
#
#	# can't match ... should not happen
#	} else {
#		$to_sithglan .= "MATC: $_\n";
#	}
#}
# close(LSOF);

# send report if I have to
if (0 && $to_sithglan) {
	my $mailer = Mail::Mailer->new("sendmail");
	$mailer->open({ From => $from, To => 'sithglan@cip', Subject => "CONN: $hostname" });

	print $mailer $to_sithglan;

	$mailer->close();
}

my $tobj = new Proc::ProcessTable;

foreach my $process (@{$tobj->table}) {
	next if (($process->uid() <= $min_uid)
              || ($process->pid() == $$)
              || ($process->state() eq 'defunct')
              || allowed_uid($process->uid())
	);

	if (process_name_2_kill($process->fname())) {
		kill_proc($process, 'prohibited process');
		next;
	}

	# check for too many inet sockets
	#if ($max_inetsocks < get_inetsocks($process->pid())) {
	#	kill_proc($process, "> $max_inetsocks INET sockets");
	#	next;
	#}
	
	# no tty + older than one day
	if (!hangingontty_or_allowed_daemon($process)
	 && olderthan($process, 1) && !not_kill($process)) {
		kill_proc($process, 'background process');
		next;
	}

	# tty + older than seven days
	if (olderthan($process, 7) && !not_kill($process)) {
		kill_proc($process, '> 7 days tty process');
		next;
	}

	if ($process->rss > $rss_limit * 1024 * 1024) {
		kill_proc($process, "> $rss_limit Megabyte RSS RAM");
		next;
	}

	# tm when 5 min CPU && troublemaker_cpu % perm cpu
	if (($process->time() > ($max_minutes_trouble_maker * $verschiekonstante))
	 && troublemaker($process->fname())
	 && toomuchcpu($process->pctcpu, $troublemaker_cpu)
	) {
		kill_proc($process, 'known troublemaker');
		next;
	}

	# only proceed if load higher than one
	next if ($load_5 < 100);

	# pctcpu_limit % cpu + cpu_limit min cputime
	if (toomuchcpu($process->pctcpu, $pctcpu_limit)
	 && ($process->time() > ( $cpu_limit * $verschiekonstante))
	 && !game($process->exec()) && !not_kill($process)
	   ) {
		kill_proc($process, "permanent > $pctcpu_limit % CPU");
		next;
	}

	if ($process->time() > ($max_minutes * $verschiekonstante)
	 && !game($process->exec()) && !not_kill($process)
	   ) {
		kill_proc($process, "> $max_minutes CPU min");
		next;
	}

}

foreach my $uid (keys(%killed_uids)) {

	my $subject ='';
	( my $to, my $gcos) = (getpwuid($uid))[0,6];
	my $name = (split (/,/, $gcos))[0];

	$to = $name . ' <' . $to . '>';

	# plural
	if (1 < @{$killed_uids{$uid}}) {
		$subject = @{ $killed_uids{$uid} } . " removed processes on $hostname";
	} else {
		$subject = "One removed process on $hostname";
	}

	my $mailer = Mail::Mailer->new("sendmail");
	$mailer->open({ From => $from, To => $to, 'X-User' => $to, Cc => $cc, Subject => $subject});
	print $mailer "Dear $name,\n";

	# plural
	if (1 < @{$killed_uids{$uid}}) {
		print $mailer @{ $killed_uids{$uid} } . " processes on $hostname have been removed for the following reasons.\n\n";
	} else {
		print $mailer "one process on $hostname has been removed for the following reason.\n\n";
	}
	
	print $mailer sprintf("%5s %4s %20s | %44s\n", 'PID', 'METH', 'reason', 'command line');
	print $mailer '-' x 80 . "\n";

	foreach my $pid (@{$killed_uids{$uid}}) {
		print $mailer sprintf("%5d %4s %20s | %.44s\n", $pid, $killed_method{$pid}, $killed_reason{$pid}, $killed_cmndline{$pid});
	}

	print $mailer "\n--\n" . '$Id: process_checker,v 1.142 2004/01/07 11:10:30 sithglan Exp $' . "\n";

	$mailer->close();
}

#-[get_listens]----------------------------------------------------------------
# returns the open connections of pid
sub get_inetsocks
{
	my $pid = $_[0];
	
	return (get_connections($pid) + get_listens($pid));
}

#-[get_listens]----------------------------------------------------------------
# returns the number of listens of pid
sub get_listens
{
	my $pid = $_[0];

	if (!defined(@{$lsof_listens{$pid}})) {
		return(0);	
	} else {
		return (@{$lsof_connections{$pid}});
	}
}

#-[get_connections]------------------------------------------------------------
# returns the number of open connections of pid
sub get_connections
{
	my $pid = $_[0];

	if (!defined(@{$lsof_listens{$pid}})) {
		return(0);	

	} else {
		return (@{$lsof_listens{$pid}});
	}
}

#-[olderthan]------------------------------------------------------------------
# This functions checks if the process is older than days
sub olderthan
{
	if ((time() - $_[0]->start) > ($_[1] * 24 * 3600)) {
		return 1;
	} else {
		return 0;
	}
}

#-[hangingontty_or_allowed_daemon]----------------------------------------------
# This function checks if user hangs on a tty or is allowed daemon
sub hangingontty_or_allowed_daemon
{
	if ($_[0]->ttynum != $ttynum_notty) {
		return 1;
	}
	# FIXME tg: Der Mechanismus funktioniert nicht unter Sun
	return grep($_[0]->fname =~ /^$_$/, @allow_daemons); 
}

#-[toomuchcpu]-----------------------------------------------------------------
# This function is needed because pctcpu can come in numeric and alpha context
sub toomuchcpu
{
	my $pctcpu = $_[0];
	my $pctcpu_limit = $_[1];

	# sanity check
	if ($pctcpu eq "nan") {
		$pctcpu = 0;
	}

	if ($pctcpu > $pctcpu_limit) {
		return 1;
	} else {
		return 0;
	}
}

#-[kill_proc]------------------------------------------------------------------
# kills process && saves uid, gid, cmndline and method
sub kill_proc 
{
	( my $process, my $reason ) = @_;

	# append pid to uid
	push(@{$killed_uids{$process->uid()}}, $process->pid());

	# save reason
	$killed_reason{$process->pid()} = $reason;

	# save cmndline or full qualified path
	if ( $process->cmndline() eq '' ) {
		$killed_cmndline{$process->pid()} = $process->exec();
	} else {
		$killed_cmndline{$process->pid()} = $process->cmndline();
	}

	#default
	$killed_method{$process->pid()} = 'FAIL';

	if (kill('TERM', $process->pid())) { 
		$killed_method{$process->pid()} = 'TERM';
	}

	sleep $time_between_term_and_kill;

	if (kill('KILL',$process->pid())) {
		$killed_method{$process->pid()} = 'KILL';
	}
}
#-[game]---------------------------------------------------------------------
# works only under Linux. Looks if executable starts with allowed path
sub game 
{
	return grep($_[0] =~ /^$_/i, @allowed_games);
}

#-[not_kill]------------------------------------------------------------------
sub not_kill
{
	if ( $uname ne 'Linux' ) {
		return 0;
	} else {
		return grep($_[0]->exec() =~ /^$_/i, @never_ever_kill);
	}
}

#-[troublemaker]--------------------------------------------------------------
# same like process_name_2_kill but for troublemaker
sub troublemaker
{
	return grep($_[0] =~ /^$_$/i, @trouble_makers);
}

#-[process_name_2_kill]-------------------------------------------------------
# if argv[0] eq @process_names_2_kill 1 is returned else 0
sub process_name_2_kill
{
	return grep($_[0] =~ /^$_$/i, @process_names_2_kill);
}

#-[allowed_uid]----------------------------------------------------------------
# if uid2check contained in @allow_uids 1 is return else 0
sub allowed_uid
{ 
	return grep($_[0] == $_,@allow_uids);
}

#-[resolve_allowed_uids]-------------------------------------------------------
# resolves members of allow_groups and allow_users and returnes there userids
sub resolve_allowed_uids
{
	my ( $my_users, $my_groups ) = @_;

	my @return_array = ();

	# Add members of allow_groups to allow_user
	foreach my $group (@$my_groups) {
		(my $name, my $passwd, my $gid, my $members) = getgrnam($group);
		push(@$my_users, split (/\s/,$members));
	}

	# Resolve usernames into uids allow_user -> allow_uid;
	foreach my $user (@$my_users){
		if ($user=~ m/\@/ ) {
			if ((split(/\@/,$user,2))[1] eq $hostname) {
				push (@return_array,
					(getpwnam((split(/\@/,$user,2))[0]))[2]);
			}

		} else {
			push (@return_array, (getpwnam($user))[2]);
		}
	}

	return @return_array;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.