123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- signals.c
- Abstract:
- This module implements signal handling functionality for the shell.
- Author:
- Evan Green 14-Jun-2013
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "sh.h"
- #include "shparse.h"
- #include <assert.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <string.h>
- #include "../swlib.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- typedef struct _SHELL_SIGNAL_STRING {
- PSTR Name;
- SHELL_SIGNAL Number;
- } SHELL_SIGNAL_STRING, *PSHELL_SIGNAL_STRING;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- BOOL
- ShSetSignalAction (
- PSHELL Shell,
- SHELL_SIGNAL Signal,
- PSTR ActionString
- );
- SHELL_SIGNAL
- ShGetSignalFromArgument (
- PSTR Argument
- );
- SHELL_SIGNAL
- ShGetSignalNumberFromName (
- PSTR Name
- );
- PSTR
- ShGetSignalNameFromNumber (
- SHELL_SIGNAL Number
- );
- PSHELL_SIGNAL_ACTION
- ShGetSignalAction (
- PSHELL Shell,
- SHELL_SIGNAL SignalNumber
- );
- PSHELL_SIGNAL_ACTION
- ShCreateSignalAction (
- SHELL_SIGNAL SignalNumber,
- PSTR ActionString,
- UINTN ActionStringSize
- );
- VOID
- ShDestroySignalAction (
- PSHELL_SIGNAL_ACTION SignalAction
- );
- BOOL
- ShPrintTraps (
- PSHELL Shell
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Store an array of which signals are pending.
- //
- volatile int ShPendingSignals[ShellSignalCount];
- //
- // Store the count of signals, which is used for quick detection of whether or
- // not new signals have come in.
- //
- volatile int ShSignalCount;
- //
- // Define the translation between signal names and numbers.
- //
- SHELL_SIGNAL_STRING ShSignalNames[] = {
- {"EXIT", ShellSignalOnExit},
- {"HUP", ShellSignalHangup},
- {"INT", ShellSignalInterrupt},
- {"QUIT", ShellSignalQuit},
- {"ILL", ShellSignalIllegalInstruction},
- {"TRAP", ShellSignalTrap},
- {"ABRT", ShellSignalAbort},
- {"FPE", ShellSignalFloatingPointException},
- {"KILL", ShellSignalKill},
- {"BUS", ShellSignalBusError},
- {"SEGV", ShellSignalSegmentationFault},
- {"SYS", ShellSignalBadSystemCall},
- {"PIPE", ShellSignalPipe},
- {"ALRM", ShellSignalAlarm},
- {"TERM", ShellSignalTerminate},
- {"URG", ShellSignalUrgentData},
- {"STOP", ShellSignalStop},
- {"TSTP", ShellSignalTerminalStop},
- {"CONT", ShellSignalContinue},
- {"CHLD", ShellSignalChild},
- {"TTIN", ShellSignalTerminalInput},
- {"TTOU", ShellSignalTerminalOutput},
- {"XCPU", ShellSignalCpuTime},
- {"XFSZ", ShellSignalFileSize},
- {"VTALRM", ShellSignalVirtualTimeAlarm},
- {"PROF", ShellSignalProfiling},
- {"WINCH", ShellSignalWindowChange},
- {"USR1", ShellSignalUser1},
- {"USR2", ShellSignalUser2},
- {NULL, 0},
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- void
- ShSignalHandler (
- int SignalNumber
- )
- /*++
- Routine Description:
- This routine is called when a signal comes in. It marks the signal as
- pending and makes an effort to get out as quickly as possible. The signal
- execution environment is fairly hostile, so there's not much that could be
- done anyway.
- Arguments:
- SignalNumber - Supplies the signal number of the signal that came in.
- Return Value:
- None.
- --*/
- {
- if (SignalNumber >= ShellSignalCount) {
- PRINT_ERROR("Unexpected signal %d came in.\n", SignalNumber);
- return;
- }
- //
- // Mark the specific signal as pending, and up the total number of signals
- // so the topmost shell knows to take a closer look.
- //
- ShPendingSignals[SignalNumber] = 1;
- ShSignalCount += 1;
- return;
- }
- VOID
- ShInitializeSignals (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine initializes the shell signals.
- Arguments:
- Shell - Supplies a pointer to the shell.
- Return Value:
- None.
- --*/
- {
- if ((Shell->Options & SHELL_OPTION_INTERACTIVE) != 0) {
- ShSetSignalDisposition(ShellSignalInterrupt,
- ShellSignalDispositionTrap);
- ShSetSignalDisposition(ShellSignalQuit, ShellSignalDispositionTrap);
- } else {
- ShSetSignalDisposition(ShellSignalInterrupt,
- ShellSignalDispositionDefault);
- ShSetSignalDisposition(ShellSignalQuit, ShellSignalDispositionDefault);
- }
- return;
- }
- VOID
- ShCheckForSignals (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine checks for any pending signals and runs their associated
- actions if they're set.
- Arguments:
- Shell - Supplies a pointer to the shell to run any pending signals on.
- Return Value:
- None.
- --*/
- {
- PSHELL_SIGNAL_ACTION Action;
- pid_t Child;
- PSTR EvalArguments[2];
- INT SavedReturnValue;
- INT Signal;
- //
- // If fork is supported, perform wait to clean up any asynchronous nodes.
- //
- if (SwForkSupported != FALSE) {
- while (TRUE) {
- Child = SwWaitPid(-1, 1, NULL);
- if (Child <= 0) {
- break;
- }
- }
- }
- if (ShSignalCount == Shell->LastSignalCount) {
- return;
- }
- Shell->LastSignalCount = ShSignalCount;
- for (Signal = 0; Signal < ShellSignalCount; Signal += 1) {
- if (ShPendingSignals[Signal] == 0) {
- continue;
- }
- ShPendingSignals[Signal] = 0;
- Action = ShGetSignalAction(Shell, Signal);
- if ((Action != NULL) && (Action->ActionSize > 1)) {
- SavedReturnValue = Shell->LastReturnValue;
- EvalArguments[0] = "eval";
- EvalArguments[1] = Action->Action;
- ShBuiltinEval(Shell, 2, EvalArguments);
- Shell->LastReturnValue = SavedReturnValue;
- }
- }
- return;
- }
- VOID
- ShRunAtExitSignal (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine runs the "exit" trap handler on signal 0 if one was set.
- Arguments:
- Shell - Supplies a pointer to the shell to run any pending signals on.
- Return Value:
- None.
- --*/
- {
- if (Shell->SkipExitSignal != FALSE) {
- return;
- }
- if (ShGetSignalAction(Shell, ShellSignalOnExit) != NULL) {
- assert(Shell->Exited != FALSE);
- Shell->Exited = FALSE;
- //
- // Avoid calling the at exit signal again if the at exit signal calls
- // exit.
- //
- Shell->SkipExitSignal = TRUE;
- //
- // This is not a real signal so doing this won't race with incrementing
- // any actual signals. The total signal count might, but it's ok if
- // it only gets incremented by 1 instead of 2, the only real important
- // thing is that it's different from what's in the shell.
- //
- ShPendingSignals[ShellSignalOnExit] += 1;
- ShSignalCount += 1;
- ShCheckForSignals(Shell);
- Shell->Exited = TRUE;
- }
- return;
- }
- VOID
- ShSetAllSignalDispositions (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine sets all the signal dispositions in accordance with the
- given shell. This is usually called when entering or exiting a subshell.
- Arguments:
- Shell - Supplies a pointer to the shell to operate in accordance with.
- Return Value:
- None.
- --*/
- {
- PSHELL_SIGNAL_ACTION Action;
- SHELL_SIGNAL Signal;
- for (Signal = 1; Signal < ShellSignalCount; Signal += 1) {
- Action = ShGetSignalAction(Shell, Signal);
- //
- // Trap the signal if one of the following is true:
- // 1) There's a requested action.
- // 2) It's an interactive shell and the signal is SIGINT or SIGQUIT.
- //
- if ((Action != NULL) ||
- (((Shell->Options & SHELL_OPTION_INTERACTIVE) != 0) &&
- ((Signal == ShellSignalInterrupt) ||
- (Signal == ShellSignalQuit)))) {
- ShSetSignalDisposition(Signal, ShellSignalDispositionTrap);
- } else {
- ShSetSignalDisposition(Signal, ShellSignalDispositionDefault);
- }
- }
- return;
- }
- VOID
- ShDestroySignalActionList (
- PLIST_ENTRY ActionList
- )
- /*++
- Routine Description:
- This routine destroys all the signal actions on the given signal action
- list.
- Arguments:
- ActionList - Supplies a pointer to the head of the action list.
- Return Value:
- None.
- --*/
- {
- PSHELL_SIGNAL_ACTION Action;
- while (LIST_EMPTY(ActionList) == FALSE) {
- Action = LIST_VALUE(ActionList->Next, SHELL_SIGNAL_ACTION, ListEntry);
- LIST_REMOVE(&(Action->ListEntry));
- ShDestroySignalAction(Action);
- }
- return;
- }
- INT
- ShBuiltinTrap (
- PSHELL Shell,
- INT ArgumentCount,
- PSTR *Arguments
- )
- /*++
- Routine Description:
- This routine implements the trap command, which handles signal catching
- within the shell.
- Arguments:
- Shell - Supplies a pointer to the shell.
- ArgumentCount - Supplies the number of arguments on the command line.
- Arguments - Supplies the array of pointers to strings representing each
- argument.
- Return Value:
- 0 on success.
- 1 on failure.
- --*/
- {
- PSHELL_SIGNAL_ACTION Action;
- ULONG ArgumentIndex;
- BOOL Result;
- SHELL_SIGNAL Signal;
- ULONG StartIndex;
- StartIndex = 1;
- //
- // A -- at the beginning is basically ignored as an argument.
- //
- if ((ArgumentCount > 1) && (strcmp(Arguments[1], "--") == 0)) {
- StartIndex = 2;
- }
- if (ArgumentCount <= StartIndex) {
- Result = ShPrintTraps(Shell);
- if (Result == FALSE) {
- return 1;
- }
- return 0;
- }
- //
- // If there's only one argument, then delete the action for the given
- // signal.
- //
- if (ArgumentCount == StartIndex + 1) {
- Signal = ShGetSignalFromArgument(Arguments[StartIndex]);
- if (Signal == ShellSignalInvalid) {
- PRINT_ERROR("trap: %s: bad trap\n", Arguments[StartIndex]);
- return 1;
- }
- Action = ShGetSignalAction(Shell, Signal);
- if (Action != NULL) {
- LIST_REMOVE(&(Action->ListEntry));
- ShDestroySignalAction(Action);
- }
- return 0;
- }
- //
- // Loop through all the signal numbers.
- //
- for (ArgumentIndex = StartIndex + 1;
- ArgumentIndex < ArgumentCount;
- ArgumentIndex += 1) {
- Signal = ShGetSignalFromArgument(Arguments[ArgumentIndex]);
- if (Signal == ShellSignalInvalid) {
- PRINT_ERROR("trap: %s: bad trap\n", Arguments[ArgumentIndex]);
- return 1;
- }
- Result = ShSetSignalAction(Shell, Signal, Arguments[StartIndex]);
- if (Result == FALSE) {
- return 1;
- }
- }
- return 0;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- BOOL
- ShSetSignalAction (
- PSHELL Shell,
- SHELL_SIGNAL Signal,
- PSTR ActionString
- )
- /*++
- Routine Description:
- This routine sets the signal action for the given signal.
- Arguments:
- Shell - Supplies a pointer to the shell where the action will be set.
- Signal - Supplies the signal number.
- ActionString - Supplies the action to perform. If this is "-" the signal
- will be ignored. Otherwise the action word will be "eval"ed when the
- signal occurs.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_SIGNAL_ACTION Action;
- UINTN ActionStringSize;
- BOOL Result;
- assert(Signal < ShellSignalCount);
- Action = ShGetSignalAction(Shell, Signal);
- //
- // If the action is "-", delete any existing action and reset the signal
- // to its default action.
- //
- if (strcmp(ActionString, "-") == 0) {
- if (Action != NULL) {
- LIST_REMOVE(&(Action->ListEntry));
- ShDestroySignalAction(Action);
- }
- //
- // Continue to trap SIGINT and SIGQUIT on interactive shells.
- //
- if (((Shell->Options & SHELL_OPTION_INTERACTIVE) != 0) &&
- ((Signal == ShellSignalInterrupt) || (Signal == ShellSignalTrap))) {
- Result = ShSetSignalDisposition(Signal,
- ShellSignalDispositionTrap);
- } else {
- Result = ShSetSignalDisposition(Signal,
- ShellSignalDispositionDefault);
- }
- if (Result == FALSE) {
- return FALSE;
- }
- //
- // Set the signal to the given action word.
- //
- } else {
- ActionStringSize = strlen(ActionString) + 1;
- if (Action != NULL) {
- if (Action->Action != NULL) {
- free(Action->Action);
- }
- Action->Action = SwStringDuplicate(ActionString, ActionStringSize);
- if (Action->Action == NULL) {
- LIST_REMOVE(&(Action->ListEntry));
- ShDestroySignalAction(Action);
- return FALSE;
- }
- Action->ActionSize = ActionStringSize;
- } else {
- Action = ShCreateSignalAction(Signal,
- ActionString,
- ActionStringSize);
- if (Action == NULL) {
- return FALSE;
- }
- INSERT_BEFORE(&(Action->ListEntry), &(Shell->SignalActionList));
- }
- if (Signal != ShellSignalOnExit) {
- Result = ShSetSignalDisposition(Signal, ShellSignalDispositionTrap);
- if (Result == FALSE) {
- return FALSE;
- }
- }
- }
- return TRUE;
- }
- SHELL_SIGNAL
- ShGetSignalFromArgument (
- PSTR Argument
- )
- /*++
- Routine Description:
- This routine returns the signal number for the given signal name.
- Arguments:
- Argument - Supplies the name of the signal.
- Return Value:
- Returns the signal number on success.
- ShellSignalInvalid if the name did not match any signals.
- --*/
- {
- PSTR AfterScan;
- SHELL_SIGNAL Signal;
- Signal = ShGetSignalNumberFromName(Argument);
- if (Signal != ShellSignalInvalid) {
- return Signal;
- }
- Signal = strtoul(Argument, &AfterScan, 10);
- if (AfterScan == Argument) {
- return ShellSignalInvalid;
- }
- if (ShGetSignalNameFromNumber(Signal) == NULL) {
- return ShellSignalInvalid;
- }
- return Signal;
- }
- SHELL_SIGNAL
- ShGetSignalNumberFromName (
- PSTR Name
- )
- /*++
- Routine Description:
- This routine returns the signal number for the given signal name.
- Arguments:
- Name - Supplies the name of the signal.
- Return Value:
- Returns the signal number on success.
- ShellSignalInvalid if the name did not match any signals.
- --*/
- {
- ULONG Index;
- Index = 0;
- while (ShSignalNames[Index].Name != NULL) {
- if (strcasecmp(Name, ShSignalNames[Index].Name) == 0) {
- return ShSignalNames[Index].Number;
- }
- Index += 1;
- }
- return ShellSignalInvalid;
- }
- PSTR
- ShGetSignalNameFromNumber (
- SHELL_SIGNAL Number
- )
- /*++
- Routine Description:
- This routine returns the signal name for the given signal number.
- Arguments:
- Number - Supplies the signal number.
- Return Value:
- Returns the name string on success. The caller does not own this memory,
- and should not modify or free it.
- NULL if the given number is not valid.
- --*/
- {
- ULONG Index;
- Index = 0;
- while (ShSignalNames[Index].Name != NULL) {
- if (ShSignalNames[Index].Number == Number) {
- return ShSignalNames[Index].Name;
- }
- Index += 1;
- }
- return NULL;
- }
- PSHELL_SIGNAL_ACTION
- ShGetSignalAction (
- PSHELL Shell,
- SHELL_SIGNAL SignalNumber
- )
- /*++
- Routine Description:
- This routine attempts to return the signal action structure associated
- with the given signal number.
- Arguments:
- Shell - Supplies a pointer to the shell whose actions should be searched.
- SignalNumber - Supplies the signal number.
- Return Value:
- Returns a pointer to the signal action structure on success.
- NULL if the signal has no action.
- --*/
- {
- PSHELL_SIGNAL_ACTION Action;
- PLIST_ENTRY CurrentEntry;
- CurrentEntry = Shell->SignalActionList.Next;
- while (CurrentEntry != &(Shell->SignalActionList)) {
- Action = LIST_VALUE(CurrentEntry, SHELL_SIGNAL_ACTION, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- if (Action->SignalNumber == SignalNumber) {
- return Action;
- }
- }
- return NULL;
- }
- PSHELL_SIGNAL_ACTION
- ShCreateSignalAction (
- SHELL_SIGNAL SignalNumber,
- PSTR ActionString,
- UINTN ActionStringSize
- )
- /*++
- Routine Description:
- This routine creates a shell action structure.
- Arguments:
- SignalNumber - Supplies the signal number this action is associated with.
- ActionString - Supplies a pointer to the action string to eval when the
- signal occurs. A copy of this string will be made.
- ActionStringSize - Supplies the size of the action string in bytes including
- the null terminator.
- Return Value:
- Returns a pointer to the shiny new action structure on success.
- NULL on failure.
- --*/
- {
- PSHELL_SIGNAL_ACTION Action;
- BOOL Result;
- Result = FALSE;
- Action = malloc(sizeof(SHELL_SIGNAL_ACTION));
- if (Action == NULL) {
- goto CreateSignalActionEnd;
- }
- memset(Action, 0, sizeof(SHELL_SIGNAL_ACTION));
- Action->SignalNumber = SignalNumber;
- Action->Action = SwStringDuplicate(ActionString, ActionStringSize);
- if (Action->Action == NULL) {
- goto CreateSignalActionEnd;
- }
- Action->ActionSize = ActionStringSize;
- Result = TRUE;
- CreateSignalActionEnd:
- if (Result == FALSE) {
- if (Action != NULL) {
- if (Action->Action != NULL) {
- free(Action->Action);
- }
- free(Action);
- Action = NULL;
- }
- }
- return Action;
- }
- VOID
- ShDestroySignalAction (
- PSHELL_SIGNAL_ACTION SignalAction
- )
- /*++
- Routine Description:
- This routine destroys a shell signal action structure. It assumes it has
- already been removed from its list.
- Arguments:
- SignalAction - Supplies a pointer to the action to destroy.
- Return Value:
- None.
- --*/
- {
- if (SignalAction->Action != NULL) {
- free(SignalAction->Action);
- }
- free(SignalAction);
- }
- BOOL
- ShPrintTraps (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine prints all the shell traps.
- Arguments:
- Shell - Supplies a pointer to the shell.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_SIGNAL_ACTION Action;
- PLIST_ENTRY CurrentEntry;
- PSTR QuotedString;
- UINTN QuotedStringSize;
- BOOL Result;
- PSTR SignalName;
- CurrentEntry = Shell->SignalActionList.Next;
- while (CurrentEntry != &(Shell->SignalActionList)) {
- Action = LIST_VALUE(CurrentEntry, SHELL_SIGNAL_ACTION, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- Result = ShStringFormatForReentry(Action->Action,
- Action->ActionSize,
- &QuotedString,
- &QuotedStringSize);
- if (Result == FALSE) {
- return FALSE;
- }
- SignalName = ShGetSignalNameFromNumber(Action->SignalNumber);
- assert(SignalName != NULL);
- printf("trap -- %s %s\n", QuotedString, SignalName);
- free(QuotedString);
- }
- return TRUE;
- }
|