/* kill -- send a signal to a process Copyright (C) 2002-2020 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 . */ /* Written by Paul Eggert. */ #include #include #include #include #include #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)); }