#! /usr/bin/csh -f
#
#  realnice -
#
#  realnice "watches" a program who's `ps -lj` listing contains a given string (name).
#  Whenever someone logged onto the console is 1) not idle, and 2) not the owner of realnice,
#  the "watched" job will be stopped.
#
#  Also, "dominant" program and user names may be specified on the realnice command line 
#  (after "name").  (The presence of these "dominators" is asessed by greping a `ps -elf`)
#  If any matching programs are running, realnice will stop it's watched program.
#
#  If the system is idle, alone, and no dominant programs are running, realnice will restart
#  the "watched" job again, and continue monitoring.
#  The idle timeout can be set in the "timeout" environment variable:
#
#	setenv timeout 0
#
#  will disable idle monitoring and just look for dominant processes
#

# debugging variables
#set debug
#set pretend

set noglob
set alone
if(! $?timeout) set timeout = 10
if(! $?HOST) set HOST = `hostname`
set minidle
set dominant


# there must be a command-line argument
if ($1 == "") then
echo "usage: realnice [ pid | name ] [ domlist ]"
echo ""
echo "realnice will stop a process matching name while someone is on the console (and not idle)"
echo "will also stop a process matching name if a process in domlist is running"
echo ""
exit 2
endif
# user-specified test run
if ($1 == realnice) then
    echo "realnice - diagnostic mode..."
    set debug
    set pretend
    set argv[1] = sh
endif

again:

# get filtered process listing from ps
set process = `ps -lju $user | grep $1 | grep -v $$`

# make sure the indicated process is actually running
if ($#process == 0) then
    if($?debug) echo $1 "is not running..."
    sleep 30
    goto again
endif

#extract PGID
set PGID=$process[6]

# (redundant) keep updating process status
if($process[2] == "T") then
    set stopped
    if($?debug) echo $1, PGID = $PGID is stopped
else
    unset stopped
    if($?debug) echo $1, PGID = $PGID is running 
endif

# lost child processes might escape, send start and stop signals now
if($?stopped) then
    if(! $?pretend) kill -STOP -$PGID
else
    if(! $?pretend) kill -CONT -$PGID
endif

# querry system for activity (converting idle times to minutes)
if ($?debug) echo "Querying system for idle times..."
set idle = `w -sh | grep " :0" | grep -v $user | nawk '{if(NF > 4) {idle = substr($4, index($4, ":")+1) +0; idle += substr($4, 0, index($4, ":")-1) * 60; if($4 !~ /[a-z]/) print idle} else {print 0}}'`

# see if there are any users logged in
if ($#idle == 0) then
    set alone
    if ($?debug) echo "realnice is alone on" $HOST
else
    # someone (other than $user) is currently on the console
    unset alone
    
    # see if they have been idle for a while
    set minidle = 99999
    foreach num ( $idle )
	if($num < $minidle) set minidle = $num
    end
    if($?debug) echo $HOST "has been idle for" $minidle "minutes, timeout is" $timeout "minutes."
    if(($minidle != 99999)&&($minidle >= $timeout)) then
	set alone 
	if($?debug) echo "realnice is not alone, but" $HOST "is idle"
    endif 
endif

# look for indicated "dominant" processes to stop $1
foreach key ( $* )
    if ($key != $1) then
        set dominant = `ps -el | grep -v $user | grep -v $$ | grep "$key"`
        if($#dominant > 16) then
            if($dominant[2] != "T") then
            	if($?debug) echo dominant program $dominant[16] found running by $dominant[3]
            	unset alone
	    else
		if($?debug) echo dominant program $dominant[16] found idle, run by $dominant[3]
	    endif
        endif
    endif
end

# stop the process if we are not alone
if( (! $?alone) && (! $?stopped)) then
    if( $?debug ) echo stopping $1 process group with PGID=$PGID at `date`
    if(! $?pretend) kill -STOP -$PGID
    set stopped
endif
    
# restart the process if we are alone again
if ($?alone && $?stopped) then
    if( $?debug ) echo restarting $1 process group with PGID=$PGID at `date`
    if(! $?pretend) kill -CONT -$PGID
    unset stopped
endif

# wait a bit
if($?alone) then
    # ever vigilant of new logins
    sleep 30
else
    # no need to keep checking until $timeout has expired
    sleep `expr 60 \* $timeout + 60`
endif

# continue monitoring
goto again


