Browse Source

Merge pull request #43563 from thaJeztah/less_signal_conversions

pass syscall.Signal for stop-signals to reduce type conversions
Sebastiaan van Stijn 3 years ago
parent
commit
b3675e1839

+ 1 - 1
api/server/router/container/backend.go

@@ -33,7 +33,7 @@ type copyBackend interface {
 // stateBackend includes functions to implement to provide container state lifecycle functionality.
 // stateBackend includes functions to implement to provide container state lifecycle functionality.
 type stateBackend interface {
 type stateBackend interface {
 	ContainerCreate(config types.ContainerCreateConfig) (container.CreateResponse, error)
 	ContainerCreate(config types.ContainerCreateConfig) (container.CreateResponse, error)
-	ContainerKill(name string, sig uint64) error
+	ContainerKill(name string, signal string) error
 	ContainerPause(name string) error
 	ContainerPause(name string) error
 	ContainerRename(oldName, newName string) error
 	ContainerRename(oldName, newName string) error
 	ContainerResize(name string, height, width int) error
 	ContainerResize(name string, height, width int) error

+ 1 - 12
api/server/router/container/container_routes.go

@@ -7,7 +7,6 @@ import (
 	"io"
 	"io"
 	"net/http"
 	"net/http"
 	"strconv"
 	"strconv"
-	"syscall"
 
 
 	"github.com/containerd/containerd/platforms"
 	"github.com/containerd/containerd/platforms"
 	"github.com/docker/docker/api/server/httpstatus"
 	"github.com/docker/docker/api/server/httpstatus"
@@ -254,18 +253,8 @@ func (s *containerRouter) postContainersKill(ctx context.Context, w http.Respons
 		return err
 		return err
 	}
 	}
 
 
-	var sig syscall.Signal
 	name := vars["name"]
 	name := vars["name"]
-
-	// If we have a signal, look at it. Otherwise, do nothing
-	if sigStr := r.Form.Get("signal"); sigStr != "" {
-		var err error
-		if sig, err = signal.ParseSignal(sigStr); err != nil {
-			return errdefs.InvalidParameter(err)
-		}
-	}
-
-	if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
+	if err := s.backend.ContainerKill(name, r.Form.Get("signal")); err != nil {
 		var isStopped bool
 		var isStopped bool
 		if errdefs.IsConflict(err) {
 		if errdefs.IsConflict(err) {
 			isStopped = true
 			isStopped = true

+ 1 - 1
builder/builder.go

@@ -65,7 +65,7 @@ type ExecBackend interface {
 	// ContainerRm removes a container specified by `id`.
 	// ContainerRm removes a container specified by `id`.
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	// ContainerKill stops the container execution abruptly.
 	// ContainerKill stops the container execution abruptly.
-	ContainerKill(containerID string, sig uint64) error
+	ContainerKill(containerID string, sig string) error
 	// ContainerStart starts a new container
 	// ContainerStart starts a new container
 	ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
 	ContainerStart(containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
 	// ContainerWait stops processing until the given container is stopped.
 	// ContainerWait stops processing until the given container is stopped.

+ 1 - 1
builder/dockerfile/containerbackend.go

@@ -61,7 +61,7 @@ func (c *containerManager) Run(ctx context.Context, cID string, stdout, stderr i
 		select {
 		select {
 		case <-ctx.Done():
 		case <-ctx.Done():
 			logrus.Debugln("Build cancelled, killing and removing container:", cID)
 			logrus.Debugln("Build cancelled, killing and removing container:", cID)
-			c.backend.ContainerKill(cID, 0)
+			c.backend.ContainerKill(cID, "")
 			c.removeContainer(cID, stdout)
 			c.removeContainer(cID, stdout)
 			cancelErrCh <- errCancelled
 			cancelErrCh <- errCancelled
 		case <-finished:
 		case <-finished:

+ 2 - 2
builder/dockerfile/mockbackend_test.go

@@ -46,7 +46,7 @@ func (m *MockBackend) CommitBuildStep(c backend.CommitConfig) (image.ID, error)
 	return "", nil
 	return "", nil
 }
 }
 
 
-func (m *MockBackend) ContainerKill(containerID string, sig uint64) error {
+func (m *MockBackend) ContainerKill(containerID string, sig string) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -129,7 +129,7 @@ func (l *mockLayer) NewRWLayer() (builder.RWLayer, error) {
 }
 }
 
 
 func (l *mockLayer) DiffID() layer.DiffID {
 func (l *mockLayer) DiffID() layer.DiffID {
-	return layer.DiffID("abcdef")
+	return "abcdef"
 }
 }
 
 
 type mockRWLayer struct {
 type mockRWLayer struct {

+ 3 - 3
container/container.go

@@ -511,16 +511,16 @@ func (container *Container) IsDestinationMounted(destination string) bool {
 }
 }
 
 
 // StopSignal returns the signal used to stop the container.
 // StopSignal returns the signal used to stop the container.
-func (container *Container) StopSignal() int {
+func (container *Container) StopSignal() syscall.Signal {
 	var stopSignal syscall.Signal
 	var stopSignal syscall.Signal
 	if container.Config.StopSignal != "" {
 	if container.Config.StopSignal != "" {
 		stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
 		stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
 	}
 	}
 
 
-	if int(stopSignal) == 0 {
+	if stopSignal == 0 {
 		stopSignal, _ = signal.ParseSignal(defaultStopSignal)
 		stopSignal, _ = signal.ParseSignal(defaultStopSignal)
 	}
 	}
-	return int(stopSignal)
+	return stopSignal
 }
 }
 
 
 // StopTimeout returns the timeout (in seconds) used to stop the container.
 // StopTimeout returns the timeout (in seconds) used to stop the container.

+ 1 - 1
container/container_unit_test.go

@@ -24,7 +24,7 @@ func TestContainerStopSignal(t *testing.T) {
 	}
 	}
 
 
 	s := c.StopSignal()
 	s := c.StopSignal()
-	if s != int(def) {
+	if s != def {
 		t.Fatalf("Expected %v, got %v", def, s)
 		t.Fatalf("Expected %v, got %v", def, s)
 	}
 	}
 
 

+ 1 - 1
daemon/cluster/executor/backend.go

@@ -45,7 +45,7 @@ type Backend interface {
 	ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error)
 	ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error)
 	ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
 	ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
-	ContainerKill(name string, sig uint64) error
+	ContainerKill(name string, sig string) error
 	SetContainerDependencyStore(name string, store exec.DependencyGetter) error
 	SetContainerDependencyStore(name string, store exec.DependencyGetter) error
 	SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error
 	SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error
 	SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error
 	SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error

+ 1 - 1
daemon/cluster/executor/container/adapter.go

@@ -417,7 +417,7 @@ func (c *containerAdapter) shutdown(ctx context.Context) error {
 }
 }
 
 
 func (c *containerAdapter) terminate(ctx context.Context) error {
 func (c *containerAdapter) terminate(ctx context.Context) error {
-	return c.backend.ContainerKill(c.container.name(), uint64(syscall.SIGKILL))
+	return c.backend.ContainerKill(c.container.name(), syscall.SIGKILL.String())
 }
 }
 
 
 func (c *containerAdapter) remove(ctx context.Context) error {
 func (c *containerAdapter) remove(ctx context.Context) error {

+ 8 - 6
daemon/container_operations_unix.go

@@ -8,6 +8,7 @@ import (
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"strconv"
 	"strconv"
+	"syscall"
 
 
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/links"
 	"github.com/docker/docker/daemon/links"
@@ -336,24 +337,25 @@ func (daemon *Daemon) cleanupSecretDir(c *container.Container) {
 
 
 func killProcessDirectly(container *container.Container) error {
 func killProcessDirectly(container *container.Container) error {
 	pid := container.GetPID()
 	pid := container.GetPID()
-	// Ensure that we don't kill ourselves
 	if pid == 0 {
 	if pid == 0 {
+		// Ensure that we don't kill ourselves
 		return nil
 		return nil
 	}
 	}
 
 
-	if err := unix.Kill(pid, 9); err != nil {
+	if err := unix.Kill(pid, syscall.SIGKILL); err != nil {
 		if err != unix.ESRCH {
 		if err != unix.ESRCH {
-			return err
+			return errdefs.System(err)
 		}
 		}
-		e := errNoSuchProcess{pid, 9}
-		logrus.WithError(e).WithField("container", container.ID).Debug("no such process")
-		return e
+		err = errNoSuchProcess{pid, syscall.SIGKILL}
+		logrus.WithError(err).WithField("container", container.ID).Debug("no such process")
+		return err
 	}
 	}
 
 
 	// In case there were some exceptions(e.g., state of zombie and D)
 	// In case there were some exceptions(e.g., state of zombie and D)
 	if system.IsProcessAlive(pid) {
 	if system.IsProcessAlive(pid) {
 		// Since we can not kill a zombie pid, add zombie check here
 		// Since we can not kill a zombie pid, add zombie check here
 		isZombie, err := system.IsProcessZombie(pid)
 		isZombie, err := system.IsProcessZombie(pid)
+		// TODO(thaJeztah) should we ignore os.IsNotExist() here? ("/proc/<pid>/stat" will be gone if the process exited)
 		if err != nil {
 		if err != nil {
 			logrus.WithError(err).WithField("container", container.ID).Warn("Container state is invalid")
 			logrus.WithError(err).WithField("container", container.ID).Warn("Container state is invalid")
 			return err
 			return err

+ 2 - 2
daemon/exec.go

@@ -279,7 +279,7 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, stdin
 	select {
 	select {
 	case <-ctx.Done():
 	case <-ctx.Done():
 		logrus.Debugf("Sending TERM signal to process %v in container %v", name, c.ID)
 		logrus.Debugf("Sending TERM signal to process %v in container %v", name, c.ID)
-		daemon.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["TERM"]))
+		daemon.containerd.SignalProcess(ctx, c.ID, name, signal.SignalMap["TERM"])
 
 
 		timeout := time.NewTimer(termProcessTimeout)
 		timeout := time.NewTimer(termProcessTimeout)
 		defer timeout.Stop()
 		defer timeout.Stop()
@@ -287,7 +287,7 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, stdin
 		select {
 		select {
 		case <-timeout.C:
 		case <-timeout.C:
 			logrus.Infof("Container %v, process %v failed to exit within %v of signal TERM - using the force", c.ID, name, termProcessTimeout)
 			logrus.Infof("Container %v, process %v failed to exit within %v of signal TERM - using the force", c.ID, name, termProcessTimeout)
-			daemon.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["KILL"]))
+			daemon.containerd.SignalProcess(ctx, c.ID, name, signal.SignalMap["KILL"])
 		case <-attachErr:
 		case <-attachErr:
 			// TERM signal worked
 			// TERM signal worked
 		}
 		}

+ 34 - 36
daemon/kill.go

@@ -17,41 +17,42 @@ import (
 
 
 type errNoSuchProcess struct {
 type errNoSuchProcess struct {
 	pid    int
 	pid    int
-	signal int
+	signal syscall.Signal
 }
 }
 
 
 func (e errNoSuchProcess) Error() string {
 func (e errNoSuchProcess) Error() string {
-	return fmt.Sprintf("Cannot kill process (pid=%d) with signal %d: no such process.", e.pid, e.signal)
+	return fmt.Sprintf("cannot kill process (pid=%d) with signal %d: no such process", e.pid, e.signal)
 }
 }
 
 
 func (errNoSuchProcess) NotFound() {}
 func (errNoSuchProcess) NotFound() {}
 
 
-// isErrNoSuchProcess returns true if the error
-// is an instance of errNoSuchProcess.
-func isErrNoSuchProcess(err error) bool {
-	_, ok := err.(errNoSuchProcess)
-	return ok
-}
-
 // ContainerKill sends signal to the container
 // ContainerKill sends signal to the container
-// If no signal is given (sig 0), then Kill with SIGKILL and wait
+// If no signal is given, then Kill with SIGKILL and wait
 // for the container to exit.
 // for the container to exit.
 // If a signal is given, then just send it to the container and return.
 // If a signal is given, then just send it to the container and return.
-func (daemon *Daemon) ContainerKill(name string, sig uint64) error {
+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))
+		}
+	}
 	container, err := daemon.GetContainer(name)
 	container, err := daemon.GetContainer(name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-
-	if sig != 0 && !signal.ValidSignalForPlatform(syscall.Signal(sig)) {
-		return fmt.Errorf("The %s daemon does not support signal %d", runtime.GOOS, sig)
-	}
-
-	// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
-	if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
+	if sig == syscall.SIGKILL {
+		// perform regular Kill (SIGKILL + wait())
 		return daemon.Kill(container)
 		return daemon.Kill(container)
 	}
 	}
-	return daemon.killWithSignal(container, int(sig))
+	return daemon.killWithSignal(container, sig)
 }
 }
 
 
 // killWithSignal sends the container the given signal. This wrapper for the
 // killWithSignal sends the container the given signal. This wrapper for the
@@ -59,8 +60,8 @@ func (daemon *Daemon) ContainerKill(name string, sig uint64) error {
 // to send the signal. An error is returned if the container is paused
 // 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
 // or not running, or if there is a problem returned from the
 // underlying kill command.
 // underlying kill command.
-func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int) error {
-	logrus.Debugf("Sending kill signal %d to container %s", sig, container.ID)
+func (daemon *Daemon) killWithSignal(container *containerpkg.Container, stopSignal syscall.Signal) error {
+	logrus.Debugf("Sending kill signal %d to container %s", stopSignal, container.ID)
 	container.Lock()
 	container.Lock()
 	defer container.Unlock()
 	defer container.Unlock()
 
 
@@ -69,12 +70,12 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int)
 	}
 	}
 
 
 	var unpause bool
 	var unpause bool
-	if container.Config.StopSignal != "" && syscall.Signal(sig) != syscall.SIGKILL {
+	if container.Config.StopSignal != "" && stopSignal != syscall.SIGKILL {
 		containerStopSignal, err := signal.ParseSignal(container.Config.StopSignal)
 		containerStopSignal, err := signal.ParseSignal(container.Config.StopSignal)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		if containerStopSignal == syscall.Signal(sig) {
+		if containerStopSignal == stopSignal {
 			container.ExitOnNext()
 			container.ExitOnNext()
 			unpause = container.Paused
 			unpause = container.Paused
 		}
 		}
@@ -95,7 +96,8 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int)
 		return nil
 		return nil
 	}
 	}
 
 
-	if err := daemon.kill(container, sig); err != nil {
+	err := daemon.containerd.SignalProcess(context.Background(), container.ID, libcontainerdtypes.InitProcessName, stopSignal)
+	if err != nil {
 		if errdefs.IsNotFound(err) {
 		if errdefs.IsNotFound(err) {
 			unpause = false
 			unpause = false
 			logrus.WithError(err).WithField("container", container.ID).WithField("action", "kill").Debug("container kill failed because of 'container not found' or 'no such process'")
 			logrus.WithError(err).WithField("container", container.ID).WithField("action", "kill").Debug("container kill failed because of 'container not found' or 'no such process'")
@@ -125,7 +127,7 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int)
 	}
 	}
 
 
 	attributes := map[string]string{
 	attributes := map[string]string{
-		"signal": fmt.Sprintf("%d", sig),
+		"signal": fmt.Sprintf("%d", stopSignal),
 	}
 	}
 	daemon.LogContainerEventWithAttributes(container, "kill", attributes)
 	daemon.LogContainerEventWithAttributes(container, "kill", attributes)
 	return nil
 	return nil
@@ -138,9 +140,9 @@ func (daemon *Daemon) Kill(container *containerpkg.Container) error {
 	}
 	}
 
 
 	// 1. Send SIGKILL
 	// 1. Send SIGKILL
-	if err := daemon.killPossiblyDeadProcess(container, int(syscall.SIGKILL)); err != nil {
+	if err := daemon.killPossiblyDeadProcess(container, syscall.SIGKILL); err != nil {
 		// kill failed, check if process is no longer running.
 		// kill failed, check if process is no longer running.
-		if isErrNoSuchProcess(err) {
+		if errors.As(err, &errNoSuchProcess{}) {
 			return nil
 			return nil
 		}
 		}
 	}
 	}
@@ -156,7 +158,7 @@ func (daemon *Daemon) Kill(container *containerpkg.Container) error {
 	logrus.WithError(status.Err()).WithField("container", container.ID).Error("Container failed to exit within 10 seconds of kill - trying direct SIGKILL")
 	logrus.WithError(status.Err()).WithField("container", container.ID).Error("Container failed to exit within 10 seconds of kill - trying direct SIGKILL")
 
 
 	if err := killProcessDirectly(container); err != nil {
 	if err := killProcessDirectly(container); err != nil {
-		if isErrNoSuchProcess(err) {
+		if errors.As(err, &errNoSuchProcess{}) {
 			return nil
 			return nil
 		}
 		}
 		return err
 		return err
@@ -173,16 +175,12 @@ func (daemon *Daemon) Kill(container *containerpkg.Container) error {
 }
 }
 
 
 // killPossibleDeadProcess is a wrapper around killSig() suppressing "no such process" error.
 // killPossibleDeadProcess is a wrapper around killSig() suppressing "no such process" error.
-func (daemon *Daemon) killPossiblyDeadProcess(container *containerpkg.Container, sig int) error {
+func (daemon *Daemon) killPossiblyDeadProcess(container *containerpkg.Container, sig syscall.Signal) error {
 	err := daemon.killWithSignal(container, sig)
 	err := daemon.killWithSignal(container, sig)
 	if errdefs.IsNotFound(err) {
 	if errdefs.IsNotFound(err) {
-		e := errNoSuchProcess{container.GetPID(), sig}
-		logrus.Debug(e)
-		return e
+		err = errNoSuchProcess{container.GetPID(), sig}
+		logrus.Debug(err)
+		return err
 	}
 	}
 	return err
 	return err
 }
 }
-
-func (daemon *Daemon) kill(c *containerpkg.Container, sig int) error {
-	return daemon.containerd.SignalProcess(context.Background(), c.ID, libcontainerdtypes.InitProcessName, sig)
-}

+ 1 - 1
daemon/stop.go

@@ -50,7 +50,7 @@ func (daemon *Daemon) containerStop(ctx context.Context, ctr *container.Containe
 		if err != nil {
 		if err != nil {
 			return errdefs.InvalidParameter(err)
 			return errdefs.InvalidParameter(err)
 		}
 		}
-		stopSignal = int(sig)
+		stopSignal = sig
 	}
 	}
 	if options.Timeout != nil {
 	if options.Timeout != nil {
 		stopTimeout = *options.Timeout
 		stopTimeout = *options.Timeout

+ 2 - 1
daemon/util_test.go

@@ -5,6 +5,7 @@ package daemon
 
 
 import (
 import (
 	"context"
 	"context"
+	"syscall"
 	"time"
 	"time"
 
 
 	"github.com/containerd/containerd"
 	"github.com/containerd/containerd"
@@ -35,7 +36,7 @@ func (c *MockContainerdClient) Create(ctx context.Context, containerID string, s
 func (c *MockContainerdClient) Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (pid int, err error) {
 func (c *MockContainerdClient) Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (pid int, err error) {
 	return 0, nil
 	return 0, nil
 }
 }
-func (c *MockContainerdClient) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
+func (c *MockContainerdClient) SignalProcess(ctx context.Context, containerID, processID string, signal syscall.Signal) error {
 	return nil
 	return nil
 }
 }
 func (c *MockContainerdClient) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {
 func (c *MockContainerdClient) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {

+ 1 - 1
libcontainerd/local/local_windows.go

@@ -688,7 +688,7 @@ func (c *client) Exec(ctx context.Context, containerID, processID string, spec *
 // SignalProcess handles `docker stop` on Windows. While Linux has support for
 // SignalProcess handles `docker stop` on Windows. While Linux has support for
 // the full range of signals, signals aren't really implemented on Windows.
 // the full range of signals, signals aren't really implemented on Windows.
 // We fake supporting regular stop and -9 to force kill.
 // We fake supporting regular stop and -9 to force kill.
-func (c *client) SignalProcess(_ context.Context, containerID, processID string, signal int) error {
+func (c *client) SignalProcess(_ context.Context, containerID, processID string, signal syscall.Signal) error {
 	ctr, p, err := c.getProcess(containerID, processID)
 	ctr, p, err := c.getProcess(containerID, processID)
 	if err != nil {
 	if err != nil {
 		return err
 		return err

+ 2 - 2
libcontainerd/remote/client.go

@@ -333,12 +333,12 @@ func (c *client) Exec(ctx context.Context, containerID, processID string, spec *
 	return int(p.Pid()), nil
 	return int(p.Pid()), nil
 }
 }
 
 
-func (c *client) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
+func (c *client) SignalProcess(ctx context.Context, containerID, processID string, signal syscall.Signal) error {
 	p, err := c.getProcess(ctx, containerID, processID)
 	p, err := c.getProcess(ctx, containerID, processID)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	return wrapError(p.Kill(ctx, syscall.Signal(signal)))
+	return wrapError(p.Kill(ctx, signal))
 }
 }
 
 
 func (c *client) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error {
 func (c *client) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error {

+ 2 - 1
libcontainerd/types/types.go

@@ -2,6 +2,7 @@ package types // import "github.com/docker/docker/libcontainerd/types"
 
 
 import (
 import (
 	"context"
 	"context"
+	"syscall"
 	"time"
 	"time"
 
 
 	"github.com/containerd/containerd"
 	"github.com/containerd/containerd"
@@ -54,7 +55,7 @@ type Client interface {
 
 
 	Create(ctx context.Context, containerID string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error
 	Create(ctx context.Context, containerID string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error
 	Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio StdioCallback) (pid int, err error)
 	Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio StdioCallback) (pid int, err error)
-	SignalProcess(ctx context.Context, containerID, processID string, signal int) error
+	SignalProcess(ctx context.Context, containerID, processID string, signal syscall.Signal) error
 	Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error)
 	Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error)
 	ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error
 	ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error
 	CloseStdin(ctx context.Context, containerID, processID string) error
 	CloseStdin(ctx context.Context, containerID, processID string) error

+ 1 - 0
pkg/system/process_unix.go

@@ -33,6 +33,7 @@ func IsProcessZombie(pid int) (bool, error) {
 	statPath := fmt.Sprintf("/proc/%d/stat", pid)
 	statPath := fmt.Sprintf("/proc/%d/stat", pid)
 	dataBytes, err := os.ReadFile(statPath)
 	dataBytes, err := os.ReadFile(statPath)
 	if err != nil {
 	if err != nil {
+		// TODO(thaJeztah) should we ignore os.IsNotExist() here? ("/proc/<pid>/stat" will be gone if the process exited)
 		return false, err
 		return false, err
 	}
 	}
 	data := string(dataBytes)
 	data := string(dataBytes)

+ 2 - 1
plugin/executor/containerd/containerd.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"context"
 	"io"
 	"io"
 	"sync"
 	"sync"
+	"syscall"
 
 
 	"github.com/containerd/containerd"
 	"github.com/containerd/containerd"
 	"github.com/containerd/containerd/cio"
 	"github.com/containerd/containerd/cio"
@@ -112,7 +113,7 @@ func (e *Executor) IsRunning(id string) (bool, error) {
 }
 }
 
 
 // Signal sends the specified signal to the container
 // Signal sends the specified signal to the container
-func (e *Executor) Signal(id string, signal int) error {
+func (e *Executor) Signal(id string, signal syscall.Signal) error {
 	return e.client.SignalProcess(context.Background(), id, libcontainerdtypes.InitProcessName, signal)
 	return e.client.SignalProcess(context.Background(), id, libcontainerdtypes.InitProcessName, signal)
 }
 }
 
 

+ 2 - 1
plugin/manager.go

@@ -11,6 +11,7 @@ import (
 	"sort"
 	"sort"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
+	"syscall"
 
 
 	"github.com/containerd/containerd/content"
 	"github.com/containerd/containerd/content"
 	"github.com/containerd/containerd/content/local"
 	"github.com/containerd/containerd/content/local"
@@ -37,7 +38,7 @@ type Executor interface {
 	Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error
 	Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error
 	IsRunning(id string) (bool, error)
 	IsRunning(id string) (bool, error)
 	Restore(id string, stdout, stderr io.WriteCloser) (alive bool, err error)
 	Restore(id string, stdout, stderr io.WriteCloser) (alive bool, err error)
-	Signal(id string, signal int) error
+	Signal(id string, signal syscall.Signal) error
 }
 }
 
 
 // EndpointResolver provides looking up registry endpoints for pulling.
 // EndpointResolver provides looking up registry endpoints for pulling.

+ 18 - 19
plugin/manager_linux.go

@@ -154,31 +154,30 @@ const shutdownTimeout = 10 * time.Second
 func shutdownPlugin(p *v2.Plugin, ec chan bool, executor Executor) {
 func shutdownPlugin(p *v2.Plugin, ec chan bool, executor Executor) {
 	pluginID := p.GetID()
 	pluginID := p.GetID()
 
 
-	err := executor.Signal(pluginID, int(unix.SIGTERM))
-	if err != nil {
+	if err := executor.Signal(pluginID, unix.SIGTERM); err != nil {
 		logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
 		logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
-	} else {
+		return
+	}
+
+	timeout := time.NewTimer(shutdownTimeout)
+	defer timeout.Stop()
+
+	select {
+	case <-ec:
+		logrus.Debug("Clean shutdown of plugin")
+	case <-timeout.C:
+		logrus.Debug("Force shutdown plugin")
+		if err := executor.Signal(pluginID, unix.SIGKILL); err != nil {
+			logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
+		}
 
 
-		timeout := time.NewTimer(shutdownTimeout)
-		defer timeout.Stop()
+		timeout.Reset(shutdownTimeout)
 
 
 		select {
 		select {
 		case <-ec:
 		case <-ec:
-			logrus.Debug("Clean shutdown of plugin")
+			logrus.Debug("SIGKILL plugin shutdown")
 		case <-timeout.C:
 		case <-timeout.C:
-			logrus.Debug("Force shutdown plugin")
-			if err := executor.Signal(pluginID, int(unix.SIGKILL)); err != nil {
-				logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
-			}
-
-			timeout.Reset(shutdownTimeout)
-
-			select {
-			case <-ec:
-				logrus.Debug("SIGKILL plugin shutdown")
-			case <-timeout.C:
-				logrus.WithField("plugin", p.Name).Warn("Force shutdown plugin FAILED")
-			}
+			logrus.WithField("plugin", p.Name).Warn("Force shutdown plugin FAILED")
 		}
 		}
 	}
 	}
 }
 }

+ 3 - 13
plugin/manager_linux_test.go

@@ -5,6 +5,7 @@ import (
 	"net"
 	"net"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
+	"syscall"
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
@@ -87,24 +88,13 @@ func newTestPlugin(t *testing.T, name, cap, root string) *v2.Plugin {
 }
 }
 
 
 type simpleExecutor struct {
 type simpleExecutor struct {
+	Executor
 }
 }
 
 
 func (e *simpleExecutor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error {
 func (e *simpleExecutor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error {
 	return errors.New("Create failed")
 	return errors.New("Create failed")
 }
 }
 
 
-func (e *simpleExecutor) Restore(id string, stdout, stderr io.WriteCloser) (bool, error) {
-	return false, nil
-}
-
-func (e *simpleExecutor) IsRunning(id string) (bool, error) {
-	return false, nil
-}
-
-func (e *simpleExecutor) Signal(id string, signal int) error {
-	return nil
-}
-
 func TestCreateFailed(t *testing.T) {
 func TestCreateFailed(t *testing.T) {
 	root, err := os.MkdirTemp("", "test-create-failed")
 	root, err := os.MkdirTemp("", "test-create-failed")
 	if err != nil {
 	if err != nil {
@@ -165,7 +155,7 @@ func (e *executorWithRunning) Restore(id string, stdout, stderr io.WriteCloser)
 	return true, nil
 	return true, nil
 }
 }
 
 
-func (e *executorWithRunning) Signal(id string, signal int) error {
+func (e *executorWithRunning) Signal(id string, signal syscall.Signal) error {
 	ch := e.exitChans[id]
 	ch := e.exitChans[id]
 	ch <- struct{}{}
 	ch <- struct{}{}
 	<-ch
 	<-ch