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