summaryrefslogtreecommitdiffstats
path: root/src/skill.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/skill.c')
-rw-r--r--src/skill.c600
1 files changed, 600 insertions, 0 deletions
diff --git a/src/skill.c b/src/skill.c
new file mode 100644
index 0000000..055f7ee
--- /dev/null
+++ b/src/skill.c
@@ -0,0 +1,600 @@
+/*
+ * skill.c - send a signal to process
+ *
+ * Copyright © 2009-2023 Craig Small <csmall@dropbear.xyz>
+ * Copyright © 2011-2023 Jim Warner <james.warner@comcast.net>
+ * Copyright © 2011-2012 Sami Kerola <kerolasa@iki.fi>
+ * Copyright © 1998-2002 Albert Cahalan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "fileutils.h"
+#include "signals.h"
+#include "strutils.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "rpmatch.h"
+
+#include "misc.h"
+#include "pids.h"
+
+#define DEFAULT_NICE 4
+
+struct run_time_conf_t {
+ int fast;
+ int interactive;
+ int verbose;
+ int warnings;
+ int noaction;
+ int debugging;
+};
+static int tty_count, uid_count, cmd_count, pid_count, namespace_count;
+static int *ttys;
+static uid_t *uids;
+static const char **cmds;
+static int *pids;
+static char **namespaces;
+static int ns_pid;
+static struct procps_ns match_namespaces;
+static int ns_flags = 0x3f;
+
+#define ENLIST(thing,addme) do{ \
+if(thing##_count < 0 || (size_t)thing##_count >= INT_MAX / sizeof(*thing##s)) \
+ xerrx(EXIT_FAILURE, _("integer overflow")); \
+thing##s = xrealloc(thing##s, sizeof(*thing##s)*(thing##_count+1)); \
+thing##s[thing##_count++] = addme; \
+}while(0)
+
+struct pids_info *Pids_info;
+
+enum pids_item items[] = {
+ PIDS_ID_PID,
+ PIDS_ID_EUID,
+ PIDS_ID_EUSER,
+ PIDS_TTY,
+ PIDS_TTY_NAME,
+ PIDS_CMD};
+enum rel_items {
+ EU_PID, EU_EUID, EU_EUSER, EU_TTY, EU_TTYNAME, EU_CMD};
+
+static int my_pid;
+
+static int sig_or_pri;
+
+enum {
+ PROG_UNKNOWN,
+ PROG_SKILL,
+ PROG_SNICE
+};
+static int program = PROG_UNKNOWN;
+
+static int parse_namespaces(char *optarg)
+{
+ char *ptr = optarg, *tmp;
+ int len, id;
+
+ ns_flags = 0;
+ while (1) {
+ if (strchr(ptr, ',') == NULL) {
+ len = -1;
+ tmp = strdup(ptr);
+ } else {
+ len = strchr(ptr, ',') - ptr;
+ tmp = strndup(ptr, len);
+ }
+
+ id = procps_ns_get_id(tmp);
+ if (id == -1) {
+ fprintf(stderr, "%s is not a valid namespace\n", tmp);
+ free(tmp);
+ return 1;
+ }
+ ns_flags |= (1 << id);
+ ENLIST(namespace, tmp);
+
+ if (len == -1)
+ break;
+
+ ptr+= len + 1;
+ }
+ return 0;
+}
+
+static int match_intlist(const int value, const int len, int *list)
+{
+ 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_ns 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;
+}
+
+#define PIDS_GETINT(e) PIDS_VAL(EU_ ## e, s_int, stack, Pids_info)
+#define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, stack, Pids_info)
+
+static int ask_user(struct pids_stack *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) {
+ free(buf);
+ 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)
+{
+ int failed;
+
+ if (run_time->interactive && !ask_user(stack))
+ return;
+
+ /* do the actual work */
+ errno = 0;
+ if (program == PROG_SKILL)
+ failed = kill(PIDS_GETINT(PID), sig_or_pri);
+ else
+ failed = setpriority(PRIO_PROCESS, PIDS_GETINT(PID), sig_or_pri);
+ if ((run_time->warnings && failed) || run_time->debugging || run_time->verbose) {
+ fprintf(stderr, "%-8s %-8s %5d %-16.16s ",
+ PIDS_GETSTR(TTYNAME),
+ PIDS_GETSTR(EUSER),
+ PIDS_GETINT(PID),
+ PIDS_GETSTR(CMD));
+ perror("");
+ return;
+ }
+ if (run_time->interactive)
+ return;
+ if (run_time->noaction) {
+ printf("%d\n", PIDS_GETINT(PID));
+ return;
+ }
+}
+
+#undef PIDS_GETINT
+#undef PIDS_GETSTR
+
+/* debug function */
+static void show_lists(void)
+{
+ int i;
+
+ fprintf(stderr, "signal: %d\n", sig_or_pri);
+
+ fprintf(stderr, "%d TTY: ", tty_count);
+ if (ttys) {
+ i = tty_count;
+ while (i--) {
+ fprintf(stderr, "%d,%d%c", (ttys[i] >> 8) & 0xff,
+ ttys[i] & 0xff, i ? ' ' : '\n');
+ }
+ } else
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d UID: ", uid_count);
+ if (uids) {
+ i = uid_count;
+ while (i--)
+ fprintf(stderr, "%d%c", uids[i], i ? ' ' : '\n');
+ } else
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d PID: ", pid_count);
+ if (pids) {
+ i = pid_count;
+ while (i--)
+ fprintf(stderr, "%d%c", pids[i], i ? ' ' : '\n');
+ } else
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d CMD: ", cmd_count);
+ if (cmds) {
+ i = cmd_count;
+ while (i--)
+ fprintf(stderr, "%s%c", cmds[i], i ? ' ' : '\n');
+ } else
+ fprintf(stderr, "\n");
+}
+
+static void scan_procs(struct run_time_conf_t *run_time)
+{
+ #define PIDS_GETINT(e) PIDS_VAL(EU_ ## e, s_int, reap->stacks[i], Pids_info)
+ #define PIDS_GETUNT(e) PIDS_VAL(EU_ ## e, u_int, reap->stacks[i], Pids_info)
+ #define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, reap->stacks[i], Pids_info)
+ struct pids_fetch *reap;
+ int i, total_procs;
+
+ if (procps_pids_new(&Pids_info, items, 6) < 0)
+ xerrx(EXIT_FAILURE,
+ _("Unable to create pid Pids_info structure"));
+ if ((reap = procps_pids_reap(Pids_info, PIDS_FETCH_TASKS_ONLY)) == NULL)
+ xerrx(EXIT_FAILURE,
+ _("Unable to load process information"));
+
+ total_procs = reap->counts->total;
+ for (i=0; i < total_procs; i++) {
+ if (PIDS_GETINT(PID) == my_pid || PIDS_GETINT(PID) == 0)
+ continue;
+ if (pids && !match_intlist(PIDS_GETINT(PID), pid_count, pids))
+ continue;
+ if (uids && !match_intlist(PIDS_GETUNT(EUID), uid_count, (int *)uids))
+ continue;
+ if (ttys && !match_intlist(PIDS_GETINT(TTY), tty_count, ttys))
+ continue;
+ 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);
+ }
+
+ #undef PIDS_GETINT
+ #undef PIDS_GETUNT
+ #undef PIDS_GETSTR
+}
+
+/* skill and snice help */
+static void __attribute__ ((__noreturn__)) skillsnice_usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+
+ if (program == PROG_SKILL) {
+ fprintf(out,
+ _(" %s [signal] [options] <expression>\n"),
+ program_invocation_short_name);
+ } else {
+ fprintf(out,
+ _(" %s [new priority] [options] <expression>\n"),
+ program_invocation_short_name);
+ }
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -f, --fast fast mode (not implemented)\n"), out);
+ fputs(_(" -i, --interactive interactive\n"), out);
+ fputs(_(" -l, --list list all signal names\n"), out);
+ fputs(_(" -L, --table list all signal names in a nice table\n"), out);
+ fputs(_(" -n, --no-action do not actually kill processes; just print what would happen\n"), out);
+ fputs(_(" -v, --verbose explain what is being done\n"), out);
+ fputs(_(" -w, --warnings enable warnings (not implemented)\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Expression can be: terminal, user, pid, command.\n"
+ "The options below may be used to ensure correct interpretation.\n"), out);
+ fputs(_(" -c, --command <command> expression is a command name\n"), out);
+ fputs(_(" -p, --pid <pid> expression is a process id number\n"), out);
+ fputs(_(" -t, --tty <tty> expression is a terminal\n"), out);
+ fputs(_(" -u, --user <username> expression is a username\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Alternatively, expression can be:\n"), out);
+ fputs(_(" --ns <pid> match the processes that belong to the same\n"
+ " namespace as <pid>\n"), out);
+ fputs(_(" --nslist <ns,...> list which namespaces will be considered for\n"
+ " the --ns option; available namespaces are\n:"
+ " ipc, mnt, net, pid, user, uts\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ if (program == PROG_SKILL) {
+ fprintf(out,
+ _("\n"
+ "The default signal is TERM. Use -l or -L to list available signals.\n"
+ "Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n"
+ "Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n"));
+ fprintf(out, USAGE_MAN_TAIL("skill(1)"));
+ } else {
+ fprintf(out,
+ _("\n"
+ "The default priority is +4. (snice +4 ...)\n"
+ "Priority numbers range from +20 (slowest) to -20 (fastest).\n"
+ "Negative priority numbers are restricted to administrative users.\n"));
+ fprintf(out, USAGE_MAN_TAIL("snice(1)"));
+ }
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+
+static int snice_prio_option(int *argc, char **argv)
+{
+ int i = 1, nargs = *argc;
+ long prio = DEFAULT_NICE;
+
+ while (i < nargs) {
+ if ((argv[i][0] == '-' || argv[i][0] == '+')
+ && isdigit(argv[i][1])) {
+ prio = strtol_or_err(argv[i],
+ _("failed to parse argument"));
+ if (prio < INT_MIN || INT_MAX < prio)
+ xerrx(EXIT_FAILURE,
+ _("priority %lu out of range"), prio);
+ memmove(argv + i, argv + i + 1,
+ sizeof(char *) * (nargs - i));
+ nargs--;
+ } else
+ i++;
+ }
+ *argc = nargs;
+ return (int)prio;
+}
+
+static void parse_options(int argc,
+ char **argv, struct run_time_conf_t *run_time)
+{
+ int signo = -1;
+ int prino = DEFAULT_NICE;
+ int ch, i;
+
+ enum {
+ NS_OPTION = CHAR_MAX + 1,
+ NSLIST_OPTION,
+ };
+
+ static const struct option longopts[] = {
+ {"command", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'd'},
+ {"fast", no_argument, NULL, 'f'},
+ {"interactive", no_argument, NULL, 'i'},
+ {"list", no_argument, NULL, 'l'},
+ {"no-action", no_argument, NULL, 'n'},
+ {"pid", required_argument, NULL, 'p'},
+ {"table", no_argument, NULL, 'L'},
+ {"tty", required_argument, NULL, 't'},
+ {"user", required_argument, NULL, 'u'},
+ {"ns", required_argument, NULL, NS_OPTION},
+ {"nslist", required_argument, NULL, NSLIST_OPTION},
+ {"verbose", no_argument, NULL, 'v'},
+ {"warnings", no_argument, NULL, 'w'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if (argc < 2)
+ skillsnice_usage(stderr);
+
+ sig_or_pri = -1;
+
+ if (program == PROG_SNICE)
+ prino = snice_prio_option(&argc, argv);
+ else if (program == PROG_SKILL) {
+ signo = skill_sig_option(&argc, argv);
+ if (-1 < signo)
+ sig_or_pri = signo;
+ }
+
+ while ((ch =
+ getopt_long(argc, argv, "c:dfilnp:Lt:u:vwhV", longopts,
+ NULL)) != -1)
+ switch (ch) {
+ case 'c':
+ ENLIST(cmd, optarg);
+ break;
+ case 'd':
+ run_time->debugging = 1;
+ break;
+ case 'f':
+ run_time->fast = 1;
+ break;
+ case 'i':
+ run_time->interactive = 1;
+ break;
+ case 'l':
+ unix_print_signals();
+ exit(EXIT_SUCCESS);
+ case 'n':
+ run_time->noaction = 1;
+ break;
+ case 'p':
+ ENLIST(pid,
+ strtol_or_err(optarg,
+ _("failed to parse argument")));
+ break;
+ case 'L':
+ pretty_print_signals();
+ exit(EXIT_SUCCESS);
+ case 't':
+ {
+ struct stat sbuf;
+ char path[32];
+ snprintf(path, 32, "/dev/%s", optarg);
+ if (stat(path, &sbuf) >= 0
+ && S_ISCHR(sbuf.st_mode)) {
+ ENLIST(tty, sbuf.st_rdev);
+ }
+ }
+ break;
+ case 'u':
+ {
+ struct passwd *passwd_data;
+ passwd_data = getpwnam(optarg);
+ if (passwd_data) {
+ ENLIST(uid, passwd_data->pw_uid);
+ }
+ }
+ break;
+ case NS_OPTION:
+ ns_pid = atoi(optarg);
+ if (ns_pid == 0) {
+ xwarnx(_("invalid pid number %s"), optarg);
+ skillsnice_usage(stderr);
+ }
+ if (procps_ns_read_pid(ns_pid, &match_namespaces) < 0) {
+ xwarnx(_("error reading reference namespace "
+ "information"));
+ skillsnice_usage(stderr);
+ }
+
+ break;
+ case NSLIST_OPTION:
+ if (parse_namespaces(optarg)) {
+ xwarnx(_("invalid namespace list"));
+ skillsnice_usage(stderr);
+ }
+ break;
+ case 'v':
+ run_time->verbose = 1;
+ break;
+ case 'w':
+ run_time->warnings = 1;
+ break;
+ case 'h':
+ skillsnice_usage(stdout);
+ case 'V':
+ fprintf(stdout, PROCPS_NG_VERSION);
+ exit(EXIT_SUCCESS);
+ default:
+ skillsnice_usage(stderr);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ for (i = 0; i < argc; i++) {
+ long num;
+ char *end = NULL;
+ errno = 0;
+ num = strtol(argv[0], &end, 10);
+ if (errno == 0 && argv[0] != end && end != NULL && *end == '\0') {
+ ENLIST(pid, num);
+ } else {
+ ENLIST(cmd, argv[0]);
+ }
+ argv++;
+ }
+
+ /* No more arguments to process. Must sanity check. */
+ if (!tty_count && !uid_count && !cmd_count && !pid_count && !ns_pid)
+ xerrx(EXIT_FAILURE, _("no process selection criteria"));
+ if ((run_time->fast | run_time->interactive | run_time->
+ verbose | run_time->warnings | run_time->noaction) & ~1)
+ xerrx(EXIT_FAILURE, _("general flags may not be repeated"));
+ if (run_time->interactive
+ && (run_time->verbose | run_time->fast | run_time->noaction))
+ xerrx(EXIT_FAILURE, _("-i makes no sense with -v, -f, and -n"));
+ if (run_time->verbose && (run_time->interactive | run_time->fast))
+ xerrx(EXIT_FAILURE, _("-v makes no sense with -i and -f"));
+ if (run_time->noaction) {
+ program = PROG_SKILL;
+ /* harmless */
+ sig_or_pri = 0;
+ }
+ if (program == PROG_SNICE)
+ sig_or_pri = prino;
+ else if (sig_or_pri < 0)
+ sig_or_pri = SIGTERM;
+}
+
+/* main body */
+int main(int argc, char ** argv)
+{
+#ifdef HAVE_PROGRAM_INVOCATION_NAME
+ program_invocation_name = program_invocation_short_name;
+#endif
+ struct run_time_conf_t run_time;
+ memset(&run_time, 0, sizeof(struct run_time_conf_t));
+ my_pid = getpid();
+
+ if (strcmp(program_invocation_short_name, "skill") == 0 ||
+ strcmp(program_invocation_short_name, "lt-skill") == 0)
+ program = PROG_SKILL;
+ else if (strcmp(program_invocation_short_name, "snice") == 0 ||
+ strcmp(program_invocation_short_name, "lt-snice") == 0)
+ program = PROG_SNICE;
+#ifdef __CYGWIN__
+ else if (strcmp(program_invocation_short_name, "prockill") == 0 ||
+ strcmp(program_invocation_short_name, "lt-prockill") == 0)
+ program = PROG_KILL;
+#endif
+
+ switch (program) {
+ case PROG_SNICE:
+ case PROG_SKILL:
+ setpriority(PRIO_PROCESS, my_pid, -20);
+ parse_options(argc, argv, &run_time);
+ if (run_time.debugging)
+ show_lists();
+ scan_procs(&run_time);
+ break;
+ default:
+ fprintf(stderr, _("skill: \"%s\" is not supported\n"),
+ program_invocation_short_name);
+ fprintf(stderr, USAGE_MAN_TAIL("skill(1)"));
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}