* 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 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list