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