2018-02-05 16:05:59 -05:00
package daemon // import "github.com/docker/docker/daemon"
2014-07-31 16:24:54 -04:00
2015-10-12 19:34:03 -04:00
import (
2017-03-30 16:52:40 -04:00
"context"
2015-10-12 19:34:03 -04:00
"fmt"
"runtime"
"syscall"
2015-11-02 18:25:26 -05:00
"time"
2015-10-12 19:34:03 -04:00
2017-03-30 23:01:41 -04:00
containerpkg "github.com/docker/docker/container"
2018-01-11 14:53:06 -05:00
"github.com/docker/docker/errdefs"
2021-07-09 18:11:57 -04:00
"github.com/moby/sys/signal"
2017-07-19 10:20:13 -04:00
"github.com/pkg/errors"
2017-07-26 17:42:13 -04:00
"github.com/sirupsen/logrus"
2015-10-12 19:34:03 -04:00
)
2014-07-31 16:24:54 -04:00
2016-03-04 15:41:06 -05:00
type errNoSuchProcess struct {
pid int
2022-05-01 18:28:17 -04:00
signal syscall . Signal
2016-03-04 15:41:06 -05:00
}
func ( e errNoSuchProcess ) Error ( ) string {
2022-05-02 08:06:37 -04:00
return fmt . Sprintf ( "cannot kill process (pid=%d) with signal %d: no such process" , e . pid , e . signal )
2016-03-04 15:41:06 -05:00
}
2017-07-19 10:20:13 -04:00
func ( errNoSuchProcess ) NotFound ( ) { }
2016-03-17 13:15:32 -04:00
// ContainerKill sends signal to the container
2022-05-01 19:00:09 -04:00
// If no signal is given, then Kill with SIGKILL and wait
2014-07-31 16:24:54 -04:00
// for the container to exit.
// If a signal is given, then just send it to the container and return.
2022-05-01 19:00:09 -04:00
func ( daemon * Daemon ) ContainerKill ( name , stopSignal string ) error {
var (
err error
sig = syscall . SIGKILL
)
if stopSignal != "" {
sig , err = signal . ParseSignal ( stopSignal )
if err != nil {
return errdefs . InvalidParameter ( err )
}
if ! signal . ValidSignalForPlatform ( sig ) {
return errdefs . InvalidParameter ( errors . Errorf ( "the %s daemon does not support signal %d" , runtime . GOOS , sig ) )
}
}
2015-12-11 12:39:28 -05:00
container , err := daemon . GetContainer ( name )
2014-12-16 18:06:35 -05:00
if err != nil {
2015-03-25 03:44:12 -04:00
return err
2014-12-16 18:06:35 -05:00
}
2022-05-01 19:00:09 -04:00
if sig == syscall . SIGKILL {
// perform regular Kill (SIGKILL + wait())
2015-11-11 20:19:39 -05:00
return daemon . Kill ( container )
2014-07-31 16:24:54 -04:00
}
2022-05-01 18:28:17 -04:00
return daemon . killWithSignal ( container , sig )
2014-07-31 16:24:54 -04: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.
2022-05-01 18:28:17 -04:00
func ( daemon * Daemon ) killWithSignal ( container * containerpkg . Container , stopSignal syscall . Signal ) error {
2022-05-01 18:05:21 -04:00
logrus . Debugf ( "Sending kill signal %d to container %s" , stopSignal , container . ID )
2015-11-02 18:25:26 -05:00
container . Lock ( )
defer container . Unlock ( )
2022-05-10 15:59:00 -04:00
task , err := container . GetRunningTask ( )
if err != nil {
return err
2015-11-02 18:25:26 -05:00
}
2017-07-09 09:34:14 -04:00
var unpause bool
2022-05-01 18:05:21 -04:00
if container . Config . StopSignal != "" && stopSignal != syscall . SIGKILL {
2016-10-24 14:10:14 -04:00
containerStopSignal , err := signal . ParseSignal ( container . Config . StopSignal )
if err != nil {
return err
}
2022-05-01 18:05:21 -04:00
if containerStopSignal == stopSignal {
2016-10-24 14:10:14 -04:00
container . ExitOnNext ( )
2017-07-09 09:34:14 -04:00
unpause = container . Paused
2016-10-24 14:10:14 -04:00
}
} else {
container . ExitOnNext ( )
2017-07-09 09:34:14 -04:00
unpause = container . Paused
2016-10-24 14:10:14 -04:00
}
2015-11-02 18:25:26 -05:00
2016-03-18 14:50:19 -04:00
if ! daemon . IsShuttingDown ( ) {
container . HasBeenManuallyStopped = true
2017-11-01 02:15:02 -04:00
container . CheckpointTo ( daemon . containersReplica )
2016-03-18 14:50:19 -04:00
}
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 06:03:37 -04: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
}
2022-05-10 15:59:00 -04:00
if err := task . Kill ( context . Background ( ) , stopSignal ) ; err != nil {
2017-12-15 10:00:15 -05:00
if errdefs . IsNotFound ( err ) {
2017-07-09 09:34:14 -04:00
unpause = false
2017-12-15 10:00:15 -05:00
logrus . WithError ( err ) . WithField ( "container" , container . ID ) . WithField ( "action" , "kill" ) . Debug ( "container kill failed because of 'container not found' or 'no such process'" )
2020-08-11 16:13:00 -04:00
go func ( ) {
// We need to clean up this container but it is possible there is a case where we hit here before the exit event is processed
// but after it was fired off.
// So let's wait the container's stop timeout amount of time to see if the event is eventually processed.
// Doing this has the side effect that if no event was ever going to come we are waiting a a longer period of time uneccessarily.
// But this prevents race conditions in processing the container.
ctx , cancel := context . WithTimeout ( context . TODO ( ) , time . Duration ( container . StopTimeout ( ) ) * time . Second )
defer cancel ( )
s := <- container . Wait ( ctx , containerpkg . WaitConditionNotRunning )
if s . Err ( ) != nil {
daemon . handleContainerExit ( container , nil )
}
} ( )
2016-04-07 10:05:29 -04:00
} else {
2017-12-15 10:00:15 -05:00
return errors . Wrapf ( err , "Cannot kill container %s" , container . ID )
2016-04-07 10:05:29 -04:00
}
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
2022-05-10 15:59:00 -04:00
if err := task . Resume ( context . Background ( ) ) ; err != nil {
2018-07-11 09:51:51 -04:00
logrus . Warnf ( "Cannot unpause container %s: %s" , container . ID , err )
2017-07-09 09:34:14 -04:00
}
}
2016-01-07 17:14:05 -05:00
attributes := map [ string ] string {
2022-05-01 18:05:21 -04:00
"signal" : fmt . Sprintf ( "%d" , stopSignal ) ,
2016-01-07 17:14:05 -05:00
}
daemon . LogContainerEventWithAttributes ( container , "kill" , attributes )
2015-11-02 18:25:26 -05:00
return nil
}
// Kill forcefully terminates a container.
2017-03-30 23:01:41 -04: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
2022-05-01 18:28:17 -04:00
if err := daemon . killPossiblyDeadProcess ( container , syscall . SIGKILL ) ; err != nil {
2020-10-24 13:56:21 -04:00
// kill failed, check if process is no longer running.
2022-05-02 08:06:37 -04:00
if errors . As ( err , & errNoSuchProcess { } ) {
2016-03-04 15:41:06 -05:00
return nil
}
2020-10-24 13:56:21 -04:00
}
2015-11-02 18:25:26 -05:00
2020-10-24 13:56:21 -04:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , 10 * time . Second )
defer cancel ( )
2017-03-30 16:52:40 -04:00
2020-10-24 13:56:21 -04:00
status := <- container . Wait ( ctx , containerpkg . WaitConditionNotRunning )
if status . Err ( ) == nil {
return nil
2015-11-02 18:25:26 -05:00
}
2020-10-24 13:56:21 -04:00
logrus . WithError ( status . Err ( ) ) . WithField ( "container" , container . ID ) . Error ( "Container failed to exit within 10 seconds of kill - trying direct SIGKILL" )
2015-11-02 18:25:26 -05:00
if err := killProcessDirectly ( container ) ; err != nil {
2022-05-02 08:06:37 -04:00
if errors . As ( err , & errNoSuchProcess { } ) {
2016-03-04 15:41:06 -05:00
return nil
}
2015-11-02 18:25:26 -05:00
return err
}
2020-10-24 13:56:21 -04:00
// wait for container to exit one last time, if it doesn't then kill didnt work, so return error
ctx2 , cancel2 := context . WithTimeout ( context . Background ( ) , 2 * time . Second )
defer cancel2 ( )
2017-03-30 16:52:40 -04:00
2020-10-24 13:56:21 -04:00
if status := <- container . Wait ( ctx2 , containerpkg . WaitConditionNotRunning ) ; status . Err ( ) != nil {
return errors . New ( "tried to kill container, but did not receive an exit event" )
}
2015-11-02 18:25:26 -05:00
return nil
}
2015-12-13 11:00:39 -05:00
// killPossibleDeadProcess is a wrapper around killSig() suppressing "no such process" error.
2022-05-01 18:28:17 -04:00
func ( daemon * Daemon ) killPossiblyDeadProcess ( container * containerpkg . Container , sig syscall . Signal ) error {
2015-11-02 18:25:26 -05:00
err := daemon . killWithSignal ( container , sig )
2017-12-15 10:00:15 -05:00
if errdefs . IsNotFound ( err ) {
2022-05-01 18:28:17 -04:00
err = errNoSuchProcess { container . GetPID ( ) , sig }
logrus . Debug ( err )
return err
2015-11-02 18:25:26 -05:00
}
return err
}