github.com/akerouanton/docker@v1.11.0-rc3/daemon/kill.go (about)

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