Skip to content

Conversation

@thaJeztah
Copy link
Member

@thaJeztah thaJeztah commented May 9, 2025

When attaching to a container, hijack puts the terminal in raw mode, and local echo is disabled. In normal cases, the terminal is restored once the container detaches;

func (h *hijackedIOStreamer) stream(ctx context.Context) error {
restoreInput, err := h.setupInput()
if err != nil {
return fmt.Errorf("unable to setup input stream: %s", err)
}

However, when the CLI is forced to exit (after 3 signals), we os.Exit(1), which causes defers to not be executed, and because of this, the terminal not being restored.

For example; start a container that's attached;

docker run -it --rm --sig-proxy=false alpine sleep 20

In another terminal send a SIGINT 3 times to force terminate;

kill -sINT $(pgrep -af docker\ run)
kill -sINT $(pgrep -af docker\ run)
kill -sINT $(pgrep -af docker\ run)

The first terminal shows that the docker cli was terminated;

got 3 SIGTERM/SIGINTs, forcefully exiting

However, the terminal was not restored, so local echo is disabled, and typing any command in the terminal does not show output (a manual stty echo is needed to restore).

With this patch, the terminal is restored before we forcefully exit the docker CLI. Restoring is a no-op if there's no previous state, so we can unconditionally execute this.

- Human readable description for the release notes

Make sure the terminal state is restored if the CLI is forcefully terminated.

- A picture of a cute animal (not mandatory but encouraged)

When attaching to a container, hijack puts the terminal in raw mode,
and local echo is disabled. In normal cases, the terminal is restored
once the container detaches;
https://github.com/docker/cli/blob/6f856263c2f03a8dc19cef3d6cb48d56a3fab5cc/cli/command/container/hijack.go#L40-L44

However, when the CLI is forced to exit (after 3 signals), we `os.Exit(1)`,
which causes defers to not be executed, and because of this, the terminal
not being restored.

For example; start a container that's attached;

    docker run -it --rm --sig-proxy=false alpine sleep 20

In another terminal send a SIGINT 3 times to force terminate;

    kill -sINT $(pgrep -af docker\ run)
    kill -sINT $(pgrep -af docker\ run)
    kill -sINT $(pgrep -af docker\ run)

The first terminal shows that the docker cli was terminated;

    got 3 SIGTERM/SIGINTs, forcefully exiting

However, the terminal was not restored, so local echo is disabled, and
typing any command in the terminal does not show output (a manual `stty echo`
is needed to restore).

With this patch, the terminal is restored before we forcefully exit the
docker CLI. Restoring is a no-op if there's no previous state, so we
can unconditionally execute this.

Signed-off-by: Sebastiaan van Stijn <[email protected]>
@thaJeztah thaJeztah added this to the 28.1.2 milestone May 9, 2025
@thaJeztah thaJeztah requested a review from Copilot May 9, 2025 13:29
@thaJeztah thaJeztah self-assigned this May 9, 2025
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR ensures that the terminal is restored to its previous state when the Docker CLI is forcefully terminated after receiving three termination signals.

  • Added a call to restore the terminal state in both the plugin termination logic and the force-exit function.
  • Changed the parameter type from io.Writer to command.Streams in forceExitAfter3TerminationSignals and removed an unused import.

@codecov-commenter
Copy link

codecov-commenter commented May 9, 2025

Codecov Report

Attention: Patch coverage is 0% with 13 lines in your changes missing coverage. Please review.

Project coverage is 59.05%. Comparing base (6f85626) to head (a17b9c5).

❌ Your patch status has failed because the patch coverage (0.00%) is below the target coverage (50.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6058      +/-   ##
==========================================
- Coverage   59.07%   59.05%   -0.02%     
==========================================
  Files         358      358              
  Lines       30013    30023      +10     
==========================================
  Hits        17731    17731              
- Misses      11301    11311      +10     
  Partials      981      981              
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@thaJeztah thaJeztah requested a review from Benehiko May 9, 2025 15:31
@thaJeztah thaJeztah merged commit dd4536e into docker:master May 12, 2025
99 checks passed
@thaJeztah thaJeztah deleted the restore_terminal branch May 12, 2025 08:35
@vvoland vvoland added the kind/bugfix PR's that fix bugs label May 15, 2025
@thaJeztah thaJeztah modified the milestones: 28.1.2, 28.2.0 May 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants