github.com/rumpl/bof@v23.0.0-rc.2+incompatible/daemon/stop.go (about) 1 package daemon // import "github.com/docker/docker/daemon" 2 3 import ( 4 "context" 5 "time" 6 7 containertypes "github.com/docker/docker/api/types/container" 8 "github.com/docker/docker/container" 9 "github.com/docker/docker/errdefs" 10 "github.com/moby/sys/signal" 11 "github.com/pkg/errors" 12 "github.com/sirupsen/logrus" 13 ) 14 15 // ContainerStop looks for the given container and stops it. 16 // In case the container fails to stop gracefully within a time duration 17 // specified by the timeout argument, in seconds, it is forcefully 18 // terminated (killed). 19 // 20 // If the timeout is nil, the container's StopTimeout value is used, if set, 21 // otherwise the engine default. A negative timeout value can be specified, 22 // meaning no timeout, i.e. no forceful termination is performed. 23 func (daemon *Daemon) ContainerStop(ctx context.Context, name string, options containertypes.StopOptions) error { 24 ctr, err := daemon.GetContainer(name) 25 if err != nil { 26 return err 27 } 28 if !ctr.IsRunning() { 29 return containerNotModifiedError{} 30 } 31 err = daemon.containerStop(ctx, ctr, options) 32 if err != nil { 33 return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name)) 34 } 35 return nil 36 } 37 38 // containerStop sends a stop signal, waits, sends a kill signal. 39 func (daemon *Daemon) containerStop(ctx context.Context, ctr *container.Container, options containertypes.StopOptions) (retErr error) { 40 if !ctr.IsRunning() { 41 return nil 42 } 43 44 var ( 45 stopSignal = ctr.StopSignal() 46 stopTimeout = ctr.StopTimeout() 47 ) 48 if options.Signal != "" { 49 sig, err := signal.ParseSignal(options.Signal) 50 if err != nil { 51 return errdefs.InvalidParameter(err) 52 } 53 stopSignal = sig 54 } 55 if options.Timeout != nil { 56 stopTimeout = *options.Timeout 57 } 58 59 var wait time.Duration 60 if stopTimeout >= 0 { 61 wait = time.Duration(stopTimeout) * time.Second 62 } 63 defer func() { 64 if retErr == nil { 65 daemon.LogContainerEvent(ctr, "stop") 66 } 67 }() 68 69 // 1. Send a stop signal 70 err := daemon.killPossiblyDeadProcess(ctr, stopSignal) 71 if err != nil { 72 wait = 2 * time.Second 73 } 74 75 var subCtx context.Context 76 var cancel context.CancelFunc 77 if stopTimeout >= 0 { 78 subCtx, cancel = context.WithTimeout(ctx, wait) 79 } else { 80 subCtx, cancel = context.WithCancel(ctx) 81 } 82 defer cancel() 83 84 if status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning); status.Err() == nil { 85 // container did exit, so ignore any previous errors and return 86 return nil 87 } 88 89 if err != nil { 90 // the container has still not exited, and the kill function errored, so log the error here: 91 logrus.WithError(err).WithField("container", ctr.ID).Errorf("Error sending stop (signal %d) to container", stopSignal) 92 } 93 if stopTimeout < 0 { 94 // if the client requested that we never kill / wait forever, but container.Wait was still 95 // interrupted (parent context cancelled, for example), we should propagate the signal failure 96 return err 97 } 98 99 logrus.WithField("container", ctr.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal) 100 101 // Stop either failed or container didn't exit, so fallback to kill. 102 if err := daemon.Kill(ctr); err != nil { 103 // got a kill error, but give container 2 more seconds to exit just in case 104 subCtx, cancel := context.WithTimeout(ctx, 2*time.Second) 105 defer cancel() 106 status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning) 107 if status.Err() != nil { 108 logrus.WithError(err).WithField("container", ctr.ID).Errorf("error killing container: %v", status.Err()) 109 return err 110 } 111 // container did exit, so ignore previous errors and continue 112 } 113 114 return nil 115 }