2 * skill.c - send a signal to process
3 * Copyright 1998-2002 by Albert Cahalan
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <sys/resource.h>
34 #include <sys/types.h>
38 #include "fileutils.h"
43 #include "proc/pwcache.h"
45 #include "proc/devname.h"
46 #include "proc/procps.h" /* char *pwcache_get_user(uid_t uid) */
47 #include "proc/readproc.h"
48 #include "proc/version.h" /* procps_version */
51 #define DEFAULT_NICE 4
53 struct run_time_conf_t {
61 static int tty_count, uid_count, cmd_count, pid_count, namespace_count;
64 static const char **cmds;
66 static char **namespaces;
68 static proc_t ns_task;
70 #define ENLIST(thing,addme) do{ \
71 if(thing##_count < 0 || (size_t)thing##_count >= INT_MAX / sizeof(*thing##s)) \
72 xerrx(EXIT_FAILURE, _("integer overflow")); \
73 thing##s = xrealloc(thing##s, sizeof(*thing##s)*(thing##_count+1)); \
74 thing##s[thing##_count++] = addme; \
78 static int sig_or_pri;
86 static int program = PROG_UNKNOWN;
88 static void display_kill_version(void)
90 fprintf(stdout, PROCPS_NG_VERSION);
93 static int ns_flags = 0x3f;
94 static int parse_namespaces(char *optarg)
96 char *ptr = optarg, *tmp;
101 if (strchr(ptr, ',') == NULL) {
105 len = strchr(ptr, ',') - ptr;
106 tmp = strndup(ptr, len);
111 fprintf(stderr, "%s is not a valid namespace\n", tmp);
115 ns_flags |= (1 << id);
116 ENLIST(namespace, tmp);
126 /* kill or nice a process */
127 static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd,
128 struct run_time_conf_t *run_time)
132 dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV);
133 if (run_time->interactive) {
136 fprintf(stderr, "%-8s %-8s %5d %-16.16s ? ",
137 (char *)dn_buf, pwcache_get_user(uid), pid, cmd);
139 if (getline(&buf, &len, stdin) == -1) {
143 if (rpmatch(buf) < 1) {
149 /* do the actual work */
151 if (program == PROG_SKILL)
152 failed = kill(pid, sig_or_pri);
154 failed = setpriority(PRIO_PROCESS, pid, sig_or_pri);
155 if ((run_time->warnings && failed) || run_time->debugging || run_time->verbose) {
156 fprintf(stderr, "%-8s %-8s %5d %-16.16s ",
157 (char *)dn_buf, pwcache_get_user(uid), pid, cmd);
161 if (run_time->interactive)
163 if (run_time->noaction) {
169 /* check one process */
170 static void check_proc(int pid, struct run_time_conf_t *run_time)
180 if (pid == my_pid || pid == 0)
182 /* pid (cmd) state ppid pgrp session tty */
183 sprintf(buf, "/proc/%d/stat", pid);
184 fd = open(buf, O_RDONLY);
186 /* process exited maybe */
187 if (run_time->warnings)
188 xwarn(_("cannot open file %s"), buf);
191 if (fstat(fd, &statbuf) != 0)
197 if (uids[i] == statbuf.st_uid)
202 len = read(fd, buf, sizeof(buf));
205 buf[sizeof(buf) -1] = '\0';
206 tmp = strrchr(buf, ')');
215 /* scan to find tty */
216 } while (*tmp++ != ' ');
226 tmp = strchr(buf, '(');
232 /* fast comparison trick -- useful? */
234 if (cmds[i][0] == *tmp && !strcmp(cmds[i], tmp))
240 if (ns_read(pid, &task))
242 for (i = 0; i < NUM_NS; i++) {
243 if (ns_flags & (1 << i)) {
244 if (task.ns[i] != ns_task.ns[i])
249 /* This is where we kill/nice something. */
250 /* for debugging purposes?
251 fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n",
252 pid, statbuf.st_uid, tty >> 8, tty & 0xf, tmp);
254 hurt_proc(tty, statbuf.st_uid, pid, tmp, run_time);
256 /* kill/nice _first_ to avoid PID reuse */
261 static void show_lists(void)
265 fprintf(stderr, "signal: %d\n", sig_or_pri);
267 fprintf(stderr, "%d TTY: ", tty_count);
271 fprintf(stderr, "%d,%d%c", (ttys[i] >> 8) & 0xff,
272 ttys[i] & 0xff, i ? ' ' : '\n');
275 fprintf(stderr, "\n");
277 fprintf(stderr, "%d UID: ", uid_count);
281 fprintf(stderr, "%d%c", uids[i], i ? ' ' : '\n');
283 fprintf(stderr, "\n");
285 fprintf(stderr, "%d PID: ", pid_count);
289 fprintf(stderr, "%d%c", pids[i], i ? ' ' : '\n');
291 fprintf(stderr, "\n");
293 fprintf(stderr, "%d CMD: ", cmd_count);
297 fprintf(stderr, "%s%c", cmds[i], i ? ' ' : '\n');
299 fprintf(stderr, "\n");
302 /* iterate over all PIDs */
303 static void iterate(struct run_time_conf_t *run_time)
311 check_proc(pids[pid], run_time);
315 /* could setuid() and kill -1 to have the kernel wipe out a user */
316 if (!ttys && !cmds && !pids && !run_time->interactive) {
319 d = opendir("/proc");
321 xerr(EXIT_FAILURE, "/proc");
322 while ((de = readdir(d))) {
323 if (de->d_name[0] > '9')
325 if (de->d_name[0] < '1')
327 pid = atoi(de->d_name);
329 check_proc(pid, run_time);
335 static void __attribute__ ((__noreturn__)) kill_usage(FILE * out)
337 fputs(USAGE_HEADER, out);
339 _(" %s [options] <pid> [...]\n"), program_invocation_short_name);
340 fputs(USAGE_OPTIONS, out);
341 fputs(_(" <pid> [...] send signal to every <pid> listed\n"), out);
342 fputs(_(" -<signal>, -s, --signal <signal>\n"
343 " specify the <signal> to be sent\n"), out);
344 fputs(_(" -l, --list=[<signal>] list all signal names, or convert one to a name\n"), out);
345 fputs(_(" -L, --table list all signal names in a nice table\n"), out);
346 fputs(USAGE_SEPARATOR, out);
347 fputs(USAGE_HELP, out);
348 fputs(USAGE_VERSION, out);
349 fprintf(out, USAGE_MAN_TAIL("kill(1)"));
350 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
353 /* skill and snice help */
354 static void __attribute__ ((__noreturn__)) skillsnice_usage(FILE * out)
356 fputs(USAGE_HEADER, out);
358 if (program == PROG_SKILL) {
360 _(" %s [signal] [options] <expression>\n"),
361 program_invocation_short_name);
364 _(" %s [new priority] [options] <expression>\n"),
365 program_invocation_short_name);
367 fputs(USAGE_OPTIONS, out);
368 fputs(_(" -f, --fast fast mode (not implemented)\n"), out);
369 fputs(_(" -i, --interactive interactive\n"), out);
370 fputs(_(" -l, --list list all signal names\n"), out);
371 fputs(_(" -L, --table list all signal names in a nice table\n"), out);
372 fputs(_(" -n, --no-action do not actually kill processes; just print what would happen\n"), out);
373 fputs(_(" -v, --verbose explain what is being done\n"), out);
374 fputs(_(" -w, --warnings enable warnings (not implemented)\n"), out);
375 fputs(USAGE_SEPARATOR, out);
376 fputs(_("Expression can be: terminal, user, pid, command.\n"
377 "The options below may be used to ensure correct interpretation.\n"), out);
378 fputs(_(" -c, --command <command> expression is a command name\n"), out);
379 fputs(_(" -p, --pid <pid> expression is a process id number\n"), out);
380 fputs(_(" -t, --tty <tty> expression is a terminal\n"), out);
381 fputs(_(" -u, --user <username> expression is a username\n"), out);
382 fputs(USAGE_SEPARATOR, out);
383 fputs(_("Alternatively, expression can be:\n"), out);
384 fputs(_(" --ns <pid> match the processes that belong to the same\n"
385 " namespace as <pid>\n"), out);
386 fputs(_(" --nslist <ns,...> list which namespaces will be considered for\n"
387 " the --ns option; available namespaces are\n:"
388 " ipc, mnt, net, pid, user, uts\n"), out);
390 fputs(USAGE_SEPARATOR, out);
391 fputs(USAGE_SEPARATOR, out);
392 fputs(USAGE_HELP, out);
393 fputs(USAGE_VERSION, out);
394 if (program == PROG_SKILL) {
397 "The default signal is TERM. Use -l or -L to list available signals.\n"
398 "Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n"
399 "Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n"));
400 fprintf(out, USAGE_MAN_TAIL("skill(1)"));
404 "The default priority is +4. (snice +4 ...)\n"
405 "Priority numbers range from +20 (slowest) to -20 (fastest).\n"
406 "Negative priority numbers are restricted to administrative users.\n"));
407 fprintf(out, USAGE_MAN_TAIL("snice(1)"));
409 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
412 static int skill_sig_option(int *argc, char **argv)
416 for (i = 1; i < *argc; i++) {
417 if (argv[i][0] == '-') {
418 signo = signal_name_to_number(argv[i] + 1);
420 memmove(argv + i, argv + i + 1,
421 sizeof(char *) * (*argc - i));
431 static void __attribute__ ((__noreturn__))
432 kill_main(int argc, char **argv)
436 int exitvalue = EXIT_SUCCESS;
439 static const struct option longopts[] = {
440 {"list", optional_argument, NULL, 'l'},
441 {"table", no_argument, NULL, 'L'},
442 {"signal", required_argument, NULL, 's'},
443 {"help", no_argument, NULL, 'h'},
444 {"version", no_argument, NULL, 'V'},
448 setlocale (LC_ALL, "");
449 bindtextdomain(PACKAGE, LOCALEDIR);
451 atexit(close_stdout);
456 signo = skill_sig_option(&argc, argv);
460 opterr=0; /* suppress errors on -123 */
461 while ((i = getopt_long(argc, argv, "l::Ls:hV", longopts, NULL)) != -1)
467 } else if (argv[optind] != NULL && argv[optind][0] != '-') {
468 sig_option = argv[optind];
472 s = strtosig(sig_option);
476 xwarnx(_("unknown signal name %s"),
480 unix_print_signals();
484 pretty_print_signals();
487 signo = signal_name_to_number(optarg);
492 display_kill_version();
495 if (!isdigit(optopt)) {
496 xwarnx(_("invalid argument %c"), optopt);
499 /* Special case for signal digit negative
501 pid = atoi(argv[optind]);
502 if (kill((pid_t)pid, signo) != 0)
503 exitvalue = EXIT_FAILURE;
506 xerrx(EXIT_FAILURE, _("internal error"));
517 for (i = 0; i < argc; i++) {
518 pid = strtol_or_err(argv[i], _("failed to parse argument"));
519 if (!kill((pid_t) pid, signo))
521 error(0, errno, "(%ld)", pid);
522 exitvalue = EXIT_FAILURE;
530 static void _skillsnice_usage(int line)
532 fprintf(stderr, _("something at line %d\n"), line);
533 skillsnice_usage(stderr);
536 #define skillsnice_usage() _skillsnice_usage(__LINE__)
539 /* common skill/snice argument parsing code */
541 static int snice_prio_option(int *argc, char **argv)
543 int i = 1, nargs = *argc;
544 long prio = DEFAULT_NICE;
547 if ((argv[i][0] == '-' || argv[i][0] == '+')
548 && isdigit(argv[i][1])) {
549 prio = strtol_or_err(argv[i],
550 _("failed to parse argument"));
551 if (prio < INT_MIN || INT_MAX < prio)
553 _("priority %lu out of range"), prio);
554 memmove(argv + i, argv + i + 1,
555 sizeof(char *) * (nargs - i));
564 static void skillsnice_parse(int argc,
565 char **argv, struct run_time_conf_t *run_time)
568 int prino = DEFAULT_NICE;
572 NS_OPTION = CHAR_MAX + 1,
576 static const struct option longopts[] = {
577 {"command", required_argument, NULL, 'c'},
578 {"debug", no_argument, NULL, 'd'},
579 {"fast", no_argument, NULL, 'f'},
580 {"interactive", no_argument, NULL, 'i'},
581 {"list", no_argument, NULL, 'l'},
582 {"no-action", no_argument, NULL, 'n'},
583 {"pid", required_argument, NULL, 'p'},
584 {"table", no_argument, NULL, 'L'},
585 {"tty", required_argument, NULL, 't'},
586 {"user", required_argument, NULL, 'u'},
587 {"ns", required_argument, NULL, NS_OPTION},
588 {"nslist", required_argument, NULL, NSLIST_OPTION},
589 {"verbose", no_argument, NULL, 'v'},
590 {"warnings", no_argument, NULL, 'w'},
591 {"help", no_argument, NULL, 'h'},
592 {"version", no_argument, NULL, 'V'},
597 skillsnice_usage(stderr);
601 if (program == PROG_SNICE)
602 prino = snice_prio_option(&argc, argv);
603 else if (program == PROG_SKILL) {
604 signo = skill_sig_option(&argc, argv);
610 getopt_long(argc, argv, "c:dfilnp:Lt:u:vwhV", longopts,
617 run_time->debugging = 1;
623 run_time->interactive = 1;
626 unix_print_signals();
629 run_time->noaction = 1;
633 strtol_or_err(optarg,
634 _("failed to parse argument")));
637 pretty_print_signals();
643 snprintf(path, 32, "/dev/%s", optarg);
644 if (stat(path, &sbuf) >= 0
645 && S_ISCHR(sbuf.st_mode)) {
646 ENLIST(tty, sbuf.st_rdev);
652 struct passwd *passwd_data;
653 passwd_data = getpwnam(optarg);
655 ENLIST(uid, passwd_data->pw_uid);
660 ns_pid = atoi(optarg);
662 xwarnx(_("invalid pid number %s"), optarg);
665 if (ns_read(ns_pid, &ns_task)) {
666 xwarnx(_("error reading reference namespace "
673 if (parse_namespaces(optarg)) {
674 xwarnx(_("invalid namespace list"));
679 run_time->verbose = 1;
682 run_time->warnings = 1;
685 skillsnice_usage(stdout);
687 display_kill_version();
690 skillsnice_usage(stderr);
696 for (i = 0; i < argc; i++) {
700 num = strtol(argv[0], &end, 10);
701 if (errno == 0 && argv[0] != end && end != NULL && *end == '\0') {
704 ENLIST(cmd, argv[0]);
709 /* No more arguments to process. Must sanity check. */
710 if (!tty_count && !uid_count && !cmd_count && !pid_count && !ns_pid)
711 xerrx(EXIT_FAILURE, _("no process selection criteria"));
712 if ((run_time->fast | run_time->interactive | run_time->
713 verbose | run_time->warnings | run_time->noaction) & ~1)
714 xerrx(EXIT_FAILURE, _("general flags may not be repeated"));
715 if (run_time->interactive
716 && (run_time->verbose | run_time->fast | run_time->noaction))
717 xerrx(EXIT_FAILURE, _("-i makes no sense with -v, -f, and -n"));
718 if (run_time->verbose && (run_time->interactive | run_time->fast))
719 xerrx(EXIT_FAILURE, _("-v makes no sense with -i and -f"));
720 if (run_time->noaction) {
721 program = PROG_SKILL;
725 if (program == PROG_SNICE)
727 else if (sig_or_pri < 0)
728 sig_or_pri = SIGTERM;
732 int main(int argc, char ** argv)
734 #ifdef HAVE_PROGRAM_INVOCATION_NAME
735 program_invocation_name = program_invocation_short_name;
737 struct run_time_conf_t run_time;
738 memset(&run_time, 0, sizeof(struct run_time_conf_t));
741 if (strcmp(program_invocation_short_name, "kill") == 0 ||
742 strcmp(program_invocation_short_name, "lt-kill") == 0)
744 else if (strcmp(program_invocation_short_name, "skill") == 0 ||
745 strcmp(program_invocation_short_name, "lt-skill") == 0)
746 program = PROG_SKILL;
747 else if (strcmp(program_invocation_short_name, "snice") == 0 ||
748 strcmp(program_invocation_short_name, "lt-snice") == 0)
749 program = PROG_SNICE;
751 else if (strcmp(program_invocation_short_name, "prockill") == 0 ||
752 strcmp(program_invocation_short_name, "lt-prockill") == 0)
759 setpriority(PRIO_PROCESS, my_pid, -20);
760 skillsnice_parse(argc, argv, &run_time);
761 if (run_time.debugging)
766 kill_main(argc, argv);
769 fprintf(stderr, _("skill: \"%s\" is not supported\n"),
770 program_invocation_short_name);
771 fprintf(stderr, USAGE_MAN_TAIL("skill(1)"));