#include <sys/user.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <signal.h>
#include <regex.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#define PHASH_SIZE 1024
struct pchain {
struct pchain *next;
struct kinfo_proc *proc;
struct pchain *parent;
};
static char *prog;
static struct pchain *phash[PHASH_SIZE];
static struct pchain *saveparents(struct kinfo_proc *procs, int nprocs,
pid_t mypid);
static int checkparent(struct pchain *mychain, pid_t pid);
static void __dead2
usage(void)
{
fprintf(stderr, "usage: %s [-c cmd] [-d|-v] [-h|-?|-help] [-j jail] "
"[-l] [-m] [-q] [-s] [-t tty] [-T] [-u user] "
"[-sig] [cmd]...\n", prog);
fprintf(stderr, "At least one option or argument to specify "
"processes must be given.\n");
exit(1);
}
static char *
upper(const char *str)
{
static char buf[80];
char *s;
strlcpy(buf, str, sizeof(buf));
for (s = buf; *s; s++)
*s = toupper((unsigned char)*s);
return buf;
}
static void
printsig(FILE *fp)
{
const char *const * p;
int cnt;
int offset = 0;
for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) {
offset += fprintf(fp, "%s ", upper(*p));
if (offset >= 75 && cnt > 1) {
offset = 0;
fprintf(fp, "\n");
}
}
fprintf(fp, "\n");
}
static void
nosig(char *name)
{
warnx("unknown signal %s; valid signals:", name);
printsig(stderr);
exit(1);
}
int
main(int ac, char **av)
{
struct kinfo_proc *procs = NULL, *newprocs;
struct pchain *mychain;
struct stat sb;
struct passwd *pw;
regex_t rgx;
regmatch_t pmatch;
int i, j;
char buf[256];
char *user = NULL;
char *tty = NULL;
char *cmd = NULL;
int qflag = 0;
int vflag = 0;
int sflag = 0;
int jflag = 0, jailid = 0;
int dflag = 0;
int mflag = 0;
int Tflag = 0;
uid_t uid = 0;
dev_t tdev = 0;
pid_t mypid;
char thiscmd[MAXCOMLEN + 1];
pid_t thispid;
uid_t thisuid;
dev_t thistdev;
int sig = SIGTERM;
const char *const *p;
char *ep;
int errors = 0;
int mib[4];
size_t miblen;
int st, nprocs;
size_t size;
int matched;
int killed = 0;
prog = av[0];
av++;
ac--;
while (ac > 0) {
if (strcmp(*av, "-l") == 0) {
printsig(stdout);
exit(0);
}
if (strcmp(*av, "-help") == 0)
usage();
if (**av == '-') {
++*av;
switch (**av) {
case 'h':
case '?':
usage();
case 'u':
++*av;
if (**av == '\0')
++av;
--ac;
user = *av;
break;
case 't':
++*av;
if (**av == '\0')
++av;
--ac;
tty = *av;
break;
case 'c':
++*av;
if (**av == '\0')
++av;
--ac;
cmd = *av;
break;
case 'j':
{
const char *errstr;
++*av;
if (**av == '\0')
++av;
--ac;
jailid = strtonum(*av, 1, INT_MAX, &errstr);
if (errstr)
errx(1, "jail id is %s: %s", errstr, *av);
jflag++;
break;
}
case 'q':
qflag++;
break;
case 'v':
vflag++;
break;
case 's':
sflag++;
break;
case 'd':
dflag++;
break;
case 'm':
mflag++;
break;
case 'T':
Tflag++;
break;
default:
if (isalpha((unsigned char)**av)) {
if (strncasecmp(*av, "sig", 3) == 0)
*av += 3;
for (sig = NSIG, p = sys_signame + 1;
--sig; ++p)
if (strcasecmp(*p, *av) == 0) {
sig = p - sys_signame;
break;
}
if (!sig)
nosig(*av);
} else if (isdigit((unsigned char)**av)) {
sig = strtol(*av, &ep, 10);
if (!*av || *ep)
errx(1, "illegal signal number: %s", *av);
if (sig < 0 || sig >= NSIG)
nosig(*av);
} else
nosig(*av);
}
++av;
--ac;
} else {
break;
}
}
if (user == NULL && tty == NULL && cmd == NULL &&
jflag == 0 && Tflag == 0 && ac == 0) {
usage();
}
if (Tflag) {
tty = ttyname(0);
if (tty)
tty = strdup(tty);
}
if (tty) {
if (strncmp(tty, "/dev/", 5) == 0)
snprintf(buf, sizeof(buf), "%s", tty);
else if (strncmp(tty, "tty", 3) == 0)
snprintf(buf, sizeof(buf), "/dev/%s", tty);
else if (isdigit(tty[0]))
snprintf(buf, sizeof(buf), "/dev/pts/%s", tty);
else
snprintf(buf, sizeof(buf), "/dev/tty%s", tty);
if (stat(buf, &sb) < 0)
err(1, "stat(%s)", buf);
if (!S_ISCHR(sb.st_mode))
errx(1, "%s: not a character device", buf);
tdev = sb.st_rdev;
if (dflag)
printf("ttydev:0x%x\n", tdev);
}
if (user) {
pw = getpwnam(user);
if (pw == NULL)
errx(1, "user %s does not exist", user);
uid = pw->pw_uid;
if (dflag)
printf("uid:%d\n", uid);
} else {
uid = getuid();
if (uid != 0) {
pw = getpwuid(uid);
if (pw)
user = pw->pw_name;
if (dflag)
printf("uid:%d\n", uid);
}
}
size = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_ALL;
mib[3] = 0;
miblen = 3;
if (user && mib[2] == KERN_PROC_ALL) {
mib[2] = KERN_PROC_RUID;
mib[3] = uid;
miblen = 4;
}
if (tty && mib[2] == KERN_PROC_ALL) {
mib[2] = KERN_PROC_TTY;
mib[3] = tdev;
miblen = 4;
}
st = sysctl(mib, miblen, NULL, &size, NULL, 0);
do {
size += size / 10;
newprocs = realloc(procs, size);
if (newprocs == NULL) {
if (procs)
free(procs);
errx(1, "could not reallocate memory");
}
procs = newprocs;
st = sysctl(mib, miblen, procs, &size, NULL, 0);
} while (st == -1 && errno == ENOMEM);
if (st == -1)
err(1, "could not sysctl(KERN_PROC)");
if (size % sizeof(struct kinfo_proc) != 0) {
fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n",
size, sizeof(struct kinfo_proc));
fprintf(stderr, "userland out of sync with kernel, recompile libkvm etc\n");
exit(1);
}
nprocs = size / sizeof(struct kinfo_proc);
if (dflag)
printf("nprocs %d\n", nprocs);
mypid = getpid();
if (Tflag && tty)
mychain = saveparents(procs, nprocs, mypid);
else
mychain = NULL;
for (i = 0; i < nprocs; i++) {
thispid = procs[i].kp_pid;
strncpy(thiscmd, procs[i].kp_comm, MAXCOMLEN);
thiscmd[MAXCOMLEN] = '\0';
thistdev = procs[i].kp_tdev;
thisuid = procs[i].kp_ruid;
if (thispid == mypid)
continue;
matched = 1;
if ((int)procs[i].kp_pid < 0)
matched = 0;
if (user) {
if (thisuid != uid)
matched = 0;
}
if (tty) {
if (thistdev != tdev)
matched = 0;
if (Tflag && checkparent(mychain, procs[i].kp_pid))
matched = 0;
}
if (jflag) {
if (procs[i].kp_jailid != jailid)
matched = 0;
}
if (cmd) {
if (mflag) {
if (regcomp(&rgx, cmd,
REG_EXTENDED|REG_NOSUB) != 0) {
mflag = 0;
warnx("%s: illegal regexp", cmd);
}
}
if (mflag) {
pmatch.rm_so = 0;
pmatch.rm_eo = strlen(thiscmd);
if (regexec(&rgx, thiscmd, 0, &pmatch,
REG_STARTEND) != 0)
matched = 0;
regfree(&rgx);
} else {
if (strcmp(thiscmd, cmd) != 0)
matched = 0;
}
}
if (matched == 0)
continue;
if (ac > 0) {
matched = 0;
for (j = 0; j < ac; j++) {
if (mflag) {
if (regcomp(&rgx, av[j],
REG_EXTENDED|REG_NOSUB) != 0) {
mflag = 0;
warnx("%s: illegal regexp", av[j]);
}
}
if (mflag) {
pmatch.rm_so = 0;
pmatch.rm_eo = strlen(thiscmd);
if (regexec(&rgx, thiscmd, 0, &pmatch,
REG_STARTEND) == 0)
matched = 1;
regfree(&rgx);
} else {
if (strcmp(thiscmd, av[j]) == 0)
matched = 1;
}
if (matched)
break;
}
if (matched == 0)
continue;
}
if (dflag)
printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
thiscmd, thispid, thistdev, thisuid);
if (vflag || sflag)
printf("kill -%s %d\n", upper(sys_signame[sig]),
thispid);
killed++;
if (!dflag && !sflag) {
if (kill(thispid, sig) < 0 ) {
warn("kill -%s %d", upper(sys_signame[sig]),
thispid);
errors = 1;
}
}
}
if (!qflag && killed == 0) {
fprintf(stderr, "No matching processes %swere found\n",
getuid() != 0 ? "belonging to you " : "");
errors = 1;
}
exit(errors);
}
static
struct pchain *
saveparents(struct kinfo_proc *procs, int nprocs, pid_t mypid)
{
struct pchain *rchain = NULL;
struct pchain *chain;
struct pchain *pchain;
int i;
int hv;
for (i = 0; i < nprocs; ++i) {
if ((int)procs[i].kp_pid < 0)
continue;
hv = (int)procs[i].kp_pid & 1023;
chain = malloc(sizeof(*chain));
chain->proc = &procs[i];
chain->parent = NULL;
chain->next = phash[hv];
phash[hv] = chain;
if (mypid == procs[i].kp_pid)
rchain = chain;
}
for (i = 0; i < nprocs; ++i) {
if ((int)procs[i].kp_pid < 0)
continue;
if ((int)procs[i].kp_ppid < 0)
continue;
hv = (int)procs[i].kp_pid & 1023;
for (chain = phash[hv]; chain; chain = chain->next) {
if (chain->proc->kp_pid == procs[i].kp_pid)
break;
}
hv = (int)procs[i].kp_ppid & 1023;
for (pchain = phash[hv]; pchain; pchain = pchain->next) {
if (pchain->proc->kp_pid == procs[i].kp_ppid) {
if (chain)
chain->parent = pchain;
break;
}
}
}
return (rchain);
}
static
int
checkparent(struct pchain *chain, pid_t pid)
{
while (chain) {
if (chain->proc->kp_pid == pid)
return(1);
chain = chain->parent;
}
return(0);
}