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