github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/daemon/kill.go (about)

     1  package daemon
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"runtime"
     7  	"strings"
     8  	"syscall"
     9  	"time"
    10  
    11  	"github.com/Sirupsen/logrus"
    12  	containerpkg "github.com/docker/docker/container"
    13  	"github.com/docker/docker/pkg/signal"
    14  )
    15  
    16  type errNoSuchProcess struct {
    17  	pid    int
    18  	signal int
    19  }
    20  
    21  func (e errNoSuchProcess) Error() string {
    22  	return fmt.Sprintf("Cannot kill process (pid=%d) with signal %d: no such process.", e.pid, e.signal)
    23  }
    24  
    25  // isErrNoSuchProcess returns true if the error
    26  // is an instance of errNoSuchProcess.
    27  func isErrNoSuchProcess(err error) bool {
    28  	_, ok := err.(errNoSuchProcess)
    29  	return ok
    30  }
    31  
    32  // ContainerKill sends signal to the container
    33  // If no signal is given (sig 0), then Kill with SIGKILL and wait
    34  // for the container to exit.
    35  // If a signal is given, then just send it to the container and return.
    36  func (daemon *Daemon) ContainerKill(name string, sig uint64) error {
    37  	container, err := daemon.GetContainer(name)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	if sig != 0 && !signal.ValidSignalForPlatform(syscall.Signal(sig)) {
    43  		return fmt.Errorf("The %s daemon does not support signal %d", runtime.GOOS, sig)
    44  	}
    45  
    46  	// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
    47  	if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
    48  		return daemon.Kill(container)
    49  	}
    50  	return daemon.killWithSignal(container, int(sig))
    51  }
    52  
    53  // killWithSignal sends the container the given signal. This wrapper for the
    54  // host specific kill command prepares the container before attempting
    55  // to send the signal. An error is returned if the container is paused
    56  // or not running, or if there is a problem returned from the
    57  // underlying kill command.
    58  func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int) error {
    59  	logrus.Debugf("Sending kill signal %d to container %s", sig, container.ID)
    60  	container.Lock()
    61  	defer container.Unlock()
    62  
    63  	// We could unpause the container for them rather than returning this error
    64  	if container.Paused {
    65  		return fmt.Errorf("Container %s is paused. Unpause the container before stopping or killing", container.ID)
    66  	}
    67  
    68  	if !container.Running {
    69  		return errNotRunning{container.ID}
    70  	}
    71  
    72  	if container.Config.StopSignal != "" && syscall.Signal(sig) != syscall.SIGKILL {
    73  		containerStopSignal, err := signal.ParseSignal(container.Config.StopSignal)
    74  		if err != nil {
    75  			return err
    76  		}
    77  		if containerStopSignal == syscall.Signal(sig) {
    78  			container.ExitOnNext()
    79  		}
    80  	} else {
    81  		container.ExitOnNext()
    82  	}
    83  
    84  	if !daemon.IsShuttingDown() {
    85  		container.HasBeenManuallyStopped = true
    86  	}
    87  
    88  	// if the container is currently restarting we do not need to send the signal
    89  	// to the process. Telling the monitor that it should exit on its next event
    90  	// loop is enough
    91  	if container.Restarting {
    92  		return nil
    93  	}
    94  
    95  	if err := daemon.kill(container, sig); err != nil {
    96  		err = fmt.Errorf("Cannot kill container %s: %s", container.ID, err)
    97  		// if container or process not exists, ignore the error
    98  		if strings.Contains(err.Error(), "container not found") ||
    99  			strings.Contains(err.Error(), "no such process") {
   100  			logrus.Warnf("container kill failed because of 'container not found' or 'no such process': %s", err.Error())
   101  		} else {
   102  			return err
   103  		}
   104  	}
   105  
   106  	attributes := map[string]string{
   107  		"signal": fmt.Sprintf("%d", sig),
   108  	}
   109  	daemon.LogContainerEventWithAttributes(container, "kill", attributes)
   110  	return nil
   111  }
   112  
   113  // Kill forcefully terminates a container.
   114  func (daemon *Daemon) Kill(container *containerpkg.Container) error {
   115  	if !container.IsRunning() {
   116  		return errNotRunning{container.ID}
   117  	}
   118  
   119  	// 1. Send SIGKILL
   120  	if err := daemon.killPossiblyDeadProcess(container, int(syscall.SIGKILL)); err != nil {
   121  		// While normally we might "return err" here we're not going to
   122  		// because if we can't stop the container by this point then
   123  		// it's probably because it's already stopped. Meaning, between
   124  		// the time of the IsRunning() call above and now it stopped.
   125  		// Also, since the err return will be environment specific we can't
   126  		// look for any particular (common) error that would indicate
   127  		// that the process is already dead vs something else going wrong.
   128  		// So, instead we'll give it up to 2 more seconds to complete and if
   129  		// by that time the container is still running, then the error
   130  		// we got is probably valid and so we return it to the caller.
   131  		if isErrNoSuchProcess(err) {
   132  			return nil
   133  		}
   134  
   135  		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
   136  		defer cancel()
   137  
   138  		if status := <-container.Wait(ctx, containerpkg.WaitConditionNotRunning); status.Err() != nil {
   139  			return err
   140  		}
   141  	}
   142  
   143  	// 2. Wait for the process to die, in last resort, try to kill the process directly
   144  	if err := killProcessDirectly(container); err != nil {
   145  		if isErrNoSuchProcess(err) {
   146  			return nil
   147  		}
   148  		return err
   149  	}
   150  
   151  	// Wait for exit with no timeout.
   152  	// Ignore returned status.
   153  	_ = <-container.Wait(context.Background(), containerpkg.WaitConditionNotRunning)
   154  
   155  	return nil
   156  }
   157  
   158  // killPossibleDeadProcess is a wrapper around killSig() suppressing "no such process" error.
   159  func (daemon *Daemon) killPossiblyDeadProcess(container *containerpkg.Container, sig int) error {
   160  	err := daemon.killWithSignal(container, sig)
   161  	if err == syscall.ESRCH {
   162  		e := errNoSuchProcess{container.GetPID(), sig}
   163  		logrus.Debug(e)
   164  		return e
   165  	}
   166  	return err
   167  }
   168  
   169  func (daemon *Daemon) kill(c *containerpkg.Container, sig int) error {
   170  	return daemon.containerd.Signal(c.ID, sig)
   171  }