github.com/rawahars/moby@v24.0.4+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(_ context.Context, ctr *container.Container, options containertypes.StopOptions) (retErr error) { 40 // Deliberately using a local context here, because cancelling the 41 // request should not cancel the stop. 42 // 43 // TODO(thaJeztah): pass context, and use context.WithoutCancel() once available: https://github.com/golang/go/issues/40221 44 ctx := context.Background() 45 46 if !ctr.IsRunning() { 47 return nil 48 } 49 50 var ( 51 stopSignal = ctr.StopSignal() 52 stopTimeout = ctr.StopTimeout() 53 ) 54 if options.Signal != "" { 55 sig, err := signal.ParseSignal(options.Signal) 56 if err != nil { 57 return errdefs.InvalidParameter(err) 58 } 59 stopSignal = sig 60 } 61 if options.Timeout != nil { 62 stopTimeout = *options.Timeout 63 } 64 65 var wait time.Duration 66 if stopTimeout >= 0 { 67 wait = time.Duration(stopTimeout) * time.Second 68 } 69 defer func() { 70 if retErr == nil { 71 daemon.LogContainerEvent(ctr, "stop") 72 } 73 }() 74 75 // 1. Send a stop signal 76 err := daemon.killPossiblyDeadProcess(ctr, stopSignal) 77 if err != nil { 78 wait = 2 * time.Second 79 } 80 81 var subCtx context.Context 82 var cancel context.CancelFunc 83 if stopTimeout >= 0 { 84 subCtx, cancel = context.WithTimeout(ctx, wait) 85 } else { 86 subCtx, cancel = context.WithCancel(ctx) 87 } 88 defer cancel() 89 90 if status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning); status.Err() == nil { 91 // container did exit, so ignore any previous errors and return 92 return nil 93 } 94 95 if err != nil { 96 // the container has still not exited, and the kill function errored, so log the error here: 97 logrus.WithError(err).WithField("container", ctr.ID).Errorf("Error sending stop (signal %d) to container", stopSignal) 98 } 99 if stopTimeout < 0 { 100 // if the client requested that we never kill / wait forever, but container.Wait was still 101 // interrupted (parent context cancelled, for example), we should propagate the signal failure 102 return err 103 } 104 105 logrus.WithField("container", ctr.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal) 106 107 // Stop either failed or container didn't exit, so fallback to kill. 108 if err := daemon.Kill(ctr); err != nil { 109 // got a kill error, but give container 2 more seconds to exit just in case 110 subCtx, cancel := context.WithTimeout(ctx, 2*time.Second) 111 defer cancel() 112 status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning) 113 if status.Err() != nil { 114 logrus.WithError(err).WithField("container", ctr.ID).Errorf("error killing container: %v", status.Err()) 115 return err 116 } 117 // container did exit, so ignore previous errors and continue 118 } 119 120 return nil 121 }