skill: use library for process scanning

skill is one of the older and more unloved programs. It was still
scanning readdir /proc. It now will use the procps library like the
rest of the programs.

Signed-off-by: Craig Small <csmall@enc.com.au>
This commit is contained in:
Craig Small 2015-09-26 14:32:56 +10:00
parent 1b6e5ae11e
commit 0a0e4c603e

256
skill.c
View File

@ -34,15 +34,15 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <proc/namespace.h>
#include <proc/pids.h>
#include "c.h" #include "c.h"
#include "fileutils.h" #include "fileutils.h"
#include "signals.h" #include "signals.h"
#include "strutils.h" #include "strutils.h"
#include "nls.h" #include "nls.h"
#include "xalloc.h" #include "xalloc.h"
#include "proc/pwcache.h"
#include "proc/devname.h"
#include <proc/namespace.h>
#include "rpmatch.h" #include "rpmatch.h"
#define DEFAULT_NICE 4 #define DEFAULT_NICE 4
@ -62,13 +62,24 @@ static const char **cmds;
static int *pids; static int *pids;
static char **namespaces; static char **namespaces;
static int ns_pid; static int ns_pid;
static struct procps_namespaces ns; static struct procps_namespaces match_namespaces;
static int ns_flags = 0x3f;
#define ENLIST(thing,addme) do{ \ #define ENLIST(thing,addme) do{ \
if(!thing##s) thing##s = xmalloc(sizeof(*thing##s)*saved_argc); \ if(!thing##s) thing##s = xmalloc(sizeof(*thing##s)*saved_argc); \
thing##s[thing##_count++] = addme; \ thing##s[thing##_count++] = addme; \
}while(0) }while(0)
enum pids_item items[] = {
PROCPS_PIDS_ID_PID,
PROCPS_PIDS_ID_EUID,
PROCPS_PIDS_ID_EUSER,
PROCPS_PIDS_TTY,
PROCPS_PIDS_TTY_NAME,
PROCPS_PIDS_CMD};
enum rel_items {
EU_PID, EU_EUID, EU_EUSER, EU_TTY, EU_TTYNAME, EU_CMD};
static int my_pid; static int my_pid;
static int saved_argc; static int saved_argc;
@ -81,7 +92,6 @@ enum {
}; };
static int program = PROG_UNKNOWN; static int program = PROG_UNKNOWN;
static int ns_flags = 0x3f;
static int parse_namespaces(char *optarg) static int parse_namespaces(char *optarg)
{ {
char *ptr = optarg, *tmp; char *ptr = optarg, *tmp;
@ -114,126 +124,100 @@ static int parse_namespaces(char *optarg)
return 0; return 0;
} }
/* kill or nice a process */ static int match_intlist(const int value, const int len, int *list)
static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd, {
int i;
for(i=0; i<len; i++)
if (list[i] == value)
return 1;
return 0;
}
static int match_strlist(const char *value, const int len, const char **list)
{
int i;
for(i=0; i<len; i++)
if (strcmp(list[i], value) == 0)
return 1;
return 0;
}
static int match_ns(const int pid)
{
struct procps_namespaces proc_ns;
int found = 1;
int i;
if (procps_ns_read_pid(pid, &proc_ns) < 0)
xerrx(EXIT_FAILURE,
_("Unable to read process namespace information"));
for (i = 0; i < PROCPS_NS_COUNT; i++) {
if (ns_flags & (1 << i)) {
if (proc_ns.ns[i] != match_namespaces.ns[i])
found = 0;
}
}
return found;
}
static int ask_user(struct pids_stack *stack)
{
#define PIDS_GETINT(e) PROCPS_PIDS_VAL(EU_ ## e, s_int, stack)
#define PIDS_GETSTR(e) PROCPS_PIDS_VAL(EU_ ## e, str, stack)
char *buf=NULL;
size_t len=0;
fprintf(stderr, "%-8s %-8s %5d %-16.16s ? ",
PIDS_GETSTR(TTYNAME),
PIDS_GETSTR(EUSER),
PIDS_GETINT(PID),
PIDS_GETSTR(CMD));
fflush(stdout);
if (getline(&buf, &len, stdin) == -1)
return 0;
if (rpmatch(buf) < 1) {
free(buf);
return 0;
}
free(buf);
return 1;
}
static void nice_or_kill(struct pids_stack *stack,
struct run_time_conf_t *run_time) struct run_time_conf_t *run_time)
{ {
int failed; int failed;
char dn_buf[1000];
dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV); if (run_time->interactive && !ask_user(stack))
if (run_time->interactive) {
char *buf;
size_t len = 0;
fprintf(stderr, "%-8s %-8s %5d %-16.16s ? ",
(char *)dn_buf, user_from_uid(uid), pid, cmd);
fflush (stdout);
if (getline(&buf, &len, stdin) == -1)
return; return;
if (rpmatch(buf) < 1) {
free(buf);
return;
}
free(buf);
}
/* do the actual work */ /* do the actual work */
errno = 0; errno = 0;
if (program == PROG_SKILL) if (program == PROG_SKILL)
failed = kill(pid, sig_or_pri); failed = kill(PIDS_GETINT(PID), sig_or_pri);
else else
failed = setpriority(PRIO_PROCESS, pid, sig_or_pri); failed = setpriority(PRIO_PROCESS, PIDS_GETINT(PID), sig_or_pri);
if ((run_time->warnings && failed) || run_time->debugging || run_time->verbose) { if ((run_time->warnings && failed) || run_time->debugging || run_time->verbose) {
fprintf(stderr, "%-8s %-8s %5d %-16.16s ", fprintf(stderr, "%-8s %-8s %5d %-16.16s ",
(char *)dn_buf, user_from_uid(uid), pid, cmd); PIDS_GETSTR(TTYNAME),
PIDS_GETSTR(EUSER),
PIDS_GETINT(PID),
PIDS_GETSTR(CMD));
perror(""); perror("");
return; return;
} }
if (run_time->interactive) if (run_time->interactive)
return; return;
if (run_time->noaction) { if (run_time->noaction) {
printf("%d\n", pid); printf("%d\n", PIDS_GETINT(PID));
return; return;
} }
} }
#undef PIDS_GETINT
/* check one process */ #undef PIDS_GETSTR
static void check_proc(int pid, struct run_time_conf_t *run_time)
{
char buf[128];
struct stat statbuf;
struct procps_namespaces pid_ns;
char *tmp;
int tty;
int fd;
int i;
if (pid == my_pid || pid == 0)
return;
/* pid (cmd) state ppid pgrp session tty */
sprintf(buf, "/proc/%d/stat", pid);
fd = open(buf, O_RDONLY);
if (fd == -1) {
/* process exited maybe */
if (run_time->warnings)
xwarn(_("cannot open file %s"), buf);
return;
}
fstat(fd, &statbuf);
if (uids) {
/* check the EUID */
i = uid_count;
while (i--)
if (uids[i] == statbuf.st_uid)
break;
if (i == -1)
goto closure;
}
if (read(fd, buf, 128) <= 0)
goto closure;
buf[127] = '\0';
tmp = strrchr(buf, ')');
*tmp++ = '\0';
i = 5;
while (i--)
while (*tmp++ != ' ')
/* scan to find tty */ ;
tty = atoi(tmp);
if (ttys) {
i = tty_count;
while (i--)
if (ttys[i] == tty)
break;
if (i == -1)
goto closure;
}
tmp = strchr(buf, '(') + 1;
if (cmds) {
i = cmd_count;
/* fast comparison trick -- useful? */
while (i--)
if (cmds[i][0] == *tmp && !strcmp(cmds[i], tmp))
break;
if (i == -1)
goto closure;
}
if (ns_pid) {
if (procps_ns_read_pid(pid, &pid_ns) < 0)
goto closure;
for (i = 0; i < PROCPS_NS_COUNT; i++) {
if (ns_flags & (1 << i)) {
if (pid_ns.ns[i] != ns.ns[i])
goto closure;
}
}
}
/* This is where we kill/nice something. */
/* for debugging purposes?
fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n",
pid, statbuf.st_uid, tty >> 8, tty & 0xf, tmp);
*/
hurt_proc(tty, statbuf.st_uid, pid, tmp, run_time);
closure:
/* kill/nice _first_ to avoid PID reuse */
close(fd);
}
/* debug function */ /* debug function */
static void show_lists(void) static void show_lists(void)
@ -277,36 +261,36 @@ static void show_lists(void)
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
/* iterate over all PIDs */ static void scan_procs(struct run_time_conf_t *run_time)
static void iterate(struct run_time_conf_t *run_time)
{ {
int pid; #define PIDS_GETINT(e) PROCPS_PIDS_VAL(EU_ ## e, s_int, reap->stacks[i])
DIR *d; #define PIDS_GETSTR(e) PROCPS_PIDS_VAL(EU_ ## e, str, reap->stacks[i])
struct dirent *de; struct procps_pidsinfo *info=NULL;
if (pids) { struct pids_reap *reap;
pid = pid_count; int i, total_procs;
while (pid--)
check_proc(pids[pid], run_time); if (procps_pids_new(&info, 6, items) < 0)
return; xerrx(EXIT_FAILURE,
} _("Unable to create pid info structure"));
#if 0 if ((reap = procps_pids_reap(info, PROCPS_REAP_TASKS_ONLY)) == NULL)
/* could setuid() and kill -1 to have the kernel wipe out a user */ xerrx(EXIT_FAILURE,
if (!ttys && !cmds && !pids && !run_time->interactive) { _("Unable to load process information"));
}
#endif total_procs = reap->counts.total;
d = opendir("/proc"); for (i=0; i < total_procs; i++) {
if (!d) if (PIDS_GETINT(PID) == my_pid || PIDS_GETINT(PID) == 0)
xerr(EXIT_FAILURE, "/proc");
while ((de = readdir(d))) {
if (de->d_name[0] > '9')
continue; continue;
if (de->d_name[0] < '1') if (uids && !match_intlist(PIDS_GETINT(EUID), uid_count, uids))
continue; continue;
pid = atoi(de->d_name); if (ttys && !match_intlist(PIDS_GETINT(TTY), tty_count, ttys))
if (pid) continue;
check_proc(pid, run_time); if (cmds && !match_strlist(PIDS_GETSTR(CMD), cmd_count, cmds))
continue;
if (namespaces && !match_ns(PIDS_GETINT(PID)))
continue;
nice_or_kill(reap->stacks[i], run_time);
} }
closedir(d);
} }
/* skill and snice help */ /* skill and snice help */
@ -393,7 +377,7 @@ static int snice_prio_option(int *argc, char **argv)
return (int)prio; return (int)prio;
} }
static void skillsnice_parse(int argc, static void parse_options(int argc,
char **argv, struct run_time_conf_t *run_time) char **argv, struct run_time_conf_t *run_time)
{ {
int signo = -1; int signo = -1;
@ -499,7 +483,7 @@ static void skillsnice_parse(int argc,
xwarnx(_("invalid pid number %s"), optarg); xwarnx(_("invalid pid number %s"), optarg);
skillsnice_usage(stderr); skillsnice_usage(stderr);
} }
if (procps_ns_read_pid(ns_pid, &ns) < 0) { if (procps_ns_read_pid(ns_pid, &match_namespaces) < 0) {
xwarnx(_("error reading reference namespace " xwarnx(_("error reading reference namespace "
"information")); "information"));
skillsnice_usage(stderr); skillsnice_usage(stderr);
@ -587,10 +571,10 @@ int main(int argc, char ** argv)
case PROG_SNICE: case PROG_SNICE:
case PROG_SKILL: case PROG_SKILL:
setpriority(PRIO_PROCESS, my_pid, -20); setpriority(PRIO_PROCESS, my_pid, -20);
skillsnice_parse(argc, argv, &run_time); parse_options(argc, argv, &run_time);
if (run_time.debugging) if (run_time.debugging)
show_lists(); show_lists();
iterate(&run_time); scan_procs(&run_time);
break; break;
default: default:
fprintf(stderr, _("skill: \"%s\" is not supported\n"), fprintf(stderr, _("skill: \"%s\" is not supported\n"),