1 /* kill -- send a signal to a process
2 Copyright (C) 2002-2023 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 /* Written by Paul Eggert. */
18
19 #include <config.h>
20 #include <stdckdint.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <sys/types.h>
24 #include <signal.h>
25
26 #include "system.h"
27 #include "sig2str.h"
28 #include "operand2sig.h"
29 #include "quote.h"
30
31 /* The official name of this program (e.g., no 'g' prefix). */
32 #define PROGRAM_NAME "kill"
33
34 #define AUTHORS proper_name ("Paul Eggert")
35
36 static char const short_options[] =
37 "0::1::2::3::4::5::6::7::8::9::"
38 "A::B::C::D::E::F::G::H::I::J::K::M::"
39 "N::O::P::Q::R::S::T::U::V::W::X::Y::Z::"
40 "Lln:s:t";
41
42 static struct option const long_options[] =
43 {
44 {"list", no_argument, nullptr, 'l'},
45 {"signal", required_argument, nullptr, 's'},
46 {"table", no_argument, nullptr, 't'},
47 {GETOPT_HELP_OPTION_DECL},
48 {GETOPT_VERSION_OPTION_DECL},
49 {nullptr, 0, nullptr, 0}
50 };
51
52 void
usage(int status)53 usage (int status)
54 {
55 if (status != EXIT_SUCCESS)
56 emit_try_help ();
57 else
58 {
59 printf (_("\
60 Usage: %s [-s SIGNAL | -SIGNAL] PID...\n\
61 or: %s -l [SIGNAL]...\n\
62 or: %s -t [SIGNAL]...\n\
63 "),
64 program_name, program_name, program_name);
65 fputs (_("\
66 Send signals to processes, or list signals.\n\
67 "), stdout);
68
69 emit_mandatory_arg_note ();
70
71 fputs (_("\
72 -s, --signal=SIGNAL, -SIGNAL\n\
73 specify the name or number of the signal to be sent\n\
74 -l, --list list signal names, or convert signal names to/from numbers\n\
75 -t, --table print a table of signal information\n\
76 "), stdout);
77 fputs (HELP_OPTION_DESCRIPTION, stdout);
78 fputs (VERSION_OPTION_DESCRIPTION, stdout);
79 fputs (_("\n\
80 SIGNAL may be a signal name like 'HUP', or a signal number like '1',\n\
81 or the exit status of a process terminated by a signal.\n\
82 PID is an integer; if negative it identifies a process group.\n\
83 "), stdout);
84 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
85 emit_ancillary_info (PROGRAM_NAME);
86 }
87 exit (status);
88 }
89
90 /* Print a row of 'kill -t' output. NUM_WIDTH is the maximum signal
91 number width, and SIGNUM is the signal number to print. The
92 maximum name width is NAME_WIDTH, and SIGNAME is the name to print. */
93
94 static void
print_table_row(int num_width,int signum,int name_width,char const * signame)95 print_table_row (int num_width, int signum,
96 int name_width, char const *signame)
97 {
98 char const *description = strsignal (signum);
99 printf ("%*d %-*s %s\n", num_width, signum, name_width, signame,
100 description ? description : "?");
101 }
102
103 /* Print a list of signal names. If TABLE, print a table.
104 Print the names specified by ARGV if nonzero; otherwise,
105 print all known names. Return a suitable exit status. */
106
107 static int
list_signals(bool table,char * const * argv)108 list_signals (bool table, char *const *argv)
109 {
110 int signum;
111 int status = EXIT_SUCCESS;
112 char signame[SIG2STR_MAX];
113
114 if (table)
115 {
116 int name_width = 0;
117
118 /* Compute the maximum width of a signal number. */
119 int num_width = 1;
120 for (signum = 1; signum <= SIGNUM_BOUND / 10; signum *= 10)
121 num_width++;
122
123 /* Compute the maximum width of a signal name. */
124 for (signum = 1; signum <= SIGNUM_BOUND; signum++)
125 if (sig2str (signum, signame) == 0)
126 {
127 idx_t len = strlen (signame);
128 if (name_width < len)
129 name_width = len;
130 }
131
132 if (argv)
133 for (; *argv; argv++)
134 {
135 signum = operand2sig (*argv, signame);
136 if (signum < 0)
137 status = EXIT_FAILURE;
138 else
139 print_table_row (num_width, signum, name_width, signame);
140 }
141 else
142 for (signum = 1; signum <= SIGNUM_BOUND; signum++)
143 if (sig2str (signum, signame) == 0)
144 print_table_row (num_width, signum, name_width, signame);
145 }
146 else
147 {
148 if (argv)
149 for (; *argv; argv++)
150 {
151 signum = operand2sig (*argv, signame);
152 if (signum < 0)
153 status = EXIT_FAILURE;
154 else
155 {
156 if (ISDIGIT (**argv))
157 puts (signame);
158 else
159 printf ("%d\n", signum);
160 }
161 }
162 else
163 for (signum = 1; signum <= SIGNUM_BOUND; signum++)
164 if (sig2str (signum, signame) == 0)
165 puts (signame);
166 }
167
168 return status;
169 }
170
171 /* Send signal SIGNUM to all the processes or process groups specified
172 by ARGV. Return a suitable exit status. */
173
174 static int
send_signals(int signum,char * const * argv)175 send_signals (int signum, char *const *argv)
176 {
177 int status = EXIT_SUCCESS;
178 char const *arg = *argv;
179
180 do
181 {
182 char *endp;
183 intmax_t n = (errno = 0, strtoimax (arg, &endp, 10));
184 pid_t pid;
185
186 if (errno == ERANGE || ckd_add (&pid, n, 0)
187 || arg == endp || *endp)
188 {
189 error (0, 0, _("%s: invalid process id"), quote (arg));
190 status = EXIT_FAILURE;
191 }
192 else if (kill (pid, signum) != 0)
193 {
194 error (0, errno, "%s", quote (arg));
195 status = EXIT_FAILURE;
196 }
197 }
198 while ((arg = *++argv));
199
200 return status;
201 }
202
203 int
main(int argc,char ** argv)204 main (int argc, char **argv)
205 {
206 int optc;
207 bool list = false;
208 bool table = false;
209 int signum = -1;
210 char signame[SIG2STR_MAX];
211
212 initialize_main (&argc, &argv);
213 set_program_name (argv[0]);
214 setlocale (LC_ALL, "");
215 bindtextdomain (PACKAGE, LOCALEDIR);
216 textdomain (PACKAGE);
217
218 atexit (close_stdout);
219
220 while ((optc = getopt_long (argc, argv, short_options, long_options, nullptr))
221 != -1)
222 switch (optc)
223 {
224 case '0': case '1': case '2': case '3': case '4':
225 case '5': case '6': case '7': case '8': case '9':
226 if (optind != 2)
227 {
228 /* This option is actually a process-id. */
229 optind--;
230 goto no_more_options;
231 }
232 FALLTHROUGH;
233 case 'A': case 'B': case 'C': case 'D': case 'E':
234 case 'F': case 'G': case 'H': case 'I': case 'J':
235 case 'K': /*case 'L':*/ case 'M': case 'N': case 'O':
236 case 'P': case 'Q': case 'R': case 'S': case 'T':
237 case 'U': case 'V': case 'W': case 'X': case 'Y':
238 case 'Z':
239 if (! optarg)
240 optarg = argv[optind - 1] + strlen (argv[optind - 1]);
241 if (optarg != argv[optind - 1] + 2)
242 {
243 error (0, 0, _("invalid option -- %c"), optc);
244 usage (EXIT_FAILURE);
245 }
246 optarg--;
247 FALLTHROUGH;
248 case 'n': /* -n is not documented, but is for Bash compatibility. */
249 case 's':
250 if (0 <= signum)
251 {
252 error (0, 0, _("%s: multiple signals specified"), quote (optarg));
253 usage (EXIT_FAILURE);
254 }
255 signum = operand2sig (optarg, signame);
256 if (signum < 0)
257 usage (EXIT_FAILURE);
258 break;
259
260 case 'L': /* -L is not documented, but is for procps compatibility. */
261 case 't':
262 table = true;
263 FALLTHROUGH;
264 case 'l':
265 if (list)
266 {
267 error (0, 0, _("multiple -l or -t options specified"));
268 usage (EXIT_FAILURE);
269 }
270 list = true;
271 break;
272
273 case_GETOPT_HELP_CHAR;
274 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
275 default:
276 usage (EXIT_FAILURE);
277 }
278 no_more_options:
279
280 if (signum < 0)
281 signum = SIGTERM;
282 else if (list)
283 {
284 error (0, 0, _("cannot combine signal with -l or -t"));
285 usage (EXIT_FAILURE);
286 }
287
288 if ( ! list && argc <= optind)
289 {
290 error (0, 0, _("no process ID specified"));
291 usage (EXIT_FAILURE);
292 }
293
294 return (list
295 ? list_signals (table, optind < argc ? argv + optind : nullptr)
296 : send_signals (signum, argv + optind));
297 }
298