daemon/stop.go
03c07617
 package daemon
 
f7d4b4fe
 import (
cfdf84d5
 	"context"
4f2a5ba3
 	"time"
 
49211715
 	containerpkg "github.com/docker/docker/container"
ebcb7d6b
 	"github.com/pkg/errors"
1009e6a4
 	"github.com/sirupsen/logrus"
f7d4b4fe
 )
c79b9bab
 
abd72d40
 // ContainerStop looks for the given container and terminates it,
 // waiting the given number of seconds before forcefully killing the
 // container. If a negative number of seconds is given, ContainerStop
 // will wait for a graceful termination. An error is returned if the
 // container is not found, is already stopped, or if there is a
 // problem stopping the container.
cc703784
 func (daemon *Daemon) ContainerStop(name string, seconds *int) error {
d7d512bb
 	container, err := daemon.GetContainer(name)
d25a6537
 	if err != nil {
c79b9bab
 		return err
03c07617
 	}
d25a6537
 	if !container.IsRunning() {
ebcb7d6b
 		return containerNotModifiedError{running: false}
d25a6537
 	}
cc703784
 	if seconds == nil {
 		stopTimeout := container.StopTimeout()
 		seconds = &stopTimeout
 	}
 	if err := daemon.containerStop(container, *seconds); err != nil {
ebcb7d6b
 		return errors.Wrapf(systemError{err}, "cannot stop container: %s", name)
d25a6537
 	}
c79b9bab
 	return nil
03c07617
 }
4f2a5ba3
 
 // containerStop halts a container by sending a stop signal, waiting for the given
 // duration in seconds, and then calling SIGKILL and waiting for the
 // process to exit. If a negative duration is given, Stop will wait
 // for the initial signal forever. If the container is not running Stop returns
 // immediately.
49211715
 func (daemon *Daemon) containerStop(container *containerpkg.Container, seconds int) error {
4f2a5ba3
 	if !container.IsRunning() {
 		return nil
 	}
 
b6c7becb
 	daemon.stopHealthchecks(container)
 
6716a3a1
 	stopSignal := container.StopSignal()
 	// 1. Send a stop signal
 	if err := daemon.killPossiblyDeadProcess(container, stopSignal); err != nil {
740e26f3
 		// While normally we might "return err" here we're not going to
 		// because if we can't stop the container by this point then
6fb05778
 		// it's probably because it's already stopped. Meaning, between
740e26f3
 		// the time of the IsRunning() call above and now it stopped.
 		// Also, since the err return will be environment specific we can't
 		// look for any particular (common) error that would indicate
 		// that the process is already dead vs something else going wrong.
 		// So, instead we'll give it up to 2 more seconds to complete and if
 		// by that time the container is still running, then the error
 		// we got is probably valid and so we force kill it.
cfdf84d5
 		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
 		defer cancel()
 
49211715
 		if status := <-container.Wait(ctx, containerpkg.WaitConditionNotRunning); status.Err() != nil {
740e26f3
 			logrus.Infof("Container failed to stop after sending signal %d to the process, force killing", stopSignal)
 			if err := daemon.killPossiblyDeadProcess(container, 9); err != nil {
 				return err
 			}
4f2a5ba3
 		}
 	}
 
 	// 2. Wait for the process to exit on its own
cfdf84d5
 	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(seconds)*time.Second)
 	defer cancel()
 
49211715
 	if status := <-container.Wait(ctx, containerpkg.WaitConditionNotRunning); status.Err() != nil {
6716a3a1
 		logrus.Infof("Container %v failed to exit within %d seconds of signal %d - using the force", container.ID, seconds, stopSignal)
4f2a5ba3
 		// 3. If it doesn't, then send SIGKILL
 		if err := daemon.Kill(container); err != nil {
cfdf84d5
 			// Wait without a timeout, ignore result.
94cefa21
 			<-container.Wait(context.Background(), containerpkg.WaitConditionNotRunning)
4f2a5ba3
 			logrus.Warn(err) // Don't return error because we only care that container is stopped, not what function stopped it
 		}
 	}
 
ca5ede2d
 	daemon.LogContainerEvent(container, "stop")
4f2a5ba3
 	return nil
 }