[lxc-devel] [lxd/master] exec: detect POLLNVAL when poll()ing

brauner on Github lxc-bot at linuxcontainers.org
Mon Jun 19 13:49:03 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 579 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170619/af4190a5/attachment.bin>
-------------- next part --------------
From 32ae26b8b7ff03538ebb75e02488c0a23601ebc3 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Mon, 19 Jun 2017 15:44:43 +0200
Subject: [PATCH] exec: detect POLLNVAL when poll()ing

In case the child simply dies or the client exits the fd will be closed and we
will receive a POLLNVAL event. We need to handle this case otherwise we will
keep poll()ing without ever exiting.

Closes #2964.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/container_exec.go |  2 ++
 shared/util_linux.go  | 13 +++++++++----
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index 9c4359007..91d860e6a 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -227,10 +227,12 @@ func (s *execWs) Do(op *operation) error {
 			conn := s.conns[0]
 			s.connsLock.Unlock()
 
+			logger.Debugf("Starting to mirror websocket")
 			readDone, writeDone := shared.WebsocketExecMirror(conn, ptys[0], ptys[0], attachedChildIsDead, int(ptys[0].Fd()))
 
 			<-readDone
 			<-writeDone
+			logger.Debugf("Finished to mirror websocket")
 
 			conn.Close()
 			wgEOF.Done()
diff --git a/shared/util_linux.go b/shared/util_linux.go
index 66e896139..5be541c6e 100644
--- a/shared/util_linux.go
+++ b/shared/util_linux.go
@@ -570,12 +570,14 @@ func ExecReaderToChannel(r io.Reader, bufferSize int, exited <-chan bool, fd int
 
 		atomic.StoreInt32(&attachedChildIsDead, 1)
 
-		ret, revents, err := GetPollRevents(fd, 0, (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP))
+		ret, revents, err := GetPollRevents(fd, 0, (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP | POLLNVAL))
 		if ret < 0 {
 			logger.Errorf("Failed to poll(POLLIN | POLLPRI | POLLHUP | POLLRDHUP) on file descriptor: %s.", err)
 		} else if ret > 0 {
 			if (revents & POLLERR) > 0 {
 				logger.Warnf("Detected poll(POLLERR) event.")
+			} else if (revents & POLLNVAL) > 0 {
+				logger.Warnf("Detected poll(POLLNVAL) event.")
 			}
 		} else if ret == 0 {
 			logger.Debugf("No data in stdout: exiting.")
@@ -595,7 +597,7 @@ func ExecReaderToChannel(r io.Reader, bufferSize int, exited <-chan bool, fd int
 			nr := 0
 			var err error
 
-			ret, revents, err := GetPollRevents(fd, -1, (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP))
+			ret, revents, err := GetPollRevents(fd, -1, (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP | POLLNVAL))
 			if ret < 0 {
 				// This condition is only reached in cases where we are massively f*cked since we even handle
 				// EINTR in the underlying C wrapper around poll(). So let's exit here.
@@ -616,6 +618,9 @@ func ExecReaderToChannel(r io.Reader, bufferSize int, exited <-chan bool, fd int
 			if (revents & POLLERR) > 0 {
 				logger.Warnf("Detected poll(POLLERR) event: exiting.")
 				return
+			} else if (revents & POLLNVAL) > 0 {
+				logger.Warnf("Detected poll(POLLNVAL) event: exiting.")
+				return
 			}
 
 			if ((revents & (POLLIN | POLLPRI)) > 0) && !both {
@@ -671,11 +676,11 @@ func ExecReaderToChannel(r io.Reader, bufferSize int, exited <-chan bool, fd int
 					//   or (POLLHUP | POLLRDHUP). Both will trigger another codepath (See [2].)
 					//   that takes care that all data of the child that is buffered in
 					//   stdout is written out.
-					ret, revents, err := GetPollRevents(fd, 0, (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP))
+					ret, revents, err := GetPollRevents(fd, 0, (POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP | POLLNVAL))
 					if ret < 0 {
 						logger.Errorf("Failed to poll(POLLIN | POLLPRI | POLLERR | POLLHUP | POLLRDHUP) on file descriptor: %s. Exiting.", err)
 						return
-					} else if (revents & (POLLHUP | POLLRDHUP)) == 0 {
+					} else if (revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) == 0 {
 						logger.Debugf("Exiting but background processes are still running.")
 						return
 					}


More information about the lxc-devel mailing list