summaryrefslogtreecommitdiffstats
path: root/src/kill.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kill.c')
-rw-r--r--src/kill.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/src/kill.c b/src/kill.c
new file mode 100644
index 0000000..be01b2b
--- /dev/null
+++ b/src/kill.c
@@ -0,0 +1,314 @@
+/* kill -- send a signal to a process
+ Copyright (C) 2002-2022 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Paul Eggert. */
+
+#include <config.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include "system.h"
+#include "error.h"
+#include "sig2str.h"
+#include "operand2sig.h"
+#include "quote.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "kill"
+
+#define AUTHORS proper_name ("Paul Eggert")
+
+#if ! (HAVE_DECL_STRSIGNAL || defined strsignal)
+# if ! (HAVE_DECL_SYS_SIGLIST || defined sys_siglist)
+# if HAVE_DECL__SYS_SIGLIST || defined _sys_siglist
+# define sys_siglist _sys_siglist
+# elif HAVE_DECL___SYS_SIGLIST || defined __sys_siglist
+# define sys_siglist __sys_siglist
+# endif
+# endif
+# if HAVE_DECL_SYS_SIGLIST || defined sys_siglist
+# define strsignal(signum) (0 <= (signum) && (signum) <= SIGNUM_BOUND \
+ ? sys_siglist[signum] \
+ : 0)
+# endif
+# ifndef strsignal
+# define strsignal(signum) 0
+# endif
+#endif
+
+static char const short_options[] =
+ "0::1::2::3::4::5::6::7::8::9::"
+ "A::B::C::D::E::F::G::H::I::J::K::M::"
+ "N::O::P::Q::R::S::T::U::V::W::X::Y::Z::"
+ "Lln:s:t";
+
+static struct option const long_options[] =
+{
+ {"list", no_argument, NULL, 'l'},
+ {"signal", required_argument, NULL, 's'},
+ {"table", no_argument, NULL, 't'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("\
+Usage: %s [-s SIGNAL | -SIGNAL] PID...\n\
+ or: %s -l [SIGNAL]...\n\
+ or: %s -t [SIGNAL]...\n\
+"),
+ program_name, program_name, program_name);
+ fputs (_("\
+Send signals to processes, or list signals.\n\
+"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ fputs (_("\
+ -s, --signal=SIGNAL, -SIGNAL\n\
+ specify the name or number of the signal to be sent\n\
+ -l, --list list signal names, or convert signal names to/from numbers\n\
+ -t, --table print a table of signal information\n\
+"), stdout);
+ fputs (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ fputs (_("\n\
+SIGNAL may be a signal name like 'HUP', or a signal number like '1',\n\
+or the exit status of a process terminated by a signal.\n\
+PID is an integer; if negative it identifies a process group.\n\
+"), stdout);
+ printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+/* Print a row of 'kill -t' output. NUM_WIDTH is the maximum signal
+ number width, and SIGNUM is the signal number to print. The
+ maximum name width is NAME_WIDTH, and SIGNAME is the name to print. */
+
+static void
+print_table_row (int num_width, int signum,
+ int name_width, char const *signame)
+{
+ char const *description = strsignal (signum);
+ printf ("%*d %-*s %s\n", num_width, signum, name_width, signame,
+ description ? description : "?");
+}
+
+/* Print a list of signal names. If TABLE, print a table.
+ Print the names specified by ARGV if nonzero; otherwise,
+ print all known names. Return a suitable exit status. */
+
+static int
+list_signals (bool table, char *const *argv)
+{
+ int signum;
+ int status = EXIT_SUCCESS;
+ char signame[SIG2STR_MAX];
+
+ if (table)
+ {
+ unsigned int name_width = 0;
+
+ /* Compute the maximum width of a signal number. */
+ unsigned int num_width = 1;
+ for (signum = 1; signum <= SIGNUM_BOUND / 10; signum *= 10)
+ num_width++;
+
+ /* Compute the maximum width of a signal name. */
+ for (signum = 1; signum <= SIGNUM_BOUND; signum++)
+ if (sig2str (signum, signame) == 0)
+ {
+ size_t len = strlen (signame);
+ if (name_width < len)
+ name_width = len;
+ }
+
+ if (argv)
+ for (; *argv; argv++)
+ {
+ signum = operand2sig (*argv, signame);
+ if (signum < 0)
+ status = EXIT_FAILURE;
+ else
+ print_table_row (num_width, signum, name_width, signame);
+ }
+ else
+ for (signum = 1; signum <= SIGNUM_BOUND; signum++)
+ if (sig2str (signum, signame) == 0)
+ print_table_row (num_width, signum, name_width, signame);
+ }
+ else
+ {
+ if (argv)
+ for (; *argv; argv++)
+ {
+ signum = operand2sig (*argv, signame);
+ if (signum < 0)
+ status = EXIT_FAILURE;
+ else
+ {
+ if (ISDIGIT (**argv))
+ puts (signame);
+ else
+ printf ("%d\n", signum);
+ }
+ }
+ else
+ for (signum = 1; signum <= SIGNUM_BOUND; signum++)
+ if (sig2str (signum, signame) == 0)
+ puts (signame);
+ }
+
+ return status;
+}
+
+/* Send signal SIGNUM to all the processes or process groups specified
+ by ARGV. Return a suitable exit status. */
+
+static int
+send_signals (int signum, char *const *argv)
+{
+ int status = EXIT_SUCCESS;
+ char const *arg = *argv;
+
+ do
+ {
+ char *endp;
+ intmax_t n = (errno = 0, strtoimax (arg, &endp, 10));
+ pid_t pid = n;
+
+ if (errno == ERANGE || pid != n || arg == endp || *endp)
+ {
+ error (0, 0, _("%s: invalid process id"), quote (arg));
+ status = EXIT_FAILURE;
+ }
+ else if (kill (pid, signum) != 0)
+ {
+ error (0, errno, "%s", quote (arg));
+ status = EXIT_FAILURE;
+ }
+ }
+ while ((arg = *++argv));
+
+ return status;
+}
+
+int
+main (int argc, char **argv)
+{
+ int optc;
+ bool list = false;
+ bool table = false;
+ int signum = -1;
+ char signame[SIG2STR_MAX];
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (close_stdout);
+
+ while ((optc = getopt_long (argc, argv, short_options, long_options, NULL))
+ != -1)
+ switch (optc)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (optind != 2)
+ {
+ /* This option is actually a process-id. */
+ optind--;
+ goto no_more_options;
+ }
+ FALLTHROUGH;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': /*case 'L':*/ case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z':
+ if (! optarg)
+ optarg = argv[optind - 1] + strlen (argv[optind - 1]);
+ if (optarg != argv[optind - 1] + 2)
+ {
+ error (0, 0, _("invalid option -- %c"), optc);
+ usage (EXIT_FAILURE);
+ }
+ optarg--;
+ FALLTHROUGH;
+ case 'n': /* -n is not documented, but is for Bash compatibility. */
+ case 's':
+ if (0 <= signum)
+ {
+ error (0, 0, _("%s: multiple signals specified"), quote (optarg));
+ usage (EXIT_FAILURE);
+ }
+ signum = operand2sig (optarg, signame);
+ if (signum < 0)
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'L': /* -L is not documented, but is for procps compatibility. */
+ case 't':
+ table = true;
+ FALLTHROUGH;
+ case 'l':
+ if (list)
+ {
+ error (0, 0, _("multiple -l or -t options specified"));
+ usage (EXIT_FAILURE);
+ }
+ list = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
+ no_more_options:
+
+ if (signum < 0)
+ signum = SIGTERM;
+ else if (list)
+ {
+ error (0, 0, _("cannot combine signal with -l or -t"));
+ usage (EXIT_FAILURE);
+ }
+
+ if ( ! list && argc <= optind)
+ {
+ error (0, 0, _("no process ID specified"));
+ usage (EXIT_FAILURE);
+ }
+
+ return (list
+ ? list_signals (table, optind < argc ? argv + optind : NULL)
+ : send_signals (signum, argv + optind));
+}