fix(cmd/docker): prevent race between force-exit goroutine and plugin wait

When a plugin ignores context cancellation and the user sends 3 SIGINTs,
the CLI kills the plugin with SIGKILL. Previously the signal goroutine
called os.Exit(1) directly; a race existed where plugincmd.Run() could
return first (plugin was SIGKILL'd, so ws.ExitStatus() = -1) and the
main goroutine would call os.Exit(-1) = exit code 255 before the
goroutine reached os.Exit(1).

Fix by moving exit-code ownership to the main goroutine. The signal
goroutine closes forceExitCh before calling Kill(), guaranteeing the
channel is closed before plugincmd.Run() returns (the plugin can only
die after Kill() delivers SIGKILL; Run() only returns after the process
is reaped). The main goroutine checks forceExitCh after Run() returns
and performs the print + os.Exit(1) itself.

Also return from the signal goroutine after the force-kill to prevent
further loop iterations from calling close(forceExitCh) a second time
(which would panic), in case additional signals arrive while the kill
is in flight.

Fixes a flaky failure in TestPluginSocketCommunication/detached/
the_main_CLI_exits_after_3_signals where exit code 255 was observed
instead of 1 on loaded CI runners (RC Docker on Alpine).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Michael Zampani <michael.zampani@docker.com>
1 file changed
tree: ee81d059e4c0c69545709894645bbedfc605f41c
  1. .github/
  2. cli/
  3. cli-plugins/
  4. cmd/
  5. contrib/
  6. dockerfiles/
  7. docs/
  8. e2e/
  9. experimental/
  10. internal/
  11. man/
  12. opts/
  13. pkg/
  14. scripts/
  15. templates/
  16. vendor/
  17. .codecov.yml
  18. .dockerignore
  19. .gitattributes
  20. .gitignore
  21. .golangci.yml
  22. .mailmap
  23. AUTHORS
  24. CONTRIBUTING.md
  25. docker-bake.hcl
  26. docker.Makefile
  27. Dockerfile
  28. LICENSE
  29. MAINTAINERS
  30. Makefile
  31. NOTICE
  32. README.md
  33. SECURITY.md
  34. TESTING.md
  35. vendor.mod
  36. vendor.sum
  37. VERSION
README.md

Docker CLI

PkgGoDev Build Status Test Status Go Report Card OpenSSF Scorecard Codecov

About

This repository is the home of the Docker CLI.

Development

docker/cli is developed using Docker.

Build CLI from source:

docker buildx bake

Build binaries for all supported platforms:

docker buildx bake cross

Build for a specific platform:

docker buildx bake --set binary.platform=linux/arm64 

Build dynamic binary for glibc or musl:

USE_GLIBC=1 docker buildx bake dynbinary 

Run all linting:

docker buildx bake lint shellcheck

Run test:

docker buildx bake test

List all the available targets:

make help

In-container development environment

Start an interactive development environment:

make -f docker.Makefile shell

Legal

Brought to you courtesy of our legal counsel. For more context, see the NOTICE document in this repo.

Use and transfer of Docker may be subject to certain restrictions by the United States and other governments.

It is your responsibility to ensure that your use and/or transfer does not violate applicable laws.

For more information, see https://www.bis.doc.gov

Licensing

docker/cli is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.