This provides an implementation of the virDomainOpenConsole
API with the QEMU driver. For the streams code, this reuses
most of the code previously added for the tunnelled migration
streams since it is generic.
* src/qemu/qemu_driver.c: Support virDomainOpenConsole
---
src/qemu/qemu_driver.c | 267 +++++++++++++++++++++++++++++++++++++----------
1 files changed, 210 insertions(+), 57 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 397bd8a..0e0e924 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -10158,7 +10158,7 @@ static void qemuDomainEventQueue(struct qemud_driver *driver,
/* Migration support. */
/* Tunnelled migration stream support */
-struct qemuStreamMigFile {
+struct qemuStreamFD {
int fd;
int watch;
@@ -10169,10 +10169,10 @@ struct qemuStreamMigFile {
virFreeCallback ff;
};
-static int qemuStreamMigRemoveCallback(virStreamPtr stream)
+static int qemuStreamFDRemoveCallback(virStreamPtr stream)
{
struct qemud_driver *driver = stream->conn->privateData;
- struct qemuStreamMigFile *qemust = stream->privateData;
+ struct qemuStreamFD *qemust = stream->privateData;
int ret = -1;
if (!qemust) {
@@ -10206,10 +10206,10 @@ cleanup:
return ret;
}
-static int qemuStreamMigUpdateCallback(virStreamPtr stream, int events)
+static int qemuStreamFDUpdateCallback(virStreamPtr stream, int events)
{
struct qemud_driver *driver = stream->conn->privateData;
- struct qemuStreamMigFile *qemust = stream->privateData;
+ struct qemuStreamFD *qemust = stream->privateData;
int ret = -1;
if (!qemust) {
@@ -10234,14 +10234,14 @@ cleanup:
return ret;
}
-static void qemuStreamMigEvent(int watch ATTRIBUTE_UNUSED,
- int fd ATTRIBUTE_UNUSED,
- int events,
- void *opaque)
+static void qemuStreamFDEvent(int watch ATTRIBUTE_UNUSED,
+ int fd ATTRIBUTE_UNUSED,
+ int events,
+ void *opaque)
{
virStreamPtr stream = opaque;
struct qemud_driver *driver = stream->conn->privateData;
- struct qemuStreamMigFile *qemust = stream->privateData;
+ struct qemuStreamFD *qemust = stream->privateData;
virStreamEventCallback cb;
void *cbopaque;
virFreeCallback ff;
@@ -10268,14 +10268,14 @@ static void qemuStreamMigEvent(int watch ATTRIBUTE_UNUSED,
}
static int
-qemuStreamMigAddCallback(virStreamPtr st,
- int events,
- virStreamEventCallback cb,
- void *opaque,
- virFreeCallback ff)
+qemuStreamFDAddCallback(virStreamPtr st,
+ int events,
+ virStreamEventCallback cb,
+ void *opaque,
+ virFreeCallback ff)
{
struct qemud_driver *driver = st->conn->privateData;
- struct qemuStreamMigFile *qemust = st->privateData;
+ struct qemuStreamFD *qemust = st->privateData;
int ret = -1;
if (!qemust) {
@@ -10293,7 +10293,7 @@ qemuStreamMigAddCallback(virStreamPtr st,
if ((qemust->watch = virEventAddHandle(qemust->fd,
events,
- qemuStreamMigEvent,
+ qemuStreamFDEvent,
st,
NULL)) < 0) {
qemuReportError(VIR_ERR_INTERNAL_ERROR,
@@ -10314,39 +10314,83 @@ cleanup:
return ret;
}
-static void qemuStreamMigFree(struct qemuStreamMigFile *qemust)
+static void qemuStreamFDFree(struct qemuStreamFD *qemust)
{
if (qemust->fd != -1)
close(qemust->fd);
VIR_FREE(qemust);
}
-static struct qemuStreamMigFile *qemuStreamMigOpen(virStreamPtr st,
- const char *unixfile)
+static struct qemuStreamFD *qemuStreamFDOpen(virStreamPtr st,
+ int fd)
{
- struct qemuStreamMigFile *qemust = NULL;
- struct sockaddr_un sa_qemu;
- int i = 0;
- int timeout = 3;
- int ret;
+ struct qemuStreamFD *qemust = NULL;
if (VIR_ALLOC(qemust) < 0) {
virReportOOMError();
return NULL;
}
- qemust->fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (qemust->fd < 0)
- goto cleanup;
+ if ((st->flags & VIR_STREAM_NONBLOCK) &&
+ virSetNonBlock(fd) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Unable to make stream non-blocking"));
+ VIR_FREE(qemust);
+ return NULL;
+ }
+ qemust->fd = fd;
+
+ return qemust;
+}
+
+
+static struct qemuStreamFD *qemuStreamFDOpenDevice(virStreamPtr st,
+ const char *path)
+{
+ int fd;
+ struct qemuStreamFD *ret;
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ virReportSystemError(errno,
+ _("unable to open %s"), path);
+ return NULL;
+ }
+
+ ret = qemuStreamFDOpen(st, fd);
+ if (!ret)
+ close(fd);
+ return ret;
+}
+
+
+static struct qemuStreamFD *qemuStreamFDOpenUNIX(virStreamPtr st,
+ const char *unixfile)
+{
+ struct sockaddr_un sa_qemu;
+ int i = 0;
+ int timeout = 3;
+ int rv;
+ int fd;
+ struct qemuStreamFD *ret = NULL;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ virReportSystemError(errno, "%s",
+ _("unable to open UNIX socket"));
+ return NULL;
+ }
memset(&sa_qemu, 0, sizeof(sa_qemu));
sa_qemu.sun_family = AF_UNIX;
- if (virStrcpy(sa_qemu.sun_path, unixfile, sizeof(sa_qemu.sun_path)) == NULL)
- goto cleanup;
+ if (virStrcpy(sa_qemu.sun_path, unixfile, sizeof(sa_qemu.sun_path)) == NULL) {
+ close(fd);
+ return NULL;
+ }
do {
- ret = connect(qemust->fd, (struct sockaddr *)&sa_qemu, sizeof(sa_qemu));
- if (ret == 0)
+ rv = connect(fd, (struct sockaddr *)&sa_qemu, sizeof(sa_qemu));
+ if (rv == 0)
break;
if (errno == ENOENT || errno == ECONNREFUSED) {
@@ -10355,31 +10399,28 @@ static struct qemuStreamMigFile *qemuStreamMigOpen(virStreamPtr
st,
continue;
}
- goto cleanup;
+ close(fd);
+ return NULL;
} while ((++i <= timeout*5) && (usleep(.2 * 1000000) <= 0));
- if ((st->flags & VIR_STREAM_NONBLOCK) && virSetNonBlock(qemust->fd)
< 0)
- goto cleanup;
-
- return qemust;
-
-cleanup:
- qemuStreamMigFree(qemust);
- return NULL;
+ ret = qemuStreamFDOpen(st, fd);
+ if (!ret)
+ close(fd);
+ return ret;
}
static int
-qemuStreamMigClose(virStreamPtr st)
+qemuStreamFDClose(virStreamPtr st)
{
struct qemud_driver *driver = st->conn->privateData;
- struct qemuStreamMigFile *qemust = st->privateData;
+ struct qemuStreamFD *qemust = st->privateData;
if (!qemust)
return 0;
qemuDriverLock(driver);
- qemuStreamMigFree(qemust);
+ qemuStreamFDFree(qemust);
st->privateData = NULL;
@@ -10388,10 +10429,10 @@ qemuStreamMigClose(virStreamPtr st)
return 0;
}
-static int qemuStreamMigWrite(virStreamPtr st, const char *bytes, size_t nbytes)
+static int qemuStreamFDWrite(virStreamPtr st, const char *bytes, size_t nbytes)
{
struct qemud_driver *driver = st->conn->privateData;
- struct qemuStreamMigFile *qemust = st->privateData;
+ struct qemuStreamFD *qemust = st->privateData;
int ret;
if (!qemust) {
@@ -10420,13 +10461,48 @@ retry:
return ret;
}
-static virStreamDriver qemuStreamMigDrv = {
- .streamSend = qemuStreamMigWrite,
- .streamFinish = qemuStreamMigClose,
- .streamAbort = qemuStreamMigClose,
- .streamAddCallback = qemuStreamMigAddCallback,
- .streamUpdateCallback = qemuStreamMigUpdateCallback,
- .streamRemoveCallback = qemuStreamMigRemoveCallback
+
+static int qemuStreamFDRead(virStreamPtr st, char *bytes, size_t nbytes)
+{
+ struct qemud_driver *driver = st->conn->privateData;
+ struct qemuStreamFD *qemust = st->privateData;
+ int ret;
+
+ if (!qemust) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("stream is not open"));
+ return -1;
+ }
+
+ qemuDriverLock(driver);
+
+retry:
+ ret = read(qemust->fd, bytes, nbytes);
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ ret = -2;
+ } else if (errno == EINTR) {
+ goto retry;
+ } else {
+ ret = -1;
+ virReportSystemError(errno, "%s",
+ _("cannot write to stream"));
+ }
+ }
+
+ qemuDriverUnlock(driver);
+ return ret;
+}
+
+
+static virStreamDriver qemuStreamFDDrv = {
+ .streamSend = qemuStreamFDWrite,
+ .streamRecv = qemuStreamFDRead,
+ .streamFinish = qemuStreamFDClose,
+ .streamAbort = qemuStreamFDClose,
+ .streamAddCallback = qemuStreamFDAddCallback,
+ .streamUpdateCallback = qemuStreamFDUpdateCallback,
+ .streamRemoveCallback = qemuStreamFDRemoveCallback
};
/* Prepare is the first step, and it runs on the destination host.
@@ -10451,7 +10527,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
int internalret;
char *unixfile = NULL;
unsigned long long qemuCmdFlags;
- struct qemuStreamMigFile *qemust = NULL;
+ struct qemuStreamFD *qemust = NULL;
qemuDomainObjPrivatePtr priv = NULL;
struct timeval now;
@@ -10557,7 +10633,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
goto endjob;
}
- qemust = qemuStreamMigOpen(st, unixfile);
+ qemust = qemuStreamFDOpenUNIX(st, unixfile);
if (qemust == NULL) {
qemudShutdownVMDaemon(driver, vm, 0);
if (!vm->persistent) {
@@ -10571,7 +10647,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
goto endjob;
}
- st->driver = &qemuStreamMigDrv;
+ st->driver = &qemuStreamFDDrv;
st->privateData = qemust;
event = virDomainEventNewFromObj(vm,
@@ -12646,6 +12722,83 @@ cleanup:
return ret;
}
+
+static int
+qemuDomainOpenConsole(virDomainPtr dom,
+ const char *devname,
+ virStreamPtr st,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ struct qemud_driver *driver = dom->conn->privateData;
+ virDomainObjPtr vm = NULL;
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ int ret = -1;
+ int i;
+ virDomainChrDefPtr chr = NULL;
+ struct qemuStreamFD *qemust;
+
+ qemuDriverLock(driver);
+ virUUIDFormat(dom->uuid, uuidstr);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ if (!vm) {
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ goto cleanup;
+ }
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto cleanup;
+ }
+
+ if (devname) {
+ if (vm->def->console &&
+ STREQ(devname, vm->def->console->info.alias))
+ chr = vm->def->console;
+ for (i = 0 ; !chr && i < vm->def->nserials ; i++) {
+ if (STREQ(devname, vm->def->serials[i]->info.alias))
+ chr = vm->def->serials[i];
+ }
+ for (i = 0 ; !chr && i < vm->def->nparallels ; i++) {
+ if (STREQ(devname, vm->def->parallels[i]->info.alias))
+ chr = vm->def->parallels[i];
+ }
+ } else {
+ if (vm->def->console)
+ chr = vm->def->console;
+ else if (vm->def->nserials)
+ chr = vm->def->serials[0];
+ }
+
+ if (!chr) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot find character device %s"), devname);
+ goto cleanup;
+ }
+
+ if (chr->type != VIR_DOMAIN_CHR_TYPE_PTY) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("character device %s is not using a PTY"), devname);
+ goto cleanup;
+ }
+
+ qemust = qemuStreamFDOpenDevice(st, chr->data.file.path);
+ if (qemust == NULL)
+ goto cleanup;
+
+ st->driver = &qemuStreamFDDrv;
+ st->privateData = qemust;
+
+ ret = 0;
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
+ return ret;
+}
+
+
static virDriver qemuDriver = {
VIR_DRV_QEMU,
"QEMU",
@@ -12746,7 +12899,7 @@ static virDriver qemuDriver = {
qemuDomainRevertToSnapshot, /* domainRevertToSnapshot */
qemuDomainSnapshotDelete, /* domainSnapshotDelete */
qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */
- NULL, /* domainOpenConsole */
+ qemuDomainOpenConsole, /* domainOpenConsole */
};
--
1.7.2.1