Browse code

add zombie check for container when killing it, alernative fix for #40735.

Signed-off-by: 屈骏 <qujun@tiduyun.com>

屈骏 authored on 2020/03/27 12:20:41
Showing 2 changed files
... ...
@@ -17,6 +17,7 @@ import (
17 17
 	"github.com/docker/docker/pkg/idtools"
18 18
 	"github.com/docker/docker/pkg/mount"
19 19
 	"github.com/docker/docker/pkg/stringid"
20
+	"github.com/docker/docker/pkg/system"
20 21
 	"github.com/docker/docker/runconfig"
21 22
 	"github.com/docker/libnetwork"
22 23
 	"github.com/opencontainers/selinux/go-selinux/label"
... ...
@@ -353,6 +354,20 @@ func killProcessDirectly(cntr *container.Container) error {
353 353
 				logrus.Debug(e)
354 354
 				return e
355 355
 			}
356
+
357
+			// In case there were some exceptions(e.g., state of zombie and D)
358
+			if system.IsProcessAlive(pid) {
359
+
360
+				// Since we can not kill a zombie pid, add zombie check here
361
+				isZombie, err := system.IsProcessZombie(pid)
362
+				if err != nil {
363
+					logrus.Warnf("Container %s state is invalid", stringid.TruncateID(cntr.ID))
364
+					return err
365
+				}
366
+				if isZombie {
367
+					return errdefs.System(errors.Errorf("container %s PID %d is zombie and can not be killed. Use the --init option when creating containers to run an init inside the container that forwards signals and reaps processes", stringid.TruncateID(cntr.ID), pid))
368
+				}
369
+			}
356 370
 		}
357 371
 	}
358 372
 	return nil
... ...
@@ -3,6 +3,9 @@
3 3
 package system // import "github.com/docker/docker/pkg/system"
4 4
 
5 5
 import (
6
+	"fmt"
7
+	"io/ioutil"
8
+	"strings"
6 9
 	"syscall"
7 10
 
8 11
 	"golang.org/x/sys/unix"
... ...
@@ -22,3 +25,20 @@ func IsProcessAlive(pid int) bool {
22 22
 func KillProcess(pid int) {
23 23
 	unix.Kill(pid, unix.SIGKILL)
24 24
 }
25
+
26
+// IsProcessZombie return true if process has a state with "Z"
27
+// http://man7.org/linux/man-pages/man5/proc.5.html
28
+func IsProcessZombie(pid int) (bool, error) {
29
+	statPath := fmt.Sprintf("/proc/%d/stat", pid)
30
+	dataBytes, err := ioutil.ReadFile(statPath)
31
+	if err != nil {
32
+		return false, err
33
+	}
34
+	data := string(dataBytes)
35
+	sdata := strings.SplitN(data, " ", 4)
36
+	if len(sdata) >= 3 && sdata[2] == "Z" {
37
+		return true, nil
38
+	}
39
+
40
+	return false, nil
41
+}