* 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
return NULL;
}
+ ret->err_watch = -1;
+
mode = fcntl(*fd, F_GETFL);
if (mode < 0) {
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;
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
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);
}