/*++ 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 #include #include #include #include #include #include #include #include #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 -- 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; }