#!/dcs/packages/python-2.4/bin/python
#!/dcs/bin/python

import os
import sys
import time
import string
import signal

def datemap(charactertime):
	#meter-root) ps -eo 'pid,ruser,etime,args'
	#13515  dmelzer  1-07:02:06 /usr/sbin/sshd -R
	#26116     root        4:30 /usr/lib/sendmail -q15m
	#18630     root    01:34:30 /usr/lib/sendmail -q15m
	#10481     wwwd    03:19:42 /usr/bin/httpd -DSSL -DPHP4 -d /Web
	# 8295     wwwd    03:47:20 /usr/bin/httpd -DSSL -DPHP4 -d /Web
	#13417     root    02:38:27 in.comsat
	#26528     root        0:01 ps -eo pid,ruser,etime,args
	#17331     root    01:49:30 /usr/lib/sendmail -q15m
	fields1 = string.splitfields(charactertime,'-')
	l1 = len(fields1)
	days=0
	hours=0
	minutes=0
	seconds=0
	if l1 == 1:
		# just hours:minutes:days
		hms=fields1[0]
	elif l1 == 2:
		# days and hours:minutes:days
		days=string.atoi(fields1[0])
		hms=fields1[1]
	else:
		sys.stderr.write('Internal error 1 in function datemap\n')
		sys.exit(1)
	fields2 = string.splitfields(hms,':')
	l2 = len(fields2)
	if l2 == 1:
		# just seconds
		seconds=string.atoi(fields2[0])
	elif l2 == 2:
		# seconds and minutes
		seconds=string.atoi(fields2[0])
		minutes=string.atoi(fields2[1])
	elif l2 == 3:
		# seconds, minutes and hours
		seconds=string.atoi(fields2[0])
		minutes=string.atoi(fields2[1])
		hours=string.atoi(fields2[2])
	else:
		sys.stderr.write('Internal error 2 in function datemap\n')
		sys.exit(1)
	#print seconds, minutes, hours, days
	return seconds + (minutes * 60 + (hours * 60 + (days * 24L)))

class pidclass:
	def __init__(self,pid,charactertime,command):
		self.pid = string.atoi(pid)
		self.charactertime = charactertime
		self.numerictime = datemap(charactertime)
		self.command = command
		#print charactertime,self.charactertime,self.numerictime

	def __cmp__(self,other):
		#print self.numerictime,other.numerictime
		if self.numerictime < other.numerictime:
			return -1
		if self.numerictime > other.numerictime:
			return 1
		return 0

	def __str__(self):
		return "%d %s %d %s" % (self.pid, self.charactertime, self.numerictime, self.command)

	__repr__ = __str__

def usage():
	sys.stderr.write('Usage: [-v n] [-d] %s command user1 user2 ... usern\n' % sys.argv[0])
	sys.stderr.write('The effect is to kill all but the most recent instance of "command" for the given list of users\n')
	sys.stderr.write('command is currently searched only by substring, not regex\n')
	sys.stderr.write('The higher n is on -v, the more verbose the program will run.  Valid values so far are 0, 1 and 2\n')
	sys.stderr.write("-d says to only do a dry run: report what would have been killed and left, but don't kill anything\n")
	sys.exit(0)

def main():
	verbosity = 0
	dryrun = 0
	while sys.argv[1:] and sys.argv[1][0:1] == '-':
		if sys.argv[1] == '-v':
			verbosity = string.atoi(sys.argv[2])
			del sys.argv[1]
			del sys.argv[1]
			if verbosity >= 3:
				usage()
		elif sys.argv[1] == '-d':
			dryrun = 1
			del sys.argv[1]
		else:
			usage()

	if not len(sys.argv) >= 2:
		usage()

	target_command = sys.argv[1]
	target_users = sys.argv[2:]

	pipe = os.popen('ps -eo "pid,ruser,etime,args"','r')
	dict = {}
	while 1:
		line = pipe.readline()
		if not line:
			break
		line = string.strip(line)
		fields = string.split(line)
		if len(fields) < 4:
			continue
		pid = fields[0]
		user = fields[1]
		charactertime = fields[2]
		command = string.join(fields[3:])
		if user in target_users:
			#>>> print string.find('abcfoobar','foo')
			#3
			#>>> print string.find('abcdefghi','foo')
			#-1
			#>>> 
			if string.find(command,target_command) != -1:
				#numerictime = datemap(charactertime)
				if not dict.has_key(user):
					dict[user] = [ pidclass(pid,charactertime,command) ]
				else:
					dict[user].append(pidclass(pid,charactertime,command))
	pipe.close()
	users = dict.keys()
	users.sort()
	for user in users:
		list = dict[user]
		#print list
		list.sort()
		#print list
		# delete the first pid - it should always be the youngest, and there should always be at least one pidclass
		# element in the list
		if verbosity >= 2:
			sys.stdout.write('Leaving pid %d, user %s, chrtime %s, ntime %d, command %s\n' % \
				(list[0].pid, user, list[0].charactertime, list[0].numerictime, list[0].command))
		del list[0]
		for pid in list:
			if verbosity >= 1:
				sys.stdout.write('Killing pid %d, user %s, chrtime %s, ntime %d, command %s\n' % \
					(pid.pid, user, pid.charactertime, pid.numerictime, pid.command))
			if dryrun:
				sys.stdout.write('Dry run - nothing really killed\n')
			else:
				try:
					os.kill(pid.pid,signal.SIGTERM)
				except:
					pass
				time.sleep(1)
				try:
					os.kill(pid.pid,signal.SIGHUP)
				except:
					pass
				time.sleep(1)
				try:
					os.kill(pid.pid,signal.SIGKILL)
				except:
					pass

main()

