123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- kill.c
- Abstract:
- This module implements the kill (process termination and signalling)
- utility.
- Author:
- Chris Stevens 19-Aug-2014
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <getopt.h>
- #include <signal.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "swlib.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define KILL_VERSION_MAJOR 1
- #define KILL_VERSION_MINOR 0
- #define KILL_USAGE \
- "usage: kill -s signal_name pid...\n" \
- " kill -l [exit_status...]\n" \
- " kill [-signal_name] pid...\n" \
- " kill [-signal_number] pid...\n\n" \
- "The kill utility sends a signal to one or more processes. Options are:\n" \
- " -l --list -- Lists all supported values for signal_name if an " \
- "exit_status\n" \
- " is not supplied. If an exit_status is supplied and it is a\n" \
- " signal_number, then the corresponding signal_name is written.\n" \
- " If the exit_status the '?' special shell character for a\n" \
- " terminated process, then signal_name of the signal that " \
- "terminated\n" \
- " the process is written.\n" \
- " -s --signal <signal_name> -- Specify a signal to send using a signal\n" \
- " name.\n" \
- " --help -- Show this help text and exit.\n" \
- " --version -- Print the application version information and exit.\n\n"
- #define KILL_OPTIONS_STRING "+ls:hV"
- //
- // Define kill options.
- //
- //
- // Set this option if the utility should list signal names and values.
- //
- #define KILL_OPTIONS_LIST_SIGNALS 0x00000001
- //
- // Set this option if the utility should send signals.
- //
- #define KILL_OPTIONS_SEND_SIGNALS 0x00000002
- //
- // Define the default signal number to send.
- //
- #define KILL_DEFAULT_SIGNAL_NUMBER SIGTERM
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- KillpPrintSignals (
- VOID
- );
- INT
- KillpPrintSignal (
- PSTR Argument
- );
- INT
- KillpParseSignalName (
- PSTR Argument,
- PINT SignalNumber
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option KillLongOptions[] = {
- {"list", no_argument, 0, 'l'},
- {"signal", required_argument, 0, 's'},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- KillMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the kill utility.
- Arguments:
- ArgumentCount - Supplies the number of command line arguments the program
- was invoked with.
- Arguments - Supplies a tokenized array of command line arguments.
- Return Value:
- Returns an integer exit code. 0 for success, non-zero otherwise.
- --*/
- {
- PSTR AfterScan;
- PSTR Argument;
- ULONG ArgumentIndex;
- BOOL ContinueLoop;
- ULONG Index;
- INT Option;
- ULONG Options;
- INT PreviousOptionIndex;
- pid_t ProcessId;
- PSTR SignalName;
- INT SignalNumber;
- INT Status;
- Status = 0;
- //
- // Disable option errors for the kill utility as it allows some
- // non-traditional options.
- //
- opterr = 0;
- //
- // The default is to send SIGTERM signals to all process IDs listed.
- //
- Options = KILL_OPTIONS_SEND_SIGNALS;
- SignalNumber = KILL_DEFAULT_SIGNAL_NUMBER;
- //
- // Process the control arguments.
- //
- SignalName = NULL;
- ContinueLoop = TRUE;
- while (ContinueLoop != FALSE) {
- PreviousOptionIndex = optind;
- Option = getopt_long(ArgumentCount,
- Arguments,
- KILL_OPTIONS_STRING,
- KillLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if (Option == ':') {
- Status = 1;
- goto MainEnd;
- }
- switch (Option) {
- case '?':
- //
- // If a signal name has already been found, then stop processing
- // and roll back the index.
- //
- if (SignalName != NULL) {
- optind = PreviousOptionIndex;
- ContinueLoop = FALSE;
- break;
- }
- //
- // Otherwise there may be a signal name at the previous index. If
- // it was just a '-9' then 'optind' incremented. If it was '-kill',
- // then 'optind' did not increment. So, go with the previous option
- // index, saving the signal name for later.
- //
- Argument = Arguments[PreviousOptionIndex];
- assert(*Argument == '-');
- Argument += 1;
- SignalName = Argument;
- Options |= KILL_OPTIONS_SEND_SIGNALS;
- //
- // Also, skip over the whole option if it did not move forward.
- //
- if (optind == PreviousOptionIndex) {
- optind += 1;
- }
- break;
- case 'l':
- Options |= KILL_OPTIONS_LIST_SIGNALS;
- break;
- case 's':
- //
- // Check to see if this is a direct signal name. For example,
- // "kill -sigkill 12345" should kill process 12345. If the option
- // index only advanced 1, then the argument is attached to the 's'.
- // Test these cases for '-sig*'.
- //
- if ((optind - 1) == PreviousOptionIndex) {
- //
- // If a signal name has already been found, exit and roll back
- // the option.
- //
- if (SignalName != NULL) {
- optind = PreviousOptionIndex;
- ContinueLoop = FALSE;
- break;
- }
- Argument = optarg;
- //
- // Move back a character to include the initial 's' and save it
- // for later.
- //
- Argument -= 1;
- assert(*Argument == 's');
- SignalName = Argument;
- Options |= KILL_OPTIONS_SEND_SIGNALS;
- //
- // Otherwise, the argument is in a separate array entry that the
- // 's' option. Save it for later.
- //
- } else {
- Argument = optarg;
- assert(Argument != NULL);
- SignalName = Argument;
- Options |= KILL_OPTIONS_SEND_SIGNALS;
- }
- break;
- case 'V':
- SwPrintVersion(KILL_VERSION_MAJOR, KILL_VERSION_MINOR);
- return 1;
- case 'h':
- printf(KILL_USAGE);
- return 1;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- //
- // One of the kill options should be sent.
- //
- assert(((Options & KILL_OPTIONS_LIST_SIGNALS) != 0) ||
- ((Options & KILL_OPTIONS_SEND_SIGNALS) != 0));
- //
- // Get the current argument index, making sure it is not too big.
- //
- ArgumentIndex = optind;
- if (ArgumentIndex > ArgumentCount) {
- Status = 1;
- goto MainEnd;
- }
- //
- // If listing the signal numbers was requested, then prefer that over
- // sending signals.
- //
- if ((Options & KILL_OPTIONS_LIST_SIGNALS) != 0) {
- if (ArgumentIndex == ArgumentCount) {
- KillpPrintSignals();
- } else {
- for (Index = ArgumentIndex; Index < ArgumentCount; Index += 1) {
- Status = KillpPrintSignal(Arguments[Index]);
- if (Status != 0) {
- goto MainEnd;
- }
- }
- }
- //
- // Sending signals is the default. Send signals to all remaining arguments,
- // interpreting them as process IDs or process groups.
- //
- } else {
- //
- // Figure out the signal to use.
- //
- if (SignalName != NULL) {
- Status = KillpParseSignalName(SignalName, &SignalNumber);
- if (Status != 0) {
- goto MainEnd;
- }
- }
- //
- // Exit and print the help if no process IDs were provided.
- //
- if (ArgumentIndex == ArgumentCount) {
- printf(KILL_USAGE);
- Status = 1;
- goto MainEnd;
- }
- for (Index = ArgumentIndex; Index < ArgumentCount; Index += 1) {
- ProcessId = (pid_t)strtol(Arguments[Index], &AfterScan, 10);
- if ((AfterScan == Arguments[Index]) || (*AfterScan != '\0')) {
- SwPrintError(0,
- NULL,
- "Invalid process ID '%s'",
- Arguments[Index]);
- continue;
- }
- Status = SwKill(ProcessId, SignalNumber);
- if (Status != 0) {
- SwPrintError(errno,
- NULL,
- "Failed to signal process %lu",
- ProcessId);
- }
- }
- }
- Status = 0;
- MainEnd:
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- KillpPrintSignals (
- VOID
- )
- /*++
- Routine Description:
- This routine prints out all the allowable signal names and signal numbers.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- PSTR Name;
- INT Signal;
- //
- // Print all signals, skipping the zero signal.
- //
- for (Signal = 1; Signal <= NSIG; Signal += 1) {
- if ((SIGRTMAX > SIGRTMIN) &&
- (Signal >= SIGRTMIN) && (Signal <= SIGRTMAX)) {
- if (Signal == SIGRTMIN) {
- printf("%d) SIGRTMIN\n", Signal);
- } else if (Signal == SIGRTMAX) {
- printf("%d) SIGRTMAX\n", Signal);
- } else {
- printf("%d) SIGRTMIN+%d\n", Signal, Signal - SIGRTMIN);
- }
- } else {
- Name = SwGetSignalNameFromNumber(Signal);
- if (Name != NULL) {
- printf("%d) %s\n", Signal, Name);
- }
- }
- }
- return;
- }
- INT
- KillpPrintSignal (
- PSTR Argument
- )
- /*++
- Routine Description:
- This routine prints the signal name for a given signal number string or
- prints the signal number for a given signal name string.
- Arguments:
- Argument - Supplies the string representation of either a signal name or
- signal number.
- Return Value:
- 0 on success.
- EINVAL if the signal argument is invalid.
- --*/
- {
- PSTR Name;
- INT SignalNumber;
- INT Status;
- Status = 0;
- SignalNumber = SwGetSignalNumberFromName(Argument);
- if (SignalNumber == -1) {
- SwPrintError(0, Argument, "Invalid signal specification");
- Status = EINVAL;
- } else {
- if (!isdigit(*Argument)) {
- printf("%d\n", SignalNumber);
- } else {
- if ((SIGRTMAX > SIGRTMIN) &&
- (SignalNumber >= SIGRTMIN) &&
- (SignalNumber <= SIGRTMAX)) {
- if (SignalNumber == SIGRTMIN) {
- printf("RTMIN\n");
- } else if (SignalNumber < SIGRTMAX) {
- printf("RTMAX\n");
- } else {
- printf("RTMIN+%d", SignalNumber - SIGRTMIN);
- }
- } else {
- Name = SwGetSignalNameFromNumber(SignalNumber);
- if (Name == NULL) {
- SwPrintError(0, Argument, "Invalid signal specification");
- Status = EINVAL;
- } else {
- printf("%s\n", Name);
- }
- }
- }
- }
- return Status;
- }
- INT
- KillpParseSignalName (
- PSTR Argument,
- PINT SignalNumber
- )
- /*++
- Routine Description:
- This routine parses the signal name argument to validate it and conver it
- into a signal number.
- Arguments:
- Argument - Supplies the argument string to parse for the signal name.
- SignalNumber - Supplies a pointer that receives the numeric signal value.
- Return Value:
- 0 on succes. Non-zero on failure.
- --*/
- {
- *SignalNumber = SwGetSignalNumberFromName(Argument);
- if (*SignalNumber == -1) {
- return EINVAL;
- }
- return 0;
- }
|