#!/usr/bin/perl
# mkill --- kill processes by name
# Author: Noah Friedman <friedman@splode.com>
# Created: 2008-01-10
# Public domain

# $Id: mkill,v 1.10 2018/05/15 23:45:07 friedman Exp $

# Commentary:
# Code:

use strict;
use Symbol;

$^W = 1; # enable warnings

(my $progname = $0) =~ s=^.*/==;

sub xcat
{
  my $fh = gensym;
  open ($fh, $_[0]) || return;
  local $/ = undef;
  return scalar <$fh>;
}

sub basename
{
  local $_ = shift;
  s=.*/==;
  return $_;
}


sub pids
{
  my $dfh = gensym;
  opendir ($dfh, "/proc") || return ;
  my @pids = sort { $a <=> $b } grep { /^\d+$/ && $_ != $$ } readdir $dfh;
  closedir ($dfh);

  if ($> != 0)
    {
      # Filter out any processes not run by us
      return grep { uid ($_) == $> } @pids;
    }
  return @pids;
}

sub cmdline
{
  my $pid = shift;
  my $cmdline = xcat ("/proc/$pid/cmdline");
  return unless defined $cmdline;
  return split (/\0/, $cmdline) unless $cmdline eq "";
  return stat_name ($pid);
}

sub exe
{
  my $pid = shift;
  my $exe = readlink ("/proc/$pid/exe");
  return $exe;
}

my %stat_data;
sub stat_data
{
  my $pid = shift;
  return $stat_data{$pid} if exists $stat_data{$pid};
  my $stat = xcat ("/proc/$pid/status");
  return unless defined $stat;
  $stat_data{$pid} = $stat;
  return $stat;
}

sub stat_name
{
  my $stat = stat_data ($_[0]);
  return unless defined $stat;
  return $1 if $stat =~ m|^Name:\s+(.*)|m;
}

sub uid
{
  my $stat = stat_data ($_[0]);
  return 0 unless defined $stat;
  return $1 if $stat =~ m|^Uid:\s+(\d+)|m;
  return 0;
}

sub pidof
{
  my ($name) = @_;
  my $re = ($name =~ m|^/(.*)/$| ? $1 : undef);

  my @allpids = pids ();
  my @pid;
  for my $p (@allpids)
    {
      my @cmdline = cmdline ($p);
      map { if (defined $_
                && ($_ eq $name
                    || basename ($_) eq $name
                    || (defined $re && $_ =~ $re)))
              {
                push @pid, $p;
                next;
              }
          } (@cmdline[0,1,2], exe($p), stat_name($p));
    }
  return @pid;
}

sub main
{
  my $signal = 15;

  if ($_[0] =~ /^-(\d+|(?:sig)?[a-z0-9+-]+)$/i)
    {
      $signal = uc ($1);
      shift;
    }

  unless (@_)
    {
      print STDERR "Usage: $progname -signal procname ...\n";
      print STDERR "   or  $progname -signal /regex/ ...\n";
      exit (1);
    }

  my $exitstat = 0;

  for my $procname (@_)
    {
      my @pid = pidof ($procname);
      unless (@pid)
        {
          # If signal is 0, we're checking for process existence.
          $exitstat = 1 if $signal == 0;
          next;
        }

      if ($> == 0)
        {
          map { my $cmdline = join (" ", cmdline ($_));
                my $user = getpwuid (uid ($_));
                printf ("%d %s: %s\n", $_, $user, $cmdline);
              } @pid;
        }
      else
        {
          map { my $cmdline = join (" ", cmdline ($_));
                printf ("%d: %s\n", $_, $cmdline);
              } @pid;
        }

      map { unless (kill ($signal, $_))
              {
                print STDERR "kill: $_: $!\n";
                $exitstat = 1;
              }
          } @pid;
    }
  exit ($exitstat);
}

main (@ARGV);

# eof
