2014-07-31 20:24:54 +00:00
|
|
|
package daemon
|
|
|
|
|
2015-10-12 16:34:03 -07:00
|
|
|
import (
|
2017-03-30 13:52:40 -07:00
|
|
|
"context"
|
2015-10-12 16:34:03 -07:00
|
|
|
"fmt"
|
|
|
|
"runtime"
|
2016-04-07 22:05:29 +08:00
|
|
|
"strings"
|
2015-10-12 16:34:03 -07:00
|
|
|
"syscall"
|
2015-11-02 18:25:26 -05:00
|
|
|
"time"
|
2015-10-12 16:34:03 -07:00
|
|
|
|
2017-03-30 20:01:41 -07:00
|
|
|
containerpkg "github.com/docker/docker/container"
|
2015-10-12 16:34:03 -07:00
|
|
|
"github.com/docker/docker/pkg/signal"
|
2017-07-19 10:20:13 -04:00
|
|
|
"github.com/pkg/errors"
|
2017-07-26 14:42:13 -07:00
|
|
|
"github.com/sirupsen/logrus"
|
2015-10-12 16:34:03 -07:00
|
|
|
)
|
2014-07-31 20:24:54 +00:00
|
|
|
|
2016-03-04 15:41:06 -05:00
|
|
|
type errNoSuchProcess struct {
|
|
|
|
pid int
|
|
|
|
signal int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e errNoSuchProcess) Error() string {
|
|
|
|
return fmt.Sprintf("Cannot kill process (pid=%d) with signal %d: no such process.", e.pid, e.signal)
|
|
|
|
}
|
|
|
|
|
2017-07-19 10:20:13 -04:00
|
|
|
func (errNoSuchProcess) NotFound() {}
|
|
|
|
|
2016-03-04 15:41:06 -05:00
|
|
|
// isErrNoSuchProcess returns true if the error
|
|
|
|
// is an instance of errNoSuchProcess.
|
|
|
|
func isErrNoSuchProcess(err error) bool {
|
|
|
|
_, ok := err.(errNoSuchProcess)
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2016-03-18 01:15:32 +08:00
|
|
|
// ContainerKill sends signal to the container
|
2014-07-31 20:24:54 +00:00
|
|
|
// If no signal is given (sig 0), then Kill with SIGKILL and wait
|
|
|
|
// for the container to exit.
|
|
|
|
// If a signal is given, then just send it to the container and return.
|
2015-09-29 13:51:40 -04:00
|
|
|
func (daemon *Daemon) ContainerKill(name string, sig uint64) error {
|
2015-12-11 12:39:28 -05:00
|
|
|
container, err := daemon.GetContainer(name)
|
2014-12-16 15:06:35 -08:00
|
|
|
if err != nil {
|
2015-03-25 08:44:12 +01:00
|
|
|
return err
|
2014-12-16 15:06:35 -08:00
|
|
|
}
|
|
|
|
|
2015-10-12 16:34:03 -07:00
|
|
|
if sig != 0 && !signal.ValidSignalForPlatform(syscall.Signal(sig)) {
|
|
|
|
return fmt.Errorf("The %s daemon does not support signal %d", runtime.GOOS, sig)
|
|
|
|
}
|
|
|
|
|
2014-12-16 15:06:35 -08:00
|
|
|
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
|
|
|
|
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
|
2015-11-11 17:19:39 -08:00
|
|
|
return daemon.Kill(container)
|
2014-07-31 20:24:54 +00:00
|
|
|
}
|
2015-11-11 17:19:39 -08:00
|
|
|
return daemon.killWithSignal(container, int(sig))
|
2014-07-31 20:24:54 +00:00
|
|
|
}
|
2015-11-02 18:25:26 -05:00
|
|
|
|
|
|
|
// killWithSignal sends the container the given signal. This wrapper for the
|
|
|
|
// host specific kill command prepares the container before attempting
|
|
|
|
// to send the signal. An error is returned if the container is paused
|
|
|
|
// or not running, or if there is a problem returned from the
|
|
|
|
// underlying kill command.
|
2017-03-30 20:01:41 -07:00
|
|
|
func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int) error {
|
2016-08-01 01:00:38 +08:00
|
|
|
logrus.Debugf("Sending kill signal %d to container %s", sig, container.ID)
|
2015-11-02 18:25:26 -05:00
|
|
|
container.Lock()
|
|
|
|
defer container.Unlock()
|
|
|
|
|
|
|
|
if !container.Running {
|
2017-07-19 10:20:13 -04:00
|
|
|
return errNotRunning(container.ID)
|
2015-11-02 18:25:26 -05:00
|
|
|
}
|
|
|
|
|
2017-07-09 09:34:14 -04:00
|
|
|
var unpause bool
|
2017-05-22 09:57:39 -04:00
|
|
|
if container.Config.StopSignal != "" && syscall.Signal(sig) != syscall.SIGKILL {
|
2016-10-24 11:10:14 -07:00
|
|
|
containerStopSignal, err := signal.ParseSignal(container.Config.StopSignal)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if containerStopSignal == syscall.Signal(sig) {
|
|
|
|
container.ExitOnNext()
|
2017-07-09 09:34:14 -04:00
|
|
|
unpause = container.Paused
|
2016-10-24 11:10:14 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
container.ExitOnNext()
|
2017-07-09 09:34:14 -04:00
|
|
|
unpause = container.Paused
|
2016-10-24 11:10:14 -07:00
|
|
|
}
|
2015-11-02 18:25:26 -05:00
|
|
|
|
2016-03-18 11:50:19 -07:00
|
|
|
if !daemon.IsShuttingDown() {
|
|
|
|
container.HasBeenManuallyStopped = true
|
|
|
|
}
|
|
|
|
|
2015-11-02 18:25:26 -05:00
|
|
|
// if the container is currently restarting we do not need to send the signal
|
2016-07-21 18:03:37 +08:00
|
|
|
// to the process. Telling the monitor that it should exit on its next event
|
2015-11-02 18:25:26 -05:00
|
|
|
// loop is enough
|
|
|
|
if container.Restarting {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := daemon.kill(container, sig); err != nil {
|
2017-07-19 10:20:13 -04:00
|
|
|
err = errors.Wrapf(err, "Cannot kill container %s", container.ID)
|
2016-04-07 22:05:29 +08:00
|
|
|
// if container or process not exists, ignore the error
|
2017-07-19 10:20:13 -04:00
|
|
|
// TODO: we shouldn't have to parse error strings from containerd
|
2016-04-07 22:05:29 +08:00
|
|
|
if strings.Contains(err.Error(), "container not found") ||
|
|
|
|
strings.Contains(err.Error(), "no such process") {
|
2016-06-11 19:42:38 +02:00
|
|
|
logrus.Warnf("container kill failed because of 'container not found' or 'no such process': %s", err.Error())
|
2017-07-09 09:34:14 -04:00
|
|
|
unpause = false
|
2016-04-07 22:05:29 +08:00
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
2015-11-02 18:25:26 -05:00
|
|
|
}
|
|
|
|
|
2017-07-09 09:34:14 -04:00
|
|
|
if unpause {
|
|
|
|
// above kill signal will be sent once resume is finished
|
|
|
|
if err := daemon.containerd.Resume(container.ID); err != nil {
|
|
|
|
logrus.Warn("Cannot unpause container %s: %s", container.ID, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-07 23:14:05 +01:00
|
|
|
attributes := map[string]string{
|
|
|
|
"signal": fmt.Sprintf("%d", sig),
|
|
|
|
}
|
|
|
|
daemon.LogContainerEventWithAttributes(container, "kill", attributes)
|
2015-11-02 18:25:26 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Kill forcefully terminates a container.
|
2017-03-30 20:01:41 -07:00
|
|
|
func (daemon *Daemon) Kill(container *containerpkg.Container) error {
|
2015-11-02 18:25:26 -05:00
|
|
|
if !container.IsRunning() {
|
2017-07-19 10:20:13 -04:00
|
|
|
return errNotRunning(container.ID)
|
2015-11-02 18:25:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 1. Send SIGKILL
|
|
|
|
if err := daemon.killPossiblyDeadProcess(container, int(syscall.SIGKILL)); err != nil {
|
|
|
|
// While normally we might "return err" here we're not going to
|
|
|
|
// because if we can't stop the container by this point then
|
2016-12-23 20:48:25 +08:00
|
|
|
// it's probably because it's already stopped. Meaning, between
|
2015-11-02 18:25:26 -05:00
|
|
|
// the time of the IsRunning() call above and now it stopped.
|
2016-03-18 12:43:17 -07:00
|
|
|
// Also, since the err return will be environment specific we can't
|
2015-11-02 18:25:26 -05:00
|
|
|
// look for any particular (common) error that would indicate
|
|
|
|
// that the process is already dead vs something else going wrong.
|
|
|
|
// So, instead we'll give it up to 2 more seconds to complete and if
|
|
|
|
// by that time the container is still running, then the error
|
|
|
|
// we got is probably valid and so we return it to the caller.
|
2016-03-04 15:41:06 -05:00
|
|
|
if isErrNoSuchProcess(err) {
|
|
|
|
return nil
|
|
|
|
}
|
2015-11-02 18:25:26 -05:00
|
|
|
|
2017-03-30 13:52:40 -07:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
2017-03-30 20:01:41 -07:00
|
|
|
if status := <-container.Wait(ctx, containerpkg.WaitConditionNotRunning); status.Err() != nil {
|
2016-08-15 16:51:45 -07:00
|
|
|
return err
|
2015-11-02 18:25:26 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. Wait for the process to die, in last resort, try to kill the process directly
|
|
|
|
if err := killProcessDirectly(container); err != nil {
|
2016-03-04 15:41:06 -05:00
|
|
|
if isErrNoSuchProcess(err) {
|
|
|
|
return nil
|
|
|
|
}
|
2015-11-02 18:25:26 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-03-30 13:52:40 -07:00
|
|
|
// Wait for exit with no timeout.
|
|
|
|
// Ignore returned status.
|
2017-09-06 16:54:24 +08:00
|
|
|
<-container.Wait(context.Background(), containerpkg.WaitConditionNotRunning)
|
2017-03-30 13:52:40 -07:00
|
|
|
|
2015-11-02 18:25:26 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-12-13 18:00:39 +02:00
|
|
|
// killPossibleDeadProcess is a wrapper around killSig() suppressing "no such process" error.
|
2017-03-30 20:01:41 -07:00
|
|
|
func (daemon *Daemon) killPossiblyDeadProcess(container *containerpkg.Container, sig int) error {
|
2015-11-02 18:25:26 -05:00
|
|
|
err := daemon.killWithSignal(container, sig)
|
|
|
|
if err == syscall.ESRCH {
|
2016-03-04 15:41:06 -05:00
|
|
|
e := errNoSuchProcess{container.GetPID(), sig}
|
|
|
|
logrus.Debug(e)
|
|
|
|
return e
|
2015-11-02 18:25:26 -05:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
2016-05-24 17:49:26 +02:00
|
|
|
|
2017-03-30 20:01:41 -07:00
|
|
|
func (daemon *Daemon) kill(c *containerpkg.Container, sig int) error {
|
2016-05-24 17:49:26 +02:00
|
|
|
return daemon.containerd.Signal(c.ID, sig)
|
|
|
|
}
|