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