[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH 3/3]: Read cmd stdout + stderr in virRun



The attached patch is my second cut at reading 
stdout and stderr of the command virRun kicks
off. There is no hard limit to the amount of
data we read now, and we use a poll loop to
avoid any possible full buffer issues.

If stdout or stderr had any content, we DEBUG
it, and if the command appears to fail we
return stderr in the error message. So now,
trying to stop a logical pool with active
volumes will return:

$ sudo virsh pool-destroy vgdata
libvir: error : internal error '/sbin/vgchange -an vgdata' exited with non-zero status 5 and signal 0:   Can't deactivate volume group "vgdata" with 2 open logical volume(s)
error: Failed to destroy pool vgdata

Thanks,
Cole

commit af7d94392bc89fd0645514cd13a2186ca5224dfc
Author: Cole Robinson <crobinso redhat com>
Date:   Thu Oct 30 13:46:06 2008 -0400

    Capture command stdout and stderr in virRun

diff --git a/src/util.c b/src/util.c
index e59e25c..8d624b2 100644
--- a/src/util.c
+++ b/src/util.c
@@ -33,6 +33,9 @@
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#if HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
 #if HAVE_SYS_WAIT_H
 #include <sys/wait.h>
 #endif
@@ -433,43 +436,129 @@ int
 virRun(virConnectPtr conn,
        const char *const*argv,
        int *status) {
-    int childpid, exitstatus, ret;
-    char *argv_str;
+    int childpid, exitstatus, execret, waitret, i;
+    int ret = -1;
+    int errfd = -1, outfd = -1;
+    char *outbuf = NULL;
+    char *errbuf = NULL;
+    char *argv_str = NULL;
+    struct pollfd fds[2];
+    int finished[2];
 
     if ((argv_str = virArgvToString(argv)) == NULL) {
         ReportError(conn, VIR_ERR_NO_MEMORY, _("command debug string"));
-        return -1;
+        goto error;
     }
     DEBUG0(argv_str);
-    VIR_FREE(argv_str);
 
-    if ((ret = __virExec(conn, argv, NULL, NULL,
-                         &childpid, -1, NULL, NULL, VIR_EXEC_NONE)) < 0)
-        return ret;
+    if ((execret = __virExec(conn, argv, NULL, NULL,
+                             &childpid, -1, &outfd, &errfd, 
+                             VIR_EXEC_NONE)) < 0) {
+        ret = execret;
+        goto error;
+    }
+
+    fds[0].fd = outfd;
+    fds[0].events = POLLIN;
+    finished[0] = 0;
+    fds[1].fd = errfd;
+    fds[1].events = POLLIN;
+    finished[1] = 0;
+
+    while(!(finished[0] && finished[1])) {
+
+        if (poll(fds, ARRAY_CARDINALITY(fds), -1) < 0) {
+            if (errno == EAGAIN)
+                continue;
+            goto pollerr;
+        }
+
+        for (i = 0; i < ARRAY_CARDINALITY(fds); ++i) {
+            char data[1024], **buf;
+            int got, size;
+
+            if (!(fds[i].revents))
+                continue;
+            else if (fds[i].revents & POLLHUP)
+                finished[i] = 1;
+
+            if (!(fds[i].revents & POLLIN)) {
+                if (fds[i].revents & POLLHUP)
+                    continue;
+
+                ReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                            "%s", _("Unknown poll response."));
+                goto error;
+            }
+
+            got = read(fds[i].fd, data, sizeof(data));
+
+            if (got == 0) {
+                finished[i] = 1;
+                continue;
+            }
+            if (got < 0) {
+                if (errno == EINTR)
+                    continue;
+                if (errno == EAGAIN)
+                    break;
+                goto pollerr;
+            }
 
-    while ((ret = waitpid(childpid, &exitstatus, 0) == -1) && errno == EINTR);
-    if (ret == -1) {
+            buf = ((fds[i].fd == outfd) ? &outbuf : &errbuf);
+            size = (*buf ? strlen(*buf) : 0);
+            if (VIR_REALLOC_N(*buf, size+got+1) < 0) {
+                ReportError(conn, VIR_ERR_NO_MEMORY, "%s", "realloc buf");
+                goto error;
+            }
+            memmove(*buf+size, data, got);
+            (*buf)[size+got] = '\0';
+        }
+        continue;
+
+    pollerr:
+        ReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                    _("poll error: %s"), strerror(errno));
+        goto error;
+    }
+
+    if (outbuf)
+        DEBUG("Command stdout: %s", outbuf);
+    if (errbuf)
+        DEBUG("Command stderr: %s", errbuf);
+
+    while ((waitret = waitpid(childpid, &exitstatus, 0) == -1) &&
+            errno == EINTR);
+    if (waitret == -1) {
         ReportError(conn, VIR_ERR_INTERNAL_ERROR,
                     _("cannot wait for '%s': %s"),
                     argv[0], strerror(errno));
-        return -1;
+        goto error;
     }
 
     if (status == NULL) {
         errno = EINVAL;
-        if (WIFEXITED(exitstatus) && WEXITSTATUS(exitstatus) == 0)
-            return 0;
+        if (WIFEXITED(exitstatus) && WEXITSTATUS(exitstatus) != 0) {
 
-        ReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                    _("%s exited with non-zero status %d and signal %d"),
-                    argv[0],
-                    WIFEXITED(exitstatus) ? WEXITSTATUS(exitstatus) : 0,
-                    WIFSIGNALED(exitstatus) ? WTERMSIG(exitstatus) : 0);
-        return -1;
+            ReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                        _("'%s' exited with non-zero status %d and "
+                          "signal %d: %s"), argv_str,
+                        WIFEXITED(exitstatus) ? WEXITSTATUS(exitstatus) : 0,
+                        WIFSIGNALED(exitstatus) ? WTERMSIG(exitstatus) : 0,
+                        (errbuf ? errbuf : ""));
+            goto error;
+        }
     } else {
         *status = exitstatus;
-        return 0;
     }
+
+    ret = 0;
+
+  error:
+    VIR_FREE(outbuf);
+    VIR_FREE(errbuf);
+    VIR_FREE(argv_str);
+    return ret;
 }
 
 #else /* __MINGW32__ */

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]