diff options
author | Jason Wessel <jason.wessel@windriver.com> | 2017-12-11 05:41:07 -0800 |
---|---|---|
committer | Bruce Ashfield <bruce.ashfield@windriver.com> | 2017-12-11 23:53:39 -0500 |
commit | 4fa904e57f7fb730195d13c49fe3d1c2b43544bc (patch) | |
tree | 5be83dab1710d3e80bdab35e0fcfa0953ce77578 /recipes-containers/runc | |
parent | 23dee8a20774c7bbfda5333ca15640b565f7559f (diff) | |
download | meta-virtualization-4fa904e57f7fb730195d13c49fe3d1c2b43544bc.tar.gz |
runc-docker: Allow "run start ..." to daemonize with $SIGUSR1_PARENT_PID
The runc-docker has all the code in it to properly run a stop hook if
you use it in the foreground. It doesn't work in the back ground
because there is no way for a golang application to fork a child exit
out of the parent process because all the golang threads stay with the
parent.
This patch has three parts that happen ONLY when $SIGUSR1_PARENT_PID
is set.
1) At the point where runc start would normally exit, it closes
stdin/stdout/stderr so it would be possible to daemonize "runc start ...".
2) The code to send a SIGUSR1 to the parent process was added. The
idea being that a parent process would simply exit at that point
because it was blocking until runc performed everything it was
required to perform.
3) The code was copied which performs the normal the signal handling
block which is used for the foreground operation of runc.
-- More information --
When you use "runc run " it is running in the "foreground", in the
sense it takes over your existing terminal.
The runc-docker doesn't have a way to start it with "runc run&" where
you can send it to the background and have everything work. With this
commit, it does allow you to do that and have all the stop hooks fire
at the time what ever runc started exits.
Lets take a quick look at what "runc run" does today:
* Starts a whole pile of threads
* Sets up all name spaces
* Starts child process for container and leaves it paused at image activation
* runs start hooks
* executes "continue" for container process
* waits for container app to exit
* executes stop hooks
Now lets look at "runc create/start" does today:
runc create
* Starts a whole pile of threads
* Sets up all name spaces
* Starts child process for container and leaves it paused at image activation
* exits -- [ NOTE: this is our problem! ]
runc start
* runs start hooks
* executes "continue" for continue process
At this point when the container app exits nothing is waiting for it
to run any kind of hooks.
Signed-off-by: Bruce Ashfield <bruce.ashfield@windriver.com>
Diffstat (limited to 'recipes-containers/runc')
-rw-r--r-- | recipes-containers/runc/runc-docker/0001-runc-docker-SIGUSR1-daemonize.patch | 131 | ||||
-rw-r--r-- | recipes-containers/runc/runc-docker_git.bb | 1 |
2 files changed, 132 insertions, 0 deletions
diff --git a/recipes-containers/runc/runc-docker/0001-runc-docker-SIGUSR1-daemonize.patch b/recipes-containers/runc/runc-docker/0001-runc-docker-SIGUSR1-daemonize.patch new file mode 100644 index 00000000..b3bd0680 --- /dev/null +++ b/recipes-containers/runc/runc-docker/0001-runc-docker-SIGUSR1-daemonize.patch | |||
@@ -0,0 +1,131 @@ | |||
1 | From cd7d76a6d1ecb1856f6ed666fb5c30dc105aa94e Mon Sep 17 00:00:00 2001 | ||
2 | From: Jason Wessel <jason.wessel@windriver.com> | ||
3 | Date: Tue, 5 Dec 2017 18:28:28 -0800 | ||
4 | Subject: [PATCH] runc-docker: Allow "run start ..." to daemonize with $SIGUSR1_PARENT_PID | ||
5 | |||
6 | The runc-docker has all the code in it to properly run a stop hook if | ||
7 | you use it in the foreground. It doesn't work in the back ground | ||
8 | because there is no way for a golang application to fork a child exit | ||
9 | out of the parent process because all the golang threads stay with the | ||
10 | parent. | ||
11 | |||
12 | This patch has three parts that happen ONLY when $SIGUSR1_PARENT_PID | ||
13 | is set. | ||
14 | |||
15 | 1) The code was copied which performs the normal the signal handling | ||
16 | block which is used for the foreground operation of runc. | ||
17 | |||
18 | 2) At the point where runc start would normally exit, it closes | ||
19 | stdin/stdout/stderr so it would be possible to daemonize "runc start ...". | ||
20 | |||
21 | 3) The code to send a SIGUSR1 to the parent process was added. The | ||
22 | idea being that a parent process would simply exit at that point | ||
23 | because it was blocking until runc performed everything it was | ||
24 | required to perform. | ||
25 | |||
26 | Signed-off-by: Jason Wessel <jason.wessel@windriver.com> | ||
27 | --- | ||
28 | signals.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++---- | ||
29 | utils_linux.go | 2 +- | ||
30 | 2 files changed, 51 insertions(+), 5 deletions(-) | ||
31 | |||
32 | diff --git a/src/import/signals.go b/src/import/signals.go | ||
33 | index 910ea1ee..b6a23476 100644 | ||
34 | --- a/src/import/signals.go | ||
35 | +++ b/src/import/signals.go | ||
36 | @@ -6,6 +6,7 @@ import ( | ||
37 | "os" | ||
38 | "os/signal" | ||
39 | "syscall" // only for Signal | ||
40 | + "strconv" | ||
41 | |||
42 | "github.com/Sirupsen/logrus" | ||
43 | "github.com/opencontainers/runc/libcontainer" | ||
44 | @@ -56,9 +57,6 @@ type signalHandler struct { | ||
45 | func (h *signalHandler) forward(process *libcontainer.Process, tty *tty, detach bool) (int, error) { | ||
46 | // make sure we know the pid of our main process so that we can return | ||
47 | // after it dies. | ||
48 | - if detach && h.notifySocket == nil { | ||
49 | - return 0, nil | ||
50 | - } | ||
51 | |||
52 | pid1, err := process.Pid() | ||
53 | if err != nil { | ||
54 | @@ -68,12 +66,60 @@ func (h *signalHandler) forward(process *libcontainer.Process, tty *tty, detach | ||
55 | if h.notifySocket != nil { | ||
56 | if detach { | ||
57 | h.notifySocket.run(pid1) | ||
58 | - return 0, nil | ||
59 | } else { | ||
60 | go h.notifySocket.run(0) | ||
61 | } | ||
62 | } | ||
63 | |||
64 | + if (detach) { | ||
65 | + // This allows the parent process to daemonize this process | ||
66 | + // so long as stdin/stderr/stdout are closed | ||
67 | + if envVal := os.Getenv("SIGUSR1_PARENT_PID"); envVal != "" { | ||
68 | + // Close stdin/stdout/stderr | ||
69 | + os.Stdin.Close() | ||
70 | + os.Stdout.Close() | ||
71 | + os.Stderr.Close() | ||
72 | + // Notify parent to detach | ||
73 | + i, err := strconv.Atoi(envVal) | ||
74 | + if (err != nil) { | ||
75 | + return 0, nil | ||
76 | + } | ||
77 | + unix.Kill(i, unix.SIGUSR1) | ||
78 | + // Loop waiting on the child to signal or exit, | ||
79 | + // after which all stop hooks will be run | ||
80 | + for s := range h.signals { | ||
81 | + switch s { | ||
82 | + case unix.SIGCHLD: | ||
83 | + exits, err := h.reap() | ||
84 | + if err != nil { | ||
85 | + logrus.Error(err) | ||
86 | + } | ||
87 | + for _, e := range exits { | ||
88 | + logrus.WithFields(logrus.Fields{ | ||
89 | + "pid": e.pid, | ||
90 | + "status": e.status, | ||
91 | + }).Debug("process exited") | ||
92 | + if e.pid == pid1 { | ||
93 | + // call Wait() on the process even though we already have the exit | ||
94 | + // status because we must ensure that any of the go specific process | ||
95 | + // fun such as flushing pipes are complete before we return. | ||
96 | + process.Wait() | ||
97 | + if h.notifySocket != nil { | ||
98 | + h.notifySocket.Close() | ||
99 | + } | ||
100 | + return e.status, nil | ||
101 | + } | ||
102 | + } | ||
103 | + default: | ||
104 | + logrus.Debugf("sending signal to process %s", s) | ||
105 | + if err := unix.Kill(pid1, s.(syscall.Signal)); err != nil { | ||
106 | + logrus.Error(err) | ||
107 | + } | ||
108 | + } | ||
109 | + } | ||
110 | + } | ||
111 | + return 0, nil | ||
112 | + } | ||
113 | // perform the initial tty resize. | ||
114 | tty.resize() | ||
115 | for s := range h.signals { | ||
116 | diff --git a/src/import/utils_linux.go b/src/import/utils_linux.go | ||
117 | index e6d31b35..1bb80a63 100644 | ||
118 | --- a/src/import/utils_linux.go | ||
119 | +++ b/src/import/utils_linux.go | ||
120 | @@ -308,7 +308,7 @@ func (r *runner) run(config *specs.Process) (int, error) { | ||
121 | if err != nil { | ||
122 | r.terminate(process) | ||
123 | } | ||
124 | - if detach { | ||
125 | + if (detach && os.Getenv("SIGUSR1_PARENT_PID") == "") { | ||
126 | return 0, nil | ||
127 | } | ||
128 | r.destroy() | ||
129 | -- | ||
130 | 2.11.0 | ||
131 | |||
diff --git a/recipes-containers/runc/runc-docker_git.bb b/recipes-containers/runc/runc-docker_git.bb index 9db48ee6..f31b82ec 100644 --- a/recipes-containers/runc/runc-docker_git.bb +++ b/recipes-containers/runc/runc-docker_git.bb | |||
@@ -10,6 +10,7 @@ SRC_URI = "git://github.com/docker/runc.git;nobranch=1;name=runc-docker \ | |||
10 | file://0001-runc-Add-console-socket-dev-null.patch \ | 10 | file://0001-runc-Add-console-socket-dev-null.patch \ |
11 | file://0001-Use-correct-go-cross-compiler.patch \ | 11 | file://0001-Use-correct-go-cross-compiler.patch \ |
12 | file://0001-Disable-building-recvtty.patch \ | 12 | file://0001-Disable-building-recvtty.patch \ |
13 | file://0001-runc-docker-SIGUSR1-daemonize.patch \ | ||
13 | " | 14 | " |
14 | 15 | ||
15 | RUNC_VERSION = "1.0.0-rc3" | 16 | RUNC_VERSION = "1.0.0-rc3" |