diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 02ca2c7af8..59d6de0892 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1335,6 +1335,7 @@ virDomainListSnapshots; virFileLoopDeviceAssociate; virFileClose; virFileDirectFdFlag; +virFileWrapperFdCatchError; virFileWrapperFdClose; virFileWrapperFdFree; virFileWrapperFdNew; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7dccf86f0a..3980c10deb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2950,6 +2950,7 @@ endjob: if (rc < 0) VIR_WARN("Unable to resume guest CPUs after save failure"); } + virFileWrapperFdCatchError(wrapperFd); } if (qemuDomainObjEndAsyncJob(driver, vm) == 0) vm = NULL; @@ -3287,9 +3288,11 @@ doCoreDump(struct qemud_driver *driver, cleanup: VIR_FORCE_CLOSE(fd); - virFileWrapperFdFree(wrapperFd); - if (ret != 0) + if (ret != 0) { + virFileWrapperFdCatchError(wrapperFd); unlink(path); + } + virFileWrapperFdFree(wrapperFd); return ret; } diff --git a/src/util/virfile.c b/src/util/virfile.c index 5b00ead2a3..9593151747 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -135,9 +135,57 @@ virFileDirectFdFlag(void) * read-write is not supported, just a single direction. */ struct _virFileWrapperFd { virCommandPtr cmd; /* Child iohelper process to do the I/O. */ + int err_fd; /* FD to read stderr of @cmd */ + char *err_msg; /* stderr of @cmd */ + size_t err_msg_len; /* strlen of err_msg so we don't + have to compute it every time */ + int err_watch; /* ID of watch in the event loop */ }; #ifndef WIN32 +/** + * virFileWrapperFdReadStdErr: + * @watch: watch ID + * @fd: the read end of pipe to iohelper's stderr + * @events: an OR-ed set of events which occurred on @fd + * @opaque: virFileWrapperFdPtr + * + * This is a callback to our eventloop which will read iohelper's + * stderr, reallocate @opaque->err_msg and copy data. + */ +static void +virFileWrapperFdReadStdErr(int watch ATTRIBUTE_UNUSED, + int fd, int events, void *opaque) +{ + virFileWrapperFdPtr wfd = (virFileWrapperFdPtr) opaque; + char ebuf[1024]; + ssize_t nread; + + if (events & VIR_EVENT_HANDLE_READABLE) { + while ((nread = saferead(fd, ebuf, sizeof(ebuf)))) { + if (nread < 0) { + if (errno != EAGAIN) + virReportSystemError(errno, "%s", + _("unable to read iohelper's stderr")); + break; + } + + if (VIR_REALLOC_N(wfd->err_msg, wfd->err_msg_len + nread + 1) < 0) { + virReportOOMError(); + return; + } + memcpy(wfd->err_msg + wfd->err_msg_len, ebuf, nread); + wfd->err_msg_len += nread; + wfd->err_msg[wfd->err_msg_len] = '\0'; + } + } + + if (events & VIR_EVENT_HANDLE_HANGUP) { + virEventRemoveHandle(watch); + wfd->err_watch = -1; + } +} + /** * virFileWrapperFdNew: * @fd: pointer to fd to wrap @@ -197,6 +245,8 @@ virFileWrapperFdNew(int *fd, const char *name, unsigned int flags) return NULL; } + ret->err_watch = -1; + mode = fcntl(*fd, F_GETFL); if (mode < 0) { @@ -229,9 +279,38 @@ virFileWrapperFdNew(int *fd, const char *name, unsigned int flags) virCommandAddArg(ret->cmd, "0"); } + /* In order to catch iohelper stderr, we must: + * - pass a FD to virCommand (-1 to auto-allocate one) + * - change iohelper's env so virLog functions print to stderr + */ + ret->err_fd = -1; + virCommandSetErrorFD(ret->cmd, &ret->err_fd); + virCommandAddEnvPair(ret->cmd, "LIBVIRT_LOG_OUTPUTS", "1:stderr"); + if (virCommandRunAsync(ret->cmd, NULL) < 0) goto error; + /* deliberately don't use virCommandNonblockingFDs here as it is all or + * nothing. And we want iohelper's stdin and stdout to block (default). + * However, stderr is read within event loop and therefore it must be + * nonblocking.*/ + if (virSetNonBlock(ret->err_fd) < 0) { + virReportSystemError(errno, "%s", + _("Failed to set non-blocking " + "file descriptor flag")); + goto error; + } + + if ((ret->err_watch = virEventAddHandle(ret->err_fd, + VIR_EVENT_HANDLE_READABLE, + virFileWrapperFdReadStdErr, + ret, NULL)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to register iohelper's " + "stderr FD in the eventloop")); + goto error; + } + if (VIR_CLOSE(pipefd[!output]) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unable to close pipe")); goto error; @@ -280,6 +359,21 @@ virFileWrapperFdClose(virFileWrapperFdPtr wfd) return virCommandWait(wfd->cmd, NULL); } + +/** + * virFileWrapperFdCatchError: + * @wfd: fd wrapper, or NULL + * + * If iohelper reported any error VIR_WARN() about it. + */ +void +virFileWrapperFdCatchError(virFileWrapperFdPtr wfd) +{ + if (wfd->err_msg) + VIR_WARN("iohelper reports: %s", wfd->err_msg); +} + + /** * virFileWrapperFdFree: * @wfd: fd wrapper, or NULL @@ -295,6 +389,11 @@ virFileWrapperFdFree(virFileWrapperFdPtr wfd) if (!wfd) return; + VIR_FORCE_CLOSE(wfd->err_fd); + if (wfd->err_watch != -1) + virEventRemoveHandle(wfd->err_watch); + VIR_FREE(wfd->err_msg); + virCommandFree(wfd->cmd); VIR_FREE(wfd); } diff --git a/src/util/virfile.h b/src/util/virfile.h index c885b73c36..80daf867a5 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -90,6 +90,8 @@ int virFileWrapperFdClose(virFileWrapperFdPtr dfd); void virFileWrapperFdFree(virFileWrapperFdPtr dfd); +void virFileWrapperFdCatchError(virFileWrapperFdPtr dfd); + int virFileLock(int fd, bool shared, off_t start, off_t len); int virFileUnlock(int fd, off_t start, off_t len);