123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- package daemon // import "github.com/docker/docker/daemon"
- import (
- "context"
- "time"
- containerpkg "github.com/docker/docker/container"
- "github.com/docker/docker/errdefs"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- )
- // ContainerStop looks for the given container and stops it.
- // In case the container fails to stop gracefully within a time duration
- // specified by the timeout argument, in seconds, it is forcefully
- // terminated (killed).
- //
- // If the timeout is nil, the container's StopTimeout value is used, if set,
- // otherwise the engine default. A negative timeout value can be specified,
- // meaning no timeout, i.e. no forceful termination is performed.
- func (daemon *Daemon) ContainerStop(name string, timeout *int) error {
- container, err := daemon.GetContainer(name)
- if err != nil {
- return err
- }
- if !container.IsRunning() {
- return containerNotModifiedError{running: false}
- }
- if timeout == nil {
- stopTimeout := container.StopTimeout()
- timeout = &stopTimeout
- }
- if err := daemon.containerStop(container, *timeout); err != nil {
- return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name))
- }
- return nil
- }
- // containerStop sends a stop signal, waits, sends a kill signal.
- func (daemon *Daemon) containerStop(container *containerpkg.Container, seconds int) error {
- // TODO propagate a context down to this function
- ctx := context.TODO()
- if !container.IsRunning() {
- return nil
- }
- var wait time.Duration
- if seconds >= 0 {
- wait = time.Duration(seconds) * time.Second
- }
- success := func() error {
- daemon.LogContainerEvent(container, "stop")
- return nil
- }
- stopSignal := container.StopSignal()
- // 1. Send a stop signal
- err := daemon.killPossiblyDeadProcess(container, stopSignal)
- if err != nil {
- wait = 2 * time.Second
- }
- var subCtx context.Context
- var cancel context.CancelFunc
- if seconds >= 0 {
- subCtx, cancel = context.WithTimeout(ctx, wait)
- } else {
- subCtx, cancel = context.WithCancel(ctx)
- }
- defer cancel()
- if status := <-container.Wait(subCtx, containerpkg.WaitConditionNotRunning); status.Err() == nil {
- // container did exit, so ignore any previous errors and return
- return success()
- }
- if err != nil {
- // the container has still not exited, and the kill function errored, so log the error here:
- logrus.WithError(err).WithField("container", container.ID).Errorf("Error sending stop (signal %d) to container", stopSignal)
- }
- if seconds < 0 {
- // if the client requested that we never kill / wait forever, but container.Wait was still
- // interrupted (parent context cancelled, for example), we should propagate the signal failure
- return err
- }
- logrus.WithField("container", container.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal)
- // Stop either failed or container didnt exit, so fallback to kill.
- if err := daemon.Kill(container); err != nil {
- // got a kill error, but give container 2 more seconds to exit just in case
- subCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
- defer cancel()
- if status := <-container.Wait(subCtx, containerpkg.WaitConditionNotRunning); status.Err() == nil {
- // container did exit, so ignore error and return
- return success()
- }
- logrus.WithError(err).WithField("container", container.ID).Error("Error killing the container")
- return err
- }
- return success()
- }
|