Blame SOURCES/docker-2000782.patch

f47bc1
From 78b46ab8595c094da910c338839455d8386d57b8 Mon Sep 17 00:00:00 2001
f47bc1
From: "W. Trevor King" <wking@tremily.us>
f47bc1
Date: Wed, 14 Jun 2017 15:27:42 -0700
f47bc1
Subject: [PATCH 1/6] libcontainer/system/proc: Add Stat and Stat_t
f47bc1
f47bc1
So we can extract more than the start time with a single read.
f47bc1
f47bc1
Signed-off-by: W. Trevor King <wking@tremily.us>
f47bc1
(cherry picked from commit 439eaa3584402d239297f278cc1f22c08dbdcc17)
f47bc1
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
f47bc1
---
f47bc1
 libcontainer/system/proc.go      | 35 ++++++++++++++++++++++++++------
f47bc1
 libcontainer/system/proc_test.go |  8 ++++----
f47bc1
 2 files changed, 33 insertions(+), 10 deletions(-)
f47bc1
f47bc1
diff --git a/libcontainer/system/proc.go b/libcontainer/system/proc.go
f47bc1
index a0e96371..4ffb8331 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/system/proc.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/system/proc.go
f47bc1
@@ -1,23 +1,43 @@
f47bc1
 package system
f47bc1
 
f47bc1
 import (
f47bc1
+	"fmt"
f47bc1
 	"io/ioutil"
f47bc1
 	"path/filepath"
f47bc1
 	"strconv"
f47bc1
 	"strings"
f47bc1
 )
f47bc1
 
f47bc1
-// look in /proc to find the process start time so that we can verify
f47bc1
-// that this pid has started after ourself
f47bc1
+// Stat_t represents the information from /proc/[pid]/stat, as
f47bc1
+// described in proc(5).
f47bc1
+type Stat_t struct {
f47bc1
+	// StartTime is the number of clock ticks after system boot (since
f47bc1
+	// Linux 2.6).
f47bc1
+	StartTime uint64
f47bc1
+}
f47bc1
+
f47bc1
+// Stat returns a Stat_t instance for the specified process.
f47bc1
+func Stat(pid int) (stat Stat_t, err error) {
f47bc1
+	bytes, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
f47bc1
+	if err != nil {
f47bc1
+		return stat, err
f47bc1
+	}
f47bc1
+	data := string(bytes)
f47bc1
+	stat.StartTime, err = parseStartTime(data)
f47bc1
+	return stat, err
f47bc1
+}
f47bc1
+
f47bc1
+// GetProcessStartTime is deprecated.  Use Stat(pid) and
f47bc1
+// Stat_t.StartTime instead.
f47bc1
 func GetProcessStartTime(pid int) (string, error) {
f47bc1
-	data, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
f47bc1
+	stat, err := Stat(pid)
f47bc1
 	if err != nil {
f47bc1
 		return "", err
f47bc1
 	}
f47bc1
-	return parseStartTime(string(data))
f47bc1
+	return fmt.Sprintf("%d", stat.StartTime), nil
f47bc1
 }
f47bc1
 
f47bc1
-func parseStartTime(stat string) (string, error) {
f47bc1
+func parseStartTime(stat string) (uint64, error) {
f47bc1
 	// the starttime is located at pos 22
f47bc1
 	// from the man page
f47bc1
 	//
f47bc1
@@ -39,5 +59,8 @@ func parseStartTime(stat string) (string, error) {
f47bc1
 	// get parts after last `)`:
f47bc1
 	s := strings.Split(stat, ")")
f47bc1
 	parts := strings.Split(strings.TrimSpace(s[len(s)-1]), " ")
f47bc1
-	return parts[22-3], nil // starts at 3 (after the filename pos `2`)
f47bc1
+	startTimeString := parts[22-3] // starts at 3 (after the filename pos `2`)
f47bc1
+	var startTime uint64
f47bc1
+	fmt.Sscanf(startTimeString, "%d", &startTime)
f47bc1
+	return startTime, nil
f47bc1
 }
f47bc1
diff --git a/libcontainer/system/proc_test.go b/libcontainer/system/proc_test.go
f47bc1
index ba910291..c7615ef9 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/system/proc_test.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/system/proc_test.go
f47bc1
@@ -3,10 +3,10 @@ package system
f47bc1
 import "testing"
f47bc1
 
f47bc1
 func TestParseStartTime(t *testing.T) {
f47bc1
-	data := map[string]string{
f47bc1
-		"4902 (gunicorn: maste) S 4885 4902 4902 0 -1 4194560 29683 29929 61 83 78 16 96 17 20 0 1 0 9126532 52965376 1903 18446744073709551615 4194304 7461796 140733928751520 140733928698072 139816984959091 0 0 16781312 137447943 1 0 0 17 3 0 0 9 0 0 9559488 10071156 33050624 140733928758775 140733928758945 140733928758945 140733928759264 0": "9126532",
f47bc1
-		"9534 (cat) R 9323 9534 9323 34828 9534 4194304 95 0 0 0 0 0 0 0 20 0 1 0 9214966 7626752 168 18446744073709551615 4194304 4240332 140732237651568 140732237650920 140570710391216 0 0 0 0 0 0 0 17 1 0 0 0 0 0 6340112 6341364 21553152 140732237653865 140732237653885 140732237653885 140732237656047 0":                                      "9214966",
f47bc1
-		"24767 (irq/44-mei_me) S 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 -51 0 1 0 8722075 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 1 50 1 0 0 0 0 0 0 0 0 0 0 0":                                                                                                                                                                         "8722075",
f47bc1
+	data := map[string]uint64{
f47bc1
+		"4902 (gunicorn: maste) S 4885 4902 4902 0 -1 4194560 29683 29929 61 83 78 16 96 17 20 0 1 0 9126532 52965376 1903 18446744073709551615 4194304 7461796 140733928751520 140733928698072 139816984959091 0 0 16781312 137447943 1 0 0 17 3 0 0 9 0 0 9559488 10071156 33050624 140733928758775 140733928758945 140733928758945 140733928759264 0": 9126532,
f47bc1
+		"9534 (cat) R 9323 9534 9323 34828 9534 4194304 95 0 0 0 0 0 0 0 20 0 1 0 9214966 7626752 168 18446744073709551615 4194304 4240332 140732237651568 140732237650920 140570710391216 0 0 0 0 0 0 0 17 1 0 0 0 0 0 6340112 6341364 21553152 140732237653865 140732237653885 140732237653885 140732237656047 0":                                      9214966,
f47bc1
+		"24767 (irq/44-mei_me) S 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 -51 0 1 0 8722075 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 1 50 1 0 0 0 0 0 0 0 0 0 0 0":                                                                                                                                                                         8722075,
f47bc1
 	}
f47bc1
 	for line, startTime := range data {
f47bc1
 		st, err := parseStartTime(line)
f47bc1
f47bc1
From c96dfcb196a08557e0ff434c2c190c288fb1b787 Mon Sep 17 00:00:00 2001
f47bc1
From: "W. Trevor King" <wking@tremily.us>
f47bc1
Date: Wed, 14 Jun 2017 15:38:45 -0700
f47bc1
Subject: [PATCH 2/6] libcontainer: Replace GetProcessStartTime with
f47bc1
 Stat_t.StartTime
f47bc1
f47bc1
And convert the various start-time properties from strings to uint64s.
f47bc1
This removes all internal consumers of the deprecated
f47bc1
GetProcessStartTime function.
f47bc1
f47bc1
Signed-off-by: W. Trevor King <wking@tremily.us>
f47bc1
(cherry picked from commit 75d98b26b7acb0023b990a7305a2553c6dbc12d4)
f47bc1
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
f47bc1
---
f47bc1
 libcontainer/container.go            |  2 +-
f47bc1
 libcontainer/container_linux.go      | 10 +++++-----
f47bc1
 libcontainer/container_linux_test.go | 10 +++++-----
f47bc1
 libcontainer/process_linux.go        | 12 +++++++-----
f47bc1
 libcontainer/restored_process.go     | 12 ++++++------
f47bc1
 5 files changed, 24 insertions(+), 22 deletions(-)
f47bc1
f47bc1
diff --git a/libcontainer/container.go b/libcontainer/container.go
f47bc1
index d51b159b..899925dd 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container.go
f47bc1
@@ -54,7 +54,7 @@ type BaseState struct {
f47bc1
 	InitProcessPid int `json:"init_process_pid"`
f47bc1
 
f47bc1
 	// InitProcessStartTime is the init process start time in clock cycles since boot time.
f47bc1
-	InitProcessStartTime string `json:"init_process_start"`
f47bc1
+	InitProcessStartTime uint64 `json:"init_process_start"`
f47bc1
 
f47bc1
 	// Created is the unix timestamp for the creation time of the container in UTC
f47bc1
 	Created time.Time `json:"created"`
f47bc1
diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
f47bc1
index 45688d60..98efcfcc 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
@@ -37,7 +37,7 @@ type linuxContainer struct {
f47bc1
 	cgroupManager        cgroups.Manager
f47bc1
 	initArgs             []string
f47bc1
 	initProcess          parentProcess
f47bc1
-	initProcessStartTime string
f47bc1
+	initProcessStartTime uint64
f47bc1
 	criuPath             string
f47bc1
 	m                    sync.Mutex
f47bc1
 	criuVersion          int
f47bc1
@@ -1117,11 +1117,11 @@ func (c *linuxContainer) refreshState() error {
f47bc1
 // and a new process has been created with the same pid, in this case, the
f47bc1
 // container would already be stopped.
f47bc1
 func (c *linuxContainer) doesInitProcessExist(initPid int) (bool, error) {
f47bc1
-	startTime, err := system.GetProcessStartTime(initPid)
f47bc1
+	stat, err := system.Stat(initPid)
f47bc1
 	if err != nil {
f47bc1
-		return false, newSystemErrorWithCausef(err, "getting init process %d start time", initPid)
f47bc1
+		return false, newSystemErrorWithCausef(err, "getting init process %d status", initPid)
f47bc1
 	}
f47bc1
-	if c.initProcessStartTime != startTime {
f47bc1
+	if c.initProcessStartTime != stat.StartTime {
f47bc1
 		return false, nil
f47bc1
 	}
f47bc1
 	return true, nil
f47bc1
@@ -1174,7 +1174,7 @@ func (c *linuxContainer) isPaused() (bool, error) {
f47bc1
 
f47bc1
 func (c *linuxContainer) currentState() (*State, error) {
f47bc1
 	var (
f47bc1
-		startTime           string
f47bc1
+		startTime           uint64
f47bc1
 		externalDescriptors []string
f47bc1
 		pid                 = -1
f47bc1
 	)
f47bc1
diff --git a/libcontainer/container_linux_test.go b/libcontainer/container_linux_test.go
f47bc1
index b69e3449..3fdd4a80 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux_test.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux_test.go
f47bc1
@@ -52,7 +52,7 @@ func (m *mockCgroupManager) Freeze(state configs.FreezerState) error {
f47bc1
 
f47bc1
 type mockProcess struct {
f47bc1
 	_pid    int
f47bc1
-	started string
f47bc1
+	started uint64
f47bc1
 }
f47bc1
 
f47bc1
 func (m *mockProcess) terminate() error {
f47bc1
@@ -63,7 +63,7 @@ func (m *mockProcess) pid() int {
f47bc1
 	return m._pid
f47bc1
 }
f47bc1
 
f47bc1
-func (m *mockProcess) startTime() (string, error) {
f47bc1
+func (m *mockProcess) startTime() (uint64, error) {
f47bc1
 	return m.started, nil
f47bc1
 }
f47bc1
 
f47bc1
@@ -150,7 +150,7 @@ func TestGetContainerState(t *testing.T) {
f47bc1
 		},
f47bc1
 		initProcess: &mockProcess{
f47bc1
 			_pid:    pid,
f47bc1
-			started: "010",
f47bc1
+			started: 10,
f47bc1
 		},
f47bc1
 		cgroupManager: &mockCgroupManager{
f47bc1
 			pids: []int{1, 2, 3},
f47bc1
@@ -174,8 +174,8 @@ func TestGetContainerState(t *testing.T) {
f47bc1
 	if state.InitProcessPid != pid {
f47bc1
 		t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid)
f47bc1
 	}
f47bc1
-	if state.InitProcessStartTime != "010" {
f47bc1
-		t.Fatalf("expected process start time 010 but received %s", state.InitProcessStartTime)
f47bc1
+	if state.InitProcessStartTime != 10 {
f47bc1
+		t.Fatalf("expected process start time 10 but received %d", state.InitProcessStartTime)
f47bc1
 	}
f47bc1
 	paths := state.CgroupPaths
f47bc1
 	if paths == nil {
f47bc1
diff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go
f47bc1
index 53df9fa5..1232a7b2 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/process_linux.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/process_linux.go
f47bc1
@@ -33,7 +33,7 @@ type parentProcess interface {
f47bc1
 	wait() (*os.ProcessState, error)
f47bc1
 
f47bc1
 	// startTime returns the process start time.
f47bc1
-	startTime() (string, error)
f47bc1
+	startTime() (uint64, error)
f47bc1
 
f47bc1
 	signal(os.Signal) error
f47bc1
 
f47bc1
@@ -54,8 +54,9 @@ type setnsProcess struct {
f47bc1
 	rootDir       *os.File
f47bc1
 }
f47bc1
 
f47bc1
-func (p *setnsProcess) startTime() (string, error) {
f47bc1
-	return system.GetProcessStartTime(p.pid())
f47bc1
+func (p *setnsProcess) startTime() (uint64, error) {
f47bc1
+	stat, err := system.Stat(p.pid())
f47bc1
+	return stat.StartTime, err
f47bc1
 }
f47bc1
 
f47bc1
 func (p *setnsProcess) signal(sig os.Signal) error {
f47bc1
@@ -406,8 +407,9 @@ func (p *initProcess) terminate() error {
f47bc1
 	return err
f47bc1
 }
f47bc1
 
f47bc1
-func (p *initProcess) startTime() (string, error) {
f47bc1
-	return system.GetProcessStartTime(p.pid())
f47bc1
+func (p *initProcess) startTime() (uint64, error) {
f47bc1
+	stat, err := system.Stat(p.pid())
f47bc1
+	return stat.StartTime, err
f47bc1
 }
f47bc1
 
f47bc1
 func (p *initProcess) sendConfig() error {
f47bc1
diff --git a/libcontainer/restored_process.go b/libcontainer/restored_process.go
f47bc1
index a96f4ca5..408916ad 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/restored_process.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/restored_process.go
f47bc1
@@ -17,20 +17,20 @@ func newRestoredProcess(pid int, fds []string) (*restoredProcess, error) {
f47bc1
 	if err != nil {
f47bc1
 		return nil, err
f47bc1
 	}
f47bc1
-	started, err := system.GetProcessStartTime(pid)
f47bc1
+	stat, err := system.Stat(pid)
f47bc1
 	if err != nil {
f47bc1
 		return nil, err
f47bc1
 	}
f47bc1
 	return &restoredProcess{
f47bc1
 		proc:             proc,
f47bc1
-		processStartTime: started,
f47bc1
+		processStartTime: stat.StartTime,
f47bc1
 		fds:              fds,
f47bc1
 	}, nil
f47bc1
 }
f47bc1
 
f47bc1
 type restoredProcess struct {
f47bc1
 	proc             *os.Process
f47bc1
-	processStartTime string
f47bc1
+	processStartTime uint64
f47bc1
 	fds              []string
f47bc1
 }
f47bc1
 
f47bc1
@@ -60,7 +60,7 @@ func (p *restoredProcess) wait() (*os.ProcessState, error) {
f47bc1
 	return st, nil
f47bc1
 }
f47bc1
 
f47bc1
-func (p *restoredProcess) startTime() (string, error) {
f47bc1
+func (p *restoredProcess) startTime() (uint64, error) {
f47bc1
 	return p.processStartTime, nil
f47bc1
 }
f47bc1
 
f47bc1
@@ -81,7 +81,7 @@ func (p *restoredProcess) setExternalDescriptors(newFds []string) {
f47bc1
 // a persisted state.
f47bc1
 type nonChildProcess struct {
f47bc1
 	processPid       int
f47bc1
-	processStartTime string
f47bc1
+	processStartTime uint64
f47bc1
 	fds              []string
f47bc1
 }
f47bc1
 
f47bc1
@@ -101,7 +101,7 @@ func (p *nonChildProcess) wait() (*os.ProcessState, error) {
f47bc1
 	return nil, newGenericError(fmt.Errorf("restored process cannot be waited on"), SystemError)
f47bc1
 }
f47bc1
 
f47bc1
-func (p *nonChildProcess) startTime() (string, error) {
f47bc1
+func (p *nonChildProcess) startTime() (uint64, error) {
f47bc1
 	return p.processStartTime, nil
f47bc1
 }
f47bc1
 
f47bc1
f47bc1
From 0d15fca2013888bb496b6239274e8a4c69386642 Mon Sep 17 00:00:00 2001
f47bc1
From: "W. Trevor King" <wking@tremily.us>
f47bc1
Date: Wed, 14 Jun 2017 16:41:16 -0700
f47bc1
Subject: [PATCH 3/6] libcontainer/system/proc: Add Stat_t.State
f47bc1
f47bc1
And Stat_t.PID and Stat_t.Name while we're at it.  Then use the new
f47bc1
.State property in runType to distinguish between running and
f47bc1
zombie/dead processes, since kill(2) does not [1].  With this change
f47bc1
we no longer claim Running status for zombie/dead processes.
f47bc1
f47bc1
I've also removed the kill(2) call from runType.  It was originally
f47bc1
added in 13841ef3 (new-api: return the Running state only if the init
f47bc1
process is alive, 2014-12-23), but we've been accessing
f47bc1
/proc/[pid]/stat since 14e95b2a (Make state detection precise,
f47bc1
2016-07-05, #930), and with the /stat access the kill(2) check is
f47bc1
redundant.
f47bc1
f47bc1
I also don't see much point to the previously-separate
f47bc1
doesInitProcessExist, so I've inlined that logic in runType.
f47bc1
f47bc1
It would be nice to distinguish between "/proc/[pid]/stat doesn't
f47bc1
exist" and errors parsing its contents, but I've skipped that for the
f47bc1
moment.
f47bc1
f47bc1
The Running -> Stopped change in checkpoint_test.go is because the
f47bc1
post-checkpoint process is a zombie, and with this commit zombie
f47bc1
processes are Stopped (and no longer Running).
f47bc1
f47bc1
[1]: https://github.com/opencontainers/runc/pull/1483#issuecomment-307527789
f47bc1
f47bc1
Signed-off-by: W. Trevor King <wking@tremily.us>
f47bc1
(cherry picked from commit 2bea4c897e68475c0e698f7ebec4ba989ea0cda0)
f47bc1
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
f47bc1
---
f47bc1
 libcontainer/container_linux.go             |  33 +------
f47bc1
 libcontainer/integration/checkpoint_test.go |   2 +-
f47bc1
 libcontainer/system/proc.go                 | 103 ++++++++++++++------
f47bc1
 libcontainer/system/proc_test.go            |  41 ++++++--
f47bc1
 4 files changed, 114 insertions(+), 65 deletions(-)
f47bc1
f47bc1
diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
f47bc1
index 98efcfcc..0d478702 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
@@ -1112,40 +1112,17 @@ func (c *linuxContainer) refreshState() error {
f47bc1
 	return c.state.transition(&stoppedState{c: c})
f47bc1
 }
f47bc1
 
f47bc1
-// doesInitProcessExist checks if the init process is still the same process
f47bc1
-// as the initial one, it could happen that the original process has exited
f47bc1
-// and a new process has been created with the same pid, in this case, the
f47bc1
-// container would already be stopped.
f47bc1
-func (c *linuxContainer) doesInitProcessExist(initPid int) (bool, error) {
f47bc1
-	stat, err := system.Stat(initPid)
f47bc1
-	if err != nil {
f47bc1
-		return false, newSystemErrorWithCausef(err, "getting init process %d status", initPid)
f47bc1
-	}
f47bc1
-	if c.initProcessStartTime != stat.StartTime {
f47bc1
-		return false, nil
f47bc1
-	}
f47bc1
-	return true, nil
f47bc1
-}
f47bc1
-
f47bc1
 func (c *linuxContainer) runType() (Status, error) {
f47bc1
 	if c.initProcess == nil {
f47bc1
 		return Stopped, nil
f47bc1
 	}
f47bc1
 	pid := c.initProcess.pid()
f47bc1
-	// return Running if the init process is alive
f47bc1
-	if err := syscall.Kill(pid, 0); err != nil {
f47bc1
-		if err == syscall.ESRCH {
f47bc1
-			// It means the process does not exist anymore, could happen when the
f47bc1
-			// process exited just when we call the function, we should not return
f47bc1
-			// error in this case.
f47bc1
-			return Stopped, nil
f47bc1
-		}
f47bc1
-		return Stopped, newSystemErrorWithCausef(err, "sending signal 0 to pid %d", pid)
f47bc1
+	stat, err := system.Stat(pid)
f47bc1
+	if err != nil {
f47bc1
+		return Stopped, nil
f47bc1
 	}
f47bc1
-	// check if the process is still the original init process.
f47bc1
-	exist, err := c.doesInitProcessExist(pid)
f47bc1
-	if !exist || err != nil {
f47bc1
-		return Stopped, err
f47bc1
+	if stat.StartTime != c.initProcessStartTime || stat.State == system.Zombie || stat.State == system.Dead {
f47bc1
+		return Stopped, nil
f47bc1
 	}
f47bc1
 	// check if the process that is running is the init process or the user's process.
f47bc1
 	// this is the difference between the container Running and Created.
f47bc1
diff --git a/libcontainer/integration/checkpoint_test.go b/libcontainer/integration/checkpoint_test.go
f47bc1
index 5fe09d5e..ea1853f4 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/integration/checkpoint_test.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/integration/checkpoint_test.go
f47bc1
@@ -130,7 +130,7 @@ func TestCheckpoint(t *testing.T) {
f47bc1
 		t.Fatal(err)
f47bc1
 	}
f47bc1
 
f47bc1
-	if state != libcontainer.Running {
f47bc1
+	if state != libcontainer.Stopped {
f47bc1
 		t.Fatal("Unexpected state checkpoint: ", state)
f47bc1
 	}
f47bc1
 
f47bc1
diff --git a/libcontainer/system/proc.go b/libcontainer/system/proc.go
f47bc1
index 4ffb8331..79232a43 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/system/proc.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/system/proc.go
f47bc1
@@ -8,9 +8,55 @@ import (
f47bc1
 	"strings"
f47bc1
 )
f47bc1
 
f47bc1
+// State is the status of a process.
f47bc1
+type State rune
f47bc1
+
f47bc1
+const ( // Only values for Linux 3.14 and later are listed here
f47bc1
+	Dead        State = 'X'
f47bc1
+	DiskSleep   State = 'D'
f47bc1
+	Running     State = 'R'
f47bc1
+	Sleeping    State = 'S'
f47bc1
+	Stopped     State = 'T'
f47bc1
+	TracingStop State = 't'
f47bc1
+	Zombie      State = 'Z'
f47bc1
+)
f47bc1
+
f47bc1
+// String forms of the state from proc(5)'s documentation for
f47bc1
+// /proc/[pid]/status' "State" field.
f47bc1
+func (s State) String() string {
f47bc1
+	switch s {
f47bc1
+	case Dead:
f47bc1
+		return "dead"
f47bc1
+	case DiskSleep:
f47bc1
+		return "disk sleep"
f47bc1
+	case Running:
f47bc1
+		return "running"
f47bc1
+	case Sleeping:
f47bc1
+		return "sleeping"
f47bc1
+	case Stopped:
f47bc1
+		return "stopped"
f47bc1
+	case TracingStop:
f47bc1
+		return "tracing stop"
f47bc1
+	case Zombie:
f47bc1
+		return "zombie"
f47bc1
+	default:
f47bc1
+		return fmt.Sprintf("unknown (%c)", s)
f47bc1
+	}
f47bc1
+}
f47bc1
+
f47bc1
 // Stat_t represents the information from /proc/[pid]/stat, as
f47bc1
-// described in proc(5).
f47bc1
+// described in proc(5) with names based on the /proc/[pid]/status
f47bc1
+// fields.
f47bc1
 type Stat_t struct {
f47bc1
+	// PID is the process ID.
f47bc1
+	PID uint
f47bc1
+
f47bc1
+	// Name is the command run by the process.
f47bc1
+	Name string
f47bc1
+
f47bc1
+	// State is the state of the process.
f47bc1
+	State State
f47bc1
+
f47bc1
 	// StartTime is the number of clock ticks after system boot (since
f47bc1
 	// Linux 2.6).
f47bc1
 	StartTime uint64
f47bc1
@@ -22,9 +68,7 @@ func Stat(pid int) (stat Stat_t, err error) {
f47bc1
 	if err != nil {
f47bc1
 		return stat, err
f47bc1
 	}
f47bc1
-	data := string(bytes)
f47bc1
-	stat.StartTime, err = parseStartTime(data)
f47bc1
-	return stat, err
f47bc1
+	return parseStat(string(bytes))
f47bc1
 }
f47bc1
 
f47bc1
 // GetProcessStartTime is deprecated.  Use Stat(pid) and
f47bc1
@@ -37,30 +81,33 @@ func GetProcessStartTime(pid int) (string, error) {
f47bc1
 	return fmt.Sprintf("%d", stat.StartTime), nil
f47bc1
 }
f47bc1
 
f47bc1
-func parseStartTime(stat string) (uint64, error) {
f47bc1
-	// the starttime is located at pos 22
f47bc1
-	// from the man page
f47bc1
-	//
f47bc1
-	// starttime %llu (was %lu before Linux 2.6)
f47bc1
-	// (22)  The  time the process started after system boot.  In kernels before Linux 2.6, this
f47bc1
-	// value was expressed in jiffies.  Since Linux 2.6, the value is expressed in  clock  ticks
f47bc1
-	// (divide by sysconf(_SC_CLK_TCK)).
f47bc1
-	//
f47bc1
-	// NOTE:
f47bc1
-	// pos 2 could contain space and is inside `(` and `)`:
f47bc1
-	// (2) comm  %s
f47bc1
-	// The filename of the executable, in parentheses.
f47bc1
-	// This is visible whether or not the executable is
f47bc1
-	// swapped out.
f47bc1
-	//
f47bc1
-	// the following is an example:
f47bc1
+func parseStat(data string) (stat Stat_t, err error) {
f47bc1
+	// From proc(5), field 2 could contain space and is inside `(` and `)`.
f47bc1
+	// The following is an example:
f47bc1
 	// 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
f47bc1
+	i := strings.LastIndex(data, ")")
f47bc1
+	if i <= 2 || i >= len(data)-1 {
f47bc1
+		return stat, fmt.Errorf("invalid stat data: %q", data)
f47bc1
+	}
f47bc1
+
f47bc1
+	parts := strings.SplitN(data[:i], "(", 2)
f47bc1
+	if len(parts) != 2 {
f47bc1
+		return stat, fmt.Errorf("invalid stat data: %q", data)
f47bc1
+	}
f47bc1
+
f47bc1
+	stat.Name = parts[1]
f47bc1
+	_, err = fmt.Sscanf(parts[0], "%d", &stat.PID)
f47bc1
+	if err != nil {
f47bc1
+		return stat, err
f47bc1
+	}
f47bc1
 
f47bc1
-	// get parts after last `)`:
f47bc1
-	s := strings.Split(stat, ")")
f47bc1
-	parts := strings.Split(strings.TrimSpace(s[len(s)-1]), " ")
f47bc1
-	startTimeString := parts[22-3] // starts at 3 (after the filename pos `2`)
f47bc1
-	var startTime uint64
f47bc1
-	fmt.Sscanf(startTimeString, "%d", &startTime)
f47bc1
-	return startTime, nil
f47bc1
+	// parts indexes should be offset by 3 from the field number given
f47bc1
+	// proc(5), because parts is zero-indexed and we've removed fields
f47bc1
+	// one (PID) and two (Name) in the paren-split.
f47bc1
+	parts = strings.Split(data[i+2:], " ")
f47bc1
+	var state int
f47bc1
+	fmt.Sscanf(parts[3-3], "%c", &state)
f47bc1
+	stat.State = State(state)
f47bc1
+	fmt.Sscanf(parts[22-3], "%d", &stat.StartTime)
f47bc1
+	return stat, nil
f47bc1
 }
f47bc1
diff --git a/libcontainer/system/proc_test.go b/libcontainer/system/proc_test.go
f47bc1
index c7615ef9..7e1acc5b 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/system/proc_test.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/system/proc_test.go
f47bc1
@@ -3,18 +3,43 @@ package system
f47bc1
 import "testing"
f47bc1
 
f47bc1
 func TestParseStartTime(t *testing.T) {
f47bc1
-	data := map[string]uint64{
f47bc1
-		"4902 (gunicorn: maste) S 4885 4902 4902 0 -1 4194560 29683 29929 61 83 78 16 96 17 20 0 1 0 9126532 52965376 1903 18446744073709551615 4194304 7461796 140733928751520 140733928698072 139816984959091 0 0 16781312 137447943 1 0 0 17 3 0 0 9 0 0 9559488 10071156 33050624 140733928758775 140733928758945 140733928758945 140733928759264 0": 9126532,
f47bc1
-		"9534 (cat) R 9323 9534 9323 34828 9534 4194304 95 0 0 0 0 0 0 0 20 0 1 0 9214966 7626752 168 18446744073709551615 4194304 4240332 140732237651568 140732237650920 140570710391216 0 0 0 0 0 0 0 17 1 0 0 0 0 0 6340112 6341364 21553152 140732237653865 140732237653885 140732237653885 140732237656047 0":                                      9214966,
f47bc1
-		"24767 (irq/44-mei_me) S 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 -51 0 1 0 8722075 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 1 50 1 0 0 0 0 0 0 0 0 0 0 0":                                                                                                                                                                         8722075,
f47bc1
+	data := map[string]Stat_t{
f47bc1
+		"4902 (gunicorn: maste) S 4885 4902 4902 0 -1 4194560 29683 29929 61 83 78 16 96 17 20 0 1 0 9126532 52965376 1903 18446744073709551615 4194304 7461796 140733928751520 140733928698072 139816984959091 0 0 16781312 137447943 1 0 0 17 3 0 0 9 0 0 9559488 10071156 33050624 140733928758775 140733928758945 140733928758945 140733928759264 0": {
f47bc1
+			PID:       4902,
f47bc1
+			Name:      "gunicorn: maste",
f47bc1
+			State:     'S',
f47bc1
+			StartTime: 9126532,
f47bc1
+		},
f47bc1
+		"9534 (cat) R 9323 9534 9323 34828 9534 4194304 95 0 0 0 0 0 0 0 20 0 1 0 9214966 7626752 168 18446744073709551615 4194304 4240332 140732237651568 140732237650920 140570710391216 0 0 0 0 0 0 0 17 1 0 0 0 0 0 6340112 6341364 21553152 140732237653865 140732237653885 140732237653885 140732237656047 0": {
f47bc1
+			PID:       9534,
f47bc1
+			Name:      "cat",
f47bc1
+			State:     'R',
f47bc1
+			StartTime: 9214966,
f47bc1
+		},
f47bc1
+
f47bc1
+		"24767 (irq/44-mei_me) S 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 -51 0 1 0 8722075 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 1 50 1 0 0 0 0 0 0 0 0 0 0 0": {
f47bc1
+			PID:       24767,
f47bc1
+			Name:      "irq/44-mei_me",
f47bc1
+			State:     'S',
f47bc1
+			StartTime: 8722075,
f47bc1
+		},
f47bc1
 	}
f47bc1
-	for line, startTime := range data {
f47bc1
-		st, err := parseStartTime(line)
f47bc1
+	for line, expected := range data {
f47bc1
+		st, err := parseStat(line)
f47bc1
 		if err != nil {
f47bc1
 			t.Fatal(err)
f47bc1
 		}
f47bc1
-		if startTime != st {
f47bc1
-			t.Fatalf("expected start time %q but received %q", startTime, st)
f47bc1
+		if st.PID != expected.PID {
f47bc1
+			t.Fatalf("expected PID %q but received %q", expected.PID, st.PID)
f47bc1
+		}
f47bc1
+		if st.State != expected.State {
f47bc1
+			t.Fatalf("expected state %q but received %q", expected.State, st.State)
f47bc1
+		}
f47bc1
+		if st.Name != expected.Name {
f47bc1
+			t.Fatalf("expected name %q but received %q", expected.Name, st.Name)
f47bc1
+		}
f47bc1
+		if st.StartTime != expected.StartTime {
f47bc1
+			t.Fatalf("expected start time %q but received %q", expected.StartTime, st.StartTime)
f47bc1
 		}
f47bc1
 	}
f47bc1
 }
f47bc1
f47bc1
From 9c340aade60d5b8ca0916b78340a199265129b8d Mon Sep 17 00:00:00 2001
f47bc1
From: Will Martin <wmartin@pivotal.io>
f47bc1
Date: Mon, 22 Jan 2018 17:03:02 +0000
f47bc1
Subject: [PATCH 4/6] Avoid race when opening exec fifo
f47bc1
f47bc1
When starting a container with `runc start` or `runc run`, the stub
f47bc1
process (runc[2:INIT]) opens a fifo for writing. Its parent runc process
f47bc1
will open the same fifo for reading. In this way, they synchronize.
f47bc1
f47bc1
If the stub process exits at the wrong time, the parent runc process
f47bc1
will block forever.
f47bc1
f47bc1
This can happen when racing 2 runc operations against each other: `runc
f47bc1
run/start`, and `runc delete`. It could also happen for other reasons,
f47bc1
e.g. the kernel's OOM killer may select the stub process.
f47bc1
f47bc1
This commit resolves this race by racing the opening of the exec fifo
f47bc1
from the runc parent process against the stub process exiting. If the
f47bc1
stub process exits before we open the fifo, we return an error.
f47bc1
f47bc1
Another solution is to wait on the stub process. However, it seems it
f47bc1
would require more refactoring to avoid calling wait multiple times on
f47bc1
the same process, which is an error.
f47bc1
f47bc1
Signed-off-by: Craig Furman <cfurman@pivotal.io>
f47bc1
(cherry picked from commit 8d3e6c9826815cf6f75dbd56c7b5a23fb5666108)
f47bc1
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
f47bc1
---
f47bc1
 libcontainer/container_linux.go | 69 ++++++++++++++++++++++++++++-----
f47bc1
 1 file changed, 60 insertions(+), 9 deletions(-)
f47bc1
f47bc1
diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
f47bc1
index 0d478702..9e128fec 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
@@ -5,6 +5,7 @@ package libcontainer
f47bc1
 import (
f47bc1
 	"bytes"
f47bc1
 	"encoding/json"
f47bc1
+	"errors"
f47bc1
 	"fmt"
f47bc1
 	"io"
f47bc1
 	"io/ioutil"
f47bc1
@@ -206,20 +207,70 @@ func (c *linuxContainer) Exec() error {
f47bc1
 
f47bc1
 func (c *linuxContainer) exec() error {
f47bc1
 	path := filepath.Join(c.root, execFifoFilename)
f47bc1
-	f, err := os.OpenFile(path, os.O_RDONLY, 0)
f47bc1
-	if err != nil {
f47bc1
-		return newSystemErrorWithCause(err, "open exec fifo for reading")
f47bc1
+
f47bc1
+	fifoOpen := make(chan struct{})
f47bc1
+	select {
f47bc1
+	case <-awaitProcessExit(c.initProcess.pid(), fifoOpen):
f47bc1
+		return errors.New("container process is already dead")
f47bc1
+	case result := <-awaitFifoOpen(path):
f47bc1
+		close(fifoOpen)
f47bc1
+		if result.err != nil {
f47bc1
+			return result.err
f47bc1
+		}
f47bc1
+		f := result.file
f47bc1
+		defer f.Close()
f47bc1
+		if err := readFromExecFifo(f); err != nil {
f47bc1
+			return err
f47bc1
+		}
f47bc1
+		return os.Remove(path)
f47bc1
 	}
f47bc1
-	defer f.Close()
f47bc1
-	data, err := ioutil.ReadAll(f)
f47bc1
+}
f47bc1
+
f47bc1
+func readFromExecFifo(execFifo io.Reader) error {
f47bc1
+	data, err := ioutil.ReadAll(execFifo)
f47bc1
 	if err != nil {
f47bc1
 		return err
f47bc1
 	}
f47bc1
-	if len(data) > 0 {
f47bc1
-		os.Remove(path)
f47bc1
-		return nil
f47bc1
+	if len(data) <= 0 {
f47bc1
+		return fmt.Errorf("cannot start an already running container")
f47bc1
 	}
f47bc1
-	return fmt.Errorf("cannot start an already running container")
f47bc1
+	return nil
f47bc1
+}
f47bc1
+
f47bc1
+func awaitProcessExit(pid int, exit <-chan struct{}) <-chan struct{} {
f47bc1
+	isDead := make(chan struct{})
f47bc1
+	go func() {
f47bc1
+		for {
f47bc1
+			select {
f47bc1
+			case <-exit:
f47bc1
+				return
f47bc1
+			case <-time.After(time.Millisecond * 100):
f47bc1
+				stat, err := system.Stat(pid)
f47bc1
+				if err != nil || stat.State == system.Zombie {
f47bc1
+					close(isDead)
f47bc1
+					return
f47bc1
+				}
f47bc1
+			}
f47bc1
+		}
f47bc1
+	}()
f47bc1
+	return isDead
f47bc1
+}
f47bc1
+
f47bc1
+func awaitFifoOpen(path string) <-chan openResult {
f47bc1
+	fifoOpened := make(chan openResult)
f47bc1
+	go func() {
f47bc1
+		f, err := os.OpenFile(path, os.O_RDONLY, 0)
f47bc1
+		if err != nil {
f47bc1
+			fifoOpened <- openResult{err: newSystemErrorWithCause(err, "open exec fifo for reading")}
f47bc1
+		}
f47bc1
+		fifoOpened <- openResult{file: f}
f47bc1
+	}()
f47bc1
+	return fifoOpened
f47bc1
+}
f47bc1
+
f47bc1
+type openResult struct {
f47bc1
+	file *os.File
f47bc1
+	err  error
f47bc1
 }
f47bc1
 
f47bc1
 func (c *linuxContainer) start(process *Process) error {
f47bc1
f47bc1
From aba6dc87301b63d06691d7f867bdebf2b291fac1 Mon Sep 17 00:00:00 2001
f47bc1
From: Ed King <eking@pivotal.io>
f47bc1
Date: Tue, 23 Jan 2018 10:46:31 +0000
f47bc1
Subject: [PATCH 5/6] Return from goroutine when it should terminate
f47bc1
f47bc1
Signed-off-by: Craig Furman <cfurman@pivotal.io>
f47bc1
(cherry picked from commit 5c0af14bf8925b845d91cb94fc1d5ab18140a81a)
f47bc1
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
f47bc1
---
f47bc1
 libcontainer/container_linux.go | 1 +
f47bc1
 1 file changed, 1 insertion(+)
f47bc1
f47bc1
diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
f47bc1
index 9e128fec..d5a30496 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
@@ -262,6 +262,7 @@ func awaitFifoOpen(path string) <-chan openResult {
f47bc1
 		f, err := os.OpenFile(path, os.O_RDONLY, 0)
f47bc1
 		if err != nil {
f47bc1
 			fifoOpened <- openResult{err: newSystemErrorWithCause(err, "open exec fifo for reading")}
f47bc1
+			return
f47bc1
 		}
f47bc1
 		fifoOpened <- openResult{file: f}
f47bc1
 	}()
f47bc1
f47bc1
From 9471a96c113b05f0a0a84c9afd8e3979e7409508 Mon Sep 17 00:00:00 2001
f47bc1
From: Jordan Liggitt <liggitt@google.com>
f47bc1
Date: Wed, 18 Dec 2019 15:20:53 +0000
f47bc1
Subject: [PATCH 6/6] Fix race checking for process exit and waiting for exec
f47bc1
 fifo
f47bc1
f47bc1
Signed-off-by: Jordan Liggitt <liggitt@google.com>
f47bc1
(cherry picked from commit 8541d9cf3d8b6d46614cc41aaf38eaa2419549df)
f47bc1
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
f47bc1
---
f47bc1
 libcontainer/container_linux.go | 83 +++++++++++++++++----------------
f47bc1
 1 file changed, 43 insertions(+), 40 deletions(-)
f47bc1
f47bc1
diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
f47bc1
index d5a30496..fda163da 100644
f47bc1
--- docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
+++ docker-7d71120b1f31f8bb4da733b5f1e5960b434696de/runc-8891bca22c049cd2dcf13ba2438c0bac8d7f3343/libcontainer/container_linux.go
f47bc1
@@ -207,22 +207,24 @@ func (c *linuxContainer) Exec() error {
f47bc1
 
f47bc1
 func (c *linuxContainer) exec() error {
f47bc1
 	path := filepath.Join(c.root, execFifoFilename)
f47bc1
-
f47bc1
-	fifoOpen := make(chan struct{})
f47bc1
-	select {
f47bc1
-	case <-awaitProcessExit(c.initProcess.pid(), fifoOpen):
f47bc1
-		return errors.New("container process is already dead")
f47bc1
-	case result := <-awaitFifoOpen(path):
f47bc1
-		close(fifoOpen)
f47bc1
-		if result.err != nil {
f47bc1
-			return result.err
f47bc1
-		}
f47bc1
-		f := result.file
f47bc1
-		defer f.Close()
f47bc1
-		if err := readFromExecFifo(f); err != nil {
f47bc1
-			return err
f47bc1
+	pid := c.initProcess.pid()
f47bc1
+	blockingFifoOpenCh := awaitFifoOpen(path)
f47bc1
+	for {
f47bc1
+		select {
f47bc1
+		case result := <-blockingFifoOpenCh:
f47bc1
+			return handleFifoResult(result)
f47bc1
+
f47bc1
+		case <-time.After(time.Millisecond * 100):
f47bc1
+			stat, err := system.Stat(pid)
f47bc1
+			if err != nil || stat.State == system.Zombie {
f47bc1
+				// could be because process started, ran, and completed between our 100ms timeout and our system.Stat() check.
f47bc1
+				// see if the fifo exists and has data (with a non-blocking open, which will succeed if the writing process is complete).
f47bc1
+				if err := handleFifoResult(fifoOpen(path, false)); err != nil {
f47bc1
+					return errors.New("container process is already dead")
f47bc1
+				}
f47bc1
+				return nil
f47bc1
+			}
f47bc1
 		}
f47bc1
-		return os.Remove(path)
f47bc1
 	}
f47bc1
 }
f47bc1
 
f47bc1
@@ -237,38 +239,39 @@ func readFromExecFifo(execFifo io.Reader) error {
f47bc1
 	return nil
f47bc1
 }
f47bc1
 
f47bc1
-func awaitProcessExit(pid int, exit <-chan struct{}) <-chan struct{} {
f47bc1
-	isDead := make(chan struct{})
f47bc1
-	go func() {
f47bc1
-		for {
f47bc1
-			select {
f47bc1
-			case <-exit:
f47bc1
-				return
f47bc1
-			case <-time.After(time.Millisecond * 100):
f47bc1
-				stat, err := system.Stat(pid)
f47bc1
-				if err != nil || stat.State == system.Zombie {
f47bc1
-					close(isDead)
f47bc1
-					return
f47bc1
-				}
f47bc1
-			}
f47bc1
-		}
f47bc1
-	}()
f47bc1
-	return isDead
f47bc1
-}
f47bc1
-
f47bc1
 func awaitFifoOpen(path string) <-chan openResult {
f47bc1
 	fifoOpened := make(chan openResult)
f47bc1
 	go func() {
f47bc1
-		f, err := os.OpenFile(path, os.O_RDONLY, 0)
f47bc1
-		if err != nil {
f47bc1
-			fifoOpened <- openResult{err: newSystemErrorWithCause(err, "open exec fifo for reading")}
f47bc1
-			return
f47bc1
-		}
f47bc1
-		fifoOpened <- openResult{file: f}
f47bc1
+		result := fifoOpen(path, true)
f47bc1
+		fifoOpened <- result
f47bc1
 	}()
f47bc1
 	return fifoOpened
f47bc1
 }
f47bc1
 
f47bc1
+func fifoOpen(path string, block bool) openResult {
f47bc1
+	flags := os.O_RDONLY
f47bc1
+	if !block {
f47bc1
+		flags |= syscall.O_NONBLOCK
f47bc1
+	}
f47bc1
+	f, err := os.OpenFile(path, flags, 0)
f47bc1
+	if err != nil {
f47bc1
+		return openResult{err: newSystemErrorWithCause(err, "open exec fifo for reading")}
f47bc1
+	}
f47bc1
+	return openResult{file: f}
f47bc1
+}
f47bc1
+
f47bc1
+func handleFifoResult(result openResult) error {
f47bc1
+	if result.err != nil {
f47bc1
+		return result.err
f47bc1
+	}
f47bc1
+	f := result.file
f47bc1
+	defer f.Close()
f47bc1
+	if err := readFromExecFifo(f); err != nil {
f47bc1
+		return err
f47bc1
+	}
f47bc1
+	return os.Remove(f.Name())
f47bc1
+}
f47bc1
+
f47bc1
 type openResult struct {
f47bc1
 	file *os.File
f47bc1
 	err  error