ls: LIST_ID_NAME/ID_NUMERIC/LOPT/LONG are the same, merge as LONG
[oweals/busybox.git] / procps / kill.c
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
95 //kbuild:lib-$(CONFIG_ASH_JOB_CONTROL) += kill.o
96 //kbuild:lib-$(CONFIG_HUSH_KILL) += kill.o
97
98 #define SH_KILL (ENABLE_ASH_JOB_CONTROL || ENABLE_HUSH_KILL)
99 /* If shells want to have "kill", for ifdefs it's like ENABLE_KILL=1 */
100 #if SH_KILL
101 # undef  ENABLE_KILL
102 # define ENABLE_KILL 1
103 #endif
104 #define KILL_APPLET_CNT (ENABLE_KILL + ENABLE_KILLALL + ENABLE_KILLALL5)
105
106 int kill_main(int argc UNUSED_PARAM, char **argv)
107 {
108         char *arg;
109         pid_t pid;
110         int signo = SIGTERM, errors = 0, quiet = 0;
111
112 #if KILL_APPLET_CNT == 1
113 # define is_killall  ENABLE_KILLALL
114 # define is_killall5 ENABLE_KILLALL5
115 #else
116 /* How to determine who we are? find 3rd char from the end:
117  * kill, killall, killall5
118  *  ^i       ^a        ^l  - it's unique
119  * (checking from the start is complicated by /bin/kill... case) */
120         const char char3 = argv[0][strlen(argv[0]) - 3];
121 # define is_killall  (ENABLE_KILLALL  && char3 == 'a')
122 # define is_killall5 (ENABLE_KILLALL5 && char3 == 'l')
123 #endif
124
125         /* Parse any options */
126         arg = *++argv;
127
128         if (!arg || arg[0] != '-') {
129                 goto do_it_now;
130         }
131
132         /* The -l option, which prints out signal names.
133          * Intended usage in shell:
134          * echo "Died of SIG`kill -l $?`"
135          * We try to mimic what kill from coreutils-6.8 does */
136         if (arg[1] == 'l' && arg[2] == '\0') {
137                 arg = *++argv;
138                 if (!arg) {
139                         /* Print the whole signal list */
140                         print_signames();
141                         return 0;
142                 }
143                 /* -l <sig list> */
144                 do {
145                         if (isdigit(arg[0])) {
146                                 signo = bb_strtou(arg, NULL, 10);
147                                 if (errno) {
148                                         bb_error_msg("unknown signal '%s'", arg);
149                                         return EXIT_FAILURE;
150                                 }
151                                 /* Exitcodes >= 0x80 are to be treated
152                                  * as "killed by signal (exitcode & 0x7f)" */
153                                 puts(get_signame(signo & 0x7f));
154                                 /* TODO: 'bad' signal# - coreutils says:
155                                  * kill: 127: invalid signal
156                                  * we just print "127" instead */
157                         } else {
158                                 signo = get_signum(arg);
159                                 if (signo < 0) {
160                                         bb_error_msg("unknown signal '%s'", arg);
161                                         return EXIT_FAILURE;
162                                 }
163                                 printf("%d\n", signo);
164                         }
165                         arg = *++argv;
166                 } while (arg);
167                 return EXIT_SUCCESS;
168         }
169
170         /* The -q quiet option */
171         if (is_killall && arg[1] == 'q' && arg[2] == '\0') {
172                 quiet = 1;
173                 arg = *++argv;
174                 if (!arg)
175                         bb_show_usage();
176                 if (arg[0] != '-')
177                         goto do_it_now;
178         }
179
180         arg++; /* skip '-' */
181
182         /* -o PID? (if present, it always is at the end of command line) */
183         if (is_killall5 && arg[0] == 'o')
184                 goto do_it_now;
185
186         if (argv[1] && arg[0] == 's' && arg[1] == '\0') { /* -s SIG? */
187                 arg = *++argv;
188         } /* else it must be -SIG */
189         signo = get_signum(arg);
190         if (signo < 0) { /* || signo > MAX_SIGNUM ? */
191                 bb_error_msg("bad signal name '%s'", arg);
192                 return EXIT_FAILURE;
193         }
194         arg = *++argv;
195
196  do_it_now:
197         pid = getpid();
198
199         if (is_killall5) {
200                 pid_t sid;
201                 procps_status_t* p = NULL;
202                 /* compat: exitcode 2 is "no one was signaled" */
203                 int ret = 2;
204
205                 /* Find out our session id */
206                 sid = getsid(pid);
207                 /* Stop all processes */
208                 if (signo != SIGSTOP && signo != SIGCONT)
209                         kill(-1, SIGSTOP);
210                 /* Signal all processes except those in our session */
211                 while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID)) != NULL) {
212                         char **args;
213
214                         if (p->sid == (unsigned)sid
215                          || p->sid == 0 /* compat: kernel thread, don't signal it */
216                          || p->pid == (unsigned)pid
217                          || p->pid == 1
218                         ) {
219                                 continue;
220                         }
221
222                         /* All remaining args must be -o PID options.
223                          * Check p->pid against them. */
224                         args = argv;
225                         while (*args) {
226                                 pid_t omit;
227
228                                 arg = *args++;
229                                 if (arg[0] != '-' || arg[1] != 'o') {
230                                         bb_error_msg("bad option '%s'", arg);
231                                         ret = 1;
232                                         goto resume;
233                                 }
234                                 arg += 2;
235                                 if (!arg[0] && *args)
236                                         arg = *args++;
237                                 omit = bb_strtoi(arg, NULL, 10);
238                                 if (errno) {
239                                         bb_error_msg("invalid number '%s'", arg);
240                                         ret = 1;
241                                         goto resume;
242                                 }
243                                 if (p->pid == omit)
244                                         goto dont_kill;
245                         }
246                         kill(p->pid, signo);
247                         ret = 0;
248  dont_kill: ;
249                 }
250  resume:
251                 /* And let them continue */
252                 if (signo != SIGSTOP && signo != SIGCONT)
253                         kill(-1, SIGCONT);
254                 return ret;
255         }
256
257 #if ENABLE_KILL || ENABLE_KILLALL
258         /* Pid or name is required for kill/killall */
259         if (!arg) {
260                 bb_error_msg("you need to specify whom to kill");
261                 return EXIT_FAILURE;
262         }
263
264         if (!ENABLE_KILL || is_killall) {
265                 /* Looks like they want to do a killall.  Do that */
266                 do {
267                         pid_t* pidList;
268
269                         pidList = find_pid_by_name(arg);
270                         if (*pidList == 0) {
271                                 errors++;
272                                 if (!quiet)
273                                         bb_error_msg("%s: no process killed", arg);
274                         } else {
275                                 pid_t *pl;
276
277                                 for (pl = pidList; *pl; pl++) {
278                                         if (*pl == pid)
279                                                 continue;
280                                         if (kill(*pl, signo) == 0)
281                                                 continue;
282                                         errors++;
283                                         if (!quiet)
284                                                 bb_perror_msg("can't kill pid %d", (int)*pl);
285                                 }
286                         }
287                         free(pidList);
288                         arg = *++argv;
289                 } while (arg);
290                 return errors;
291         }
292 #endif
293
294 #if ENABLE_KILL
295         /* Looks like they want to do a kill. Do that */
296         while (arg) {
297 # if SH_KILL
298                 /*
299                  * We need to support shell's "hack formats" of
300                  * " -PRGP_ID" (yes, with a leading space)
301                  * and " PID1 PID2 PID3" (with degenerate case "")
302                  */
303                 while (*arg != '\0') {
304                         char *end;
305                         if (*arg == ' ')
306                                 arg++;
307                         pid = bb_strtoi(arg, &end, 10);
308                         if (errno && (errno != EINVAL || *end != ' ')) {
309                                 bb_error_msg("invalid number '%s'", arg);
310                                 errors++;
311                                 break;
312                         }
313                         if (kill(pid, signo) != 0) {
314                                 bb_perror_msg("can't kill pid %d", (int)pid);
315                                 errors++;
316                         }
317                         arg = end; /* can only point to ' ' or '\0' now */
318                 }
319 # else /* ENABLE_KILL but !SH_KILL */
320                 pid = bb_strtoi(arg, NULL, 10);
321                 if (errno) {
322                         bb_error_msg("invalid number '%s'", arg);
323                         errors++;
324                 } else if (kill(pid, signo) != 0) {
325                         bb_perror_msg("can't kill pid %d", (int)pid);
326                         errors++;
327                 }
328 # endif
329                 arg = *++argv;
330         }
331         return errors;
332 #endif
333 }