Created
February 5, 2019 23:21
-
-
Save geraldlai/fc87a7fa4ae92ff07683adb20249e406 to your computer and use it in GitHub Desktop.
snuff: kill processes gracefully
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
# snuff: kill processes gracefully | |
# Author: Gerald Lai | |
# License: 0BSD | |
use warnings; | |
use strict; | |
use Getopt::Long qw(GetOptions); | |
Getopt::Long::Configure(qw(posix_default no_ignore_case pass_through bundling)); | |
use Pod::Usage qw(pod2usage); | |
use Time::HiRes qw(usleep); | |
my %OPT = (grace => 1); | |
GetOptions( \%OPT, | |
"grace|t=f", | |
"single|s", | |
"quiet|q", | |
"help|h|?" => \&help, | |
"man" => \&man, | |
) or pod2usage( | |
-message => "snuff: Error parsing command arguments.\n", | |
-verbose => 0, # SYNOPSIS only | |
-exitval => 22, | |
-output => \*STDERR, | |
); | |
# no input | |
(@ARGV == 0) and pod2usage( | |
-message => "snuff: No PIDs given.\n", | |
-verbose => 0, # SYNOPSIS only | |
-exitval => 22, | |
-output => \*STDERR, | |
); | |
$OPT{grace} = 1 if $OPT{grace} < 0; | |
$OPT{grace} *= 1e6; | |
my @snuff; | |
for my $pidarg (@ARGV) | |
{ | |
next unless $pidarg =~ /^\d+$/; | |
my %psinfo; | |
my @pids = $pidarg; | |
# obtain list of child PIDs | |
unless ($OPT{single}) | |
{ | |
# take snapshot of process table | |
my %pspid; | |
for (qx(/bin/ps -A -o ppid= -o pid= -o etime= -o args=)) | |
{ | |
chomp; | |
my ($ppid, $pid, $etime, @cmd) = split; | |
push @{ $pspid{$ppid} }, $pid; | |
$psinfo{$pid} = " # [$etime] @cmd"; | |
} | |
# build dependency list | |
my @search = $pidarg; # begin with PID as parent | |
while (@search) | |
{ | |
my @temp = @search; | |
@search = (); | |
# search for children and prepend them to PID snuff list | |
for my $ppid (@temp) | |
{ | |
my $children_ref = $pspid{$ppid}; | |
push @search, @{ $children_ref } if defined($children_ref); | |
} | |
unshift @pids, @search; | |
} | |
} | |
# snuff by systematically sending sequence of signals | |
for my $pid (@pids) | |
{ | |
my $pidtxt = $pid; | |
$pidtxt .= $psinfo{$pid} if defined($psinfo{$pid}); | |
print STDERR "\n" unless $OPT{quiet}; | |
next unless kill 0 => $pid; | |
push @snuff, $pid; | |
print STDERR "[SIGTERM] kill -15 $pidtxt\n" unless $OPT{quiet}; | |
kill TERM => $pid; | |
usleep $OPT{grace}; | |
next unless kill 0 => $pid; | |
print STDERR "[SIGINT] kill -2 $pidtxt\n" unless $OPT{quiet}; | |
kill INT => $pid; | |
usleep $OPT{grace}; | |
next unless kill 0 => $pid; | |
print STDERR "[SIGHUP] kill -1 $pidtxt\n" unless $OPT{quiet}; | |
kill HUP => $pid; | |
usleep $OPT{grace}; | |
next unless kill 0 => $pid; | |
print STDERR "[SIGKILL] kill -9 $pidtxt\n" unless $OPT{quiet}; | |
kill KILL => $pid; | |
} | |
} | |
# print PIDs in snuffed order | |
if (@snuff && !$OPT{quiet}) | |
{ | |
print STDERR "\n"; | |
print "@snuff\n"; | |
} | |
exit (@snuff ? 0 : 1); | |
# SYNOPSIS | |
sub help { | |
pod2usage( | |
-verbose => 1, | |
-exitval => 1, | |
-output => \*STDERR, | |
); | |
} | |
# man page of POD | |
sub man { | |
pod2usage( | |
-verbose => 2, | |
-exitval => 1, | |
-output => \*STDOUT, | |
); | |
} | |
__END__ | |
=head1 NAME | |
snuff - Kill Unix processes gracefully | |
=head1 SYNOPSIS | |
=over | |
=item snuff | |
[ -t, --grace <secs> ] Grace period before sending next signal. Default 1. | |
[ -s, --single ] Do not snuff child processes. Default disabled. | |
[ -q, --quiet ] Be non-verbose about snuffing processes. Default disabled. | |
[ -h ] [ --man ] Command usage / manpage. | |
pid1 [ pid2 ... ] Specify one or more PIDs. | |
=back | |
=head1 DEPENDENCIES | |
ps(1) -- C</bin/ps> | |
No non-core modules are required. | |
=head1 AUTHOR | |
Gerald Lai | |
=cut | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment