Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 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 | Index: git/src/import/signals.go |
| 33 | =================================================================== |
| 34 | --- git.orig/src/import/signals.go |
| 35 | +++ git/src/import/signals.go |
| 36 | @@ -6,6 +6,7 @@ |
| 37 | "os" |
| 38 | "os/signal" |
| 39 | "syscall" // only for Signal |
| 40 | + "strconv" |
| 41 | |
| 42 | "github.com/opencontainers/runc/libcontainer" |
| 43 | "github.com/opencontainers/runc/libcontainer/system" |
| 44 | @@ -56,9 +57,6 @@ |
| 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,61 @@ |
| 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 | + |
| 114 | // Perform the initial tty resize. Always ignore errors resizing because |
| 115 | // stdout might have disappeared (due to races with when SIGHUP is sent). |
| 116 | _ = tty.resize() |
| 117 | Index: git/src/import/utils_linux.go |
| 118 | =================================================================== |
| 119 | --- git.orig/src/import/utils_linux.go |
| 120 | +++ git/src/import/utils_linux.go |
| 121 | @@ -338,7 +338,7 @@ |
| 122 | if err != nil { |
| 123 | r.terminate(process) |
| 124 | } |
| 125 | - if detach { |
| 126 | + if (detach && os.Getenv("SIGUSR1_PARENT_PID") == "") { |
| 127 | return 0, nil |
| 128 | } |
| 129 | r.destroy() |