#!/usr/local/bin/perl

################################################################################
#                                                                              #
#  ext|scripts (C) 2000-2004 Pavel Novikov (pavel@ext.by)                      #
#                                                                              #
################################################################################

################################################################################
#
#  This one makes "top(1)" and then maps pids to jails.
#
#  Usage: ./jtop.pl [<top options>] [lj|long_jails] [ll|long_lines] [b|batch] [<delay in seconds>] [i|install]
#
#  TODO:
#
#   - sort by jail
#   - interactive mode
#


#:-)
use strict;

#:)
eval
{
require Term::ReadKey;
};

#no buffer
$|=1;

#constants
my $TOP_BIN="/usr/bin/top";
my $TOP_OPTIONS;
my $DIR_PROC="/proc";
my $FILE_STATUS="status";

#get sizes
my ($MAX_WIDTH,$MAX_HEIGHT)=(80,25);
eval
{
($MAX_WIDTH,$MAX_HEIGHT)=Term::ReadKey::GetTerminalSize();
};

#other defaults
my $MAX_LINE_SIZE=$MAX_WIDTH;
my $MAX_LINE_COUNT=$MAX_HEIGHT;
my $O_CUT_LINES=1;
my $O_CUT_JAILS=1;
my $O_BATCH=0;
my $O_DELAY=2;
my $JAIL_DEAD="<died>";

#clear sign - tricky shit
my $CLEAR=`/usr/bin/clear`;
my $CTL=$CLEAR;
$CTL=~s;H.*$;H;gsmio;

#this will kill us
local($SIG{INT})=sub{$O_BATCH=1;};

#strip shit
sub strip
{

	#come on
	my $line=shift;

	#leading spacing ones
	$line=~s{^\s\s*(.*)$}{$1}gs;

	#trailing spacing ones
	$line=~s{^(.*)\s\s*$}{$1}gs;

	#add eol
	return($line);

}

#read options
while(my $option=shift)
{
	if(($option eq 'lj')||($option eq 'long_jails'))
	{
		$O_CUT_JAILS=0;
	}
	elsif(($option eq 'll')||($option eq 'long_lines'))
	{
		$O_CUT_LINES=0;
	}
	elsif(($option eq 'b')||($option eq 'batch'))
	{
		$O_BATCH=1;
	}
	elsif($option =~ '^(\d\d*)$')
	{
		$O_DELAY=$1;
	}
	elsif(($option eq "i")||($option eq "install"))
	{
		system("cd /usr/ports/devel/p5-Term-ReadKey && make install");
		exit(0);
	}
	else
	{
		$TOP_OPTIONS=$TOP_OPTIONS." ".$option;
	}
}
if(!$TOP_OPTIONS){$TOP_OPTIONS=" -Stbnq";}

#use only as much as we need
$TOP_OPTIONS=$TOP_OPTIONS." -d1 ".($MAX_LINE_COUNT-5);

#cycling
for(;;)
{

	#data
	my @lines;
	my %pids;
	my $pid_line=-1;
	my $pid_position=-1;
	my $max_jail=length('JAIL');

	#flush data

	#open a pipe
	if(open(P,$TOP_BIN.$TOP_OPTIONS." |"))
	{

		#fetch data
		my $c=0;
		while(<P>)
		{

			#fix & save
			chomp($_);
			my $line=$_;
			push(@lines,$line);

			#split data
			my @line=split(/\s\s*/,&strip($line));

			#header
			if($pid_position==-1)
			{
				my $i=0;
				foreach my $field (@line)
				{
					if($field eq 'PID')
					{
						$pid_position=$i;
						$pid_line=$c;
						last;
					}
					++$i;
				}
			}

			#line
			else
			{
				if($pid_position>=0)
				{
					my $pid=@line[$pid_position];
					if(open(F,"<".$DIR_PROC."/".$pid."/".$FILE_STATUS))
					{
						while(<F>)
						{
							chomp($_);
							my @data=split(/\s\s*/,$_);
							my $jail=$data[$#data];
							if($jail)
							{
								if($O_CUT_JAILS)
								{
									$jail=~s/^([^\.]*)\..*$/$1/gsmi;
								}
								$pids{$pid}=$jail;
								if(length($jail) > $max_jail){$max_jail=length($jail);}
							}
						}
						close(F);
					}
					else
					{
						$pids{$pid}=$JAIL_DEAD;
						if(length($JAIL_DEAD) > $max_jail){$max_jail=length($JAIL_DEAD);}
					}
				}
			}

			#next
			++$c;

		}

		#close the pipe
		close(P);

		#make a format
		my $f;
		if($O_CUT_LINES)
		{
			$f="%".$max_jail."s %-.".($MAX_LINE_SIZE-$max_jail-1)."s";
		}
		else
		{
			$f="%".$max_jail."s %s";
		}

		#clear
		print "$CTL";

		#output
		my $i=0;
		foreach my $line (@lines)
		{
			if(&strip($line))
			{
				if(($i>=$pid_line)&&(&strip($line)))
				{
					my @data=split(/\s\s*/,&strip($line));
					if($i==$pid_line){print (" " x $MAX_WIDTH)."\n";}
					my $o=sprintf($f,($i==$pid_line)?'JAIL':$pids{$data[$pid_position]},$line);
					if(length($o)<$MAX_WIDTH)
					{
						$o=$o.(" " x ($MAX_WIDTH-length($o)));
					}
					print $o."\n";
				}
				else
				{
					my $o=sprintf($f,$line);
					if(length($o)<$MAX_WIDTH)
					{
						$o=$o.(" " x ($MAX_WIDTH-length($o)));
					}
					print $o."\n";
				}
			}
			++$i;
			if($i>$MAX_LINE_COUNT){last;}
		}

	}

	#check if batch
	if($O_BATCH){last;}

	#delay
	sleep($O_DELAY);

}
