busybox/procps/kill.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini kill/killall[5] implementation for busybox
   4 *
   5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
   6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
   7 *
   8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   9 */
  10//config:config KILL
  11//config:       bool "kill"
  12//config:       default y
  13//config:       help
  14//config:         The command kill sends the specified signal to the specified
  15//config:         process or process group. If no signal is specified, the TERM
  16//config:         signal is sent.
  17//config:
  18//config:config KILLALL
  19//config:       bool "killall"
  20//config:       default y
  21//config:       help
  22//config:         killall sends a signal to all processes running any of the
  23//config:         specified commands. If no signal name is specified, SIGTERM is
  24//config:         sent.
  25//config:
  26//config:config KILLALL5
  27//config:       bool "killall5"
  28//config:       default y
  29//config:       help
  30//config:         The SystemV killall command. killall5 sends a signal
  31//config:         to all processes except kernel threads and the processes
  32//config:         in its own session, so it won't kill the shell that is running
  33//config:         the script it was called from.
  34
  35//applet:IF_KILL(APPLET(kill, BB_DIR_BIN, BB_SUID_DROP))
  36//applet:IF_KILLALL(APPLET_ODDNAME(killall, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall))
  37//applet:IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_SBIN, BB_SUID_DROP, killall5))
  38
  39//kbuild:lib-$(CONFIG_KILL) += kill.o
  40//kbuild:lib-$(CONFIG_KILLALL) += kill.o
  41//kbuild:lib-$(CONFIG_KILLALL5) += kill.o
  42
  43//usage:#define kill_trivial_usage
  44//usage:       "[-l] [-SIG] PID..."
  45//usage:#define kill_full_usage "\n\n"
  46//usage:       "Send a signal (default: TERM) to given PIDs\n"
  47//usage:     "\n        -l      List all signal names and numbers"
  48/* //usage:  "\n        -s SIG  Yet another way of specifying SIG" */
  49//usage:
  50//usage:#define kill_example_usage
  51//usage:       "$ ps | grep apache\n"
  52//usage:       "252 root     root     S [apache]\n"
  53//usage:       "263 www-data www-data S [apache]\n"
  54//usage:       "264 www-data www-data S [apache]\n"
  55//usage:       "265 www-data www-data S [apache]\n"
  56//usage:       "266 www-data www-data S [apache]\n"
  57//usage:       "267 www-data www-data S [apache]\n"
  58//usage:       "$ kill 252\n"
  59//usage:
  60//usage:#define killall_trivial_usage
  61//usage:       "[-l] [-q] [-SIG] PROCESS_NAME..."
  62//usage:#define killall_full_usage "\n\n"
  63//usage:       "Send a signal (default: TERM) to given processes\n"
  64//usage:     "\n        -l      List all signal names and numbers"
  65/* //usage:  "\n        -s SIG  Yet another way of specifying SIG" */
  66//usage:     "\n        -q      Don't complain if no processes were killed"
  67//usage:
  68//usage:#define killall_example_usage
  69//usage:       "$ killall apache\n"
  70//usage:
  71//usage:#define killall5_trivial_usage
  72//usage:       "[-l] [-SIG] [-o PID]..."
  73//usage:#define killall5_full_usage "\n\n"
  74//usage:       "Send a signal (default: TERM) to all processes outside current session\n"
  75//usage:     "\n        -l      List all signal names and numbers"
  76//usage:     "\n        -o PID  Don't signal this PID"
  77/* //usage:  "\n        -s SIG  Yet another way of specifying SIG" */
  78
  79#include "libbb.h"
  80
  81/* Note: kill_main is directly called from shell in order to implement
  82 * kill built-in. Shell substitutes job ids with process groups first.
  83 *
  84 * This brings some complications:
  85 *
  86 * + we can't use xfunc here
  87 * + we can't use applet_name
  88 * + we can't use bb_show_usage
  89 * (Above doesn't apply for killall[5] cases)
  90 *
  91 * kill %n gets translated into kill ' -<process group>' by shell (note space!)
  92 * This is needed to avoid collision with kill -9 ... syntax
  93 */
  94
  95int kill_main(int argc UNUSED_PARAM, char **argv)
  96{
  97        char *arg;
  98        pid_t pid;
  99        int signo = SIGTERM, errors = 0, quiet = 0;
 100#if ENABLE_KILL && !ENABLE_KILLALL && !ENABLE_KILLALL5
 101# define killall  0
 102# define killall5 0
 103#elif !ENABLE_KILL && ENABLE_KILLALL && !ENABLE_KILLALL5
 104# define killall  1
 105# define killall5 0
 106#elif !ENABLE_KILL && !ENABLE_KILLALL && ENABLE_KILLALL5
 107# define killall  0
 108# define killall5 1
 109#else
 110/* How to determine who we are? find 3rd char from the end:
 111 * kill, killall, killall5
 112 *  ^i       ^a        ^l  - it's unique
 113 * (checking from the start is complicated by /bin/kill... case) */
 114        const char char3 = argv[0][strlen(argv[0]) - 3];
 115# define killall  (ENABLE_KILLALL && char3 == 'a')
 116# define killall5 (ENABLE_KILLALL5 && char3 == 'l')
 117#endif
 118
 119        /* Parse any options */
 120        arg = *++argv;
 121
 122        if (!arg || arg[0] != '-') {
 123                goto do_it_now;
 124        }
 125
 126        /* The -l option, which prints out signal names.
 127         * Intended usage in shell:
 128         * echo "Died of SIG`kill -l $?`"
 129         * We try to mimic what kill from coreutils-6.8 does */
 130        if (arg[1] == 'l' && arg[2] == '\0') {
 131                arg = *++argv;
 132                if (!arg) {
 133                        /* Print the whole signal list */
 134                        print_signames();
 135                        return 0;
 136                }
 137                /* -l <sig list> */
 138                do {
 139                        if (isdigit(arg[0])) {
 140                                signo = bb_strtou(arg, NULL, 10);
 141                                if (errno) {
 142                                        bb_error_msg("unknown signal '%s'", arg);
 143                                        return EXIT_FAILURE;
 144                                }
 145                                /* Exitcodes >= 0x80 are to be treated
 146                                 * as "killed by signal (exitcode & 0x7f)" */
 147                                puts(get_signame(signo & 0x7f));
 148                                /* TODO: 'bad' signal# - coreutils says:
 149                                 * kill: 127: invalid signal
 150                                 * we just print "127" instead */
 151                        } else {
 152                                signo = get_signum(arg);
 153                                if (signo < 0) {
 154                                        bb_error_msg("unknown signal '%s'", arg);
 155                                        return EXIT_FAILURE;
 156                                }
 157                                printf("%d\n", signo);
 158                        }
 159                        arg = *++argv;
 160                } while (arg);
 161                return EXIT_SUCCESS;
 162        }
 163
 164        /* The -q quiet option */
 165        if (killall && arg[1] == 'q' && arg[2] == '\0') {
 166                quiet = 1;
 167                arg = *++argv;
 168                if (!arg)
 169                        bb_show_usage();
 170                if (arg[0] != '-')
 171                        goto do_it_now;
 172        }
 173
 174        arg++; /* skip '-' */
 175
 176        /* -o PID? (if present, it always is at the end of command line) */
 177        if (killall5 && arg[0] == 'o')
 178                goto do_it_now;
 179
 180        if (argv[1] && arg[0] == 's' && arg[1] == '\0') { /* -s SIG? */
 181                arg = *++argv;
 182        } /* else it must be -SIG */
 183        signo = get_signum(arg);
 184        if (signo < 0) { /* || signo > MAX_SIGNUM ? */
 185                bb_error_msg("bad signal name '%s'", arg);
 186                return EXIT_FAILURE;
 187        }
 188        arg = *++argv;
 189
 190 do_it_now:
 191        pid = getpid();
 192
 193        if (killall5) {
 194                pid_t sid;
 195                procps_status_t* p = NULL;
 196                /* compat: exitcode 2 is "no one was signaled" */
 197                int ret = 2;
 198
 199                /* Find out our session id */
 200                sid = getsid(pid);
 201                /* Stop all processes */
 202                if (signo != SIGSTOP && signo != SIGCONT)
 203                        kill(-1, SIGSTOP);
 204                /* Signal all processes except those in our session */
 205                while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID)) != NULL) {
 206                        char **args;
 207
 208                        if (p->sid == (unsigned)sid
 209                         || p->sid == 0 /* compat: kernel thread, don't signal it */
 210                         || p->pid == (unsigned)pid
 211                         || p->pid == 1
 212                        ) {
 213                                continue;
 214                        }
 215
 216                        /* All remaining args must be -o PID options.
 217                         * Check p->pid against them. */
 218                        args = argv;
 219                        while (*args) {
 220                                pid_t omit;
 221
 222                                arg = *args++;
 223                                if (arg[0] != '-' || arg[1] != 'o') {
 224                                        bb_error_msg("bad option '%s'", arg);
 225                                        ret = 1;
 226                                        goto resume;
 227                                }
 228                                arg += 2;
 229                                if (!arg[0] && *args)
 230                                        arg = *args++;
 231                                omit = bb_strtoi(arg, NULL, 10);
 232                                if (errno) {
 233                                        bb_error_msg("invalid number '%s'", arg);
 234                                        ret = 1;
 235                                        goto resume;
 236                                }
 237                                if (p->pid == omit)
 238                                        goto dont_kill;
 239                        }
 240                        kill(p->pid, signo);
 241                        ret = 0;
 242 dont_kill: ;
 243                }
 244 resume:
 245                /* And let them continue */
 246                if (signo != SIGSTOP && signo != SIGCONT)
 247                        kill(-1, SIGCONT);
 248                return ret;
 249        }
 250
 251        /* Pid or name is required for kill/killall */
 252        if (!arg) {
 253                bb_error_msg("you need to specify whom to kill");
 254                return EXIT_FAILURE;
 255        }
 256
 257        if (killall) {
 258                /* Looks like they want to do a killall.  Do that */
 259                do {
 260                        pid_t* pidList;
 261
 262                        pidList = find_pid_by_name(arg);
 263                        if (*pidList == 0) {
 264                                errors++;
 265                                if (!quiet)
 266                                        bb_error_msg("%s: no process killed", arg);
 267                        } else {
 268                                pid_t *pl;
 269
 270                                for (pl = pidList; *pl; pl++) {
 271                                        if (*pl == pid)
 272                                                continue;
 273                                        if (kill(*pl, signo) == 0)
 274                                                continue;
 275                                        errors++;
 276                                        if (!quiet)
 277                                                bb_perror_msg("can't kill pid %d", (int)*pl);
 278                                }
 279                        }
 280                        free(pidList);
 281                        arg = *++argv;
 282                } while (arg);
 283                return errors;
 284        }
 285
 286        /* Looks like they want to do a kill. Do that */
 287        while (arg) {
 288#if ENABLE_ASH || ENABLE_HUSH
 289                /*
 290                 * We need to support shell's "hack formats" of
 291                 * " -PRGP_ID" (yes, with a leading space)
 292                 * and " PID1 PID2 PID3" (with degenerate case "")
 293                 */
 294                while (*arg != '\0') {
 295                        char *end;
 296                        if (*arg == ' ')
 297                                arg++;
 298                        pid = bb_strtoi(arg, &end, 10);
 299                        if (errno && (errno != EINVAL || *end != ' ')) {
 300                                bb_error_msg("invalid number '%s'", arg);
 301                                errors++;
 302                                break;
 303                        }
 304                        if (kill(pid, signo) != 0) {
 305                                bb_perror_msg("can't kill pid %d", (int)pid);
 306                                errors++;
 307                        }
 308                        arg = end; /* can only point to ' ' or '\0' now */
 309                }
 310#else
 311                pid = bb_strtoi(arg, NULL, 10);
 312                if (errno) {
 313                        bb_error_msg("invalid number '%s'", arg);
 314                        errors++;
 315                } else if (kill(pid, signo) != 0) {
 316                        bb_perror_msg("can't kill pid %d", (int)pid);
 317                        errors++;
 318                }
 319#endif
 320                arg = *++argv;
 321        }
 322        return errors;
 323}
 324