* 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