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  }