[libvirt] [PATCH 09/11] DEMO: Implement get/put for test driver

Daniel P. Berrange berrange at redhat.com
Mon Aug 24 20:51:12 UTC 2009


* src/test.c: Implement get/put APIs
* .x-sc_avoid_write: ignore src/test.c
---
 .x-sc_avoid_write |    1 +
 src/test.c        |  479 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 478 insertions(+), 2 deletions(-)

diff --git a/.x-sc_avoid_write b/.x-sc_avoid_write
index c37574d..201a064 100644
--- a/.x-sc_avoid_write
+++ b/.x-sc_avoid_write
@@ -2,6 +2,7 @@
 ^src/xend_internal\.c$
 ^src/util-lib\.c$
 ^src/libvirt\.c$
+^src/test\.c$
 ^src/virsh\.c$
 ^qemud/qemud.c$
 ^gnulib/
diff --git a/src/test.c b/src/test.c
index cda7be7..3a4731e 100644
--- a/src/test.c
+++ b/src/test.c
@@ -30,6 +30,9 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include <libxml/xmlsave.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
 
 
 #include "virterror_internal.h"
@@ -4174,6 +4177,478 @@ static void testDomainEventQueue(testConnPtr driver,
 }
 
 
+struct testStreamFile {
+    int pid;
+    int fd;
+    char *filename;
+    int watch;
+    unsigned int create : 1;
+    unsigned int dispatching : 1;
+    unsigned int cbRemoved : 1;
+    virStreamEventCallback cb;
+    void *opaque;
+    virFreeCallback ff;
+};
+typedef struct testStreamFile *testStreamFilePtr;
+
+
+static int testStreamFileFree(testStreamFilePtr fs,
+                              int aborting)
+{
+    int ret = 0;
+    int saved = 0;
+    int childstat = 0;
+
+    if (!fs)
+        return 0;
+
+    if (fs->fd != -1) {
+        if (close(fs->fd) != 0 && !aborting) {
+            saved = errno;
+            ret = -1;
+        }
+    }
+    /* Wait for intermediate process to exit */
+    while (waitpid(fs->pid, &childstat, 0) == -1 &&
+           errno == EINTR);
+
+    if (childstat != 0) {
+        saved = EIO;
+        ret = -1;
+    }
+
+    if ((aborting || ret != 0) && fs->create)
+        unlink(fs->filename);
+    VIR_FREE(fs->filename);
+    VIR_FREE(fs);
+    if (ret != 0)
+        errno = saved;
+
+    return ret;
+}
+
+static int testStreamFileClose(virStreamPtr st,
+                               int aborting)
+{
+    testStreamFilePtr fs = st->privateData;
+    int ret = 0;
+
+    if (!fs)
+        return 0;
+
+    if (fs->watch > 0)
+        virEventRemoveHandle(fs->watch);
+
+    ret = testStreamFileFree(fs, aborting);
+
+    st->privateData = NULL;
+    return ret;
+}
+
+static testStreamFilePtr
+testStreamFileOpen(virStreamPtr st,
+                   const char *filename,
+                   int create)
+{
+    testStreamFilePtr fs = NULL;
+    int ret;
+    char *arg = NULL;
+    int fds[2] = { -1, -1 };
+    const char *cmd[] = {
+        "dd", arg, NULL
+    };
+
+    if (!create &&
+        access(filename, R_OK) < 0) {
+        virReportSystemError(st->conn, errno,
+                             _("cannot access '%s'"), filename);
+        goto error;
+    }
+
+    if (VIR_ALLOC(fs) < 0)
+        goto no_memory;
+    fs->fd = -1;
+    fs->watch = 0;
+    fs->create = create;
+
+    if (!(fs->filename = strdup(filename)))
+        goto no_memory;
+
+    if (create) {
+        ret = virAsprintf(&arg, "of=%s", filename);
+    } else {
+        ret = virAsprintf(&arg, "if=%s", filename);
+    }
+    if (ret < 0)
+        goto no_memory;
+    cmd[1] = arg;
+
+    if (pipe(fds) < 0) {
+        virReportSystemError(st->conn, errno, "%s",
+                             _("cannot allocate pipe"));
+        goto error;
+    }
+
+    if ((st->flags & VIR_STREAM_NONBLOCK) &&
+        virSetNonBlock(create ? fds[1] : fds[0]) < 0) {
+        virReportSystemError(st->conn, errno, "%s",
+                             _("cannot make stream non-blocking"));
+        goto error;
+    }
+
+    ret = virExec(st->conn, cmd, NULL, NULL, &fs->pid,
+                  create ? fds[0] : -1,
+                  create ? NULL : &fds[1], NULL, 0);
+
+    if (ret < 0)
+        goto error;
+
+    if (create) {
+        close(fds[0]);
+        fs->fd = fds[1];
+    } else {
+        close(fds[1]);
+        fs->fd = fds[0];
+    }
+
+    VIR_FREE(arg);
+    return fs;
+
+no_memory:
+    virReportOOMError(st->conn);
+error:
+    testStreamFileFree(fs, 1);
+    VIR_FREE(arg);
+    if (fds[0] != -1) close(fds[0]);
+    if (fds[1] != -1) close(fds[1]);
+    return NULL;
+}
+
+static int
+testStreamFileAbort(virStreamPtr st)
+{
+    testConnPtr privconn = st->conn->privateData;
+    int ret;
+
+    testDriverLock(privconn);
+
+    ret = testStreamFileClose(st, 1);
+
+    testDriverUnlock(privconn);
+    return 0;
+}
+
+static int
+testStreamFileFinish(virStreamPtr st)
+{
+    testConnPtr privconn = st->conn->privateData;
+    int ret = -1;
+
+    if (!st->privateData) {
+        testError(st->conn, VIR_ERR_INTERNAL_ERROR,
+                  "%s", _("stream is not open"));
+        return -1;
+    }
+
+    testDriverLock(privconn);
+
+    ret = testStreamFileClose(st, 0);
+    if (ret != 0)
+        virReportSystemError(st->conn, errno, "%s",
+                             _("cannot close stream"));
+
+    testDriverUnlock(privconn);
+    return ret;
+}
+
+static int
+testStreamFileWrite(virStreamPtr st,
+                    const char *bytes,
+                    size_t nbytes)
+{
+    testConnPtr privconn = st->conn->privateData;
+    testStreamFilePtr fs = st->privateData;
+    int ret;
+
+    if (!fs) {
+        testError(st->conn, VIR_ERR_INTERNAL_ERROR,
+                  "%s", _("stream is not open"));
+        return -1;
+    }
+
+    testDriverLock(privconn);
+
+retry:
+    ret = write(fs->fd, bytes, nbytes);
+    if (ret < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            ret = -2;
+        } else if (errno == EINTR) {
+            goto retry;
+        } else {
+            ret = -1;
+            virReportSystemError(st->conn, errno, "%s",
+                                 _("cannot write to stream"));
+            testStreamFileClose(st, 1);
+        }
+    }
+
+    testDriverUnlock(privconn);
+    return ret;
+}
+
+static int
+testStreamFileRead(virStreamPtr st,
+                   char *bytes,
+                   size_t nbytes)
+{
+    testConnPtr privconn = st->conn->privateData;
+    testStreamFilePtr fs = st->privateData;
+    int ret;
+
+    if (!fs) {
+        testError(st->conn, VIR_ERR_INTERNAL_ERROR,
+                  "%s", _("stream is not open"));
+        return -1;
+    }
+
+    testDriverLock(privconn);
+
+retry:
+    ret = read(fs->fd, bytes, nbytes);
+    if (ret < 0) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            ret = -2;
+        } else if (errno == EINTR) {
+            goto retry;
+        } else {
+            ret = -1;
+            virReportSystemError(st->conn, errno, "%s",
+                                 _("cannot read from stream"));
+            testStreamFileClose(st, 1);
+        }
+    }
+
+    testDriverUnlock(privconn);
+    return ret;
+}
+
+static void
+testStreamFileEvent(int watch ATTRIBUTE_UNUSED,
+                    int fd ATTRIBUTE_UNUSED,
+                    int events, void *opaque) {
+    virStreamPtr stream = opaque;
+    testConnPtr privconn = stream->conn->privateData;
+    testStreamFilePtr fs = stream->privateData;
+    virStreamEventCallback cb;
+    void *cbopaque;
+    virFreeCallback ff;
+
+    testDriverLock(privconn);
+
+    if (!fs || !fs->cb) {
+        testDriverUnlock(privconn);
+        return;
+    }
+
+    cb = fs->cb;
+    cbopaque = fs->opaque;
+    ff = fs->ff;
+    fs->dispatching = 1;
+    testDriverUnlock(privconn);
+
+    cb(stream, events, cbopaque);
+
+    testDriverLock(privconn);
+    if (stream->privateData == NULL) {
+        if (ff)
+            (ff)(cbopaque);
+    } else {
+        fs->dispatching = 0;
+        if (fs->cbRemoved && ff)
+            (ff)(cbopaque);
+    }
+    testDriverUnlock(privconn);
+}
+
+static int
+testStreamFileAddCallback(virStreamPtr stream,
+                          int events,
+                          virStreamEventCallback cb,
+                          void *opaque,
+                          virFreeCallback ff)
+{
+    testConnPtr privconn = stream->conn->privateData;
+    testStreamFilePtr fs = stream->privateData;
+    int ret = -1;
+
+    if (!fs) {
+        testError(stream->conn, VIR_ERR_INTERNAL_ERROR,
+                  "%s", _("stream is not open"));
+        return -1;
+    }
+
+    testDriverLock(privconn);
+
+    if (fs->watch != 0) {
+        testError(stream->conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                  _("stream already has a callback registered"));
+        goto cleanup;
+    }
+
+    if ((fs->watch = virEventAddHandle(fs->fd, events,
+                                       testStreamFileEvent,
+                                       stream,
+                                       NULL)) < 0) {
+        testError(stream->conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                  _("cannot register file watch on stream"));
+        goto cleanup;
+    }
+
+    fs->cbRemoved = 0;
+    fs->cb = cb;
+    fs->opaque = opaque;
+    fs->ff = ff;
+    virStreamRef(stream);
+
+    ret = 0;
+
+cleanup:
+    testDriverUnlock(privconn);
+    return ret;
+}
+
+static int
+testStreamFileUpdateCallback(virStreamPtr stream,
+                             int events)
+{
+    testConnPtr privconn = stream->conn->privateData;
+    testStreamFilePtr fs = stream->privateData;
+    int ret = -1;
+
+    if (!fs) {
+        testError(stream->conn, VIR_ERR_INTERNAL_ERROR,
+                  "%s", _("stream is not open"));
+        return -1;
+    }
+
+    testDriverLock(privconn);
+
+    if (fs->watch == 0) {
+        testError(stream->conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                  _("stream does not have a callback registered"));
+        goto cleanup;
+    }
+
+    virEventUpdateHandle(fs->watch, events);
+
+    ret = 0;
+
+cleanup:
+    testDriverUnlock(privconn);
+    return ret;
+}
+
+static int
+testStreamFileRemoveCallback(virStreamPtr stream)
+{
+    testConnPtr privconn = stream->conn->privateData;
+    testStreamFilePtr fs = stream->privateData;
+    int ret = -1;
+
+    if (!fs) {
+        testError(stream->conn, VIR_ERR_INTERNAL_ERROR,
+                  "%s", _("stream is not open"));
+        return -1;
+    }
+
+    testDriverLock(privconn);
+
+    if (fs->watch == 0) {
+        testError(stream->conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                  _("stream does not have a callback registered"));
+        goto cleanup;
+    }
+
+    virEventRemoveHandle(fs->watch);
+    if (fs->dispatching)
+        fs->cbRemoved = 1;
+    else if (fs->ff)
+        (fs->ff)(fs->opaque);
+
+    fs->watch = 0;
+    fs->ff = NULL;
+    fs->cb = NULL;
+    fs->opaque = NULL;
+
+    virStreamFree(stream);
+
+    ret = 0;
+
+cleanup:
+    testDriverUnlock(privconn);
+    return ret;
+}
+
+
+static virStreamDriver streamFileDrv = {
+    .streamRecv = testStreamFileRead,
+    .streamSend = testStreamFileWrite,
+    .streamFinish = testStreamFileFinish,
+    .streamAbort = testStreamFileAbort,
+    .streamAddCallback = testStreamFileAddCallback,
+    .streamUpdateCallback = testStreamFileUpdateCallback,
+    .streamRemoveCallback = testStreamFileRemoveCallback
+};
+
+static int
+testConnectPutFile(virConnectPtr conn,
+                   const char *name,
+                   virStreamPtr st)
+{
+    testConnPtr privconn = conn->privateData;
+    testStreamFilePtr fs = NULL;
+    int ret = -1;
+    testDriverLock(privconn);
+
+    if (!(fs = testStreamFileOpen(st, name, 1)))
+        goto cleanup;
+
+    st->driver = &streamFileDrv;
+    st->privateData = fs;
+    ret = 0;
+
+cleanup:
+    testDriverUnlock(privconn);
+
+    return ret;
+}
+
+
+static int
+testConnectGetFile(virConnectPtr conn,
+                   const char *name,
+                   virStreamPtr st)
+{
+    testConnPtr privconn = conn->privateData;
+    testStreamFilePtr fs = NULL;
+    int ret = -1;
+    testDriverLock(privconn);
+
+    if (!(fs = testStreamFileOpen(st, name, 0)))
+        goto cleanup;
+
+    st->driver = &streamFileDrv;
+    st->privateData = fs;
+    ret = 0;
+
+cleanup:
+    testDriverUnlock(privconn);
+
+    return ret;
+}
+
+
 static virDriver testDriver = {
     VIR_DRV_TEST,
     "Test",
@@ -4242,8 +4717,8 @@ static virDriver testDriver = {
     NULL, /* nodeDeviceDettach */
     NULL, /* nodeDeviceReAttach */
     NULL, /* nodeDeviceReset */
-    NULL,
-    NULL,
+    testConnectPutFile,
+    testConnectGetFile,
 };
 
 static virNetworkDriver testNetworkDriver = {
-- 
1.6.2.5




More information about the libvir-list mailing list