killp.pl - Perl script to kill processes safely
#!/usr/local/bin/perl
#
# /usr/local/bin/killp - kill user processes with confirmation
#
# Author: Jim Wildman, 3X HealthCare Solutions Group
# jawildman@cfanet.com, jim.wildman@3x.com
# Date: 01/28/2000
# Updated: 02/04/2000
#
# On the Sigma1 system, this is wrapped in a script called 'killp'
# which consists of 'sudo /usr/local/bin/killp.pl -u $1'
#
# The Syslog module requires that the Perl header files have been built
# with 'cd /usr/include; h2ph * sys/*'
#
# On the HPUX 10.20 boxes, this process generated some bad Perl header files.
# The Perl header files will be identified the first time you run the
# script. Edit them at the lines indicated and change "" to " in the
# die() statements. The h2ph man page warns that it isn't perfect, so
# this doesn't really seem to be a bug. More likely it is something
# dicey in the HP header files, but we won't go there.
#
# The /etc/syslog.conf file could be edited to log all of this stuff
# somewhere else. You would need to change the openlog() call to use
# a facility other than 'user'. But why create more log files to
# rotate?
use Getopt::Std;
use Sys::Syslog;
# This is the list of logins/users that we can't kill with this script
@notkillable = ("root","sigma","mqm","daemon","bin","sys","adm","uucp",
"lp","nuucp","hpdb","www","tftp");
# Right now the -u is required. This is partly due to my inexperience
# with perl, partly because I want people to think about it before they
# do the dirty deed.
getopts('hsu:') || die "Usage: killp [-h] | [-s] -u <Userstringto_kill>\n";
if(($opt_u eq "") || ($opt_h)) {
printf("Usage: killu [-h] | [-s] [-p processtokill] \n");
printf(" List, select and kill processes by user id\n");
printf(" -h display this help message\n");
printf(" -s simulate, don't kill anything\n");
printf(" -u actually kill the processes\n");
exit;
}
# This sets up the syslog command. Syslog entries will have
# ident = killp
# log_option = pid
# facility = user
# See man syslog or man syslogd for more details
openlog( 'killp', 'pid', 'user' );
# set our kill string to match on
$kill_string = $opt_u;
# Walk the notkillable list checking the kill string against each entry
# This depends on all of the 'system' ids being in the notkillable array
# The getpwuid call doesn't really work right because the process is run
# with sudo. But sudo logs everything anyway, so we get the id.
foreach $i (@notkillable) {
if($kill_string =~ $notkillable[$i]) {
$myowner = getpwuid $<;
# syslog requires a properly formatted string, so set up
# $mstring as a message string
$mstring = $myowner . " tried to kill a restricted process" ;
# log the attempt
syslog( 'info', $mstring);
syslog( 'info', $kill_string);
# Yell at the user
print( "NO! You are not allowed to kill system processes\n");
print("System processes have to be killed from a root login\n\n");
exit();
}
}
# Get the uid of the kill_string from the password file
$kuserid = "" ;
while( ($name,$passw,$uid) = getpwent) {
if($kill_string =~ $name) {
$kuserid = $uid;
}
}
# If we don't match against the password file, then we quit. This means
# no partial user matches etc. It has to be right.
if($kuserid == "") {
print("This >>$kill_string<< doesn't seem to be a valid user name.\n");
exit();
}
# Get our list of processes
@processes = `ps -fu $kill_string`;
while ( ($item=&Menu) > ($#processes + 1) ) { };
$prstring = $processes[$item];
chomp $prstring;
# split the ps output. We really just need the first 3 fields
($user,$pid,$ppid) = split(" ",$prstring) ;
# Check for the -s (summary) flag
if($opt_s ne "") {
print( "Right here we would kill \n>>$prstring<<\n");
print( "But you have the -s (summary) option turned on. Boohoo.\n");
exit();
}
print "\n\nLast chance. When you hit 'y<return>'" ;
print "\n******They will be gone!!******" ;
print "\nLocked, loaded and ready to kill\n>>$prstring<<\n\tOkay? : ";
$confirm = <STDIN>;
chomp $confirm;
$myowner = getpwuid $<;
if( ($confirm eq "y") || ($confirm eq "Y")) {
print "Killing $user, $pid\n";
$cnt = kill 'KILL', $pid;
if($cnt == 0){
print "No processes killed. Do you have permission to do this?\n";
$mstring = "Failed killp attempt on";
syslog( 'info', $mstring);
syslog( 'info', $prstring);
exit;
}
else {
print "$cnt processes killed. Hope you're happy!\n";
# log the event
syslog( 'info', $prstring);
exit;
}
}
# User bailed out or mistyped Y/y
else {
print "Well, you're either smart or chicken :-)\n";
print "No kill performed\n";
}
print "\n";
####
# This subroutine displays the contents of the @processes list with each
# item numbered. It then waits for a matching number to be entered. It
# also presents the user with the option to quit.
sub Menu
{
my ($rc) = 0;
system("clear");
print("\tProcesses belonging to $kill_string\n");
print("\tEnter the menu number of the process to kill\n\n");
# The first entry in @processes is the header line from ps
for ($i = 1; $i<= $#processes; $i++)
{
print $i . ". " . $processes[$i] . "\n";
}
#
# Add a quit option
print $i . ". " . "Quit\n";
print "\n\nSelect option: ";
$s=<STDIN>;
chop $s;
# This means they picked the highest number, which is 'Quit'
# or entered 0 which is invalid and does the same thing. Other
# numbers will be kicked back by the while() loop which called
# us
if( ($s == $i) || ($s == 0) ){
print "\nHave it your way. Bye\n";
exit;
}
return ($s);
}