From: Jim Meyering Date: Sat, 15 Dec 2001 16:45:38 +0000 (+0000) Subject: from Marcus Brinkmann X-Git-Tag: FILEUTILS-4_1_4~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=83c58cadbc45b4bb27d5b383c69dddcf67621ccc;p=platform%2Fupstream%2Fcoreutils.git from Marcus Brinkmann --- diff --git a/src/kill.c b/src/kill.c new file mode 100644 index 0000000..2be53d9 --- /dev/null +++ b/src/kill.c @@ -0,0 +1,768 @@ +/* kill -- send a signal to a process + Copyright (C) 2001 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by Marcus Brinkmann. */ + +#include +#include +#include +#include + +#include + +#include "system.h" +#include "closeout.h" +#include "human.h" +#include "error.h" +#include "xstrtol.h" + +/* The official name of this program (e.g., no `g' prefix). */ +#define PROGRAM_NAME "kill" + +#define AUTHORS "Marcus Brinkmann" + + +/* An invalid signal number. */ +#define NO_SIG -1 + +/* A structure holding the number and the name of a signal. */ +struct sigspec +{ + int signum; + char *signame; +}; + +/* The list of signals was taken from bash 2.05. The best name for a + signal comes after any possible alias, so it is read it from back + to front. This is why the terminating null entry comes first. */ +static struct sigspec sigspecs[] = +{ + { NO_SIG, NULL }, + + /* Null is used to test for the existance and ownership of a PID. */ + { 0, "0" }, + +/* AIX */ +#if defined (SIGLOST) /* resource lost (eg, record-lock lost) */ + { SIGLOST, "LOST" }, +#endif + +#if defined (SIGMSG) /* HFT input data pending */ + { SIGMSG, "MSG" }, +#endif + +#if defined (SIGDANGER) /* system crash imminent */ + { SIGDANGER, "DANGER" }, +#endif + +#if defined (SIGMIGRATE) /* migrate process to another CPU */ + { SIGMIGRATE, "MIGRATE" }, +#endif + +#if defined (SIGPRE) /* programming error */ + { SIGPRE, "PRE" }, +#endif + +#if defined (SIGVIRT) /* AIX virtual time alarm */ + { SIGVIRT, "VIRT" }, +#endif + +#if defined (SIGALRM1) /* m:n condition variables */ + { SIGALRM1, "ALRM1" }, +#endif + +#if defined (SIGWAITING) /* m:n scheduling */ + { SIGWAITING, "WAITING" }, +#endif + +#if defined (SIGGRANT) /* HFT monitor mode granted */ + { SIGGRANT, "GRANT" }, +#endif + +#if defined (SIGKAP) /* keep alive poll from native keyboard */ + { SIGKAP, "KAP" }, +#endif + +#if defined (SIGRETRACT) /* HFT monitor mode retracted */ + { SIGRETRACT, "RETRACT" }, +#endif + +#if defined (SIGSOUND) /* HFT sound sequence has completed */ + { SIGSOUND, "SOUND" }, +#endif + +#if defined (SIGSAK) /* Secure Attention Key */ + { SIGSAK, "SAK" }, +#endif + +/* SunOS5 */ +#if defined (SIGLWP) /* special signal used by thread library */ + { SIGLWP, "LWP" }, +#endif + +#if defined (SIGFREEZE) /* special signal used by CPR */ + { SIGFREEZE, "FREEZE" }, +#endif + +#if defined (SIGTHAW) /* special signal used by CPR */ + { SIGTHAW, "THAW" }, +#endif + +#if defined (SIGCANCEL) /* thread cancellation signal used by libthread */ + { SIGCANCEL, "CANCEL" }, +#endif + +/* HP-UX */ +#if defined (SIGDIL) /* DIL signal (?) */ + { SIGDIL, "DIL" }, +#endif + +/* System V */ +#if defined (SIGCLD) /* Like SIGCHLD. */ + { SIGCLD, "CLD" }, +#endif + +#if defined (SIGPWR) /* power state indication */ + { SIGPWR, "PWR" }, +#endif + +#if defined (SIGPOLL) /* Pollable event (for streams) */ + { SIGPOLL, "POLL" }, +#endif + +/* Unknown */ +#if defined (SIGWINDOW) + { SIGWINDOW, "WINDOW" }, +#endif + +/* Common */ +#if defined (SIGHUP) /* hangup */ + { SIGHUP, "HUP" }, +#endif + +#if defined (SIGINT) /* interrupt */ + { SIGINT, "INT" }, +#endif + +#if defined (SIGQUIT) /* quit */ + { SIGQUIT, "QUIT" }, +#endif + +#if defined (SIGILL) /* illegal instruction (not reset when caught) */ + { SIGILL, "ILL" }, +#endif + +#if defined (SIGTRAP) /* trace trap (not reset when caught) */ + { SIGTRAP, "TRAP" }, +#endif + +#if defined (SIGIOT) /* IOT instruction */ + { SIGIOT, "IOT" }, +#endif + +#if defined (SIGABRT) /* Cause current process to dump core. */ + { SIGABRT, "ABRT" }, +#endif + +#if defined (SIGEMT) /* EMT instruction */ + { SIGEMT, "EMT" }, +#endif + +#if defined (SIGFPE) /* floating point exception */ + { SIGFPE, "FPE" }, +#endif + +#if defined (SIGKILL) /* kill (cannot be caught or ignored) */ + { SIGKILL, "KILL" }, +#endif + +#if defined (SIGBUS) /* bus error */ + { SIGBUS, "BUS" }, +#endif + +#if defined (SIGSEGV) /* segmentation violation */ + { SIGSEGV, "SEGV" }, +#endif + +#if defined (SIGSYS) /* bad argument to system call */ + { SIGSYS, "SYS" }, +#endif + +#if defined (SIGPIPE) /* write on a pipe with no one to read it */ + { SIGPIPE, "PIPE" }, +#endif + +#if defined (SIGALRM) /* alarm clock */ + { SIGALRM, "ALRM" }, +#endif + +#if defined (SIGTERM) /* software termination signal from kill */ + { SIGTERM, "TERM" }, +#endif + +#if defined (SIGURG) /* urgent condition on IO channel */ + { SIGURG, "URG" }, +#endif + +#if defined (SIGSTOP) /* sendable stop signal not from tty */ + { SIGSTOP, "STOP" }, +#endif + +#if defined (SIGTSTP) /* stop signal from tty */ + { SIGTSTP, "TSTP" }, +#endif + +#if defined (SIGCONT) /* continue a stopped process */ + { SIGCONT, "CONT" }, +#endif + +#if defined (SIGCHLD) /* to parent on child stop or exit */ + { SIGCHLD, "CHLD" }, +#endif + +#if defined (SIGTTIN) /* to readers pgrp upon background tty read */ + { SIGTTIN, "TTIN" }, +#endif + +#if defined (SIGTTOU) /* like TTIN for output if (tp->t_local<OSTOP) */ + { SIGTTOU, "TTOU" }, +#endif + +#if defined (SIGIO) /* input/output possible signal */ + { SIGIO, "IO" }, +#endif + +#if defined (SIGXCPU) /* exceeded CPU time limit */ + { SIGXCPU, "XCPU" }, +#endif + +#if defined (SIGXFSZ) /* exceeded file size limit */ + { SIGXFSZ, "XFSZ" }, +#endif + +#if defined (SIGVTALRM) /* virtual time alarm */ + { SIGVTALRM, "VTALRM" }, +#endif + +#if defined (SIGPROF) /* profiling time alarm */ + { SIGPROF, "PROF" }, +#endif + +#if defined (SIGWINCH) /* window changed */ + { SIGWINCH, "WINCH" }, +#endif + +/* 4.4 BSD */ +#if defined (SIGINFO) && !defined (_SEQUENT_) /* information request */ + { SIGINFO, "INFO" }, +#endif + +#if defined (SIGUSR1) /* user defined signal 1 */ + { SIGUSR1, "USR1" }, +#endif + +#if defined (SIGUSR2) /* user defined signal 2 */ + { SIGUSR2, "USR2" }, +#endif + +#if defined (SIGKILLTHR) /* BeOS: Kill Thread */ + { SIGKILLTHR, "KILLTHR" } +#endif +}; + +/* The last entry of the complete signal list, including real time + signals. */ +struct sigspec *sigspecs_last; + +/* The number of sigspecs in the list. */ +int sigspecs_size; + +/* The number of digits in NSIG (approx.). */ +int nsig_digits; + + +/* The name this program was run with, for error messages. */ +char *program_name; + +/* All options which require an argument are known to the + pre-scan loop in main(). */ +#define OPT_SIGSPEC_LONG "sigspec" +#define OPT_SIGNUM_LONG "signum" + +static struct option const long_options[] = +{ + {"list", no_argument, NULL, 'l'}, + {"long-list", no_argument, NULL, 'L'}, + {OPT_SIGSPEC_LONG, required_argument, NULL, 's'}, + {OPT_SIGNUM_LONG, required_argument, NULL, 'n'}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, + {NULL, 0, NULL, 0} +}; + +void +usage (int status) +{ + if (status != 0) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else + { + printf (_("\ +Usage: %s [-s SIGSPEC | -n SIGNUM | -SIGSPEC] PID ...\n\ + or: %s -l [SIGSPEC] ...\n\ +"), + program_name, program_name); + printf (_("\ +Send the signal named by SIGSPEC or SIGNUM to processes named by PID.\n\ +\n\ + -s, --" OPT_SIGSPEC_LONG " SIGSPEC name or number of signal to be sent\n\ + -n, --" OPT_SIGNUM_LONG " SIGNUM number of signal to be sent\n\ +\n\ + -l, --list list the signal names\n\ + -L, --long-list list the signal names with their numbers\n\ +\n\ + --help display this help and exit\n\ + --version output version information and exit\n\ +")); + printf (_("\ +\n\ +kill returns true if at least one signal was successfully sent, or\n\ +false if an error occurs or an invalid option is encountered.\n\ +")); + puts (_("\nReport bugs to .")); + } + exit (status); +} + + +/* The function is called once before option parsing. It calculates + the maximum number of digits in a signum, adds the realtime signal + specs to the signal list, and adds all signals of the form -SIGTERM + and -TERM as possible options. */ +static void +initialize (void) +{ + char number[LONGEST_HUMAN_READABLE + 1]; + struct sigspec *newspecs; + + nsig_digits = strlen (human_readable ((uintmax_t) NSIG, number, 1, 1)); + + sigspecs_size = sizeof (sigspecs) / sizeof (struct sigspec); + +#if defined (SIGRTMIN) || defined (SIGRTMAX) + /* POSIX 1003.1b-1993 defines real time signals. The following code + is so convoluted because it also takes care of incomplete + implementations. */ +# ifndef SIGRTMIN +# define SIGRTMIN SIGRTMAX +# endif +# ifndef SIGRTMAX +# define SIGRTMAX SIGRTMIN +# endif + + /* Sanity check. */ + if (SIGRTMAX >= SIGRTMIN) + { + int rtsigc = SIGRTMAX - SIGRTMIN + 1; + int i; + /* Account for "RTMIN+" resp "RTMAX-", the number and '\0'. */ + int maxlength = nsig_digits + 6 + 1; + + newspecs = xmalloc (sizeof (struct sigspec) + * (sigspecs_size + rtsigc)); + + /* After this, newspecs will always point to the last element of + the array. */ + newspecs->signum = NO_SIG; + newspecs->signame = NULL; + + (++newspecs)->signum = SIGRTMAX; + newspecs->signame = "RTMAX"; + if (rtsigc > 1) + { + (++newspecs)->signum = SIGRTMIN; + newspecs->signame = "RTMIN"; + } + + /* Create new elements for all missing realtime signals. */ + for (i = 0; i < rtsigc - 2; i++) + { + (++newspecs)->signum = SIGRTMIN + 1 + i; + newspecs->signame = xmalloc (maxlength); + + snprintf (newspecs->signame, maxlength, "%s%d", + (i < (rtsigc - 2)/2 + ? "RTMIN+" : "RTMAX-"), + (i < (rtsigc - 2)/2 + ? i + 1 : (rtsigc - 2) - i)); + } + + /* Copy the existing elements in the following space. */ + for (i = 1; i < sigspecs_size; i++) + *(++newspecs) = sigspecs[i]; + + sigspecs_last = newspecs; + sigspecs_size += rtsigc; + } +#else + sigspecs_last = sigspecs + (sigspecs_size - 1); +#endif +} + + +typedef enum { LIST_NONE, LIST_FLAT, LIST_PRETTY } list_t; + +/* Print out a table listing all signals specifications with their + preferred name. */ +static void +list_signals (list_t type) +{ + int i = 0; + int entrylen = 0; + int unsorted; + int entries; + int last_signum = NO_SIG; + int column = 0; + struct sigspec *spec = sigspecs_last; + struct sigspec **specs = xmalloc (sizeof (struct sigspec *) + * (sigspecs_size - 1)); + + + /* Gather maximum name length and prepare sort array. Note that the + list is reversed in the array. This is taken into account by the + output routine below. */ + while (spec->signum != NO_SIG) + { + specs[i++] = spec; + if (spec->signame && strlen (spec->signame) > entrylen) + entrylen = strlen (spec->signame); + spec--; + } + + /* Sort the array by signal number. This is a simple bubble sort, + but the point is that the order of entries with the same signum + is presevered (otherwise the preferred alias is lost). */ + if (sigspecs_size > 2) + do + { + unsorted = 0; + + for (i = 0; i < sigspecs_size - 2; i++) + { + if (specs[i]->signum > specs[i+1]->signum) + { + struct sigspec *saved = specs[i]; + specs[i] = specs[i+1]; + specs[i+1] = saved; + unsorted = 1; + } + } + } + while (unsorted); + + /* Account for "NR) NAME ". Calculate for 79 columns, the 80 takes + into account that the last entry is not followed by a space. */ + entrylen += nsig_digits + 2 + 1; + entries = 80 / entrylen; + if (entries < 1) + entries = 1; + + for (i = 0; i < sigspecs_size - 1; i++) + { + /* Skip duplicated signal numbers, signums without name and the + special signal number `0'. */ + if (specs[i]->signame && specs[i]->signum + && (last_signum == NO_SIG || last_signum != specs[i]->signum)) + { + switch (type) + { + case LIST_PRETTY: + column++; + printf ("%*i) %-*s", nsig_digits, specs[i]->signum, + entrylen - nsig_digits - 2 + (column != entries ? 1 : 0), + specs[i]->signame); + if (column == entries - 1) + { + column = 0; + putchar ('\n'); + } + break; + case LIST_FLAT: + column += printf ("%s%s", + (column == 0 ? "" + : (column + strlen (specs[i]->signame) > 78 + ? "\n" : " ")), + specs[i]->signame); + if (column > 79) + column = strlen (specs[i]->signame); + break; + default: + break; + } + last_signum = specs[i]->signum; + } + if (i == sigspecs_size - 2) + putchar ('\n'); + } +} + + +/* Turn a string into a signal number, or a number into a signal + number. If STRING is "2", or "INT", then return the integer 2. + Return NO_SIG if STRING doesn't contain a valid signal + descriptor. */ +static int +decode_signal (char const *sigspec) +{ + struct sigspec const *spec = sigspecs_last; + long l; + + if (xstrtol (sigspec, NULL, 0, &l, "") == LONGINT_OK) + { + int sig = l; + + if (sig != l) + return NO_SIG; + + while (spec->signum != NO_SIG && spec->signum != sig) + spec--; + return spec->signum; + } + + /* A leading `SIG' may be omitted. */ + while (spec->signum != NO_SIG) + { + if (spec->signame + && (strcasecmp (sigspec, spec->signame) == 0 + || (strncasecmp (sigspec, "SIG", 3) == 0 + && strcasecmp (sigspec + 3, spec->signame) == 0))) + return (spec->signum); + spec--; + } + return NO_SIG; +} + +/* Find the preferred name for the signal with the number SIGNUM. */ +static char const *const +name_signal (int signum) +{ + struct sigspec const *spec = sigspecs_last; + + while (spec->signum != NO_SIG && spec->signum != signum) + spec--; + return spec->signame; +} + +/* Send the signal SIGNUM to process PID, using kill (). If an error + occurs, it is reported and passed through to the caller. */ +static int +send_signal (pid_t pid, int signum) +{ + int err; + + err = kill (pid, signum); + if (err) + { + uintmax_t nr = (uintmax_t) (pid < 0 ? -pid : pid); + char number[LONGEST_HUMAN_READABLE + 1]; + + error (0, errno, "(%s%s)", (pid < 0 ? "-" : ""), + human_readable (nr, number, 1, 1)); + } + return err; +} + + +int +main (int argc, char **argv) +{ + int optc; + int err = 0; + int success = 0; + list_t list = LIST_NONE; + int sig_num = NO_SIG; + int i; + char **extra_opt; + int extra_opt_size = 0; + + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + atexit (close_stdout); + + initialize (); + + extra_opt = xmalloc ((argc - 1) * sizeof (char *)); + for (i = 1; i < argc; i++) + { + intmax_t dummy; + + /* getopt will ignore everything following `--', so we do as + well. */ + if (!strcmp ("--", argv[i])) + break; + + /* Skip this argument if it doesn't look like an option. */ + if (argv[i][0] != '-') + continue; + + /* Skip it if it follows an option requiring an argument. */ + if (i > 1 && argv[i-1][0] == '-') + { + /* A short option that doesn't look like -nTERM. */ + if ((argv[i-1][1] == 'n' || argv[i-1][1] == 's') + && argv[i-1][2] == '\0') + continue; + + /* A long option (not `--', which was excluded above). */ + if (argv[i-1][1] == '-' + && (!strncmp (OPT_SIGNUM_LONG, &(argv[i-1][2]), + strlen (&argv[i-1][2])) + || !strncmp (OPT_SIGSPEC_LONG, &(argv[i-1][2]), + strlen (&argv[i-1][2])))) + continue; + } + + /* At this point we know that this argument is not argument to + another option, and that it starts with `-' but is not `--'. */ + + if (decode_signal (&(argv[i][1])) != NO_SIG + || xstrtoimax (argv[i], NULL, 10, &dummy, "") == LONGINT_OK) + { + /* It is either a valid signal specifier or potentially a + valid process group. So remember it and make getopt not + care about it. */ + extra_opt[extra_opt_size++] = argv[i]; + argv[i][0] = 'X'; + } + } + + while ((optc = getopt_long (argc, argv, "s:n:lL", long_options, NULL)) + != -1) + switch (optc) + { + case 's': + case 'n': + if (sig_num != NO_SIG) + { + error (0, 0, _("%s: only one signal specififier allowed"), optarg); + usage (1); + } + sig_num = decode_signal (optarg); + if (sig_num == NO_SIG) + error (1, 0, _("%s: invalid signal specifier"), optarg); + break; + case 'l': + list = LIST_FLAT; + break; + case 'L': + list = LIST_PRETTY; + break; + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: + usage (1); + } + + argc -= optind; + argv += optind; + + for (i = 0; i < extra_opt_size; i++) + extra_opt[i][0] = '-'; + + if (extra_opt_size > 0 && sig_num == NO_SIG) + { + char **arg = argv; + + /* Find the first extra option collected in the remaining + argument list and if necessary, replace it with the first + remaining argument. This is a precaution in case getopt() + mangles the order of non-option arguments. */ + + while (*arg && *arg != extra_opt[0]) + arg++; + if (*arg && *arg != argv[0]) + *arg = argv[0]; + + argc--; + argv++; + + /* Interpret the first one as a signal specifier. */ + sig_num = decode_signal (&(extra_opt[0][1])); + if (sig_num == NO_SIG) + error (1, 0, _("%s: invalid signal specifier"), extra_opt[0]); + } + + if (!list && sig_num == NO_SIG) + sig_num = SIGTERM; + + if (argc == 0) + { + if (list) + list_signals (list); + else + { + error (0, 0, _("too few arguments")); + usage (1); + } + } + else + { + int j; + for (j = 0; j < argc; j++) + if (list) + { + int signum = decode_signal (argv[j]); + if (signum == NO_SIG) + { + error (0, 0, _("%s: invalid signal specifier"), argv[j]); + err = 1; + } + else + { + char const *const name = name_signal (signum); + printf ("%s\n", name ? name : "(unknown)"); + success = 1; + } + } + else + { + intmax_t nr; + pid_t pid; + int inval = 0; + + if (xstrtoimax (argv[j], NULL, 10, &nr, "") != LONGINT_OK) + inval = 1; + pid = (pid_t) nr; + if (inval || pid != nr) + { + error (0, 0, _("%s: invalid process id"), argv[j]); + err = 1; + continue; + } + if (send_signal (pid, sig_num)) + err = 1; + else + success = 1; + } + } + + exit ((success || !err) ? 0 : 1); +}