|
|
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
|