There is a bug in qemu currently on hanling agent channels.
As a result if you open agent channel for guest without running
an agent several times in raw you get hang on connect syscall
eventually.
Qemu will be fixed eventually but it is nice to handle hanging
connect condition in libvirt. Let's add 30s connect timeout.
---
src/util/virfdstream.c | 17 +++++++++++++++
src/util/virutil.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/util/virutil.h | 2 ++
3 files changed, 78 insertions(+)
diff --git a/src/util/virfdstream.c b/src/util/virfdstream.c
index 1d064a7..029cc4e 100644
--- a/src/util/virfdstream.c
+++ b/src/util/virfdstream.c
@@ -1176,6 +1176,11 @@ int virFDStreamConnectUNIX(virStreamPtr st,
goto error;
}
+ if (virSetNonBlock(fd) < 0) {
+ virReportSystemError(errno, "%s", _("Unable to set non-blocking
mode"));
+ goto error;
+ }
+
if (virTimeBackOffStart(&timeout, 1, 3*1000 /* ms */) < 0)
goto error;
while (virTimeBackOffWait(&timeout)) {
@@ -1189,10 +1194,22 @@ int virFDStreamConnectUNIX(virStreamPtr st,
continue;
}
+ if (errno == EINPROGRESS || errno == EAGAIN) {
+ if (virConnectWait(fd, 30 * 1000) < 0)
+ goto error;
+
+ break;
+ }
+
virReportSystemError(errno, "%s", _("Unable to connect to UNIX
socket"));
goto error;
}
+ if (virSetBlocking(fd, true) < 0) {
+ virReportSystemError(errno, "%s", _("Unable to set blocking
mode"));
+ goto error;
+ }
+
if (virFDStreamOpenInternal(st, fd, NULL, 0) < 0)
goto error;
return 0;
diff --git a/src/util/virutil.c b/src/util/virutil.c
index 8bdcb02..75abd4f 100644
--- a/src/util/virutil.c
+++ b/src/util/virutil.c
@@ -80,6 +80,7 @@
#include "nonblocking.h"
#include "virprocess.h"
#include "virstring.h"
+#include "virtime.h"
#include "virutil.h"
verify(sizeof(gid_t) <= sizeof(unsigned int) &&
@@ -195,6 +196,64 @@ int virSetSockReuseAddr(int fd, bool fatal)
#endif
int
+virConnectWait(int fd, unsigned long long timeout)
+{
+ struct pollfd fds[1];
+ unsigned long long then;
+ unsigned long long now;
+ int rc;
+ int optval;
+ socklen_t optlen = sizeof(optval);
+
+ fds[0].fd = fd;
+ fds[0].events = POLLOUT;
+ fds[0].revents = 0;
+
+ if (virTimeMillisNow(&now) < 0)
+ return -1;
+
+ then = now + timeout;
+
+ retry:
+ rc = poll(fds, 1, then - now);
+
+ if (rc < 0 && (errno == EAGAIN || errno == EINTR)) {
+ if (virTimeMillisNow(&now) < 0)
+ return -1;
+
+ if (now >= then) {
+ virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
+ _("Connection timeout expired"));
+ return -1;
+ }
+
+ goto retry;
+ }
+
+ if (rc < 0) {
+ virReportSystemError(errno, "%s", _("Unable to connect to
socket"));
+ return -1;
+ } else if (rc == 0) {
+ virReportError(VIR_ERR_OPERATION_TIMEOUT, "%s",
+ _("Connection timeout expired"));
+ return -1;
+ }
+
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Cannot check socket connection status"));
+ return -1;
+ }
+
+ if (optval != 0) {
+ virReportSystemError(optval, "%s", _("Unable to connect to
socket"));
+ return -1;
+ }
+
+ return 0;
+}
+
+int
virPipeReadUntilEOF(int outfd, int errfd,
char **outbuf, char **errbuf) {
diff --git a/src/util/virutil.h b/src/util/virutil.h
index ff89d1a..b0c9672 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -43,6 +43,8 @@ int virSetInherit(int fd, bool inherit) ATTRIBUTE_RETURN_CHECK;
int virSetCloseExec(int fd) ATTRIBUTE_RETURN_CHECK;
int virSetSockReuseAddr(int fd, bool fatal) ATTRIBUTE_RETURN_CHECK;
+int virConnectWait(int fd, unsigned long long timeout);
+
int virPipeReadUntilEOF(int outfd, int errfd,
char **outbuf, char **errbuf);
--
1.8.3.1