797c91
From 3d99c51e1b38a440804a55c9f314f62cc50b8902 Mon Sep 17 00:00:00 2001
797c91
From: Giuseppe Scrivano <gscrivan@redhat.com>
797c91
Date: Fri, 25 May 2018 18:04:06 +0200
797c91
Subject: [PATCH] sd-notify: do not hang when NOTIFY_SOCKET is used with create
797c91
797c91
if NOTIFY_SOCKET is used, do not block the main runc process waiting
797c91
for events on the notify socket.  Bind mount the parent directory of
797c91
the notify socket, so that "start" can create the socket and it is
797c91
still accessible from the container.
797c91
797c91
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
797c91
---
797c91
 notify_socket.go | 112 ++++++++++++++++++++++++++++++++++-------------
797c91
 signals.go       |   4 +-
797c91
 start.go         |  13 +++++-
797c91
 utils_linux.go   |  12 ++++-
797c91
 4 files changed, 105 insertions(+), 36 deletions(-)
797c91
797c91
diff --git a/notify_socket.go b/notify_socket.go
797c91
index e7453c62..d961453a 100644
797c91
--- a/notify_socket.go
797c91
+++ b/notify_socket.go
797c91
@@ -7,11 +7,13 @@ import (
797c91
 	"fmt"
797c91
 	"net"
797c91
 	"os"
797c91
+	"path"
797c91
 	"path/filepath"
797c91
+	"strconv"
797c91
+	"time"
797c91
 
797c91
+	"github.com/opencontainers/runc/libcontainer"
797c91
 	"github.com/opencontainers/runtime-spec/specs-go"
797c91
-
797c91
-	"github.com/sirupsen/logrus"
797c91
 	"github.com/urfave/cli"
797c91
 )
797c91
 
797c91
@@ -27,12 +29,12 @@ func newNotifySocket(context *cli.Context, notifySocketHost string, id string) *
797c91
 	}
797c91
 
797c91
 	root := filepath.Join(context.GlobalString("root"), id)
797c91
-	path := filepath.Join(root, "notify.sock")
797c91
+	socketPath := filepath.Join(root, "notify", "notify.sock")
797c91
 
797c91
 	notifySocket := &notifySocket{
797c91
 		socket:     nil,
797c91
 		host:       notifySocketHost,
797c91
-		socketPath: path,
797c91
+		socketPath: socketPath,
797c91
 	}
797c91
 
797c91
 	return notifySocket
797c91
@@ -44,13 +46,19 @@ func (s *notifySocket) Close() error {
797c91
 
797c91
 // If systemd is supporting sd_notify protocol, this function will add support
797c91
 // for sd_notify protocol from within the container.
797c91
-func (s *notifySocket) setupSpec(context *cli.Context, spec *specs.Spec) {
797c91
-	mount := specs.Mount{Destination: s.host, Source: s.socketPath, Options: []string{"bind"}}
797c91
+func (s *notifySocket) setupSpec(context *cli.Context, spec *specs.Spec) error {
797c91
+	pathInContainer := filepath.Join("/run/notify", path.Base(s.socketPath))
797c91
+	mount := specs.Mount{
797c91
+		Destination: path.Dir(pathInContainer),
797c91
+		Source:      path.Dir(s.socketPath),
797c91
+		Options:     []string{"bind", "nosuid", "noexec", "nodev", "ro"},
797c91
+	}
797c91
 	spec.Mounts = append(spec.Mounts, mount)
797c91
-	spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", s.host))
797c91
+	spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", pathInContainer))
797c91
+	return nil
797c91
 }
797c91
 
797c91
-func (s *notifySocket) setupSocket() error {
797c91
+func (s *notifySocket) bindSocket() error {
797c91
 	addr := net.UnixAddr{
797c91
 		Name: s.socketPath,
797c91
 		Net:  "unixgram",
797c91
@@ -71,45 +79,89 @@ func (s *notifySocket) setupSocket() error {
797c91
 	return nil
797c91
 }
797c91
 
797c91
-// pid1 must be set only with -d, as it is used to set the new process as the main process
797c91
-// for the service in systemd
797c91
-func (s *notifySocket) run(pid1 int) {
797c91
-	buf := make([]byte, 512)
797c91
-	notifySocketHostAddr := net.UnixAddr{Name: s.host, Net: "unixgram"}
797c91
+func (s *notifySocket) setupSocketDirectory() error {
797c91
+	return os.Mkdir(path.Dir(s.socketPath), 0755)
797c91
+}
797c91
+
797c91
+func notifySocketStart(context *cli.Context, notifySocketHost, id string) (*notifySocket, error) {
797c91
+	notifySocket := newNotifySocket(context, notifySocketHost, id)
797c91
+	if notifySocket == nil {
797c91
+		return nil, nil
797c91
+	}
797c91
+
797c91
+	if err := notifySocket.bindSocket(); err != nil {
797c91
+		return nil, err
797c91
+	}
797c91
+	return notifySocket, nil
797c91
+}
797c91
+
797c91
+func (n *notifySocket) waitForContainer(container libcontainer.Container) error {
797c91
+	s, err := container.State()
797c91
+	if err != nil {
797c91
+		return err
797c91
+	}
797c91
+	return n.run(s.InitProcessPid)
797c91
+}
797c91
+
797c91
+func (n *notifySocket) run(pid1 int) error {
797c91
+	if n.socket == nil {
797c91
+		return nil
797c91
+	}
797c91
+	notifySocketHostAddr := net.UnixAddr{Name: n.host, Net: "unixgram"}
797c91
 	client, err := net.DialUnix("unixgram", nil, &notifySocketHostAddr)
797c91
 	if err != nil {
797c91
-		logrus.Error(err)
797c91
-		return
797c91
+		return err
797c91
 	}
797c91
-	for {
797c91
-		r, err := s.socket.Read(buf)
797c91
-		if err != nil {
797c91
-			break
797c91
+
797c91
+	ticker := time.NewTicker(time.Millisecond * 100)
797c91
+	defer ticker.Stop()
797c91
+
797c91
+	fileChan := make(chan []byte)
797c91
+	go func() {
797c91
+		for {
797c91
+			buf := make([]byte, 512)
797c91
+			r, err := n.socket.Read(buf)
797c91
+			if err != nil {
797c91
+				return
797c91
+			}
797c91
+			got := buf[0:r]
797c91
+			if !bytes.HasPrefix(got, []byte("READY=")) {
797c91
+				continue
797c91
+			}
797c91
+			fileChan <- got
797c91
+			return
797c91
 		}
797c91
-		var out bytes.Buffer
797c91
-		for _, line := range bytes.Split(buf[0:r], []byte{'\n'}) {
797c91
-			if bytes.HasPrefix(line, []byte("READY=")) {
797c91
+	}()
797c91
+
797c91
+	for {
797c91
+		select {
797c91
+		case <-ticker.C:
797c91
+			_, err := os.Stat(filepath.Join("/proc", strconv.Itoa(pid1)))
797c91
+			if err != nil {
797c91
+				return nil
797c91
+			}
797c91
+		case b := <-fileChan:
797c91
+			for _, line := range bytes.Split(b, []byte{'\n'}) {
797c91
+				var out bytes.Buffer
797c91
 				_, err = out.Write(line)
797c91
 				if err != nil {
797c91
-					return
797c91
+					return err
797c91
 				}
797c91
 
797c91
 				_, err = out.Write([]byte{'\n'})
797c91
 				if err != nil {
797c91
-					return
797c91
+					return err
797c91
 				}
797c91
 
797c91
 				_, err = client.Write(out.Bytes())
797c91
 				if err != nil {
797c91
-					return
797c91
+					return err
797c91
 				}
797c91
 
797c91
 				// now we can inform systemd to use pid1 as the pid to monitor
797c91
-				if pid1 > 0 {
797c91
-					newPid := fmt.Sprintf("MAINPID=%d\n", pid1)
797c91
-					client.Write([]byte(newPid))
797c91
-				}
797c91
-				return
797c91
+				newPid := fmt.Sprintf("MAINPID=%d\n", pid1)
797c91
+				client.Write([]byte(newPid))
797c91
+				return nil
797c91
 			}
797c91
 		}
797c91
 	}
797c91
diff --git a/signals.go b/signals.go
797c91
index b67f65a0..dd25e094 100644
797c91
--- a/signals.go
797c91
+++ b/signals.go
797c91
@@ -70,6 +70,7 @@ func (h *signalHandler) forward(process *libcontainer.Process, tty *tty, detach
797c91
 			h.notifySocket.run(pid1)
797c91
 			return 0, nil
797c91
 		}
797c91
+		h.notifySocket.run(os.Getpid())
797c91
 		go h.notifySocket.run(0)
797c91
 	}
797c91
 
797c91
@@ -97,9 +98,6 @@ func (h *signalHandler) forward(process *libcontainer.Process, tty *tty, detach
797c91
 					// status because we must ensure that any of the go specific process
797c91
 					// fun such as flushing pipes are complete before we return.
797c91
 					process.Wait()
797c91
-					if h.notifySocket != nil {
797c91
-						h.notifySocket.Close()
797c91
-					}
797c91
 					return e.status, nil
797c91
 				}
797c91
 			}
797c91
diff --git a/start.go b/start.go
797c91
index 2bb698b2..3a1769a4 100644
797c91
--- a/start.go
797c91
+++ b/start.go
797c91
@@ -3,6 +3,7 @@ package main
797c91
 import (
797c91
 	"errors"
797c91
 	"fmt"
797c91
+	"os"
797c91
 
797c91
 	"github.com/opencontainers/runc/libcontainer"
797c91
 	"github.com/urfave/cli"
797c91
@@ -31,7 +32,17 @@ your host.`,
797c91
 		}
797c91
 		switch status {
797c91
 		case libcontainer.Created:
797c91
-			return container.Exec()
797c91
+			notifySocket, err := notifySocketStart(context, os.Getenv("NOTIFY_SOCKET"), container.ID())
797c91
+			if err != nil {
797c91
+				return err
797c91
+			}
797c91
+			if err := container.Exec(); err != nil {
797c91
+				return err
797c91
+			}
797c91
+			if notifySocket != nil {
797c91
+				return notifySocket.waitForContainer(container)
797c91
+			}
797c91
+			return nil
797c91
 		case libcontainer.Stopped:
797c91
 			return errors.New("cannot start a container that has stopped")
797c91
 		case libcontainer.Running:
797c91
diff --git a/utils_linux.go b/utils_linux.go
797c91
index 984e6b0f..46c26246 100644
797c91
--- a/utils_linux.go
797c91
+++ b/utils_linux.go
797c91
@@ -408,7 +408,9 @@ func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOp
797c91
 
797c91
 	notifySocket := newNotifySocket(context, os.Getenv("NOTIFY_SOCKET"), id)
797c91
 	if notifySocket != nil {
797c91
-		notifySocket.setupSpec(context, spec)
797c91
+		if err := notifySocket.setupSpec(context, spec); err != nil {
797c91
+			return -1, err
797c91
+		}
797c91
 	}
797c91
 
797c91
 	container, err := createContainer(context, id, spec)
797c91
@@ -417,10 +419,16 @@ func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOp
797c91
 	}
797c91
 
797c91
 	if notifySocket != nil {
797c91
-		err := notifySocket.setupSocket()
797c91
+		err := notifySocket.setupSocketDirectory()
797c91
 		if err != nil {
797c91
 			return -1, err
797c91
 		}
797c91
+		if action == CT_ACT_RUN {
797c91
+			err := notifySocket.bindSocket()
797c91
+			if err != nil {
797c91
+				return -1, err
797c91
+			}
797c91
+		}
797c91
 	}
797c91
 
797c91
 	// Support on-demand socket activation by passing file descriptors into the container init process.
797c91
-- 
797c91
2.21.0
797c91