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