Skip to content

fix: restore terminal foreground control to support interactive commands#1329

Open
mkaaad wants to merge 3 commits into
mvdan:masterfrom
mkaaad:feat/process-group-cancel-test2
Open

fix: restore terminal foreground control to support interactive commands#1329
mkaaad wants to merge 3 commits into
mvdan:masterfrom
mkaaad:feat/process-group-cancel-test2

Conversation

@mkaaad
Copy link
Copy Markdown

@mkaaad mkaaad commented Apr 25, 2026

Problem

Previously, only the process group was modified, but terminal foreground ownership was not transferred to child processes.
This broke interactive programs such as bash, vim, and REPL environments:

  • Terminal signals(SIGINT/Ctrl+C)could not be delivered to child processes
  • Interactive input/output abnormal
  • Interactive shells and editors could not work properly

Solution

Add explicit foreground process group control via TIOCSPGRP ioctl:

  1. Switch the child process group to the terminal foreground before waiting
  2. Restore the original shell process group as foreground after the command exits
  3. Fully deliver terminal keyboard signals to child processes

Key Changes

  • Add setProcessForeground / restoreForeground helper functions
  • Switch foreground process group when starting interactive commands
  • Restore shell foreground control by defer

About ignore SIGTTOU

When a background process calls the TIOCSPGRP ioctl to modify the terminal foreground group:

  1. The kernel sends SIGTTOU to the calling process by default
  2. The default disposition of SIGTTOU is stop the process, which will freeze the program
  3. Temporarily ignoring SIGTTOU avoids suspension during foreground switching
  4. Restore the original signal handler immediately after the syscall to avoid side effects

mkaaad added 2 commits April 22, 2026 22:53
Add setProcessForeground and restoreForeground functions to manage terminal foreground process group. This ensures signals like SIGINT are delivered to the command's process group when executing with timeout.

Also add comments explaining the purpose of these calls.
@mvdan
Copy link
Copy Markdown
Owner

mvdan commented Apr 26, 2026

Could you clarify if this is indeed what shells like Bash do already?

@mvdan
Copy link
Copy Markdown
Owner

mvdan commented Apr 26, 2026

cc @andreynering

Remove trailing whitespace from prepareCommand stub.
@mkaaad
Copy link
Copy Markdown
Author

mkaaad commented Apr 27, 2026

Based on the Bash source code (jobs.c), its terminal management mechanism works as follows:

When spawning a child process group, Bash transfers terminal ownership to the foreground process group: (jobs.c:3905-3918)

/* Save the tty settings before we start the job in the foreground. */
if (foreground)
  {
#if defined (READLINE)
    /* Don't fetch the terminal attributes if we're doing this from a key
       binding or programmable completion. */
    if (RL_ISSTATE(RL_STATE_COMPLETING|RL_STATE_DISPATCHING|RL_STATE_TERMPREPPED) == 0)
#endif
    get_tty_state ();
    save_stty = shell_tty_info;
    jobs[job]->flags &= ~J_ASYNC;	/* no longer async */
    /* Give the terminal to this job. */
    if (IS_JOBCONTROL (job))
      give_terminal_to (jobs[job]->pgrp, 0);

When the foreground child process group exits, Bash restores terminal control back to the shell's own process group. (jobs.c:3250-3253)

    if ((flags & JWAIT_NOTERM) == 0 && running_in_background == 0 &&
        (job == NO_JOB || IS_ASYNC (job) == 0 || IS_FOREGROUND (job)) &&
        (subshell_environment & (SUBSHELL_ASYNC|SUBSHELL_PIPE)) == 0)
      give_terminal_to (shell_pgrp, 0);

Reference source: https://cgit.git.savannah.gnu.org/cgit/bash.git/tree/jobs.c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants