MiniDevil As beautiful as a shell
Signal handling strategy

4 Signal modes

The shell switches between 4 signal configurations depending on context:

Mode SIGINT SIGQUIT Active while… Setup
interactive interactive_sigint_handler SIG_IGN waiting on readline() setup_interactive_signals()
exec SIG_IGN SIG_IGN parent waiting for child setup_execution_signals()
child SIG_DFL SIG_DFL after fork(), before execve() reset_child_signals()
heredoc heredoc_sigint_handler SIG_IGN heredoc readline() loop setup_heredoc_signals()

Note: interactive uses SA_RESTART but heredoc does not.

The global variable

volatile sig_atomic_t g_signal = 0;
volatile sig_atomic_t g_signal
The single global variable.
Definition: signals.c:17
  • Set inside signal handlers
  • Checked in main_loop() after readline() returns
    If g_signal == SIGINT set exit_status = 130 and clear
  • Checked in read_heredoc_lines() after each readline() call
    If g_signal == SIGINT return immediately (interrupted)

Signal flow for CTRL C

Idle prompt:

  • interactive_sigint_handler() sets g_signal, prints newline and calls rl_replace_line("") + rl_redisplay().
  • SA_RESTART ensures readline() retries transparently.
  • After readline returns the next input, main_loop sees that g_signal == SIGINT and sets exit_status = 130.

During external command:

  • Parent ignores SIGINT (SIG_IGN). Child has SIG_DFL and gets killed.
  • Parent's wait_for_child() sees WIFSIGNALED and returns 128 + WTERMSIG(status)

DUring heredoc: