stop.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package daemon // import "github.com/docker/docker/daemon"
  2. import (
  3. "context"
  4. "time"
  5. "github.com/containerd/log"
  6. containertypes "github.com/docker/docker/api/types/container"
  7. "github.com/docker/docker/api/types/events"
  8. "github.com/docker/docker/container"
  9. "github.com/docker/docker/errdefs"
  10. "github.com/docker/docker/internal/compatcontext"
  11. "github.com/moby/sys/signal"
  12. "github.com/pkg/errors"
  13. )
  14. // ContainerStop looks for the given container and stops it.
  15. // In case the container fails to stop gracefully within a time duration
  16. // specified by the timeout argument, in seconds, it is forcefully
  17. // terminated (killed).
  18. //
  19. // If the timeout is nil, the container's StopTimeout value is used, if set,
  20. // otherwise the engine default. A negative timeout value can be specified,
  21. // meaning no timeout, i.e. no forceful termination is performed.
  22. func (daemon *Daemon) ContainerStop(ctx context.Context, name string, options containertypes.StopOptions) error {
  23. ctr, err := daemon.GetContainer(name)
  24. if err != nil {
  25. return err
  26. }
  27. if !ctr.IsRunning() {
  28. // This is not an actual error, but produces a 304 "not modified"
  29. // when returned through the API to indicates the container is
  30. // already in the desired state. It's implemented as an error
  31. // to make the code calling this function terminate early (as
  32. // no further processing is needed).
  33. return errdefs.NotModified(errors.New("container is already stopped"))
  34. }
  35. err = daemon.containerStop(ctx, ctr, options)
  36. if err != nil {
  37. return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name))
  38. }
  39. return nil
  40. }
  41. // containerStop sends a stop signal, waits, sends a kill signal. It uses
  42. // a [context.WithoutCancel], so cancelling the context does not cancel
  43. // the request to stop the container.
  44. func (daemon *Daemon) containerStop(ctx context.Context, ctr *container.Container, options containertypes.StopOptions) (retErr error) {
  45. // Cancelling the request should not cancel the stop.
  46. ctx = compatcontext.WithoutCancel(ctx)
  47. if !ctr.IsRunning() {
  48. return nil
  49. }
  50. var (
  51. stopSignal = ctr.StopSignal()
  52. stopTimeout = ctr.StopTimeout()
  53. )
  54. if options.Signal != "" {
  55. sig, err := signal.ParseSignal(options.Signal)
  56. if err != nil {
  57. return errdefs.InvalidParameter(err)
  58. }
  59. stopSignal = sig
  60. }
  61. if options.Timeout != nil {
  62. stopTimeout = *options.Timeout
  63. }
  64. var wait time.Duration
  65. if stopTimeout >= 0 {
  66. wait = time.Duration(stopTimeout) * time.Second
  67. }
  68. defer func() {
  69. if retErr == nil {
  70. daemon.LogContainerEvent(ctr, events.ActionStop)
  71. }
  72. }()
  73. // 1. Send a stop signal
  74. err := daemon.killPossiblyDeadProcess(ctr, stopSignal)
  75. if err != nil {
  76. wait = 2 * time.Second
  77. }
  78. var subCtx context.Context
  79. var cancel context.CancelFunc
  80. if stopTimeout >= 0 {
  81. subCtx, cancel = context.WithTimeout(ctx, wait)
  82. } else {
  83. subCtx, cancel = context.WithCancel(ctx)
  84. }
  85. defer cancel()
  86. if status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning); status.Err() == nil {
  87. // container did exit, so ignore any previous errors and return
  88. return nil
  89. }
  90. if err != nil {
  91. // the container has still not exited, and the kill function errored, so log the error here:
  92. log.G(ctx).WithError(err).WithField("container", ctr.ID).Errorf("Error sending stop (signal %d) to container", stopSignal)
  93. }
  94. if stopTimeout < 0 {
  95. // if the client requested that we never kill / wait forever, but container.Wait was still
  96. // interrupted (parent context cancelled, for example), we should propagate the signal failure
  97. return err
  98. }
  99. log.G(ctx).WithField("container", ctr.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal)
  100. // Stop either failed or container didn't exit, so fallback to kill.
  101. if err := daemon.Kill(ctr); err != nil {
  102. // got a kill error, but give container 2 more seconds to exit just in case
  103. subCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
  104. defer cancel()
  105. status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning)
  106. if status.Err() != nil {
  107. log.G(ctx).WithError(err).WithField("container", ctr.ID).Errorf("error killing container: %v", status.Err())
  108. return err
  109. }
  110. // container did exit, so ignore previous errors and continue
  111. }
  112. return nil
  113. }