stop.go 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. package daemon
  2. import (
  3. "fmt"
  4. "net/http"
  5. "time"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/docker/api/errors"
  8. "github.com/docker/docker/container"
  9. )
  10. // ContainerStop looks for the given container and terminates it,
  11. // waiting the given number of seconds before forcefully killing the
  12. // container. If a negative number of seconds is given, ContainerStop
  13. // will wait for a graceful termination. An error is returned if the
  14. // container is not found, is already stopped, or if there is a
  15. // problem stopping the container.
  16. func (daemon *Daemon) ContainerStop(name string, seconds *int) error {
  17. container, err := daemon.GetContainer(name)
  18. if err != nil {
  19. return err
  20. }
  21. if !container.IsRunning() {
  22. err := fmt.Errorf("Container %s is already stopped", name)
  23. return errors.NewErrorWithStatusCode(err, http.StatusNotModified)
  24. }
  25. if seconds == nil {
  26. stopTimeout := container.StopTimeout()
  27. seconds = &stopTimeout
  28. }
  29. if err := daemon.containerStop(container, *seconds); err != nil {
  30. return fmt.Errorf("Cannot stop container %s: %v", name, err)
  31. }
  32. return nil
  33. }
  34. // containerStop halts a container by sending a stop signal, waiting for the given
  35. // duration in seconds, and then calling SIGKILL and waiting for the
  36. // process to exit. If a negative duration is given, Stop will wait
  37. // for the initial signal forever. If the container is not running Stop returns
  38. // immediately.
  39. func (daemon *Daemon) containerStop(container *container.Container, seconds int) error {
  40. if !container.IsRunning() {
  41. return nil
  42. }
  43. daemon.stopHealthchecks(container)
  44. stopSignal := container.StopSignal()
  45. // 1. Send a stop signal
  46. if err := daemon.killPossiblyDeadProcess(container, stopSignal); err != nil {
  47. // While normally we might "return err" here we're not going to
  48. // because if we can't stop the container by this point then
  49. // it's probably because it's already stopped. Meaning, between
  50. // the time of the IsRunning() call above and now it stopped.
  51. // Also, since the err return will be environment specific we can't
  52. // look for any particular (common) error that would indicate
  53. // that the process is already dead vs something else going wrong.
  54. // So, instead we'll give it up to 2 more seconds to complete and if
  55. // by that time the container is still running, then the error
  56. // we got is probably valid and so we force kill it.
  57. if _, err := container.WaitStop(2 * time.Second); err != nil {
  58. logrus.Infof("Container failed to stop after sending signal %d to the process, force killing", stopSignal)
  59. if err := daemon.killPossiblyDeadProcess(container, 9); err != nil {
  60. return err
  61. }
  62. }
  63. }
  64. // 2. Wait for the process to exit on its own
  65. if _, err := container.WaitStop(time.Duration(seconds) * time.Second); err != nil {
  66. logrus.Infof("Container %v failed to exit within %d seconds of signal %d - using the force", container.ID, seconds, stopSignal)
  67. // 3. If it doesn't, then send SIGKILL
  68. if err := daemon.Kill(container); err != nil {
  69. container.WaitStop(-1 * time.Second)
  70. logrus.Warn(err) // Don't return error because we only care that container is stopped, not what function stopped it
  71. }
  72. }
  73. daemon.LogContainerEvent(container, "stop")
  74. return nil
  75. }