test: avoid false failure with setgid directories
[coreutils.git] / src / kill.c
blob8b9a2650fbf55715828e966dafaf383e3f6fc558
1 /* kill -- send a signal to a process
2 Copyright (C) 2002-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Paul Eggert. */
19 #include <config.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <sys/types.h>
23 #include <signal.h>
25 #include "system.h"
26 #include "sig2str.h"
27 #include "operand2sig.h"
28 #include "quote.h"
30 /* The official name of this program (e.g., no 'g' prefix). */
31 #define PROGRAM_NAME "kill"
33 #define AUTHORS proper_name ("Paul Eggert")
35 static char const short_options[] =
36 "0::1::2::3::4::5::6::7::8::9::"
37 "A::B::C::D::E::F::G::H::I::J::K::M::"
38 "N::O::P::Q::R::S::T::U::V::W::X::Y::Z::"
39 "Lln:s:t";
41 static struct option const long_options[] =
43 {"list", no_argument, nullptr, 'l'},
44 {"signal", required_argument, nullptr, 's'},
45 {"table", no_argument, nullptr, 't'},
46 {GETOPT_HELP_OPTION_DECL},
47 {GETOPT_VERSION_OPTION_DECL},
48 {nullptr, 0, nullptr, 0}
51 void
52 usage (int status)
54 if (status != EXIT_SUCCESS)
55 emit_try_help ();
56 else
58 printf (_("\
59 Usage: %s [-s SIGNAL | -SIGNAL] PID...\n\
60 or: %s -l [SIGNAL]...\n\
61 or: %s -t [SIGNAL]...\n\
62 "),
63 program_name, program_name, program_name);
64 fputs (_("\
65 Send signals to processes, or list signals.\n\
66 "), stdout);
68 emit_mandatory_arg_note ();
70 fputs (_("\
71 -s, --signal=SIGNAL, -SIGNAL\n\
72 specify the name or number of the signal to be sent\n\
73 -l, --list list signal names, or convert signal names to/from numbers\n\
74 -t, --table print a table of signal information\n\
75 "), stdout);
76 fputs (HELP_OPTION_DESCRIPTION, stdout);
77 fputs (VERSION_OPTION_DESCRIPTION, stdout);
78 fputs (_("\n\
79 SIGNAL may be a signal name like 'HUP', or a signal number like '1',\n\
80 or the exit status of a process terminated by a signal.\n\
81 PID is an integer; if negative it identifies a process group.\n\
82 "), stdout);
83 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
84 emit_ancillary_info (PROGRAM_NAME);
86 exit (status);
89 /* Print a row of 'kill -t' output. NUM_WIDTH is the maximum signal
90 number width, and SIGNUM is the signal number to print. The
91 maximum name width is NAME_WIDTH, and SIGNAME is the name to print. */
93 static void
94 print_table_row (int num_width, int signum,
95 int name_width, char const *signame)
97 char const *description = strsignal (signum);
98 printf ("%*d %-*s %s\n", num_width, signum, name_width, signame,
99 description ? description : "?");
102 /* Print a list of signal names. If TABLE, print a table.
103 Print the names specified by ARGV if nonzero; otherwise,
104 print all known names. Return a suitable exit status. */
106 static int
107 list_signals (bool table, char *const *argv)
109 int signum;
110 int status = EXIT_SUCCESS;
111 char signame[SIG2STR_MAX];
113 if (table)
115 int name_width = 0;
117 /* Compute the maximum width of a signal number. */
118 int num_width = 1;
119 for (signum = 1; signum <= SIGNUM_BOUND / 10; signum *= 10)
120 num_width++;
122 /* Compute the maximum width of a signal name. */
123 for (signum = 1; signum <= SIGNUM_BOUND; signum++)
124 if (sig2str (signum, signame) == 0)
126 idx_t len = strlen (signame);
127 if (name_width < len)
128 name_width = len;
131 if (argv)
132 for (; *argv; argv++)
134 signum = operand2sig (*argv);
135 if (signum < 0)
136 status = EXIT_FAILURE;
137 else
139 if (sig2str (signum, signame) != 0)
140 snprintf (signame, sizeof signame, "SIG%d", signum);
141 print_table_row (num_width, signum, name_width, signame);
144 else
145 for (signum = 1; signum <= SIGNUM_BOUND; signum++)
146 if (sig2str (signum, signame) == 0)
147 print_table_row (num_width, signum, name_width, signame);
149 else
151 if (argv)
152 for (; *argv; argv++)
154 signum = operand2sig (*argv);
155 if (signum < 0)
156 status = EXIT_FAILURE;
157 else if (ISDIGIT (**argv))
159 if (sig2str (signum, signame) == 0)
160 puts (signame);
161 else
162 printf ("%d\n", signum);
164 else
165 printf ("%d\n", signum);
167 else
168 for (signum = 1; signum <= SIGNUM_BOUND; signum++)
169 if (sig2str (signum, signame) == 0)
170 puts (signame);
173 return status;
176 /* Send signal SIGNUM to all the processes or process groups specified
177 by ARGV. Return a suitable exit status. */
179 static int
180 send_signals (int signum, char *const *argv)
182 int status = EXIT_SUCCESS;
183 char const *arg = *argv;
187 char *endp;
188 intmax_t n = (errno = 0, strtoimax (arg, &endp, 10));
189 pid_t pid;
191 if (errno == ERANGE || ckd_add (&pid, n, 0)
192 || arg == endp || *endp)
194 error (0, 0, _("%s: invalid process id"), quote (arg));
195 status = EXIT_FAILURE;
197 else if (kill (pid, signum) != 0)
199 if (errno == EINVAL)
200 error (0, errno, "%d", signum);
201 else
202 error (0, errno, "%s", quote (arg));
203 status = EXIT_FAILURE;
206 while ((arg = *++argv));
208 return status;
212 main (int argc, char **argv)
214 int optc;
215 bool list = false;
216 bool table = false;
217 int signum = -1;
219 initialize_main (&argc, &argv);
220 set_program_name (argv[0]);
221 setlocale (LC_ALL, "");
222 bindtextdomain (PACKAGE, LOCALEDIR);
223 textdomain (PACKAGE);
225 atexit (close_stdout);
227 while ((optc = getopt_long (argc, argv, short_options, long_options, nullptr))
228 != -1)
229 switch (optc)
231 case '0': case '1': case '2': case '3': case '4':
232 case '5': case '6': case '7': case '8': case '9':
233 if (optind != 2)
235 /* This option is actually a process-id. */
236 optind--;
237 goto no_more_options;
239 FALLTHROUGH;
240 case 'A': case 'B': case 'C': case 'D': case 'E':
241 case 'F': case 'G': case 'H': case 'I': case 'J':
242 case 'K': /*case 'L':*/ case 'M': case 'N': case 'O':
243 case 'P': case 'Q': case 'R': case 'S': case 'T':
244 case 'U': case 'V': case 'W': case 'X': case 'Y':
245 case 'Z':
246 if (! optarg)
247 optarg = argv[optind - 1] + strlen (argv[optind - 1]);
248 if (optarg != argv[optind - 1] + 2)
250 error (0, 0, _("invalid option -- %c"), optc);
251 usage (EXIT_FAILURE);
253 optarg--;
254 FALLTHROUGH;
255 case 'n': /* -n is not documented, but is for Bash compatibility. */
256 case 's':
257 if (0 <= signum)
259 error (0, 0, _("%s: multiple signals specified"), quote (optarg));
260 usage (EXIT_FAILURE);
262 signum = operand2sig (optarg);
263 if (signum < 0)
264 usage (EXIT_FAILURE);
265 break;
267 case 'L': /* -L is not documented, but is for procps compatibility. */
268 case 't':
269 table = true;
270 FALLTHROUGH;
271 case 'l':
272 if (list)
274 error (0, 0, _("multiple -l or -t options specified"));
275 usage (EXIT_FAILURE);
277 list = true;
278 break;
280 case_GETOPT_HELP_CHAR;
281 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
282 default:
283 usage (EXIT_FAILURE);
285 no_more_options:
287 if (signum < 0)
288 signum = SIGTERM;
289 else if (list)
291 error (0, 0, _("cannot combine signal with -l or -t"));
292 usage (EXIT_FAILURE);
295 if ( ! list && argc <= optind)
297 error (0, 0, _("no process ID specified"));
298 usage (EXIT_FAILURE);
301 return (list
302 ? list_signals (table, optind < argc ? argv + optind : nullptr)
303 : send_signals (signum, argv + optind));