2018-02-05 21:05:59 +00:00
package daemon // import "github.com/docker/docker/daemon"
2014-07-31 20:24:54 +00:00
2015-10-12 23:34:03 +00:00
import (
2017-03-30 20:52:40 +00:00
"context"
2015-10-12 23:34:03 +00:00
"fmt"
"runtime"
2022-10-05 14:28:35 +00:00
"strconv"
2015-10-12 23:34:03 +00:00
"syscall"
2015-11-02 23:25:26 +00:00
"time"
2015-10-12 23:34:03 +00:00
2023-09-13 15:41:45 +00:00
"github.com/containerd/log"
2023-08-26 13:24:46 +00:00
"github.com/docker/docker/api/types/events"
2017-03-31 03:01:41 +00:00
containerpkg "github.com/docker/docker/container"
2018-01-11 19:53:06 +00:00
"github.com/docker/docker/errdefs"
2021-07-09 22:11:57 +00:00
"github.com/moby/sys/signal"
2017-07-19 14:20:13 +00:00
"github.com/pkg/errors"
2015-10-12 23:34:03 +00:00
)
2014-07-31 20:24:54 +00:00
2016-03-04 20:41:06 +00:00
type errNoSuchProcess struct {
pid int
2022-05-01 22:28:17 +00:00
signal syscall . Signal
2016-03-04 20:41:06 +00:00
}
func ( e errNoSuchProcess ) Error ( ) string {
2022-05-02 12:06:37 +00:00
return fmt . Sprintf ( "cannot kill process (pid=%d) with signal %d: no such process" , e . pid , e . signal )
2016-03-04 20:41:06 +00:00
}
2017-07-19 14:20:13 +00:00
func ( errNoSuchProcess ) NotFound ( ) { }
2016-03-17 17:15:32 +00:00
// ContainerKill sends signal to the container
2022-05-01 23:00:09 +00:00
// If no signal is given, then Kill with SIGKILL and wait
2014-07-31 20:24:54 +00:00
// for the container to exit.
// If a signal is given, then just send it to the container and return.
2022-05-01 23:00:09 +00: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 17:39:28 +00:00
container , err := daemon . GetContainer ( name )
2014-12-16 23:06:35 +00:00
if err != nil {
2015-03-25 07:44:12 +00:00
return err
2014-12-16 23:06:35 +00:00
}
2022-05-01 23:00:09 +00:00
if sig == syscall . SIGKILL {
// perform regular Kill (SIGKILL + wait())
2015-11-12 01:19:39 +00:00
return daemon . Kill ( container )
2014-07-31 20:24:54 +00:00
}
2022-05-01 22:28:17 +00:00
return daemon . killWithSignal ( container , sig )
2014-07-31 20:24:54 +00:00
}
2015-11-02 23:25:26 +00: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 22:28:17 +00:00
func ( daemon * Daemon ) killWithSignal ( container * containerpkg . Container , stopSignal syscall . Signal ) error {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Debugf ( "Sending kill signal %d to container %s" , stopSignal , container . ID )
2015-11-02 23:25:26 +00:00
container . Lock ( )
defer container . Unlock ( )
2022-05-10 19:59:00 +00:00
task , err := container . GetRunningTask ( )
if err != nil {
return err
2015-11-02 23:25:26 +00:00
}
2017-07-09 13:34:14 +00:00
var unpause bool
2022-05-01 22:05:21 +00:00
if container . Config . StopSignal != "" && stopSignal != syscall . SIGKILL {
2016-10-24 18:10:14 +00:00
containerStopSignal , err := signal . ParseSignal ( container . Config . StopSignal )
if err != nil {
return err
}
2022-05-01 22:05:21 +00:00
if containerStopSignal == stopSignal {
2016-10-24 18:10:14 +00:00
container . ExitOnNext ( )
2017-07-09 13:34:14 +00:00
unpause = container . Paused
2016-10-24 18:10:14 +00:00
}
} else {
container . ExitOnNext ( )
2017-07-09 13:34:14 +00:00
unpause = container . Paused
2016-10-24 18:10:14 +00:00
}
2015-11-02 23:25:26 +00:00
2016-03-18 18:50:19 +00:00
if ! daemon . IsShuttingDown ( ) {
container . HasBeenManuallyStopped = true
2023-08-14 14:17:09 +00:00
if err := container . CheckpointTo ( daemon . containersReplica ) ; err != nil {
log . G ( context . TODO ( ) ) . WithFields ( log . Fields {
"error" : err ,
"container" : container . ID ,
} ) . Warn ( "error checkpointing container state" )
}
2016-03-18 18:50:19 +00:00
}
2015-11-02 23:25:26 +00:00
// if the container is currently restarting we do not need to send the signal
2016-07-21 10:03:37 +00:00
// to the process. Telling the monitor that it should exit on its next event
2015-11-02 23:25:26 +00:00
// loop is enough
if container . Restarting {
return nil
}
2022-05-10 19:59:00 +00:00
if err := task . Kill ( context . Background ( ) , stopSignal ) ; err != nil {
2017-12-15 15:00:15 +00:00
if errdefs . IsNotFound ( err ) {
2017-07-09 13:34:14 +00:00
unpause = false
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . 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 20:13:00 +00: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 {
2023-08-14 14:15:07 +00:00
if err := daemon . handleContainerExit ( container , nil ) ; err != nil {
log . G ( context . TODO ( ) ) . WithFields ( log . Fields {
"error" : err ,
"container" : container . ID ,
"action" : "kill" ,
} ) . Warn ( "error while handling container exit" )
}
2020-08-11 20:13:00 +00:00
}
} ( )
2016-04-07 14:05:29 +00:00
} else {
2017-12-15 15:00:15 +00:00
return errors . Wrapf ( err , "Cannot kill container %s" , container . ID )
2016-04-07 14:05:29 +00:00
}
2015-11-02 23:25:26 +00:00
}
2017-07-09 13:34:14 +00:00
if unpause {
// above kill signal will be sent once resume is finished
2022-05-10 19:59:00 +00:00
if err := task . Resume ( context . Background ( ) ) ; err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Warnf ( "Cannot unpause container %s: %s" , container . ID , err )
2017-07-09 13:34:14 +00:00
}
}
2023-08-26 13:24:46 +00:00
daemon . LogContainerEventWithAttributes ( container , events . ActionKill , map [ string ] string {
2022-10-05 14:28:35 +00:00
"signal" : strconv . Itoa ( int ( stopSignal ) ) ,
} )
2015-11-02 23:25:26 +00:00
return nil
}
// Kill forcefully terminates a container.
2017-03-31 03:01:41 +00:00
func ( daemon * Daemon ) Kill ( container * containerpkg . Container ) error {
2015-11-02 23:25:26 +00:00
if ! container . IsRunning ( ) {
2017-07-19 14:20:13 +00:00
return errNotRunning ( container . ID )
2015-11-02 23:25:26 +00:00
}
// 1. Send SIGKILL
2022-05-01 22:28:17 +00:00
if err := daemon . killPossiblyDeadProcess ( container , syscall . SIGKILL ) ; err != nil {
2020-10-24 17:56:21 +00:00
// kill failed, check if process is no longer running.
2022-05-02 12:06:37 +00:00
if errors . As ( err , & errNoSuchProcess { } ) {
2016-03-04 20:41:06 +00:00
return nil
}
2020-10-24 17:56:21 +00:00
}
2015-11-02 23:25:26 +00:00
2023-01-31 20:19:08 +00:00
waitTimeout := 10 * time . Second
if runtime . GOOS == "windows" {
waitTimeout = 75 * time . Second // runhcs can be sloooooow.
}
ctx , cancel := context . WithTimeout ( context . Background ( ) , waitTimeout )
2020-10-24 17:56:21 +00:00
defer cancel ( )
2017-03-30 20:52:40 +00:00
2020-10-24 17:56:21 +00:00
status := <- container . Wait ( ctx , containerpkg . WaitConditionNotRunning )
if status . Err ( ) == nil {
return nil
2015-11-02 23:25:26 +00:00
}
2023-11-29 14:43:08 +00:00
log . G ( ctx ) . WithFields ( log . Fields { "error" : status . Err ( ) , "container" : container . ID } ) . Warnf ( "Container failed to exit within %v of kill - trying direct SIGKILL" , waitTimeout )
2020-10-24 17:56:21 +00:00
2015-11-02 23:25:26 +00:00
if err := killProcessDirectly ( container ) ; err != nil {
2022-05-02 12:06:37 +00:00
if errors . As ( err , & errNoSuchProcess { } ) {
2016-03-04 20:41:06 +00:00
return nil
}
2015-11-02 23:25:26 +00:00
return err
}
2020-10-24 17:56:21 +00: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 20:52:40 +00:00
2020-10-24 17:56:21 +00: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 23:25:26 +00:00
return nil
}
2023-04-25 05:39:28 +00:00
// killPossiblyDeadProcess is a wrapper around killSig() suppressing "no such process" error.
2022-05-01 22:28:17 +00:00
func ( daemon * Daemon ) killPossiblyDeadProcess ( container * containerpkg . Container , sig syscall . Signal ) error {
2015-11-02 23:25:26 +00:00
err := daemon . killWithSignal ( container , sig )
2017-12-15 15:00:15 +00:00
if errdefs . IsNotFound ( err ) {
2022-05-01 22:28:17 +00:00
err = errNoSuchProcess { container . GetPID ( ) , sig }
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Debug ( err )
2022-05-01 22:28:17 +00:00
return err
2015-11-02 23:25:26 +00:00
}
return err
}