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