Signal Overview
Minishell handles Unix signals to provide a user experience consistent with standard shells. The primary signals handled are SIGINT (Ctrl+C) and SIGQUIT (Ctrl+\).Signal Setup
Signals are configured during shell initialization:signals.c:25
This function is called once in
main() during shell startup, before entering the read-eval-print loop.Signal Behavior
SIGINT (Ctrl+C)
Caught and handled with custom handler to interrupt current line
SIGQUIT (Ctrl+\)
Ignored in the parent shell process
SIGINT Handler
Thehandle_sigint() function provides graceful interrupt handling:
signals.c:15
Handler Breakdown
Readline Integration
Minishell uses GNU Readline for input, which requires special handling with signals.Readline Functions
rl_on_new_line()
rl_on_new_line()
Informs the readline library that the cursor has moved to a new line. This is essential after printing the newline in the signal handler to keep readline’s internal state consistent.When called: After printing output that moves the cursor
rl_replace_line()
rl_replace_line()
Replaces the current line buffer with a new string. The second argument controls the undo list behavior.Signature:
void rl_replace_line(const char *text, int clear_undo)Usage in Minishell: rl_replace_line("", 0) clears the input bufferrl_redisplay()
rl_redisplay()
Redraws the current line, including the prompt and any input. Called after modifying the line buffer to update the display.Effect: User sees a clean, new prompt without the interrupted input
Global Status Variable
The exit status is stored in a global variable accessible throughout the codebase:main.c:15
Status Values
| Value | Meaning | Set By |
|---|---|---|
| 0 | Success | Successful command execution |
| 1-255 | Command exit code | Child process exit |
| 127 | Command not found | execve() failure |
| 130 | Interrupted by SIGINT | Signal handler |
| 2 | Syntax error | Parser validation |
Status in Variable Expansion
The special variable$? expands to the current status:
env_parsed2.c:57
Exit Status Extraction
After waiting for child processes, the raw status is converted to a standard exit code:main.c:118
The
waitpid() function stores the exit code in the high byte of the status integer. The bit shift >> 8 extracts the actual exit code (0-255).Why the Check?
- 2: Syntax errors set by the parser
- 1: Special error conditions
Signal Behavior in Child Processes
Child processes inherit signal handlers from the parent. However, for proper job control, child processes should have default signal behavior.Current Implementation
Minishell’s current implementation allows children to inherit the parent’s signal handlers. In a more complete implementation, child processes would reset signals:With
SIG_IGN set for SIGQUIT in the parent, children inherit this behavior, meaning Ctrl+\ won’t generate a core dump in child processes as it would in bash.Interactive vs Non-Interactive
The current signal setup assumes interactive mode. In a complete shell implementation, non-interactive mode (script execution) would handle signals differently:Interactive Mode (Current)
- SIGINT: Display new prompt
- SIGQUIT: Ignored
Non-Interactive Mode (Hypothetical)
- SIGINT: Terminate shell
- SIGQUIT: Terminate shell
Signal Safety
Thehandle_sigint() function calls several functions that are not technically async-signal-safe:
While this works in practice for readline-based applications, strictly conforming signal handlers should only call async-signal-safe functions. Readline provides its own signal handling facilities that are designed to work with signals.
Exit Status in Pipelines
In pipelines, the exit status is taken from the last command:ft_execute_commands.c:92
waitpid() overwrites g_status, so the final value reflects the last command in the pipeline: